<?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/case/</link>
<atom:link href="http://plaza-r.co.jp/case/rss2.xml" rel="self" type="application/rss+xml" />
<language>ja</language>
<copyright>Copyright (C) 2026 リフォーム施工事例 All rights reserved.</copyright>
<lastBuildDate>Mon, 17 Aug 2026 17:37:36 +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/case/ownedhouse/reformcase-0333.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-65bc4aba-c352-5b00-a997-41b34b214b04" data-caption="施工前｜エレガントなインテリアが空間を狭く感じさせていました。"
       href="/archives/002/202603/large-7d5b97402692eaa6c816dbcec33b9bcd.jpg">
      <img src="/archives/002/202603/large-7d5b97402692eaa6c816dbcec33b9bcd.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="900" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">エレガントなインテリアが空間を狭く感じさせていました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-65bc4aba-c352-5b00-a997-41b34b214b04" data-caption="施工後｜壁を取り払い、キッチン・和室・リビングの3部屋を1の空間にすることで、明るく開放感のある、広々としたLDKになりました。"
       href="/archives/002/202603/large-3adc71f0ed55e1735c3d5f58b4915c05.jpg">
      <img src="/archives/002/202603/large-3adc71f0ed55e1735c3d5f58b4915c05.jpg" alt="施工後" class="beforeAfter_img" width="1200" height="800" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">壁を取り払い、キッチン・和室・リビングの3部屋を1の空間にすることで、明るく開放感のある、広々としたLDKになりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-df66e5a7-43e8-5b2c-8d84-0c9420becf76" data-caption="施工前｜ゴシック調のカーテンが景観を遮っており、海が見えにくい構造でした。"
       href="/archives/002/202603/large-23478d08a9b57fb93be709d235d2f3c6.jpg">
      <img src="/archives/002/202603/large-23478d08a9b57fb93be709d235d2f3c6.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="900" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">ゴシック調のカーテンが景観を遮っており、海が見えにくい構造でした。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-df66e5a7-43e8-5b2c-8d84-0c9420becf76" data-caption="施工後｜奥様憧れの海が一望できるLDK空間。バーチカルブラインドで景観をさえぎらず楽しめるように。"
       href="/archives/002/202603/large-33f0514f1c130ac2542bca4bc27d60fc.jpg">
      <img src="/archives/002/202603/large-33f0514f1c130ac2542bca4bc27d60fc.jpg" alt="施工後" class="beforeAfter_img" width="1200" height="800" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">奥様憧れの海が一望できるLDK空間。バーチカルブラインドで景観をさえぎらず楽しめるように。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-2441c3bc-0933-58dc-ab5f-ac8c95ee4ca3" data-caption="施工前｜広さはあるが暗い印象だったリビング。"
       href="/archives/002/202603/large-3f0545b901f91bc371606f3c3f5a1942.jpg">
      <img src="/archives/002/202603/large-3f0545b901f91bc371606f3c3f5a1942.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="900" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">広さはあるが暗い印象だったリビング。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-2441c3bc-0933-58dc-ab5f-ac8c95ee4ca3" data-caption="施工後｜デザイナーの奥様のために、リビングにリモートスペースを設置。仕事をしながらも海を眺めることができるようになりました。"
       href="/archives/002/202603/large-d428c291b93abc56dde67ea1402201c3.jpg">
      <img src="/archives/002/202603/large-d428c291b93abc56dde67ea1402201c3.jpg" alt="施工後" class="beforeAfter_img" width="1200" height="800" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">デザイナーの奥様のために、リビングにリモートスペースを設置。仕事をしながらも海を眺めることができるようになりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-3c4332c3-6730-58cd-86ca-0a42e05e7590" data-caption="施工前｜小上がりの和室が空間に圧迫感を与えており、活かしきれていない印象でした。"
       href="/archives/002/202603/large-1b12b5c0ab141b734521aff75b78d86b.jpg">
      <img src="/archives/002/202603/large-1b12b5c0ab141b734521aff75b78d86b.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="900" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">小上がりの和室が空間に圧迫感を与えており、活かしきれていない印象でした。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-3c4332c3-6730-58cd-86ca-0a42e05e7590" data-caption="施工後｜床や照明など、細部までこだわった小上がりの和室。LDKのイメージとも合うように仕上げました。リビングに不足していた分、和室の床の間に収納スペースを設けました。"
       href="/archives/002/202603/large-db8dba1a71af87cd7af88cceb61713a7.jpg">
      <img src="/archives/002/202603/large-db8dba1a71af87cd7af88cceb61713a7.jpg" alt="施工後" class="beforeAfter_img" width="1200" height="800" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">床や照明など、細部までこだわった小上がりの和室。LDKのイメージとも合うように仕上げました。リビングに不足していた分、和室の床の間に収納スペースを設けました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-72340e0e-ccdc-52cf-b286-780ea6667e78" data-caption="施工前｜独立型のキッチン。料理中は海を眺めることはできませんでした。"
       href="/archives/002/202603/large-e8fbacf309c75e72f259f03c84111368.jpg">
      <img src="/archives/002/202603/large-e8fbacf309c75e72f259f03c84111368.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="900" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">独立型のキッチン。料理中は海を眺めることはできませんでした。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-72340e0e-ccdc-52cf-b286-780ea6667e78" data-caption="施工後｜対面式へ変更しました。キッチンからは部屋全体と海を見渡すことができます。"
       href="/archives/002/202603/large-5aea3f81440bc41deacf187ac115a55c.jpg">
      <img src="/archives/002/202603/large-5aea3f81440bc41deacf187ac115a55c.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="800" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">対面式へ変更しました。キッチンからは部屋全体と海を見渡すことができます。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-898932ee-e3b3-5b17-ab8c-93cd09b752e6" data-caption="施工前｜窓もなく、採光が取れない閉鎖的な空間でした。"
       href="/archives/002/202603/large-4e85fb494463144ed1dbf8ae7a088b50.jpg">
      <img src="/archives/002/202603/large-4e85fb494463144ed1dbf8ae7a088b50.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">窓もなく、採光が取れない閉鎖的な空間でした。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-898932ee-e3b3-5b17-ab8c-93cd09b752e6" data-caption="施工後｜LDKのイメージに合わせて、キッチンの家具や家電はブラックで統一。一体感をもたせました。パントリー内の照明を人感センサーにするなど、使い勝手にもこだわりました。"
       href="/archives/002/202603/large-7b5d86b89dfb3c3b57b4e5303e36d4fe.jpg">
      <img src="/archives/002/202603/large-7b5d86b89dfb3c3b57b4e5303e36d4fe.jpg" alt="施工後" class="beforeAfter_img" width="1200" height="800" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">LDKのイメージに合わせて、キッチンの家具や家電はブラックで統一。一体感をもたせました。パントリー内の照明を人感センサーにするなど、使い勝手にもこだわりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-ad939e38-dc24-536e-b3fc-a185fc6ab9d2" data-caption="施工前｜洗面化粧台も好みのデザインではありませんでした。"
       href="/archives/002/202603/large-44850f5844c1f9df64c5615c9e604c6f.jpg">
      <img src="/archives/002/202603/large-44850f5844c1f9df64c5615c9e604c6f.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="900" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">洗面化粧台も好みのデザインではありませんでした。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-ad939e38-dc24-536e-b3fc-a185fc6ab9d2" data-caption="施工後｜モノトーンでまとめ海外のような洗面空間へ。タオルや小物類は収納棚へ入れているため、モダンでホテルライクな空間を維持できます。"
       href="/archives/002/202603/large-57a7a835248eaf5f14bd3ddfac6f0c09.jpg">
      <img src="/archives/002/202603/large-57a7a835248eaf5f14bd3ddfac6f0c09.jpg" alt="施工後" class="beforeAfter_img" width="1200" height="800" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">モノトーンでまとめ海外のような洗面空間へ。タオルや小物類は収納棚へ入れているため、モダンでホテルライクな空間を維持できます。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-1de784f9-b975-54df-bb07-acefef8ce3c9" data-caption="施工前｜大きな造り付けの手洗い器が空間を圧迫していました。"
       href="/archives/002/202603/large-1d138754348e2a5cae54313aa5981d58.jpg">
      <img src="/archives/002/202603/large-1d138754348e2a5cae54313aa5981d58.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="900" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">大きな造り付けの手洗い器が空間を圧迫していました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-1de784f9-b975-54df-bb07-acefef8ce3c9" data-caption="施工後｜すっきりとしたデザインの手洗い器がトイレを広く見せる、シンプルでスタイリッシュな空間になりました。"
       href="/archives/002/202603/large-82e5800c9faf07e2a65a9fbf6d7f82f8.jpg">
      <img src="/archives/002/202603/large-82e5800c9faf07e2a65a9fbf6d7f82f8.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="799" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">すっきりとしたデザインの手洗い器がトイレを広く見せる、シンプルでスタイリッシュな空間になりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-19936dd9-dcf3-5138-b6cc-2baa7ca06512" data-caption="施工前｜玄関収納は使いづらい場所にあり、ホールを狭く感じさせていました。"
       href="/archives/002/202603/large-7eb237471e84e3db969b7ba28e4f64bf.jpg">
      <img src="/archives/002/202603/large-7eb237471e84e3db969b7ba28e4f64bf.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="900" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">玄関収納は使いづらい場所にあり、ホールを狭く感じさせていました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-19936dd9-dcf3-5138-b6cc-2baa7ca06512" data-caption="施工後｜玄関収納は使いやすい位置へ変更し、ホールには趣味のバイクを壁掛けできるようにしました。"
       href="/archives/002/202603/large-ce9f497b90f49ac9282d402fa19dfe36.jpg">
      <img src="/archives/002/202603/large-ce9f497b90f49ac9282d402fa19dfe36.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="800" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">玄関収納は使いやすい位置へ変更し、ホールには趣味のバイクを壁掛けできるようにしました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-785d3709-8e19-5691-a69e-925458bf3fc4" data-caption="施工前｜"
       href="/archives/002/202603/large-7351a085da9d805e184b229f71e79763.jpg">
      <img src="/archives/002/202603/large-7351a085da9d805e184b229f71e79763.jpg" alt="施工前" class="beforeAfter_img" width="1200" height="1155" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-785d3709-8e19-5691-a69e-925458bf3fc4" data-caption="施工後｜"
       href="/archives/002/202603/large-8befeae9bc9bd319dabaaf40fd79c581.jpg">
      <img src="/archives/002/202603/large-8befeae9bc9bd319dabaaf40fd79c581.jpg" alt="施工後" class="beforeAfter_img" width="1200" height="1176" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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











]]></description>
<category>中古住宅購入後</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/ownedhouse/reformcase-0333.html</guid>
<pubDate>Mon, 30 Mar 2026 15:35:09 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>これからもお手入れしやすいトイレへ</title>
<link>http://plaza-r.co.jp/case/toilet/reformcase-0332.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-08a5b67a-3009-5cda-8c60-bd17e1ddba7f" data-caption="施工前｜長年お手入れのしにくさに悩まれていたウォシュレット。"
       href="/archives/002/202603/large-5fa1ea4e490b85c05f1a9d6e475fa098.jpg">
      <img src="/archives/002/202603/large-5fa1ea4e490b85c05f1a9d6e475fa098.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">長年お手入れのしにくさに悩まれていたウォシュレット。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-08a5b67a-3009-5cda-8c60-bd17e1ddba7f" data-caption="施工後｜継ぎ目やフチのないデザインのウォシュレットを選定したことで、今後もお手入れのしやすいトイレになりました。"
       href="/archives/002/202603/large-96661e313b1aa09dd8ca4ceba9c7d1e0.jpg">
      <img src="/archives/002/202603/large-96661e313b1aa09dd8ca4ceba9c7d1e0.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">継ぎ目やフチのないデザインのウォシュレットを選定したことで、今後もお手入れのしやすいトイレになりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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











]]></description>
<category>トイレ</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/toilet/reformcase-0332.html</guid>
<pubDate>Mon, 23 Mar 2026 12:27:46 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>トイレを変えただけなのに...驚きの空間変身リフォーム</title>
<link>http://plaza-r.co.jp/case/toilet/reformcase-0331.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-4d16280c-6682-5754-8440-a702142f71bd" data-caption="施工前｜サイドのカウンターはオープンになっており、小物置場として使用していました。"
       href="/archives/002/202603/large-950cb47dd1019cce28f98fde09052d08.jpg">
      <img src="/archives/002/202603/large-950cb47dd1019cce28f98fde09052d08.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="480" height="640" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">サイドのカウンターはオープンになっており、小物置場として使用していました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-4d16280c-6682-5754-8440-a702142f71bd" data-caption="施工後｜タンクと収納が一体になったトイレを採用することで、まるで新築時からの造り付けのような一体感のある空間になりました。ペーパーホルダーやタオル掛けも合わせ、高級感のある雰囲気です。"
       href="/archives/002/202603/large-67fb6e7ba1f352af2b567c725fd8d0bf.jpg">
      <img src="/archives/002/202603/large-67fb6e7ba1f352af2b567c725fd8d0bf.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="750" height="1000" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">タンクと収納が一体になったトイレを採用することで、まるで新築時からの造り付けのような一体感のある空間になりました。ペーパーホルダーやタオル掛けも合わせ、高級感のある雰囲気です。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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











]]></description>
<category>トイレ</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/toilet/reformcase-0331.html</guid>
<pubDate>Mon, 16 Mar 2026 13:40:31 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>ゆったりとくつろげる理想の空間へ</title>
<link>http://plaza-r.co.jp/case/toilet/reformcase-0330.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-35816300-157c-527e-a572-c796387a15e6" data-caption="施工前｜1階トイレ。長年使用した便器は老朽化しており、クロスの汚れもありました。"
       href="/archives/002/202603/large-56ff7d2b834e89c20c0ff2006c99ef49.jpg">
      <img src="/archives/002/202603/large-56ff7d2b834e89c20c0ff2006c99ef49.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">1階トイレ。長年使用した便器は老朽化しており、クロスの汚れもありました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-35816300-157c-527e-a572-c796387a15e6" data-caption="施工後｜広さは変えていませんが、タンクレストイレにしたことで全体的にすっきりとした空間に。手洗い器を新設したことで利便性が上がりました。"
       href="/archives/002/202603/large-8d86296eed086ee69a16eaa10c585338.jpg">
      <img src="/archives/002/202603/large-8d86296eed086ee69a16eaa10c585338.jpg" alt="施工後" class="beforeAfter_img" width="1200" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">広さは変えていませんが、タンクレストイレにしたことで全体的にすっきりとした空間に。手洗い器を新設したことで利便性が上がりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-1cb44c59-025c-5d0b-8c03-db94fcb8c22a" data-caption="施工前｜給水ホースが邪魔になり、掃除がしにくくホコリが溜まりやすくなっていました。"
       href="/archives/002/202603/large-8ece4c477ac6676be1a5f484b68ac700.jpg">
      <img src="/archives/002/202603/large-8ece4c477ac6676be1a5f484b68ac700.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">給水ホースが邪魔になり、掃除がしにくくホコリが溜まりやすくなっていました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-1cb44c59-025c-5d0b-8c03-db94fcb8c22a" data-caption="施工後｜給水管隠ぺいタイプの便器を取り付けたことで長年悩んでいたお掃除がしやすくなりました。コンセントの新設もポイント。冷暖房機を置くことができます。"
       href="/archives/002/202603/large-bc847a594bc3ebe16124fc12c5fab0fb.jpg">
      <img src="/archives/002/202603/large-bc847a594bc3ebe16124fc12c5fab0fb.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">給水管隠ぺいタイプの便器を取り付けたことで長年悩んでいたお掃除がしやすくなりました。コンセントの新設もポイント。冷暖房機を置くことができます。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-19484ebe-16ba-5aa9-b17b-00c25301abe8" data-caption="施工前｜2階トイレ。老朽化に加え、配管ボックスはむき出しで見た目も悪く、掃除も大変でした。"
       href="/archives/002/202603/large-95b309805c3ee875d41c8d0acb1a9aa8.jpg">
      <img src="/archives/002/202603/large-95b309805c3ee875d41c8d0acb1a9aa8.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">2階トイレ。老朽化に加え、配管ボックスはむき出しで見た目も悪く、掃除も大変でした。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-19484ebe-16ba-5aa9-b17b-00c25301abe8" data-caption="施工後｜必要なものは全て背面収納に収まり、清潔感のある空間となりました。"
       href="/archives/002/202603/large-0b2e1098ddd4de59fe6cd01700bffb2b.jpg">
      <img src="/archives/002/202603/large-0b2e1098ddd4de59fe6cd01700bffb2b.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="750" height="1000" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">必要なものは全て背面収納に収まり、清潔感のある空間となりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-dccb6c3a-51db-5118-aa05-9fad66b8468d" data-caption="施工前｜"
       href="/archives/002/202603/large-71aacaa95cdedcb2ed71648514b46a5b.jpg">
      <img src="/archives/002/202603/large-71aacaa95cdedcb2ed71648514b46a5b.jpg" alt="1F施工前" class="beforeAfter_img" width="400" height="300" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-dccb6c3a-51db-5118-aa05-9fad66b8468d" data-caption="施工後｜"
       href="/archives/002/202603/large-c2abe98c8d2361c994a2d2c0a3c7800d.jpg">
      <img src="/archives/002/202603/large-c2abe98c8d2361c994a2d2c0a3c7800d.jpg" alt="1F施工後" class="beforeAfter_img" width="400" height="300" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-ce441e49-cbc3-5f93-a79e-829db166aa2f" data-caption="施工前｜"
       href="/archives/002/202603/large-6e7f7989314a56e670d96a17588eae62.jpg">
      <img src="/archives/002/202603/large-6e7f7989314a56e670d96a17588eae62.jpg" alt="2F施工前" class="beforeAfter_img" width="400" height="300" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-ce441e49-cbc3-5f93-a79e-829db166aa2f" data-caption="施工後｜"
       href="/archives/002/202603/large-cb39e4702bdbd8c628affd5857dbbeba.jpg">
      <img src="/archives/002/202603/large-cb39e4702bdbd8c628affd5857dbbeba.jpg" alt="2F施工後" class="beforeAfter_img" width="400" height="300" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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











]]></description>
<category>トイレ</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/toilet/reformcase-0330.html</guid>
<pubDate>Mon, 09 Mar 2026 11:38:44 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>変化を壁紙で</title>
<link>http://plaza-r.co.jp/case/toilet/reformcase-0329.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-b60dd8c4-8b14-546d-a53c-bca54b39981a" data-caption="施工前｜経年劣化により水漏れがしており、汚れも気になっていました。"
       href="/archives/002/202603/large-9053dd9e991729808e68107cb3d517dc.jpg">
      <img src="/archives/002/202603/large-9053dd9e991729808e68107cb3d517dc.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">経年劣化により水漏れがしており、汚れも気になっていました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-b60dd8c4-8b14-546d-a53c-bca54b39981a" data-caption="施工後｜ピンクのアクセントクロスで気分一新!明るくてかわいい空間になりました。水撥ねが気になっていた手洗い器も無くしたのでスッキリしました。"
       href="/archives/002/202603/large-ca12d93a7e08f60903f4341c69834bd5.jpg">
      <img src="/archives/002/202603/large-ca12d93a7e08f60903f4341c69834bd5.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">ピンクのアクセントクロスで気分一新!明るくてかわいい空間になりました。水撥ねが気になっていた手洗い器も無くしたのでスッキリしました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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











]]></description>
<category>トイレ</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/toilet/reformcase-0329.html</guid>
<pubDate>Mon, 02 Mar 2026 13:37:08 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>タンクレストイレですっきり広々</title>
<link>http://plaza-r.co.jp/case/toilet/reformcase-0328.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-06952f1c-bdec-5fb2-93c8-01329d3ee824" data-caption="施工前｜深い色合いのキャビネットが空間を窮屈に見せている印象でした。"
       href="/archives/002/202602/large-f21406e8f05ba2512d1affd3013d0b10.jpg">
      <img src="/archives/002/202602/large-f21406e8f05ba2512d1affd3013d0b10.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">深い色合いのキャビネットが空間を窮屈に見せている印象でした。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-06952f1c-bdec-5fb2-93c8-01329d3ee824" data-caption="施工後｜キャビネットを撤去し、シンプルなデザインのタンクレストイレに取り替えました。空間が明るく広く感じられるようになりました。"
       href="/archives/002/202602/large-eaf2f0a71939ada8efbfd68c9601f46e.jpg">
      <img src="/archives/002/202602/large-eaf2f0a71939ada8efbfd68c9601f46e.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="899" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">キャビネットを撤去し、シンプルなデザインのタンクレストイレに取り替えました。空間が明るく広く感じられるようになりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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











]]></description>
<category>トイレ</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/toilet/reformcase-0328.html</guid>
<pubDate>Tue, 24 Feb 2026 13:49:34 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>お母様へのプレゼント ~トイレリモデル~</title>
<link>http://plaza-r.co.jp/case/toilet/reformcase-0327.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-bb6fd425-a330-5856-9f02-0301e86fe17a" data-caption="施工前｜便座の機能部からオイルが漏れ、床にも落ちてしまい、汚れが取れなくなっていました。"
       href="/archives/002/202602/large-48a6d76a98bc6ab2057be3b8037040d1.jpg">
      <img src="/archives/002/202602/large-48a6d76a98bc6ab2057be3b8037040d1.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">便座の機能部からオイルが漏れ、床にも落ちてしまい、汚れが取れなくなっていました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-bb6fd425-a330-5856-9f02-0301e86fe17a" data-caption="施工後｜以前は便器や床も可愛らしい雰囲気のものを使用されていましが、今回はどちらもホワイトを選び、すっきりとモダンな印象の空間に仕上がりました。"
       href="/archives/002/202602/large-391e80cd51af51cc7f369baf3d933e7d.jpg">
      <img src="/archives/002/202602/large-391e80cd51af51cc7f369baf3d933e7d.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="750" height="1000" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">以前は便器や床も可愛らしい雰囲気のものを使用されていましが、今回はどちらもホワイトを選び、すっきりとモダンな印象の空間に仕上がりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

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

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

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

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

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

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

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

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

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
























































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

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

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

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

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

<!--
  余白ユニット。

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

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

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

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

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


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

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

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

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

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

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

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

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


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

    <hr class="hr01" />

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




<!--
  目次ユニット。

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

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

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

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

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>トイレ</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/toilet/reformcase-0327.html</guid>
<pubDate>Mon, 16 Feb 2026 13:20:27 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>気分一新! こだわりのトイレ空間</title>
<link>http://plaza-r.co.jp/case/toilet/reformcase-0326.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-4d6fa3c1-fbe5-5b88-b421-f540071e65a8" data-caption="施工前｜綺麗にお手入れされていましたが、ウォシュレットリモコンの故障など、不具合が出ていました。"
       href="/archives/002/202602/large-f231afba8cbd0067c22f2d2564be2377.jpg">
      <img src="/archives/002/202602/large-f231afba8cbd0067c22f2d2564be2377.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">綺麗にお手入れされていましたが、ウォシュレットリモコンの故障など、不具合が出ていました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-4d6fa3c1-fbe5-5b88-b421-f540071e65a8" data-caption="施工後｜タンクを無くしスッキリとした空間に。内装は以前のトイレ空間の色合いを参考にしました。丸いフォルムのトイレと調和し、柔らかい雰囲気になりました。"
       href="/archives/002/202602/large-e45b19f2821b2eb7c745d76914fdee9b.jpg">
      <img src="/archives/002/202602/large-e45b19f2821b2eb7c745d76914fdee9b.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">タンクを無くしスッキリとした空間に。内装は以前のトイレ空間の色合いを参考にしました。丸いフォルムのトイレと調和し、柔らかい雰囲気になりました。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-b08caeb0-7458-531e-85a2-4562c4d10073" data-caption="施工前｜タンク上の手洗器はボウルが浅く水撥ねが気になりました。"
       href="/archives/002/202602/large-cb34b2d6c3e60bd9334429cc362ca555.jpg">
      <img src="/archives/002/202602/large-cb34b2d6c3e60bd9334429cc362ca555.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">タンク上の手洗器はボウルが浅く水撥ねが気になりました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-b08caeb0-7458-531e-85a2-4562c4d10073" data-caption="施工後｜タンク上の手洗器は付けずすっきりと。スヌーピーのクロスを採用し、印象が大きく変わりました。キャラクター柄でも落ち着いた色合いなので空間にしっかり馴染んでいます。"
       href="/archives/002/202602/large-a0d4fd99e26a6f7335e71ee95d426623.jpg">
      <img src="/archives/002/202602/large-a0d4fd99e26a6f7335e71ee95d426623.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">タンク上の手洗器は付けずすっきりと。スヌーピーのクロスを採用し、印象が大きく変わりました。キャラクター柄でも落ち着いた色合いなので空間にしっかり馴染んでいます。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>トイレ</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/toilet/reformcase-0326.html</guid>
<pubDate>Mon, 09 Feb 2026 10:02:23 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>スタイリッシュなキッチン</title>
<link>http://plaza-r.co.jp/case/kitchen/reformcase-0325.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-054b6647-a463-5a0f-90b5-d0b4a81a35dc" data-caption="施工前｜経年劣化による油汚れや換気扇の音がとても気になっていました。"
       href="/archives/002/202602/large-2cf35515cc1a585f294775db48b88ba6.jpg">
      <img src="/archives/002/202602/large-2cf35515cc1a585f294775db48b88ba6.jpg" alt="施工前" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">経年劣化による油汚れや換気扇の音がとても気になっていました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-054b6647-a463-5a0f-90b5-d0b4a81a35dc" data-caption="施工後｜スッキリとした空間になりました。レンジフードとIHがフラットなのでお手入れも楽々です。グリル皿タイプの両面焼きグリルがついているので、グリル料理がもっと身近になりました。"
       href="/archives/002/202602/large-3a08f7cfdd5d386194732341b3a4a31a.jpg">
      <img src="/archives/002/202602/large-3a08f7cfdd5d386194732341b3a4a31a.jpg" alt="施工後" class="beforeAfter_img beforeAfter_imgPortrait" width="900" height="1200" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <!-- BEGIN_IF [スッキリとした空間になりました。
レンジフードとIHがフラットなのでお手入れも楽々です。グリル皿タイプの両面焼きグリルがついているので、グリル料理がもっと身近になりました。/nem] --><span class="beforeAfter_text">スッキリとした空間になりました。<br />
レンジフードとIHがフラットなのでお手入れも楽々です。グリル皿タイプの両面焼きグリルがついているので、グリル料理がもっと身近になりました。</span>
    </figcaption>
  </figure>
</div>

<!-- END_IF _ -->


<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>キッチン</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/kitchen/reformcase-0325.html</guid>
<pubDate>Mon, 02 Feb 2026 11:34:19 +0900</pubDate>
</item>
<item>
<dc:creator>作業用アカウント</dc:creator>
<title>内窓で暖かなわが家へ</title>
<link>http://plaza-r.co.jp/case/door_window/reformcase-0324.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 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->





<h2 class="caseDetail_baHead beforeAfter_head"><span class="caseDetail_baHeadText">ビフォーアフター</span></h2>
<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-c46beccf-f7b2-574f-b89c-6bb43046f352" data-caption="施工前｜大きな掃き出し窓からは、夏も冬も熱や寒さが入ってくるため、悩まされていました。"
       href="/archives/002/202601/large-73a68525509c49a9667a2fd4ad2ee179.jpg">
      <img src="/archives/002/202601/large-73a68525509c49a9667a2fd4ad2ee179.jpg" alt="施工前" class="beforeAfter_img" width="1000" height="750" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">大きな掃き出し窓からは、夏も冬も熱や寒さが入ってくるため、悩まされていました。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-c46beccf-f7b2-574f-b89c-6bb43046f352" data-caption="施工後｜内窓はYKKap製プラマードUを取り付けました。既存の窓の内側に取り付けるため、工期も1日あれば十分です。"
       href="/archives/002/202601/large-1fe4c94703c6afc0778ce438b3176be0.jpg">
      <img src="/archives/002/202601/large-1fe4c94703c6afc0778ce438b3176be0.jpg" alt="施工後" class="beforeAfter_img" width="1000" height="750" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">内窓はYKKap製プラマードUを取り付けました。既存の窓の内側に取り付けるため、工期も1日あれば十分です。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_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` を足してレターボックス。
-->






<div class="beforeAfter">
  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-0e924f64-0d93-5158-95d6-306e10697619" data-caption="施工前｜防犯面や外から漏れて聞こえてくる騒音が気がかりでした。"
       href="/archives/002/202601/large-f560993e7f7e3f5d4daec97fd6489f18.jpg">
      <img src="/archives/002/202601/large-f560993e7f7e3f5d4daec97fd6489f18.jpg" alt="施工前" class="beforeAfter_img" width="1000" height="750" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工前</span>
      <span class="beforeAfter_text">防犯面や外から漏れて聞こえてくる騒音が気がかりでした。</span>
    </figcaption>
  </figure>

  <span class="beforeAfter_arrow" aria-hidden="true">
    <svg class="beforeAfter_arrowIcon" viewBox="0 0 20 44" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0 L20 22 L0 44" stroke="currentColor" stroke-width="4" vector-effect="non-scaling-stroke" />
    </svg>
  </span>

  <figure class="beforeAfter_item">
    <a class="beforeAfter_link" data-rel="SmartPhoto" data-group="ba-0e924f64-0d93-5158-95d6-306e10697619" data-caption="施工後｜Low-E複層ガラスは、通常のガラスよりも強靭で衝撃に強いため、防犯性能がアップします。"
       href="/archives/002/202601/large-693125db15d37726a38e92b0740a6f99.jpg">
      <img src="/archives/002/202601/large-693125db15d37726a38e92b0740a6f99.jpg" alt="施工後" class="beforeAfter_img" width="1000" height="750" loading="lazy" />
    </a>
    <figcaption class="beforeAfter_caption">
      <span class="beforeAfter_label">施工後</span>
      <span class="beforeAfter_text">Low-E複層ガラスは、通常のガラスよりも強靭で衝撃に強いため、防犯性能がアップします。</span>
    </figcaption>
  </figure>
</div>




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->
























































<!--
  カスタムユニットの**出力**。編集画面は `admin/entry/unit/extend.html`。

  ⚠ ブロック名は `unit#`。編集画面側は `#` 無しの
     タイプ名そのままなので、片方だけ直すと**エラーを出さずに何も出なくなる**。

  ⚠ フィールド名は `` 抜きで書く（保存時に `Field::retouchCustomUnit()` が
     末尾の id を落とすため）。編集画面の `unit_space_class` は
     ここでは ``。

  ⚠ **a-blog cms はカスタムユニットに器を付けない。** 素の HTML がそのまま
     本文に並ぶので、`.column-image` のようなクラスは自分で書く。

  ⚠ **編集設定への登録が別に要る。** テンプレートを置いただけでは
     ユニット追加メニューに出てこない。管理ページ > コンフィグ > 編集設定 で
     「ユニット追加ボタン」にタイプ名を足すこと（`column_add_type` ＋
     `column_def_add__type`）。**編集セットごと**に必要で、
     このプロジェクトは `reform2026`（設定セット 6）と
     `reform2026_case`（同 10・施工事例 bid 2 用）の2つがある。
-->

<!--
  余白ユニット。

    プリセット   <div class="spacer spacer_l" aria-hidden="true"></div>
    クラス指定   <div class="spacer mtSp_s mtPc_xl" aria-hidden="true"></div>

  ⚠ **クラス指定（項目2）に値があればそちらだけを出し、プリセット（項目1）の
     `spacer_*` は付けない。** 両方付けると `.spacer_l` の height と `mt_*` の
     margin が足し算になり、編集者から見て「クラスを書いたのに値が合わない」
     という壊れ方をする。

  ⚠ どちらも空のときは `.spacer` だけが出て高さ 0（間隔ゼロ）になる。
     編集画面は未選択のときに M が checked になるので、通常はここに落ちない。

  ⚠ `` は a-blog cms が HTML エスケープして出すので、
     `"` を入れられても属性からは抜け出せない。`[raw]` を付けないこと。

  ⚠ 中身が無いので読み上げには元から出ないが、`aria-hidden` を明示しておく。
  スタイルは `src/style/front/component/spacer/_spacer.scss` と
  `src/style/front/utility/_spacing.scss`。
-->


<!--
  ボタンユニット。

    <a href="/guide/" class="btn01 btn_sky">リフォームの流れを見る</a>

  ⚠ **クラスは `.btn01` ＋ 色クラスの2枚だけ。** 形・寸法・右端の矢印・中央寄せは
     `.btn01` が持ち（`component/button/_button.scss` が `.btn` / `.btn_unit` /
     `.btn_arrow` の各セレクタに `.btn01` を足してある）、色は併記のクラスが決める。
     **`.btn btn_unit btn_arrow` を並べないこと** —— 並べる前提で組んでいない。

  ⚠ **文言が空のときはボタンごと出さない。** 空の `<a>` を出すと 245x70 の
     色付きの箱だけが公開ページに残る（実測で確認済み）。
     ⚠⚠ **`unit_btn_text:veil` のブロック（BEGIN/END で囲む形）は使えない**（2026-08-22 実測）。
     カスタムユニットの描画は `Custom::render` が親ブロックに変数を add する
     だけで veil ブロックを個別に登録しないため、**値が空でも中身が出てしまう**。
     エラーは出ないので気付けない。**`BEGIN_IF` を使うこと。**
     ✅ **`BEGIN_IF` の入れ子は動く**（同日実測。href の有無との2段で確認）。

  ⚠ **色が空でも壊れない。** `.btn01` は色クラスが付かなかったときの保険として
     ブルーを持っている（`_button.scss` の「色クラスが付かなかったときの保険」）。

  ⚠ **`` は a-blog cms が HTML エスケープして出す**ので、`"` を
     入れられても属性から抜け出せない。`[raw]` を付けないこと。
     🔴 ただし `javascript:` スキームは CSS でもエスケープでも防げない。
     編集できるのは管理者だけなので受け入れているが、**権限を広げるときは
     バリデータ（`unit_btn_url:v#regex`）で `^(/|https?://)` に絞ること**。

  ⚠ 外部リンクの `open_in_new` は付かない。あれは `.textLink[href^='http']` の
     指定で、本文中のテキストリンク専用（`component/entry-body`）。カンプの
     ボタンにもアイコンは無く、右端の矢印は `.btn01` の ::after が出す。

  ✅ **「別タブで開く」の項目は要らない。** 外部ドメインのリンクには
     a-blog cms 同梱の `externalLinks`（`src/js/lib/build-in.js`。`main.js` が
     読んでいる）が **`target="_blank"` と `rel="noopener noreferrer"` を
     実行時に付ける**。自ホストのリンクには付かない。カンプの CMS登録 欄も
     「リンク先・文言・色の3つ」なので、項目は増やさない。
-->


<!--
  区切り線ユニット。編集画面は `admin/entry/unit/extend.html`。

    <hr class="hr01" />

  ⚠ **クラスはここに直書きする。編集画面の `unit_hr_marker` とは繋げないこと。**
     あちらは値を使わない保険の隠しフィールドで、`` を出力に使うと
     「値が消えたときにクラスも消える」という壊れ方の口ができる。
     ✅ **フィールドが1つも無くても公開側は空にならない**（2026-08-27 実測。
     `Common\Helper::loadField()` が `new Field()` を先に作るので 0 件でも空の Field が
     返る）。残している理由と実測の中身は編集画面側のコメントが正。

  ⚠ **`hr01` はカンプ支給のクラス名ではない。** カンプ（「ベース-ユニットパーツ」）は
     区切り線を**クラス無しの `<hr>`** で描いており、支給された16個のクラス一覧にも
     入っていない。`.heading02` / `.ul01` / `.table01` / `.btn01` と同じ連番の規則に
     合わせて 2026-08-27 に決めた名前。**CMS 側に登録する外部の名前と同じ扱い**なので、
     developkits の命名規則（ブロック_要素）に寄せて直さないこと。

  ⚠ **スタイルは要素セレクタ側（`hr`）が持っている**
     （`component/entry-body/_entry-body.scss` の「区切り線」の節。1px の #999 で
     4px 実線 / 4px 空きの破線）。`<hr class="hr01">` は `hr` にも当たるので
     見た目・余白・`clear: both` はそのまま効く。**SCSS 側に `.hr01` を足していない**
     のは、`:where(hr, .column-table)`（0,1,0）と `h2 + :not(…)`（0,1,1）の詳細度の
     釣り合いで「見出しの直後だけ 24px」を出しているため（同ファイルの注記）。
     クラスを足すと詳細度が動いてこの出し分けが壊れる。

  ⚠ **`.entry_body` 側が持つのは `margin-top` だけ**（SP 56 / PC 88px）。下の空きは
     次のユニットの `margin-top` が決める（段落なら SP 28 / PC 44px）。ユニット側では
     どちらも持たない。
-->


<!--
  旧ビフォーアフターユニット。編集画面は `admin/entry/unit/extend.html`。

    <div class="beforeAfter">
      <figure class="beforeAfter_item"> 写真 + 「施工前」 + 説明文 </figure>
      <span class="beforeAfter_arrow"><svg class="beforeAfter_arrowIcon">…</svg></span>
      <figure class="beforeAfter_item"> 写真 + 「施工後」 + 説明文 </figure>
    </div>

  ⚠ **マークアップは `static_html/case-detail.html` の `.beforeAfter` と同一。**
     スタイルは `src/style/front/component/before-after/_before-after.scss`。
     **片方を直したらもう片方も直すこと**（矢印の幅 20px は
     996 = 464 + 24 + 20 + 24 + 464 で写真の幅を決めているので、
     別のものに替えると写真が痩せる）。
  ⚠⚠ **矢印はアイコンフォントではなくインライン SVG**（花田さんが 2026-08-26 に
     カンプから起こしたもの）。カンプの `Vector 52` は**ブランド青・太さ 4px の
     折れ線**で、`arrow_forward_ios` では出せない —— Material のシェブロンは
     高さ÷幅が wght300 で 1.7437 なのに対し、カンプは PC 2.2 / SP 2.27 と
     明らかに細長く、太さも別物。

  ⚠ **写真が2枚そろっていないときは何も出さない。** 2枚を横に並べて
     見比べる形なので、片方だけだと矢印と半分の箱だけが残る。
     編集画面の説明文にも「2枚とも必要」と書いてある。
     ✅ `BEGIN_IF` の入れ子は動く（2026-08-22 実測）。

  ⚠ **見出しは `unit_ba_head` がチェックされたユニットだけが出す。**
     カンプの固定セクション（`<section class="caseDetail_ba">` + 見出し）は
     ユニットからは作れない（自分より後ろの兄弟ユニットを包めない）ので、
     **見出しだけを `.beforeAfter` の直前に置いて、余白を CSS で作っている**。
     ⚠ **クラスを2枚付けているのは意図的。**
       `.caseDetail_baHead`  … 見た目（16/20px 太字 ＋ 右へ伸びるブランド青 4px の罫線）。
                               定義は `pages/_case-detail.scss`。受賞版の「平面図」の
                               節も同じクラスを使っているので**複製せず借りている**
       `.beforeAfter_head`   … `.entry_body` の中での上下の余白だけを持つ。
                               定義は `component/before-after/_before-after.scss`
     🚩 `.caseDetail_baHead` はページ固有の名前なので、本来はコンポーネントへ
        引き上げたい。**このユニットは編集セット10（施工事例 bid 2）にしか
        登録していない**ので実害は無いが、他ブログで使うことになったら移すこと。

  ⚠ **`alt` は `@alt` の値をそのまま出す**（2026-08-26 に `alt=""` 固定から変更）。
     移行元の画像ユニットに 3,091件 の代替テキストが入っており、
     **捨てずに引き継ぐ**ため。空なら `alt=""` になり、直後の `<figcaption>`
     （「施工前」＋説明文）が `<figure>` の読み上げ名になるので情報は落ちない。

  ⚠ **`width` / `height` は `@x` / `@y` があるときだけ出す。**
     `.beforeAfter_img` は `aspect-ratio: 3/2` で、既定は `object-fit: cover`。
     **縦位置の写真のときだけ `beforeAfter_imgPortrait` を足してレターボックス**
     （`object-fit: contain` ＋ 地色 #f3f3f3）にする。判定は `@y > @x`。
     ⚠ **この修飾は花田さんがカンプ（`275:*`）から起こしたもの。** 器
     （`image 45`）は 464x309・#F3F3F3 で、中の写真は実寸比のまま中央に置かれる。
     `width` / `height` は表示比を決めないが、読み込み中のガタつき（CLS）を抑える。
     ⚠ 移行元の画像（`@path` に入る素のファイル）は 200〜308px しかなく、
     `archives/` に 400〜1200px の `large-` が別にある。**移行 SQL では
     `large-` のほうを `@path` に入れる**（カンプの表示幅が PC 464px のため）。

  ⚠ **拡大表示は smartphoto**（2026-08-26 追加。設計①の指示）。テーマの
     `src/js/main.js` が `smartPhoto(context)` を呼んでおり、拾うセレクタは
     `a[data-rel^=SmartPhoto], .js-smartphoto`（`src/js/lib/build-in.js:100`）。
     `data-group` を施工前・施工後で揃えてあるので、拡大したまま2枚を行き来できる。
     ⚠ **リンク先は `@largePath` があればそちら、無ければ `@path`。**
       移行データは `@largePath` を持たない（`@path` に旧サイトの `large-` を
       そのまま入れる）ので `@path` に落ちる。**それでも意味はある** ——
       器は 464px なのに元は最大 1200px あるので、拡大すると実際に大きく見える。
     ✅ **画像ユニット（補足写真）も同じ smartphoto に乗っている**（2026-08-26 実測）。
       `unit.html` が `<a href=""  data-group="">` を出し、
       `` の実体は `entry_body_image_viewer`。**既定値が
       `data-rel="SmartPhoto"`**（`private/config.system.default.yaml:483`）なので
       そのまま繋がる。施工事例・イベントの実ページで `prettyPhoto` 0件 /
       `data-rel="SmartPhoto"` を確認済み。
       ⚠ `acms_config` には旧サイト由来の `rel="prettyPhoto[]"` が
       bid 1 / 2 に残っているが**使われていない**（V2 モジュールはこの行を読まない）。
       紛らわしいので消してよいが、消さなくても動作は変わらない。

  ⚠ **説明文は `[nl2br]` を通す。** textarea なので改行が入りうる。
     `` は既定で HTML エスケープされるため `[raw]` は付けない。

  ⚠ **`.entry_body` 内での上余白の出どころ。**
       見出し無し … `pages/_case-detail.scss` の `.beforeAfter `
                    （あのルールはページ名でネストされていないので全体に掛かる）
       見出しあり … `.entry_body .beforeAfter_head` が 48/72px、続く
                    `.beforeAfter` は 24/48px に抑える
                    （`component/before-after/_before-after.scss`）
     ⚠ `_entry-body.scss` のユニット間余白リスト（`:where(…)` 28/44px）には
       `.beforeAfter` を**入れていない**。上の2つで足りているため。
-->
<!--
  ══════════════════════════════════════════════════════════════
  ビフォーアフター（新／メディア管理版）　`unit#custom_unit_ba`
  ══════════════════════════════════════════════════════════════

  **マークアップは旧版（`unit#custom_unit_ba_old`）と1文字も変えていない。**
  スタイルは `component/before-after/_before-after.scss` を共有する。
  **片方を直したらもう片方も直すこと。**

  ■ 旧版との差は写真の取り出し方だけ（`Media\Helper::injectMediaField` の実測）

  | | 旧（旧画像CF） | 新（新画像CF＝メディア管理） |
  |---|---|---|
  | 画像URL | `/archives/` ＋ `` | **`/media/` ＋ ``** |
  | 拡大用 | ``（無ければ `@path`） | **`@largePath` は無い。`@path` を使う** |
  | 寸法 | `` / `` | **`` / ``** |
  | 代替テキスト | ユニット側の `@alt` | **メディア管理側の `@alt`**（`media_field_3`） |

  🔴🔴 **`@path` は「メディアディレクトリからの相対パス」であってフルURLではない**
     （2026-08-27 実測。最初フルURLだと思って前置を省き、画像が全部 404 になった）。
     `Media\Helper::injectMediaField` は `Common::resolveUrl($path, MEDIA_LIBRARY_DIR)`
     を通すが、**`resolveUrl` が offset（＝ `media/`）を足すのは API ビルドか
     V2 モジュールのときだけ**（`php/Services/Common/Helper.php` の
     `if (isApiBuildOrV2Module())`）。カスタムユニットは `Common::setForceV1Build(true)`
     の従来記法なので**この分岐に入らず、`026/202608/xxx.png?v=…` がそのまま返る**。
     **`/media/`（= `/media/`）を自分で前置すること。**
     ⚠ Twig 側（`entry.fields.*.value.path`）は V2 モジュール経由なので `/media/` 込みで
     返る。**同じ `@path` でも経路によって中身が違う**ので、片方の挙動から推測しないこと。

  🔑 **キャッシュバスティングのクエリ（`?v=…`）は `@path` に既に含まれている。**

  🔑 **メディアは1ファイルなので拡大用の別サイズが無い。** 旧版は `large-` を持って
     いたが、こちらは同じ画像を拡大に使う。SmartPhoto の見え方は変わらない
     （元画像が表示サイズより大きいため）。

  ⚠ **`injectMediaField` は `Custom::render` が `$force = true` で呼んでいる**
     （`php/Services/Unit/Models/Custom.php:402`）ので、公開側でも `@path` などが
     必ず生える。⚠ **エントリーのカスタムフィールドの経路は `$force` 無し**で
     `ACMS_POST` のときしか通らないので、同じ挙動だと思わないこと。

  ⚠ **写真が2枚そろわないときは器ごと出さない**（2枚を並べて見比べる形のため）。
     `:veil` はカスタムユニットで効かないので `BEGIN_IF [.../nem]` の入れ子で書く。

  ⚠ 縦位置（`@height > @width`）は `.beforeAfter_imgPortrait` を足してレターボックス。
-->




<!--
  目次ユニット。

  ✅ **2026-08-28 に develop テーマの Tailwind 版からカンプ版へ差し替えた。**
  出典: 「要約と目次のセット」（`275:47676`）の**下半分**。上半分の要約は
  **本文の外**（エントリーのカスタムフィールド `pageSummary`）。
    PC 帯   `I275:47676;5789:66271` index-title-close-01-pc
    PC 中身 `I275:47676;5789:66272` index01-pc
    SP      `275:96946` index-title-close-01-sp / `275:96947` index-title-open-01-sp
  ⚠ **`275:48003` / `275:48004` は別物**（それぞれ「引用-pc」「強調2（strong）pc」）。
    2026-08-28 に一度そう書いてしまい、レビューで実測により訂正した。
    **ノードIDは推測で書かず `get_metadata` で取ること。**
  ✅ SP の `275:96947`（開いた状態）は `expand_circle_down` を使っており、
    SCSS の「開くと下向き」の判断はカンプと一致している（同日実測）。
  静的コーディングは `static_html/base-detail.html` と `base-unit-parts.html`。

  ⚠⚠ **中身（ol / li / a）は書かない。** a-blog cms 同梱の document-outliner が
     `.js-outline`（＝ `.entry_body`）の見出しを走査して `.js-outline-yield` に
     流し込む。器だけを用意するのがこのユニットの役目。
     入力側の `data-*` はテンプレート（`include/pages/base-detail.twig` ほか）が
     持っている。アンカーは `data-anchorName="heading-$1"`、深さは
     `data-levelLimit="2"`（h2 と h3 だけ）。
     スタイルは `src/style/front/component/page-contents-index/`。

  ⚠⚠ **`data-listClassName` などを空にしても既定クラスは消えない**
     （2026-08-28 に実測で判明。それまでここに「空にしてあるので素の ol / li / a に
     なる」と書いてあったが**誤り**）。`src/js/lib/buildIn/document-outliner.js:28` の
     取り込みが `if (value)` で、**空文字は falsy なので上書きされず既定値が残る**。
     実際に出るのは

       <ol class="level-1 acms-ol"><li class="acms-ol-item">
         <a class="scrollTo" href="#heading-1">…

     ⚠ **見た目は壊れない。** `acms-ol` / `acms-ol-item` / `scrollTo` に当たる CSS は
     このテーマに1行も無く、スタイルは `level-1` / `level-2` と要素セレクタで
     組んであるため。**「素の ol / li / a が来る」を前提に SCSS を書き換えないこと。**
     🚩 `scrollTo` は a-blog cms 慣習のフッククラス（`js/config.js` の
     `scrollToMark: 'a.scrollTo'`）。**このテーマは今のところ何も紐付けていない**が、
     将来スムーススクロールを足すと目次のリンクだけ挙動が変わる。
     本当に素にしたいときは `src/js/main.js` の `documentOutliner(document)` に
     `` を渡す
     （`makeList` は `` の順なので
     options が既定に勝つ）。**全ての `.js-outline` に効くので影響範囲を見てから。**

  ⚠ **見出しが1つも無いエントリーでは器ごと消える。** 旧実装は Tailwind の
     `[&:not(:has(li))]:hidden` でやっていたが、クラスを外した以上 SCSS 側が
     同じ役目を持っている（`.pageContentsIndex:not(:has(li))`）。**外すと
     「目次」の帯だけが中身ゼロで出る。**

  ⚠ **`open` を消さないこと。** カンプは開いた状態。閉じた状態は
     `.pageContentsIndexToggle` が 90° 戻るだけで、初期値はこの属性が決める。

  ⚠ **開閉のアイコンは `expand_circle_right` 固定で、CSS が回している**
     （`.pageContentsIndex[open] ... `）。
     `.icon` はテキスト内容（リガチャ）でグリフが決まるため CSS で差し替えられない。
     ⚠ **`icon_fill` を付けないこと**（FILL 0。中は白抜き）。

  ⚠⚠ **開閉アニメーションのフックは `.pageContentsIndex` そのもの**
     （`src/js/component/details-accordion.js` の `ANIMATED_SELECTOR`）。
     `js-` 始まりのクラスは足さない —— 2026-08-26 に花田さんがこのクラス名を
     直接見る形で実装しており、**足しても効かず、名前を変えると黙って止まる。**
-->











]]></description>
<category>玄関・窓</category>
<guid isPermaLink="true">http://plaza-r.co.jp/case/door_window/reformcase-0324.html</guid>
<pubDate>Mon, 26 Jan 2026 14:02:15 +0900</pubDate>
</item>
</channel>
</rss>
