<?xml version="1.0"?>
<FMAdd_on version="2.2.1.0" Source="20.3.2" File="Remarks_addon.fmp12" UUID="CDE46066-6133-4B66-B072-33456E5F2E24" locale="English" timestamp="2025-11-27T09:04:23">
	<Data membercount="1">
		<AddAction membercount="4">
			<Records>
				<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
				<RowList membercount="1">
					<Row membercount="13" id="1">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="1" name="com.fmi.basetable.field.Remarks_g::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="D66874E4-D77F-4031-A8DF-79803E0C0304">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[F1251424-92FF-49EE-9190-B99281D3B358]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="7" name="com.fmi.basetable.field.Remarks_g::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="E7B1453C-8E13-4736-AC4D-9B36B9573101">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="8" name="com.fmi.basetable.field.Remarks_g::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="DC9C2C97-C152-416F-B21E-F9E197FB4777">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="9" name="com.fmi.basetable.field.Remarks_g::F3B01C52897CF90DED3BFC6C0C6B94CF" repetition="1" UUID="DB66B7BA-D993-4F3E-BF1C-B064BAF1D440">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_g::F89CB77D46507AFE985D80822B6B6C39" repetition="1" UUID="6DDA6C7E-A43F-421C-B84C-D3BE0306F1C4">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="15" name="com.fmi.basetable.field.Remarks_g::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="09591866-E539-4DE5-A46F-E87BC3D43D4E">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="16" name="com.fmi.basetable.field.Remarks_g::52E9F5D3BD0C965A47120720F5FEB691" repetition="1" UUID="E4F89AE1-65C8-479C-83B2-3C95C46B4DF1">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="17" name="com.fmi.basetable.field.Remarks_g::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="956CAC47-C00B-4913-937E-3DB66E4A848D">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[300]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="19" name="com.fmi.basetable.field.Remarks_g::4F7547D511D4027C7F8C079F08091F50" repetition="1" UUID="3F31B139-3FDB-48A0-9FF7-DE7E557A82FE">
								<BaseTableReference id="129" name="com.fmi.basetable.191D3587A2A0896E20E1E22641572E42" UUID="9F1A07E8-7B6D-440C-A7A4-D930A35339CB"></BaseTableReference>
							</FieldReference>
						</Cell>
					</Row>
				</RowList>
			</Records>
			<Records>
				<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
				<RowList membercount="1">
					<Row membercount="13" id="1">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="1" name="com.fmi.basetable.field.Remarks_setting::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="5540D6D7-188F-4AEE-835F-0D5B867D83A3">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[4417DAC2-C54D-42BB-98DB-DAC87462E015]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="7" name="com.fmi.basetable.field.Remarks_setting::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="08DAFE4C-71C9-4821-8366-F5BB15088B8C">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="8" name="com.fmi.basetable.field.Remarks_setting::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="17E6C946-348F-4719-9942-1FA845CE1742">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="9" name="com.fmi.basetable.field.Remarks_setting::DE0CC8E4E57C8FAD7A46F9EF39C84C3D" repetition="1" UUID="68CF199B-88D2-40E0-8B41-EB99FFBFE16E">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[<!DOCTYPE html>
<!--
  文章作成支援アドオン / field_mapping.html
  目的:
    - 入力データのフィールド（例: CSV 列名）とテンプレート内の変数名の対応関係を、UI 上で定義・確認・保存する。
  概要:
    - 単一ファイルで完結する補助画面。index.html と同じ UX 方針（固定フッター/モバイル対応）。
  主な構成:
    - <style>: 最小限のレイアウトとフォーム UI（Bootstrap 風）。
    - <body>: ヘッダー、マッピング編集ビュー、プレビュー/ヘルプ、固定フッターの操作ボタン。
    - <script>: マッピングのロード/バリデーション/保存、画面遷移（簡易ビュー制御）。
  連携先:
    - index.html: テンプレート機能で利用されるフィールド→変数の対応定義。
    - fields.csv / data.json: 参照・プレビューのためのサンプルデータ（任意）。
  編集ガイド:
    - 変数名はテンプレート内で参照されるため、変更時は index 側の参照箇所も確認。
    - フォーム検証（未選択/重複）はエラーメッセージを伴う。メッセージ文言を統一。
  変更時のチェックリスト:
    1) 保存フォーマット（キー/値）と既存データの互換性。
    2) マッピングの未定義時の既定動作（プレースホルダ表示など）。
    3) モバイルでの操作性（セレクトのタップ領域、スクロール固定）。
-->
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
    <title>文章作成支援アドオン - フィールドマッピング</title>
    <style>
        /* =========================================
         * フィールドマッピング画面 用の最小スタイル
         * - index.html に合わせて固定フッターを用意
         * - モバイルも考慮したレイアウト
         * ========================================= */
        :root { --footer-h: 56px; }
        html, body { height: 100%; margin: 0; padding: 0; }
        body { font-family: system-ui, -apple-system, "Hiragino Kaku Gothic ProN", Meiryo, Arial, sans-serif; background: #fff; color: #333; display: flex; flex-direction: column; min-height: 100dvh; }
        .page-body { position: relative; flex: 1 1 auto; min-height: 0; overflow: auto; padding: 16px; padding-bottom: calc(16px + var(--footer-h) + env(safe-area-inset-bottom)); box-sizing: border-box; }
        /* 1列の縦積み + 中央寄せ（Bootstrap の .container/.row の雰囲気） */
        .row { display: flex; flex-direction: column; gap: 24px; margin: 0 auto 16px; max-width: 720px; width: 100%; }
        .field { display: flex; flex-direction: column; gap: 10px; width: 100%; padding: 8px 0; }
        /* 項目間の区切り線（先頭は無し） */
        .row .field + .field { border-top: 1px solid #e9ecef; padding-top: 16px; }
        label { font-weight: 600; font-size: 14px; margin-bottom: 6px; }
        /* Bootstrap 風 .form-select の見た目 */
        select {
            width: 100%;
            display: block;
            box-sizing: border-box;
            padding: .375rem 2.25rem .375rem .75rem;
            font-size: 1rem;
            font-weight: 400;
            line-height: 1.5;
            color: #212529;
            background-color: #fff;
            background-clip: padding-box;
            border: 1px solid #ced4da;
            border-radius: .375rem; /* 6px 相当 */
            transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
            /* ネイティブ矢印を消してカスタム矢印を表示 */
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
            background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba(33,37,41,0.75)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
            background-repeat: no-repeat;
            background-position: right .75rem center;
            background-size: 16px 12px;
        }
        select:focus { outline: none; border-color: #86b7fe; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); }
        select:disabled { background-color: #e9ecef; opacity: 1; }
        /* プレースホルダ選択時の薄い色 */
        select.is-placeholder { color: #adb5bd; }
        /* 複数選択セレクトの見た目調整 */
        select[multiple] { background-image: none; padding-right: .75rem; min-height: 140px; }
        /* iOS 向け: チェックボックス型マルチセレクト */
        .checkbox-list { display: flex; flex-direction: column; gap: 6px; border: 1px solid #ced4da; border-radius: .375rem; padding: 8px 10px; max-height: 220px; overflow: auto; }
        .checkbox-item { display: flex; align-items: center; gap: 8px; font-size: 14px; }
        .checkbox-item input[type="checkbox"] { width: 16px; height: 16px; }
        /* 検証用スタイル（Bootstrap 風） */
        .is-invalid { border-color: #dc3545 !important; padding-right: calc(2.25rem + 2px); background-image: none; }
        .invalid-feedback { display: none; width: 100%; margin-top: .25rem; font-size: .875em; color: #dc3545; }
        select.is-invalid + .invalid-feedback { display: block; }
        .help { margin-top: .25rem; font-size: .875em; color: #6c757d; }
        .page-title { font-weight: 700; font-size: 20px; margin: 0; }
        .header { position: sticky; top: 0; background: #fff; border-bottom: 1px solid #e5e5e5; z-index: 9998; padding: 12px 16px; box-sizing: border-box; }
        .footer { position: fixed; left: 0; right: 0; bottom: 0; height: var(--footer-h); background: #f7f7f7; border-top: 1px solid #e5e5e5; display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 8px 12px; box-sizing: border-box; z-index: 9999; }
        .btn { appearance: none; border: 1px solid #d0d0d0; background: #fff; color: #333; border-radius: 8px; padding: 10px 14px; font-size: 14px; cursor: pointer; }
        .btn.primary { background: #007bff; color: #fff; border-color: #007bff; }
        /* アニメーション: フッターの表示/非表示 */
        .footer { transition: transform .24s ease, opacity .24s ease; }
        .footer.hidden { transform: translateY(calc(var(--footer-h) + 12px)); opacity: 0; pointer-events: none; }
        /* アニメーション: ビューのフェード */
        .view { opacity: 1; transition: opacity .2s ease; }
        .view.is-hiding { opacity: 0; }
        .view.is-hidden { display: none; }
    </style>
</head>
<body>
    <!--
      レイアウト概要（主要領域）
      - .header: タイトル/戻る/ヘルプ
      - .page-body: 本文のスクロール領域（固定フッター分の余白を確保）
      - #mapping-view: マッピング編集 UI
      - #preview-view: プレビュー UI
      - #help-view: 使い方/注意点
      - .footer: 画面下部固定の操作バー（保存/キャンセル）
    -->
    <div class="header" style="display:flex; align-items:center; justify-content:space-between; gap:8px;">
        <h1 id="page-title" class="page-title">フィールドマッピング</h1>
        <div class="header-actions" style="display:flex; align-items:center; gap:8px;">
            <span id="back-trigger" title="戻る" style="display:none; align-items:center; justify-content:center; width:24px; height:24px; border-radius:50%; background:#e9ecef; color:#333; font-weight:700; cursor:pointer; user-select:none;">←</span>
            <span id="help-trigger" title="アドオンの概要" style="display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:50%; background:#e9ecef; color:#333; font-weight:700; cursor:pointer; user-select:none;">?</span>
        </div>
    </div>
    <div class="page-body">
        <div id="mapping-view" class="view">
        <!--
            ここに FileMaker から呼び出される select 要素を配置してください。
            target 値に一致するように id を付与します。
        -->
        <div class="row">
            <div class="field">
                <label for="select_table_name">テーブル名</label>
                <select id="select_table_name">
                </select>
                <div class="invalid-feedback">テーブル名を選択してください。</div>
                <div class="help">対象フィールドのテーブル名を選択してください。</div>
            </div>
            <div class="field">
                <label for="select_primary_field_name">主キーフィールド</label>
                <select id="select_primary_field_name">
                </select>
                <div class="invalid-feedback">主キーフィールドを選択してください。</div>
                <div class="help">テーブル内の各レコードを一意に識別できるフィールドを選択してください ( 例 : 主キー / PrimaryKey / _id など UUID が設定されているフィールド )。重複しない値を持つフィールドを指定します。フィールドタイプはテキストのみ対応です。主キーフィールドの値の生成には Get ( UUID ) の利用を推奨します。</div>
            </div>
            <div class="field">
                <label for="select_target_field_name">対象フィールド</label>
                <select id="select_target_field_name">
                </select>
                <div class="invalid-feedback">対象フィールドを選択してください。</div>
                <div class="help">文章を転記して欲しいフィールドを指定してください。</div>
            </div>
            <div class="field">
                <label for="select_layout_name">レイアウト名</label>
                <select id="select_layout_name">
                </select>
                <div class="invalid-feedback">レイアウト名を選択してください。</div>
                <div class="help">文章を転記して欲しいフィールドが配置されているレイアウト名を指定してください。対象フィールドだけが配置されているレイアウトがベストです。レイアウト上に画像フィールドが配置されているとパフォーマンスに影響する場合があります。指定したレイアウトにはスクリプトトリガなどは設定しないでください。</div>
            </div>
            <div class="field">
                <label for="select_output_layout_names">出力で利用するレイアウト名（複数選択可）</label>
                <select id="select_output_layout_names" multiple size="6"></select>
                <div id="ios_output_layout_list" class="checkbox-list" style="display:none;"></div>
                <!-- <div class="help">印刷や出力で利用するレイアウトを選択します。Remarks_data テーブルのレイアウト ( バージョン 21 以降の場合 )、または、Remarks_data_printで始まるレイアウト ( バージョン 20 の場合 ) のみ候補に表示されます。チェックボックスで複数選択できます。</div> -->
                <div class="help">印刷や出力で利用するレイアウトを選択します。Remarks_data テーブルのレイアウトのみ候補に表示されます。チェックボックスで複数選択できます。</div>             
            </div>
        </div>
        </div>

        <div id="overview-view" class="view is-hidden" style="display:none;">
            <div class="row">
                <div class="field">
                    <label style="font-weight:700; font-size:16px;">アドオンの概要</label>
                    <div class="help" style="margin-top:6px; color:#333; font-size:14px;">
                        このアドオンは、よく使う定型文と自由入力を組み合わせて、短時間で読みやすい文章を作るためのツールです。
                    </div>
                    <ul style="margin:8px 0 0 1.1em; padding:0; color:#333; font-size:14px;">
                        <li style="margin:3px 0;">テンプレートを選ぶ: 一覧や検索で探し、クリックで本文に反映</li>
                        <li style="margin:3px 0;">その場で整える: 反映後の本文は自由に追記・削除・修正OK</li>
                        <li style="margin:3px 0;">入力はかんたん: 日付や数値などもフォームで迷わず入力</li>
                        <li style="margin:3px 0;">よく使う値は次回も: 入力内容は次回の初期値として設定</li>
                        <li style="margin:3px 0;">いつでもやり直せる: テンプレートの再選択や上書きが可能</li>
                    </ul>
                </div>
                <div class="field">
                    <label style="font-weight:700; font-size:16px;">この画面（フィールドマッピング）について</label>
                    <div class="help" style="margin-top:6px; color:#333; font-size:14px;">
                        作成した本文をどのフィールドに書き込むかを最初に決めます。次の順で選んでください。
                    </div>
                    <ol style="margin:8px 0 0 1.2em; padding:0; color:#333; font-size:14px;">
                        <li style="margin:3px 0;">テーブル名を選ぶ</li>
                        <li style="margin:3px 0;">主キーフィールドを選ぶ</li>
                        <li style="margin:3px 0;">対象フィールド（本文を書き込む先）を選ぶ</li>
                        <li style="margin:3px 0;">レイアウト名を選ぶ（選んだテーブルに合う候補だけ表示されます）</li>
                    </ol>
                    <ul style="margin:10px 0 0 1.1em; padding:0; color:#333; font-size:14px;">
                        <li style="margin:3px 0;">すべて必須です。未選択の項目は赤色でお知らせします。</li>
                        <li style="margin:3px 0;">保存で設定が反映されます。キャンセルで変更は破棄されます。</li>
                        <li style="margin:3px 0;">レイアウトは「対象フィールドだけが置かれているもの」を選ぶと確実です。</li>
                    </ul>
                </div>
                <div class="field">
                    <label style="font-weight:700; font-size:16px;">サンプルファイルについて</label>
                    <div class="help" style="margin-top:6px; color:#333; font-size:14px;">
                        Claris、Claris パートナー各社、およびコンテンツ提供者は、FileMaker Pro、FileMaker Go、FileMaker Server、および外部連携の学習を目的としてトレーニング用のサンプルファイルを提供します。当該ファイルを提供する目的は、FileMaker プラットフォームにおけるデータベース構造の学習および外部連携に関する知識を深めるために作成されたものです。本ファイルは、商用サービスで提供する目的では提供されておりません。本ファイルは、完成された本番環境で利用するソリューションとして、もしくは、外部システムとの連携稼働を保証するものではありません。なお、当該ファイルに記述されたデザインおよびアイコンなどの画像は、制作者に帰属し、再頒布を認めるものではありません。
                    </div>
                </div>
                <div class="field">
                    <label style="font-weight:700; font-size:16px;">免責事項</label>
                    <div class="help" style="margin-top:6px; color:#333; font-size:14px;">
                        Claris、Claris パートナー各社、およびコンテンツ提供者は、本ファイルおよび、外部システムとの連携に関して一切の動作保証を致しません。このファイルに不備があった場合も Claris、Claris パートナー各社、およびコンテンツ提供者は訂正する義務を負いません。お客様がこのファイルにより、直接または間接的に損害を生じたとしても、Claris、Claris パートナー各社、およびコンテンツ提供者は、いかなる責任および義務をも負わないものとし、一切の賠償も行わないものとします。このファイルの情報は、一部の機能を除き、無料で提供されています。
                    </div>
                </div>
                <div class="field">
                    <label style="font-weight:700; font-size:16px;">法的権利と責任</label>
                    <div class="help" style="margin-top:6px; color:#333; font-size:14px;">
                        このファイルを利用した情報の保存や複製その他ユーザーによる任意の利用方法により必要な法的権利を有しているか否か、著作権、秘密保持、名誉毀損、品位保持および輸出に関する法規その他法令上の義務に従うことなど、ユーザーご自身の責任において行っていただきますようお願いいたします。このファイルが紹介しているソースやソフトウェアの合法性、正確性、道徳性、最新性、適切性、著作権の許諾や有無など、その内容については一切の保証を致しかねます。
                    </div>
                </div>
                <div class="field">
                    <label style="font-weight:700; font-size:16px;">注意事項</label>
                    <div class="help" style="margin-top:6px; color:#333; font-size:14px;">
                        このアドオンをアンインストールする際、追加されたテーブルやスクリプトなども削除されます。削除したくない場合は、「アドオンが使用するテーブル、フィールド、レコードデータを全て完全に削除」チェックボックスのチェックを外してから、アドオンをアンインストールしてください。
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="footer">
        <button id="btn-cancel" class="btn">キャンセル</button>
        <button id="btn-save" class="btn primary">保存</button>
    </div>

    <script>
    // =========================================
    // フィールドマッピング - スクリプト概要
    // =========================================
    // 目次:
    //  A) データ構造
    //  B) 画面構成と主なDOM要素
    //  C) 処理フロー（初期化→入力→検証→保存）
    //  D) FileMaker 連携
    //  E) バリデーション方針
    // -----------------------------------------
    // A) データ構造
    //   - REQUIRED_IDS: 入力必須の <select> 要素 ID 一覧
    //   - PLACEHOLDER_BY_ID: 各 <select> のプレースホルダ文言
    //   - FM_SCRIPTS: FileMaker へ通知/要求するスクリプト名一覧
    //   - __layoutOptionsRaw: レイアウト候補の元データ（テーブル依存のフィルタ対象）
    //   - __tableInfoMap: レイアウト value → baseTable の対応表
    // -----------------------------------------
    // B) 画面構成と主なDOM要素
    //   - ヘッダー（タイトル/ヘルプ/戻る）
    //   - 本文（マッピング編集ビュー、プレビュー、ヘルプ）
    //   - 固定フッター（保存/キャンセル）
    // -----------------------------------------
    // C) フロー（概略）
    //   1) 初期化: プレースホルダ設定 → FileMaker へロード通知
    //   2) 入力: テーブル選択 → 依存セレクト更新 → 妥当性チェック
    //   3) 保存: 選択値をまとめて送信（JSON 文字列）
    //   4) キャンセル: 呼び出し元へ通知
    // -----------------------------------------
    // D) FileMaker 連携
    //   - window.FileMaker.PerformScriptWithOption(name, param, 5)
    //   - 未注入時は短い待機の後に再試行
    // -----------------------------------------
    // E) バリデーション方針
    //   - 未選択の検出（is-invalid クラス + メッセージ表示）
    //   - 相互依存（テーブル→レイアウト）の不整合回避
    // =========================================
    // FileMaker 連携ユーティリティ
    // - index.html と同じ呼び出し方式: window.FileMaker.PerformScriptWithOption(name, param, 5)
    // - 非同期ロード対策: FileMaker が未注入なら待機して再試行
    // =========================================
    function callFileMaker(scriptName, param) {
        try {
            if (window.FileMaker != null && typeof window.FileMaker.PerformScriptWithOption === 'function') {
                window.FileMaker.PerformScriptWithOption(String(scriptName), String(param ?? ''), 5);
                return true;
            }
        } catch (_) {}
        return false;
    }

    // DOMContentLoaded 後にロード通知スクリプトを実行
    (function notifyLoaded() {
        function exec() {
            if (!callFileMaker(FM_SCRIPTS.loaded, '')) {
                setTimeout(exec, 250);
            }
        }
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', exec, { once: true });
        } else {
            setTimeout(exec, 0);
        }
    })();

    // 共通定数・ヘルパー
    const ID = {
        table: 'select_table_name',
        target: 'select_target_field_name',
        primary: 'select_primary_field_name',
        layout: 'select_layout_name',
        output: 'select_output_layout_names'
    };
    const REQUIRED_IDS = [ID.table, ID.target, ID.primary, ID.layout];
    const PLACEHOLDER_TEXT = '選択してください...';
    const PLACEHOLDER_BY_ID = {
        [ID.table]: 'テーブルを選択してください...',
        [ID.target]: '対象フィールドを選択してください...',
        [ID.primary]: '主キーフィールドを選択してください...',
        [ID.layout]: 'レイアウトを選択してください...'
    };
    const FM_SCRIPTS = {
        loaded: 'addon_remarks_field_mapping_loaded',
        tableChanged: 'addon_remarks_field_mapping_table_changed',
        submit: 'addon_remarks_field_mapping_submit',
        cancel: 'addon_remarks_field_mapping_cancel'
    };
    const BTN = {
        save: 'btn-save',
        cancel: 'btn-cancel'
    };

    function getSelect(id) {
        const el = document.getElementById(id);
        return (el && el.tagName === 'SELECT') ? el : null;
    }

    // 初期表示時に各セレクトのプレースホルダー文言を一元的に設定
    function ensureInitialPlaceholders() {
        for (const id of REQUIRED_IDS) {
            const select = getSelect(id);
            if (!select) continue;
            const text = PLACEHOLDER_BY_ID[id] ?? PLACEHOLDER_TEXT;
            const first = select.options[0];
            if (!first || String(first.value ?? '') !== '') {
                const o = document.createElement('option');
                o.value = '';
                o.textContent = text;
                select.insertBefore(o, select.firstChild);
            } else {
                first.textContent = text;
            }
            // 初期状態の薄色表示
            select.classList.toggle('is-placeholder', String(select.value ?? '') === '');
            // 変更時にクラスを切り替え
            if (!select.dataset.placeholderBound) {
                select.addEventListener('change', () => {
                    select.classList.toggle('is-placeholder', String(select.value ?? '') === '');
                });
                select.dataset.placeholderBound = '1';
            }
        }
    }

    // DOM構築後すぐに初期プレースホルダーをセット
    ensureInitialPlaceholders();

    // レイアウト候補の元データ（テーブル依存フィルタ用）
    let __layoutOptionsRaw = null;
    // table_info から作る表示名(value) → baseTable の対応表
    let __tableInfoMap = null;
    // 出力用レイアウトツリーの元データ（フォルダ構造を含む）
    let __outputLayoutTreeRaw = null;

    // value に対する baseTable を取得（なければ value を返す）
    function getBaseTableForValue(value) {
        const v = String(value ?? '');
        if (!v) return '';
        if (__tableInfoMap && Object.prototype.hasOwnProperty.call(__tableInfoMap, v)) {
            return String(__tableInfoMap[v] ?? '');
        }
        return v;
    }

    // レイアウト候補1件から baseTable を推定
    function getLayoutItemBaseTable(item) {
        if (!item || typeof item !== 'object') return '';
        // レイアウト側に baseTable があれば最優先
        const explicit = item.baseTable ?? '';
        if (explicit) return String(explicit);
        // なければ table を table_info のマップで変換
        const tableName = item.table ?? '';
        if (tableName) return getBaseTableForValue(tableName);
        // 最後に name をテーブル名相当として扱う（後方互換）
        const name = item.name ?? '';
        if (name) return getBaseTableForValue(name);
        return '';
    }

    // 現在のテーブル選択に応じたレイアウト候補を取得
    function getFilteredLayoutsForCurrentTable() {
        const tableSelect = getSelect(ID.table);
        const selectedValue = String(tableSelect?.value ?? '');
        const selectedBase = getBaseTableForValue(selectedValue);
		const base = Array.isArray(__layoutOptionsRaw) ? __layoutOptionsRaw : [];
        // テーブル未選択時は候補を表示しない（プレースホルダのみ）
        if (!selectedBase) return [];
        return base.filter(it => {
            const itemBase = String(getLayoutItemBaseTable(it));
            const hasExplicitTable = !!(it && typeof it === 'object' && ((it.table ?? '') || (it.baseTable ?? '')));
            // table/baseTable が未指定のレイアウトは常に候補に含める
            return itemBase === selectedBase || !hasExplicitTable;
        });
    }

    // select を指定配列で描画するユーティリティ
    function renderSelectOptions(select, list, initialValue) {
        while (select.firstChild) select.removeChild(select.firstChild);
        // 空値用のプレースホルダーを先頭に追加
        const placeholder = document.createElement('option');
        placeholder.value = '';
        const placeholderText = (select && typeof select.id === 'string')
            ? (PLACEHOLDER_BY_ID[select.id] ?? PLACEHOLDER_TEXT)
            : PLACEHOLDER_TEXT;
        placeholder.textContent = placeholderText;
        select.appendChild(placeholder);
        // 実データを続けて追加
        for (const opt of list) {
            const value = String(opt.value ?? '');
            if (!value) continue; // null/空値は表示しない
            const o = document.createElement('option');
            o.value = value;
            o.textContent = String(opt.label ?? value);
            select.appendChild(o);
        }
        // 初期選択: 値が指定されていればそれを、なければ空値
        if (initialValue != null) {
            select.value = String(initialValue);
        } else {
            select.value = '';
        }
        // 一致する値がない場合はプレースホルダーにフォールバック
        if (select.selectedIndex === -1) select.selectedIndex = 0;
        // 見た目: プレースホルダ選択状態で薄色表示
        select.classList.toggle('is-placeholder', String(select.value ?? '') === '');
    }

    // テーブル選択に応じてレイアウト名プルダウンをフィルタリング
    function refilterLayoutsByTable() {
        const tableSelect = getSelect(ID.table);
        const layoutSelect = getSelect(ID.layout);
        if (!tableSelect || !layoutSelect || !Array.isArray(__layoutOptionsRaw)) return;
        const filtered = getFilteredLayoutsForCurrentTable();
        const normalized = normalizeOptions(filtered);
        // レイアウトは候補を維持しつつ、選択値のみクリア
        renderSelectOptions(layoutSelect, normalized, '');

        // 対象フィールド/主キーフィールドは候補ごとクリア
        const empty = [];
        const targetSel = getSelect(ID.target);
        const primarySel = getSelect(ID.primary);
        if (targetSel) renderSelectOptions(targetSel, empty, '');
        if (primarySel) renderSelectOptions(primarySel, empty, '');

        // 出力用レイアウトは populateOutputLayoutOptions のみで制御するため、ここでは触らない

        // テーブル変更を FileMaker に通知
        try {
            const selected = String(tableSelect.value ?? '');
            callFileMaker(FM_SCRIPTS.tableChanged, selected);
        } catch (_) {}
    }

    // 生の options をレイアウト用にオブジェクト配列へ正規化
    function parseLayoutRaw(raw) {
        if (typeof raw === 'string') {
            const s = raw.trim();
            if (s.startsWith('[') && s.endsWith(']')) {
                try {
                    const parsed = JSON.parse(s);
                    return Array.isArray(parsed) ? parsed : [];
                } catch (_) { /* noop */ }
            }
            // 改行のときは name のみ（table 情報は無い）
            const lines = s.length === 0 ? [] : s.split(/\r\n?|\n|\r/);
            return lines.map(v => ({ name: v }));
        }
        if (Array.isArray(raw)) {
            return raw.map(item => {
                if (item == null) return { name: '' };
                if (typeof item === 'string' || typeof item === 'number') return { name: String(item) };
                if (typeof item === 'object') return item; // { name, table } を想定
                return { name: '' };
            });
        }
        return [];
    }

	// レイアウト配列/ツリーをフラット化（フォルダー配下のレイアウトを展開）
	function flattenLayouts(nodes, out) {
		const acc = out || [];
		if (!nodes) return acc;
		if (Array.isArray(nodes)) {
			for (const n of nodes) flattenLayouts(n, acc);
			return acc;
		}
		if (typeof nodes === 'object') {
			if (nodes.isFolder && Array.isArray(nodes.folderLayoutNames)) {
				flattenLayouts(nodes.folderLayoutNames, acc);
			} else {
				acc.push(nodes);
			}
		}
		return acc;
	}

    // =========================================
    // updatePulldown 実装
    // - FileMaker からテキスト(文字列)として渡される JSON を想定
    // - 入力は以下の配列形式（文字列）:
    //   [
    //     { options: string | Array | オブジェクト配列, target: string, value: string }
    //   ]
    // - options の型バリエーション:
    //   1) JSON文字列の配列文字列（例: '[{"baseTable":"Remarks_g","name":"Remarks_g"}, ...]')
    //   2) 改行区切り文字列（例: 'a\r\nb' または 'a\nb' または 'a\rb'）
    //   3) 配列（例: [{ name: 'x', table: 'y' }] や ['x','y']）
    // - select 要素の id は target と一致することを想定
    // - value は初期選択値（そのまま select.value に設定）
    // =========================================
    window.updatePulldown = function(inputText, tableInfo) {
        // メンテナンス性のため、例外は握りつぶさず安全側で終了
        try {
            // 第2引数: table_info を受け取ってマップを構築（任意）
            if (tableInfo != null) {
                try {
                    const raw = (typeof tableInfo === 'string') ? JSON.parse(tableInfo) : tableInfo;
                    if (Array.isArray(raw)) {
                        const map = Object.create(null);
                        for (const item of raw) {
                            if (!item || typeof item !== 'object') continue;
                            const base = String(item.baseTable ?? '');
                            const candidates = [];
                            if (item.name != null) candidates.push(String(item.name));
                            if (item.table != null) candidates.push(String(item.table));
                            // name/table どちらにも同じ baseTable を対応付け
                            for (const key of candidates) {
                                if (!key) continue;
                                map[key] = base || key;
                            }
                        }
                        __tableInfoMap = map;
                    }
                } catch (_) { /* noop: tableInfo が不正でも既存動作を維持 */ }
            }

            const entries = typeof inputText === 'string' ? JSON.parse(inputText) : inputText;
            if (!Array.isArray(entries)) return;

            for (const entry of entries) {
                if (!entry || typeof entry !== 'object') continue;
                const targetId = String(entry.target ?? '').trim();
                if (!targetId) continue;
                // 出力用レイアウトは populateOutputLayoutOptions のみで制御するため、ここでは無視する
                if (targetId === ID.output) continue;
                const select = getSelect(targetId);
                if (!select) continue;

                // レイアウト名はテーブル選択に応じてフィルタして表示
				if (targetId === 'select_layout_name') {
					const parsedLayouts = parseLayoutRaw(entry.options);
					__layoutOptionsRaw = Array.isArray(parsedLayouts)
						? flattenLayouts(parsedLayouts, [])
						: [];
                    const tableSelect = getSelect(ID.table);
                    const selectedValue = tableSelect ? String(tableSelect.value ?? '') : '';
                    const selectedBase = getBaseTableForValue(selectedValue);
                    const base = __layoutOptionsRaw || [];
                    let filtered;
                    if (!selectedBase) {
                        // テーブル未選択時は候補を表示しない（プレースホルダのみ）
                        filtered = [];
                    } else {
                        filtered = base.filter(it => {
                            const itemBase = String(getLayoutItemBaseTable(it));
                            const hasExplicitTable = !!(it && typeof it === 'object' && ((it.table ?? '') || (it.baseTable ?? '')));
                            // table/baseTable が未指定のレイアウトは常に候補に含める
                            return itemBase === selectedBase || !hasExplicitTable;
                        });
                    }
                    const normalized = normalizeOptions(filtered);
                    renderSelectOptions(select, normalized, entry.value);
                } else {
                    // 通常のセレクト描画
                    const normalized = normalizeOptions(entry.options);
                    renderSelectOptions(select, normalized, entry.value);
                }
            }

            // 依存関係: テーブル変更でレイアウトを再フィルタ
            const tableSelect = getSelect(ID.table);
            if (tableSelect && !tableSelect.dataset.layoutFilterBound) {
                tableSelect.addEventListener('change', refilterLayoutsByTable);
                tableSelect.dataset.layoutFilterBound = '1';
            }

            // レイアウト変更時の FileMaker 通知は廃止
        } catch (e) {
            console.error('updatePulldown error:', e);
        }
    };

    // =========================================
    // FileMaker -> JavaScript: テーブルに紐づくフィールド一覧の受け取り口
    // 使用例（FileMaker 側から）:
    //   window.updateFieldsForTable('["FieldA","FieldB"]');
    //   または window.updateFieldsForTable(["FieldA","FieldB"]);
    //   または window.updatePulldown(JSON.stringify([...])) を直接呼んでもOK
    // 振る舞い: 対象フィールド/主キーフィールドのプルダウンへ同一候補を設定
    // =========================================
    window.updateFieldsForTable = function(fields) {
        try {
            // 受け取った配列/文字列をそのまま options として使い、既存値を維持
            const currentTarget = document.getElementById('select_target_field_name')?.value ?? '';
            const currentPrimary = document.getElementById('select_primary_field_name')?.value ?? '';
            const payload = [
                { options: fields, target: 'select_target_field_name', value: currentTarget },
                { options: fields, target: 'select_primary_field_name', value: currentPrimary }
            ];
            // updatePulldown はテキスト(JSON)でもオブジェクトでも受けられるように実装済み
            if (typeof window.updatePulldown === 'function') {
                window.updatePulldown(JSON.stringify(payload));
            }
        } catch (e) {
            console.error('updateFieldsForTable error:', e);
        }
    };

    // options を [{label, value}] 配列に正規化
    function normalizeOptions(raw) {
        // 1) 文字列: JSON配列 or 改行区切り
        if (typeof raw === 'string') {
            const s = raw.trim();
            if (s.startsWith('[') && s.endsWith(']')) {
                // JSON 文字列として解釈
                try {
                    const parsed = JSON.parse(s);
                    return normalizeOptions(parsed);
                } catch (_) {
                    // 失敗した場合は改行区切りとして解釈にフォールバック
                }
            }
            // 改行コード: CRLF / LF / CR(FileMaker) 対応
            const lines = s.length === 0 ? [] : s.split(/\r\n?|\n|\r/);
            return lines.map(v => ({ label: v, value: v }));
        }

        // 2) 配列: '文字列配列' or 'オブジェクト配列'
        if (Array.isArray(raw)) {
            return raw.map(item => {
                if (item == null) return { label: '', value: '' };
                if (typeof item === 'string' || typeof item === 'number') {
                    const v = String(item);
                    return { label: v, value: v };
                }
                if (typeof item === 'object') {
                    // よくある候補キー: name, table, baseTable
                    const label = (item.name ?? item.table ?? item.baseTable ?? item.value ?? '');
                    const value = (item.name ?? item.table ?? item.baseTable ?? item.value ?? '');
                    return { label: String(label), value: String(value) };
                }
                return { label: '', value: '' };
            });
        }

        // 3) それ以外は空
        return [];
    }

    // =========================================
    // フッターボタン: 保存/キャンセル
    // =========================================
    function validateRequiredSelect(selectId) {
        const el = getSelect(selectId);
        if (!el || el.tagName !== 'SELECT') return true;
        const isValid = String(el.value ?? '') !== '';
        if (isValid) {
            el.classList.remove('is-invalid');
        } else {
            el.classList.add('is-invalid');
        }
        return isValid;
    }

    function bindClearInvalidOnChange(selectId) {
        const el = getSelect(selectId);
        if (!el) return;
        if (!el.dataset.boundClearInvalid) {
            el.addEventListener('change', () => {
                if (String(el.value ?? '') !== '') el.classList.remove('is-invalid');
            });
            el.dataset.boundClearInvalid = '1';
        }
    }

    REQUIRED_IDS.forEach(bindClearInvalidOnChange);

    document.getElementById(BTN.save).addEventListener('click', function() {
        // 入力チェック（全て必須）
        const ids = REQUIRED_IDS;
        const results = ids.map(validateRequiredSelect);
        const allValid = results.every(Boolean);
        if (!allValid) {
            // 最初の未入力にスクロール＆フォーカス
            const firstInvalid = ids
                .map(id => document.getElementById(id))
                .find(el => el && el.classList.contains('is-invalid'));
            if (firstInvalid) {
                try { firstInvalid.scrollIntoView({ behavior: 'smooth', block: 'center' }); } catch (_) {}
                try { firstInvalid.focus({ preventScroll: true }); } catch (_) {}
            }
            return; // スクリプト呼び出しを中断
        }

        // 送信ペイロード: 現在のセレクト値をまとめて返す
        // iOS チェックボックス UI の選択値を吸い上げ
        function readOutputSelectedValues() {
            const listBox = document.getElementById('ios_output_layout_list');
            if (listBox && listBox.style.display !== 'none') {
                const boxes = listBox.querySelectorAll('input[type="checkbox"]');
                return Array.from(boxes).filter(b => b.checked).map(b => String(b.value || ''));
            }
            return Array.from(document.getElementById(ID.output)?.selectedOptions ?? []).map(o => String(o.value || ''));
        }

        const payload = {
            table: getSelect(ID.table)?.value ?? '',
            baseTable: getBaseTableForValue(getSelect(ID.table)?.value ?? ''),
            targetField: getSelect(ID.target)?.value ?? '',
            primaryField: getSelect(ID.primary)?.value ?? '',
            layout: getSelect(ID.layout)?.value ?? '',
            outputLayouts: readOutputSelectedValues()
        };
        callFileMaker(FM_SCRIPTS.submit, JSON.stringify(payload));
    });

    document.getElementById(BTN.cancel).addEventListener('click', function() {
        callFileMaker(FM_SCRIPTS.cancel, '');
    });

    // =========================================
    // ビュー切替（マッピング / 概要）
    // =========================================
    (function setupViewToggle() {
        const title = document.getElementById('page-title');
        const help = document.getElementById('help-trigger');
        const back = document.getElementById('back-trigger');
        const mapping = document.getElementById('mapping-view');
        const overview = document.getElementById('overview-view');
        if (!title || !help || !back || !mapping || !overview) return;

        function showOverview() {
            // ビュー切替（フェード）
            mapping.classList.add('is-hiding');
            setTimeout(() => {
                mapping.classList.remove('is-hiding');
                mapping.classList.add('is-hidden');
                mapping.style.display = 'none';
                overview.style.display = 'block';
                requestAnimationFrame(() => overview.classList.remove('is-hidden'));
            }, 200);
            title.textContent = 'アドオンの概要';
            back.style.display = 'inline-flex';
            help.style.display = 'none';
            // フッターを隠す（アニメーション）
            const footer = document.querySelector('.footer');
            if (footer) footer.classList.add('hidden');
            try { window.scrollTo({ top: 0 }); } catch(_) {}
        }
        function showMapping() {
            // ビュー切替（フェード）
            overview.classList.add('is-hiding');
            setTimeout(() => {
                overview.classList.remove('is-hiding');
                overview.classList.add('is-hidden');
                overview.style.display = 'none';
                mapping.style.display = 'block';
                requestAnimationFrame(() => mapping.classList.remove('is-hidden'));
            }, 200);
            title.textContent = 'フィールドマッピング';
            back.style.display = 'none';
            help.style.display = 'inline-flex';
            // フッターを戻す（アニメーション）
            const footer = document.querySelector('.footer');
            if (footer) footer.classList.remove('hidden');
            try { window.scrollTo({ top: 0 }); } catch(_) {}
        }
        help.addEventListener('click', showOverview);
        back.addEventListener('click', showMapping);
    })();

    // 旧カード/モーダルは廃止
    
    // =========================================
    // 出力用レイアウト（複数選択）の選択肢生成 API
    // - 第1引数: tableName(string) フィルタ対象のテーブル名
    // - 第2引数: selectedValues(string|Array) 事前選択したい値（配列/JSON/区切り文字可）
    // - 第3引数: json(Array|String) 以下のようなツリー構造
    //   [ { name, table } | { isFolder:true, folderLayoutNames:[ ... ] } , ... ]
    // =========================================
    function normalizeSelectedValues(values) {
        const set = new Set();
        if (values == null) return set;
        if (Array.isArray(values)) {
            for (const v of values) { if (v != null && String(v).length) set.add(String(v)); }
            return set;
        }
        if (typeof values === 'string') {
            const s = values.trim();
            if (s.startsWith('[') && s.endsWith(']')) {
                try {
                    const arr = JSON.parse(s);
                    if (Array.isArray(arr)) { for (const v of arr) set.add(String(v)); return set; }
                } catch (_) {}
            }
            const parts = s.split(/\r\n?|\n|\r|,|\t/).map(x => x.trim()).filter(Boolean);
            for (const p of parts) set.add(p);
            return set;
        }
        set.add(String(values));
        return set;
    }

    function flattenLayoutTree(nodes, out) {
        const acc = out || [];
        if (!nodes) return acc;
        if (Array.isArray(nodes)) {
            for (const n of nodes) flattenLayoutTree(n, acc);
            return acc;
        }
        if (typeof nodes === 'object') {
            if (nodes.isFolder && Array.isArray(nodes.folderLayoutNames)) {
                flattenLayoutTree(nodes.folderLayoutNames, acc);
            } else {
                const name = nodes.name ?? '';
                const table = nodes.table ?? '';
                if (name) acc.push({ name: String(name), table: String(table) });
            }
        }
        return acc;
    }

    function renderOutputLayoutsIntoSelect(selectEl, items, selectedSet) {
        while (selectEl.firstChild) selectEl.removeChild(selectEl.firstChild);
        for (const it of items) {
            const o = document.createElement('option');
            o.value = String(it.name);
            o.textContent = String(it.name);
            if (selectedSet && selectedSet.has(o.value)) o.selected = true;
            selectEl.appendChild(o);
        }
    }

    function renderOutputLayoutsIntoCheckboxList(listEl, items, selectedSet) {
        while (listEl.firstChild) listEl.removeChild(listEl.firstChild);
        // iOS の VoiceOver 操作性向上: 全選択/全解除
        if (items.length > 0) {
            const tools = document.createElement('div');
            tools.className = 'checkbox-item';
            const btnAll = document.createElement('button');
            btnAll.type = 'button';
            btnAll.className = 'btn';
            btnAll.textContent = 'すべて選択';
            const btnNone = document.createElement('button');
            btnNone.type = 'button';
            btnNone.className = 'btn';
            btnNone.textContent = 'すべて解除';
            btnAll.addEventListener('click', () => {
                listEl.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = true);
            });
            btnNone.addEventListener('click', () => {
                listEl.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
            });
            tools.appendChild(btnAll);
            tools.appendChild(btnNone);
            listEl.appendChild(tools);
        }
        for (const it of items) {
            const id = 'ios_out_' + encodeURIComponent(String(it.name)).replace(/%/g, '_');
            const wrap = document.createElement('label');
            wrap.className = 'checkbox-item';
            const cb = document.createElement('input');
            cb.type = 'checkbox';
            cb.value = String(it.name);
            cb.id = id;
            if (selectedSet && selectedSet.has(cb.value)) cb.checked = true;
            const txt = document.createElement('span');
            txt.textContent = String(it.name);
            wrap.appendChild(cb);
            wrap.appendChild(txt);
            listEl.appendChild(wrap);
        }
    }

    window.populateOutputLayoutOptions = function(tableName, selectedValues, json) {
        try {
            const raw = (typeof json === 'string') ? JSON.parse(json) : json;
            __outputLayoutTreeRaw = raw;
            const list = flattenLayoutTree(raw, []);
            // items に table 情報が含まれていれば tableName でフィルタ、
            // 含まれていなければフィルタせず全件表示
            const hasAnyTableInfo = list.some(it => String(it.table || '').length > 0);
            const table = String(tableName ?? '');
            const filtered = (hasAnyTableInfo && table)
                ? list.filter(it => String(it.table || '') === table)
                : list;
            // 指定レイアウト名を除外（出力候補から外す）
            // - Remarks_data
            // - Remarks_data_layout
            // - Remarks_data | phrase_picker
            const EXCLUDED_OUTPUT_LAYOUTS = new Set([
                'Remarks_data',
                'Remarks_data_layout',
                'Remarks_data | phrase_picker'
            ]);
            const filtered2 = filtered.filter(it => !EXCLUDED_OUTPUT_LAYOUTS.has(String(it?.name || '')));
            const select = document.getElementById(ID.output);
            const listBox = document.getElementById('ios_output_layout_list');
            if (!select || !listBox) return;

            const selected = normalizeSelectedValues(selectedValues);
            // 常にチェックボックス UI を使用
            select.style.display = 'none';
            listBox.style.display = '';
            renderOutputLayoutsIntoCheckboxList(listBox, filtered2, selected);
        } catch (e) {
            console.error('populateOutputLayoutOptions error:', e);
        }
    };
    </script>
</body>
</html>
]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_setting::9E261C827302A0D36D8120D16C479926" repetition="1" UUID="DAD71E35-507A-4D1F-A760-CFA587229AA4">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[<!DOCTYPE html>
<!--
  文章作成支援アドオン / index.html
  目的:
    - 文章を作成・編集・管理する単一ページアプリケーションの中核ファイル。
  概要:
    - 純粋な HTML/CSS/JavaScript で構成（ビルド/外部依存なし）。
    - セキュリティ: head 内の Content-Security-Policy を採用（inline 許可の最小化を維持）。
  構成の目安（本ファイル内の主な塊）:
    - <style> 内: グローバル CSS 変数、ユーティリティ、レイアウト、UI コンポーネント、テンプレート/変数入力、モーダル、通知など。
    - <body> 内: ヘッダー、メイン領域、フッター、ダイアログ/ポップオーバー、テンプレート、各種ビューのラッパ。
    - <script> 内: ビュー制御、テンプレート適用、イベント配線、ストレージ/入出力、ユーティリティ群。
  関連ファイル:
    - field_mapping.html: フィールド名とテンプレート変数の対応付け UI。
    - data.json / fields.csv / render.json: 入出力に関わるサンプル/補助データ。
  編集ガイド:
    - 命名: 役割が推測できる語を用い、省略名称は避ける（例: btnSave, templateRenderer 等）。
    - DOM: セレクタは id を優先。複数箇所で使う要素は data-* 属性で役割を明示。
    - アクセシビリティ: インタラクティブ要素は必ずロール/ラベル/フォーカス順を確認。
    - レイアウト: 余白/影は CSS 変数とユーティリティクラスを活用（--space-* / .u-*）。
  変更時のチェックリスト:
    1) CSP を破らない（外部スクリプト/スタイル追加時は要見直し）。
    2) フッター固定/モーダル重なりの z-index を維持。
    3) モバイル表示（viewport-fit、ソフトキーボード）でのオーバーフロー確認。
    4) ローカル保存/読み込み（該当する場合）の I/O エラーハンドリング確認。
  メモ:
    - 本コメントは保守向け概要。実装詳細は各セクション先頭のブロックコメントを参照。
  貢献ガイドライン（簡易）:
    - コードスタイル: 役割が伝わる命名・JSDoc コメント・早期 return を推奨。
    - ログ: 直接 console を使わず `App.log` を経由（レベル/有効化の切替が可能）。
    - イベント: `addEventListener` 直呼び出しではなく、安全ラッパ（`App.utils.addSafeEventListener`）の利用を検討。
    - 初期化: DOM 準備後に実行すべき処理は `App.safeInit` を利用（多重起動防止/例外隔離）。
-->
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
    <!-- TODO: インラインJS/CSS撤廃後に 'unsafe-inline' を削除する（短期計画の下準備） -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self';">
    <title>文章作成支援アドオン</title>
    <style>
        /**
         * ========================================
         * 文章作成支援アドオン - メインスタイルシート
         * ========================================
         * 
         * 構成:
         * 1. CSS変数定義
         * 2. 基本レイアウト（フレックスボックス）
         * 3. レスポンシブ対応
         * 4. モーダル・ポップオーバー
         * 5. フォーム要素
         * 6. ボタン・アイコン
         * 7. テンプレート機能
         * 8. 変数入力機能
         * 9. ユーティリティクラス
         */

        /* CSS変数定義 - システム全体で使用する値 */
        :root {
            --footer-h: 56px;
            --header-h: 60px;
            --color-border: #ddd;
            --color-bg-subtle: #f8f9fa;
            --shadow-top: 0 -2px 5px rgba(0, 0, 0, 0.1);
            --shadow-header: 0 2px 5px rgba(0, 0, 0, 0.08);
            --shadow-card: 0 4px 15px rgba(0, 0, 0, 0.15);
            --shadow-popover: 0 8px 24px rgba(0, 0, 0, 0.18);
            /* z-index scale */
            --z-base: 1;
            --z-layer-2: 2;
            --z-layer-3: 3;
            --z-menu: 10;
            --z-drag: 50;
            --z-card-float: 1000;
            --z-overlay-dim: 11998;
            --z-modal-overlay: 11999;
            --z-modal: 12000;
            --z-modal-ui: 12001;
            --z-footer: 12990;
            --z-toast: 13000;
            --z-popover: 13050;
            /* Spacing scale */
            --space-0: 0;
            --space-2: 2px;
            --space-4: 4px;
            --space-6: 6px;
            --space-8: 8px;
            --space-10: 10px;
            --space-12: 12px;
            --space-14: 14px;
            --space-16: 16px;
            --space-20: 20px;
            --space-24: 24px;
            --space-30: 30px;
            --space-40: 40px;
        }

        /* ユーティリティクラス */
        .u-border { border: 1px solid var(--color-border) !important; }
        .u-bg-subtle { background-color: var(--color-bg-subtle) !important; }
        .u-shadow-top { box-shadow: var(--shadow-top) !important; }
        .u-shadow-header { box-shadow: var(--shadow-header) !important; }
        .u-card { background: #fff !important; border-radius: 8px !important; box-shadow: var(--shadow-card) !important; }
        .u-flex-end { display:flex !important; justify-content:flex-end !important; }
        .u-gap-8 { gap: var(--space-8) !important; }
        .u-gap-10 { gap: var(--space-10) !important; }
        .is-hidden { display: none !important; }
        /* Spacing utilities */
        .u-p-8 { padding: var(--space-8) !important; }
        .u-p-10 { padding: var(--space-10) !important; }
        .u-p-12 { padding: var(--space-12) !important; }
        .u-p-16 { padding: var(--space-16) !important; }
        .u-px-16 { padding-left: var(--space-16) !important; padding-right: var(--space-16) !important; }
        .u-py-10 { padding-top: var(--space-10) !important; padding-bottom: var(--space-10) !important; }
        .u-mb-0 { margin-bottom: 0 !important; }
        .u-mb-8 { margin-bottom: var(--space-8) !important; }
        .u-mb-10 { margin-bottom: var(--space-10) !important; }
        /* トースト通知 */
        .toast-container {
            position: fixed;
            left: 50%;
            bottom: calc(var(--footer-h) + var(--space-16));
            transform: translateX(-50%);
            z-index: var(--z-toast);
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: var(--space-8);
            pointer-events: none;
        }
        .toast-message {
            background: rgba(0, 0, 0, 0.85);
            color: #fff;
            border-radius: 8px;
            padding: var(--space-10) var(--space-14);
            font-size: 14px;
            line-height: 1.4;
            box-shadow: 0 4px 12px rgba(0,0,0,0.25);
            opacity: 0;
            transform: translateY(6px);
            transition: opacity 160ms ease, transform 160ms ease;
            max-width: min(92vw, 520px);
            pointer-events: auto;
        }
        .toast-message.show {
            opacity: 1;
            transform: translateY(0);
        }

        /**
         * ========================================
         * 基本レイアウト設定
         * ========================================
         */

        /* ページ全体の基本設定 */
        body, html {
            margin: 0;
            padding: 0;
            height: 100%;
            overflow: hidden; /* 本体のスクロールバーを非表示 */
        }
        body {
            display: flex;
            flex-direction: column;
            min-height: 100dvh; /* モバイルの動的ビューポートに追随 */
            font-family: system-ui, -apple-system, "Hiragino Kaku Gothic ProN", Meiryo, Arial, sans-serif;
            box-sizing: border-box;
            background-color: #ffffff;
            color: #333;
        }
        @supports not (min-height: 100dvh) {
            body { min-height: 100vh; }
        }
        /* iOS等で100vh問題が出た場合の保険（必要に応じて有効化）
        html { height: -webkit-fill-available; }
        body { min-height: -webkit-fill-available; }
        */
        .page-body {
            position: relative;
            flex: 1 1 auto;
            min-height: 0;
            overflow: hidden; /* 子でスクロール */
            display: flex;
            height: calc(100dvh - var(--footer-h));
        }

        .container {
            flex: 1 1 auto; /* 残り高さを占有 */
            display: flex;
            flex-wrap: wrap;
            width: 100%;
            max-width: 1200px;
            margin: var(--space-20) auto;
            min-height: 0; /* 子のoverflowを有効にするため */
            box-sizing: border-box;
            overflow: hidden; /* 子カラムでスクロール */
            height: calc(100dvh - var(--footer-h)); /* 横並び時に各カラムへ明確な高さを提供 */
            background: #fff;
            border-radius: 8px;
        }
        .column {
            flex: 1 1 50%;
            position: relative;
            height: 100%;
            max-height: 100%;
            min-height: 0; /* 親flex内でのスクロールを許可 */
            overflow-y: auto;
            -webkit-overflow-scrolling: touch; /* iOS Safari */
            overscroll-behavior: contain;
            border-right: 1px solid var(--color-border);
            box-sizing: border-box;
            padding-bottom: calc(var(--footer-h) + var(--space-8)); /* 固定フッターの下に内容が隠れないよう余白 */
            scroll-padding-bottom: calc(var(--footer-h) + var(--space-16));
            /* フッターより前面に来ないように（z-indexコンテキストを作らない） */
        }

        .column:last-child {
            border-right: none;
        }
        .column h3 {
            margin-top: 0;
            font-size: 1.5em;
            color: #555;
            border-bottom: none;
            padding-bottom: 0;
        }
        textarea {
            width: 100%;
            box-sizing: border-box;
            font-size: 1em;
            padding: var(--space-10);
            margin-bottom: var(--space-20);
            border: 1px solid var(--color-border);
            border-radius: 4px;
            background-color: #fefefe;
            resize: vertical;
            /* iOS Safari の anchor スクロールによるジャンプ抑止 */
            overflow-anchor: none;
        }
        /* 全体備考と同様に手動リサイズを許可する対象 */
        #global-note-textarea,
        #direct-input,
        #original-textarea,
        #modalTextarea,
        #createTemplateContent,
        .modal-content textarea,
        .inline-popover textarea.variable-input {
            resize: vertical;
            overflow: auto;
        }
        #createTemplateContent,
        #modalTextarea { overflow: auto !important; max-height: none; }
        textarea:focus {
            outline: none;
            border-color: #007BFF;
            box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
        }
        label {
            font-weight: bold;
            display: block;
            margin: 0;
        }
        .content-area > div,
        .variable-row,
        .modal-field-row {
            margin-bottom: 15px;
        }
        @media (max-width: 768px) {
            html, body { overflow: auto; }
            .page-body, .container {
                margin: 0;
                border-radius: 0;
                overflow: visible; /* ページ全体でスクロール */
            }
            .container { display: flex; flex-direction: column !important; flex-wrap: nowrap !important; height: auto; }
            .column {
                flex: 0 0 auto;
                width: 100%;
                max-width: 100%;
                height: auto;
                min-height: auto;
                overflow-y: visible; /* カラム内スクロールは無効化 */
                border-right: none; /* 横境界線を削除 */
            }
            /* 縦並び時の区切りは2カラム以上並ぶ場合のみ表示（残骸の横線を防止） */
            .column + .column { border-bottom: 1px solid var(--color-border); }
        }
        /* 重複定義を整理: .tooltip と .display-text は下部で集約定義 */
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            /* フッターより前面、トーストより下に配置（--z-modal-overlay） */
            z-index: var(--z-modal-overlay);
        }

        .modal-content {
            position: relative;
            background-color: #fff;
            margin: 3% auto;
            padding: 0; /* 外枠のパディングを除去し、フッターが高く見えないように */
            width: 90%;
            max-width: 900px;
            max-height: 80vh;
            overflow: hidden; /* 内側で本文/フッターを分離するため */
            border-radius: 8px;
            box-shadow: var(--shadow-card);
            /* テンプレート選択はドラッグ不要。全体でのドラッグUIは無効化 */
            overflow-x: hidden;
        }
        /* カードウインドウ（モーダル）の影をやや濃くする */
        /* 編集モーダル: 本文スクロール専用内側ラッパー（ボタンは常時下端表示） */
        #editModal { overflow: hidden; }
        #editModal .modal-content { height: 100%; }
        #editModal .modal-header { position: sticky; top: 0; z-index: var(--z-base); }
        #editModal .modal-buttons { position: sticky; bottom: 0; z-index: var(--z-base); }
        #editModal .modal-scroll-body { max-height: none; overflow: auto; -webkit-overflow-scrolling: touch; display: flex; flex-direction: column; }
        /* iPhone/小画面用: モーダル全面表示（ドラッグ不可） */
        .modal-content.fullscreen {
            margin: 0 !important;
            width: 100% !important;
            max-width: 100% !important;
            height: 100% !important;
            max-height: none !important;
            border-radius: 0 !important;
            box-sizing: border-box;
            /* セーフエリア考慮 */
            padding-left: env(safe-area-inset-left);
            padding-right: env(safe-area-inset-right);
            padding-top: env(safe-area-inset-top);
            padding-bottom: env(safe-area-inset-bottom);
            overflow: hidden; /* 内側のgridでスクロールを管理 */
        }

        /* すべてのモーダルはヘッダー + 本文 + フッターの3行構成 */
        .modal .modal-content {
            display: grid;
            grid-template-rows: auto 1fr auto; /* ヘッダー + 本文(スクロール) + フッター */
            z-index: var(--z-modal); /* オーバーレイ（--z-modal-overlay）より上 */
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.24), 0 16px 56px rgba(0, 0, 0, 0.20);
        }
        /* モーダルの開閉状態クラス（JS同期） */
        .modal.is-open { display: block !important; }
        /* 内容編集モーダル: ヘッダー/本文/フッターの3行グリッドで本文をスクロール */
        /* 見出しの下余白をテンプレートと統一 */
        #editModal #editModalTitle { margin: 0; }
        .modal .modal-buttons,
        .phrase-popover .modal-buttons {
            background-color: var(--color-bg-subtle); /* フッターと同色 */
            padding: var(--space-10) var(--space-10); /* フッターと同じパディング */
            margin-top: 0; /* フッターは余白なし */
            border-top: 1px solid #eee;
            box-shadow: var(--shadow-top); /* フッターの影に合わせる */
            display: flex;
            justify-content: flex-end;
            align-items: center;
            box-sizing: border-box;
            z-index: var(--z-modal-ui);
        }
        /*
         * モーダル共通ヘッダー
         * - すべてのモーダルで header-left / header-right の二分構造に統一
         * - 見出し(h3)は header-left に、操作アイコン（検索/新規/?）は header-right に配置
         * - このルールにより、テンプレート/編集で完全に同一のレイアウトを保つ
         */
        .modal .modal-header,
        .phrase-popover .modal-header {
            background-color: var(--color-bg-subtle);
            padding: var(--space-10) var(--space-10);
            border-bottom: 1px solid #eee;
            box-shadow: var(--shadow-header);
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: var(--space-10);
            box-sizing: border-box;
            z-index: var(--z-modal-ui);
        }
        /* モーダルのヘッダー右側コンテナ（テンプレ/編集で共通化） */
        #templateModal .modal-header .header-right,
        #editModal .modal-header .header-right {
            display: flex;
            align-items: center;
            gap: var(--space-8);
        }
        
        /* テンプレート選択モーダル: 件数行を非表示 */
        #templateModal .template-meta-row { display: none !important; }
        /* フレーズヘッダーと同様にタイトルの下余白を除去 */
        #templateModal #templateModalTitle { margin: 0; }
        /* ヘッダー内での行要素の余白を調整 */
        #templateModal .modal-header .template-search-row { margin-bottom: 0; }
        #templateModal .modal-header .template-actions-row { margin-bottom: 0; }
        /*
         * モード別: ヘルプと検索ボタンの表示切替（DOM構造は共通のまま切替）
         * - #templateModal[data-mode] 属性で視覚状態のみを変更（DOMを増減させない）
         * - create/edit: 検索は隠す / [?] は表示
         * - select(=data-mode無し): 検索は表示 / [?] は隠す
         */
        #templateModal[data-mode="create"] #templateSearchToggle,
        #templateModal[data-mode="create"] #templateSearchClose,
        #templateModal[data-mode="edit"] #templateSearchToggle,
        #templateModal[data-mode="edit"] #templateSearchClose { display: none !important; }
        #templateModal[data-mode="create"] #syntaxHelpTriggerTemplateEdit,
        #templateModal[data-mode="edit"] #syntaxHelpTriggerTemplateEdit { display: inline-flex !important; }
        #templateModal:not([data-mode]) #syntaxHelpTriggerTemplateEdit { display: none !important; }
        /* フレーズUIに合わせてヘッダー内検索UIのサイズ・揃えを統一 */
        #templateModal .modal-header .template-search { width: 220px; height: 32px; margin: 0; }
        #templateModal .modal-header #templateSearchRow { align-items: center; gap: 6px; margin: 0; }
        #templateModal .modal-header #copySearchRow { align-items: center; gap: 6px; margin: 0; }
        /* ヘッダー分の見た目調整 */
        #templateModal .template-modal-body { padding-top: var(--space-12); padding-bottom: calc(var(--footer-h) + 80px); }
        @supports (padding: env(safe-area-inset-bottom)) {
            #templateModal .template-modal-body { padding-bottom: calc(var(--footer-h) + env(safe-area-inset-bottom) + 80px); }
        }
        /* 本文の最下部固定余白は不要（実フッター高さで計算するため） */
        #templateModal .template-modal-body::after { content: none; display: none; height: 0; }
        .modal .modal-buttons > .bar,
        .phrase-popover .modal-buttons > .bar { display:flex; gap: var(--space-10); justify-content:flex-end; }
        /* 本文スクロール領域 */
        .modal .modal-scroll-body,
        #templateModal .template-modal-body {
            overflow: auto;
            -webkit-overflow-scrolling: touch;
            padding: var(--space-16) var(--space-16) 0; /* 下端に余白を設けず、フッターと密着 */
            scroll-padding-bottom: calc(var(--footer-h) + var(--space-16));
        }
        /* テンプレート作成/編集時の本文下余白を拡張（上記のpadding: ... 0 を後勝ちで上書き）*/
        #templateModal .template-modal-body { padding-bottom: calc(var(--footer-h) + 80px); }
        @supports (padding: env(safe-area-inset-bottom)) {
            #templateModal .template-modal-body { padding-bottom: calc(var(--footer-h) + env(safe-area-inset-bottom) + 80px); }
        }
        /* 新規作成/編集の本文高さをウインドウに追従させるための柔軟レイアウト */
        #templateModal .template-modal-body { display: flex; flex-direction: column; min-height: 0; }
        #templateCreateSection { display: none; flex: 1 1 auto; min-height: 0; }
        #templateCreateSection .modal-field-row { min-height: 0; }
        #templateCreateSection #createTemplateContent { flex: 1 1 auto; min-height: 0; }
        #editModal .modal-scroll-body { overflow: auto; -webkit-overflow-scrolling: touch; padding: var(--space-16) var(--space-16) calc(var(--footer-h) + var(--space-40)); display: flex; flex-direction: column; }
        #editModal .modal-scroll-body::after { content: ''; display: block; height: 128px; }
        @supports (padding: env(safe-area-inset-bottom)) {
            #editModal .modal-scroll-body { padding-bottom: calc(var(--footer-h) + env(safe-area-inset-bottom) + var(--space-40)); }
        }
        #editModal #modalTemplateName { flex: 0 0 auto; }
        #editModal .modal-scroll-body { display: flex; flex-direction: column; min-height: 0; }
        #editModal #modalTextarea { flex: 1 1 auto; min-height: 120px; height: auto; overflow: auto; min-height: 0; }
        #editModal .modal-option-row { flex: 0 0 auto; }

        /* 確認/メッセージのカードダイアログ: 共通スタイルに統一（小さめ幅・中央寄せ） */
        #confirmModal,
        #messageModal {
            z-index: var(--z-toast);
        }
        #confirmModal .modal-content,
        #messageModal .modal-content {
            max-width: 500px;
            margin: 10% auto;
        }
        #confirmModal .modal-scroll-body { padding: var(--space-30); text-align: center; }
        #messageModal .modal-scroll-body { padding: var(--space-24); text-align: center; }
        #confirmModal .modal-buttons,
        #messageModal .modal-buttons { justify-content: center; gap: var(--space-15, 15px); }
        #confirmModalTitle { margin: 0; color: #dc3545; }
        #messageModalTitle { margin: 0; color: #0d6efd; }
        #confirmModalMessage { font-size: 1.1em; margin-bottom: 30px; color: #495057; }
        #messageModalMessage { font-size: 1.05em; margin-bottom: 24px; color: #495057; }

        /* ドラッグ中はテキスト選択を抑制 */
        body.dragging, body.dragging * { user-select: none !important; }

        .modal-content h3 {
            margin-top: 0;
            color: #2c3e50;
        }

        .modal-content textarea {
            min-height: 180px; /* 元々より小さくして可変領域を広げる */
            width: 100%;
            box-sizing: border-box;
            overflow: auto; /* 長文時はスクロール */
            resize: vertical; /* ユーザーリサイズは維持 */
        }

        /* 入力系のフォントを本文に統一 */
        input, select, button, textarea {
            font-family: inherit;
        }
        /* ヘルプ（はてな）アイコン */
        .help-icon {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 20px;
            height: 20px;
            margin-left: 8px;
            border-radius: 50%;
            background-color: #e9ecef;
            color: #333;
            font-weight: bold;
            font-size: 13px;
            cursor: pointer;
            user-select: none;
        }
        .help-icon:hover { background-color: #dee2e6; }
        /* 見出し右上に固定配置 */
        .modal-header { position: relative; }
        .modal-header .help-icon {
            position: absolute;
            right: 12px;
            top: 10px;
            margin-left: 0;
            z-index: var(--z-layer-2);
        }
        /*
         * ヘルプ[?] の配置/サイズ
         * - 以前は絶対配置だったが、header-right に収めるため static に変更
         * - [+] と同じ 36px 角 / 角丸 / ホバー色に統一
         */
        #templateModal .modal-header .help-icon,
        #editModal .modal-header .help-icon,
        #phraseModal .modal-header .help-icon {
            position: static;
            right: auto;
            top: auto;
            margin-left: 8px;
            /* [+] と同じサイズ/見た目に揃える */
            width: 36px;
            height: 36px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            line-height: 1;
            box-sizing: border-box;
            padding: 0;
            border-radius: 6px;
            border: 1px solid transparent;
            background-color: #fff;
            color: #333;
            font-size: 18px;
            cursor: pointer;
            transition: background 0.2s ease, border-color 0.2s ease, filter 0.2s ease;
        }
        #templateModal .modal-header .help-icon:hover,
        #editModal .modal-header .help-icon:hover,
        #phraseModal .modal-header .help-icon:hover { background-color: #f1f3f5; }
        /* ヘルプポップオーバー */
        .syntax-help-popover {
            position: absolute;
            z-index: var(--z-popover);
            max-width: 600px;
            background: #fff;
            color: #333;
            border: 1px solid var(--color-border);
            border-radius: 8px;
            box-shadow: 0 8px 24px rgba(0,0,0,0.12);
            padding: 12px 14px;
            display: none;
        }
        .syntax-help-popover h4 { margin: 0 0 6px 0; font-size: 14px; }
        .syntax-help-popover ul { margin: 6px 0 0 1.1em; padding:0; }
        .syntax-help-popover li { margin: 2px 0; }
        /* 既存の説明ブロックは非表示（はてなアイコンから表示） */
        /* 縦に並ぶプルダウンの重なり回避と余白調整 */
        .curly-select {
            display: inline-block;
            vertical-align: middle;
            height: 32px;
            box-sizing: border-box;
            max-width: 100%;
            margin: 2px 0 4px 0; /* 上下に少し余白 */
        }
        #display-text .curly-select {
            display: inline-block;
            vertical-align: middle;
        }

        /* iPhone等の小画面でフォーカス時の自動ズームを防ぐ（入力だけ16px以上） */
        @media (max-width: 480px) {
            input, select, textarea,
            .variable-input,
            .modal-content input[type="text"],
            .modal-content textarea,
            #global-note-textarea,
            #direct-input,
            #original-textarea {
                font-size: 16px !important;
            }
        }
        /* iOS デバイスでは備考欄を内部スクロールに（ジャンプ抑止の現実的対処） */
        .is-ios #global-note-textarea {
            overflow: auto;
            -webkit-overflow-scrolling: touch;
        }
        /* iOS デバイスでは内容入力欄（original/direct）も内部スクロールに */
        .is-ios #original-textarea,
        .is-ios #direct-input {
            overflow: auto;
            -webkit-overflow-scrolling: touch;
        }
        /* iPad の場合は「内容」「備考」入力欄の最小高さを拡大（約2.5倍） */
        .is-ipad #original-textarea,
        .is-ipad #direct-input,
        .is-ipad #global-note-textarea {
            min-height: 240px;
        }
        /* #editModal 専用の入力スタイルは削除し、
           共通スタイル（.modal-content input[type="text"] など）に統一 */
        /* 変数設定モーダルの入力見た目を他モーダルと統一 */
        #varSettingsModal input[type="text"] {
            width: 100%;
            box-sizing: border-box;
            padding: 8px;
            border: 1px solid var(--color-border);
            border-radius: 4px;
            font-size: 1em;
        }
        #varSettingsModal input[type="text"]:disabled {
            border: none;
            box-shadow: none;
            background: transparent;
        }
        #varSettingsModal textarea {
            width: 100%;
            box-sizing: border-box;
            font-size: 1em;
            padding: 8px;
            border: 1px solid var(--color-border);
            border-radius: 4px;
            resize: vertical;
            min-height: 80px; /* 設定用なので小さめ */
        }

        /* 変数設定モーダル内のセレクトを他の入力と統一 */
        #varSettingsModal select {
            height: 36px;
            box-sizing: border-box;
            border: 1px solid var(--color-border);
            border-radius: 6px;
            background-color: #fff;
            padding: 6px 8px;
            font-size: var(--font-size-base);
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
        }
        #varSettingsModal select:focus {
            outline: none;
            border-color: #007BFF;
            box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
        }
        /* 変数設定モーダル内のテキスト入力/テキストエリアも共通スタイルで統一 */
        #varSettingsModal input[type="text"],
        #varSettingsModal textarea {
            font-size: var(--font-size-base);
            line-height: var(--line-height-base);
            padding: 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            background-color: #fefefe;
        }
        #varSettingsModal input[type="text"]:focus,
        #varSettingsModal textarea:focus {
            outline: none;
            border-color: #007BFF;
            box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
        }

        /* .modal-buttons は下部でflexレイアウトとして定義。不要な .modal-button は削除 */

        .cancel-button {
            background-color: #6c757d;
            color: white;
        }

        .save-button {
            background-color: #007BFF;
            color: white;
        }
        .edit-button {
            padding: 0;
            background-color: transparent;
            color: #333;
            border: none;
            cursor: pointer;
            font-size: 1.2em;
            position: relative;
        }
        /* .edit-button のアイコンは下部に集約 */

        /* 検索ボタン */
        .search-button {
            padding: 0;
            background-color: transparent;
            color: #333;
            border: none;
            cursor: pointer;
            font-size: 1.2em;
            position: relative;
        }

        .display-text {
            background-color: #fff;
            border: 1px solid #e0e0e0;
            border-radius: 6px;
            padding: 15px;
            margin-top: 0;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05);
            white-space: pre-wrap; /* 改行と空白を表示に反映 */
            overflow-wrap: anywhere; /* 非常に長い単語やURLを折り返す */
            word-break: break-word;  /* Safari等での後方互換用 */
            hyphens: auto;           /* 必要に応じてハイフネーションを有効化 */
        }

        .memo-indicator {
            display: inline-block;
            margin-left: 8px;
            padding: 2px 6px;
            font-size: 0.75em;
            color: rgba(60, 64, 67, 0.8); /* グレー・やや透明 */
            background: rgba(0, 0, 0, 0.05); /* 薄いグレー */
            border: 1px solid rgba(0,0,0,0.08);
            border-radius: 6px;
            vertical-align: middle;
        }

        .clickable-placeholder {
            cursor: pointer;
            color: #007BFF;
            transition: color 0.2s;
            font-weight: normal; /* strong でも太字にしない */
        }

        .clickable-placeholder:hover {
            color: #0056b3;
        }
        /* 現在編集中の {{変数}} を強調 */
        .clickable-placeholder.editing {
            background-color: #fff3cd;
            box-shadow: inset 0 0 0 2px #ffe08a;
            border-radius: 3px;
        }

        /* 閉じた直後や反映後に一時的に点灯させるアニメーション */
        @keyframes highlightFlash {
            0% { box-shadow: inset 0 0 0 2px #ffe08a; }
            100% { box-shadow: inset 0 0 0 0 rgba(255,224,138,0); }
        }
        .clickable-placeholder.editing.flash-highlight {
            animation: highlightFlash 1s ease-out;
        }

        /* 「次回も」設定がある変数は下線表示 */
        .clickable-placeholder.persist {
            text-decoration: underline;
            text-underline-offset: 2px;
        }

        /* 内容内に表示するタイプアイコン */
        .clickable-placeholder .placeholder-type-icon {
            width: 1em;
            height: 1em;
            color: #9aa0a6; /* 薄めのグレー */
            opacity: 0.7;   /* 目立ちすぎないように */
            display: inline-block;
            overflow: visible;
        }

        /* タイプアイコンのラッパー（メモドット重ね用） */
        .clickable-placeholder .type-icon-wrapper {
            position: relative;
            display: inline-block;
            margin-left: 4px;
            vertical-align: -0.15em;
        }

        /* メモがある場合に、タイプアイコン右下へ小さなグレーの丸を表示 */
        .clickable-placeholder.has-memo .type-icon-wrapper::after {
            content: '';
            position: absolute;
            right: -1px;
            bottom: 0.2em;
            width: 0.32em;
            height: 0.32em;
            background: #9aa0a6; /* gray */
            border-radius: 50%;
        }
        /* 「メモを表示する」ONのときは丸を非表示 */
        body.show-memos-inline .clickable-placeholder.has-memo .type-icon-wrapper::after {
            content: none;
        }
        .clickable-placeholder.has-memo .type-icon-wrapper::before {
            content: none; /* 吹き出しのしっぽは使わない */
        }

        .title-section {
            position: sticky;
            top: 0;
            background: white;
            padding: 0 20px;
            height: var(--header-h);
            display: flex;
            align-items: center;
            z-index: var(--z-menu);
            border-bottom: 1px solid #eee;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
            margin-bottom: 0; /* ヘッダー下の余白をなくす */
            flex: 0 0 auto;
        }

        .title-section h3 {
            margin: 0;
            font-size: 1.2em;
            color: #555;
            line-height: 1.2;
        }
        .edit-section {
            display: flex;
            align-items: center;
            justify-content: space-between;
            width: 100%;
            margin: 0;
        }
        .title-left { display: inline-flex; align-items: center; gap: var(--space-8); }
        .change-nav { display: inline-flex; align-items: center; gap: 4px; }
        .change-nav .search-button[disabled] { opacity: 0.4; cursor: default; }
        .change-status { min-width: 56px; text-align: center; color: #555; font-size: 0.95em; }

        

        .content-area {
            padding: var(--space-8) 20px var(--header-h); /* 下側をヘッダーと同じに */
            min-height: 0; /* 内側の伸びで親の100%高を超えないように */
        }
        /* 全体備考セクション */
        .global-note-section { margin-top: 12px; }
        .global-note-section label { display: block; font-weight: bold; margin: 0; }
        /* 共通のボタンスタイル（統一） */
        .button {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: var(--space-6);
            padding: var(--space-8) var(--space-16);
            min-height: 36px;
            border: 1px solid transparent;
            border-radius: 6px;
            font-weight: 600;
            font-size: 0.95em;
            color: #fff;
            background-color: #0d6efd;
            cursor: pointer;
            text-decoration: none;
            user-select: none;
            transition: background-color 0.2s ease, filter 0.2s ease, opacity 0.2s ease;
            white-space: nowrap;
            word-break: keep-all;
        }

        .button:hover {
            background-color: #0056b3;
        }

        /* キャンセルボタン用のスタイル */
        .button-secondary { background-color: #6c757d; color: #fff; border-color: #6c757d; }

        .button-secondary:hover {
            background-color: #5a6268;
        }

        /* 小画面では水平パディングを少し減らして収まりを良くする */
        @media (max-width: 480px) {
            .button { padding: var(--space-8) var(--space-12); }
        }

        /* 危険操作（削除など）用のスタイル */
        .button-danger { background-color: #dc3545; border-color: #dc3545; }
        .button-danger:hover { background-color: #c82333; }
        .button:disabled, .button[disabled] { opacity: .5; cursor: default; pointer-events: none; }


        /* モーダルボタンコンテナ（構造統一: 右寄せ・一定パディング・上枠線） */
        .modal-buttons {
            display: flex;
            align-items: center;
            justify-content: flex-end;
            gap: var(--space-10);
            padding: var(--space-10);
            margin: 0;
            border-top: 1px solid #eee;
            background: #f8f9fa;
        }
        /* 内側に .bar がある場合でも見た目を揃える */
        .modal-buttons > .bar { display: flex; gap: var(--space-10); justify-content: flex-end; width: 100%; }



        .flex-container {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }
        #instruction-container { min-height: 36px; margin-bottom: 5px; }

        .action-buttons {
            display: inline-flex;
            align-items: center;
            gap: var(--space-10);
        }

        .title-actions {
            display: inline-flex;
            align-items: center;
            gap: var(--space-10);
        }

        /* テンプレート選択モーダル用 */
        .template-list {
            display: flex;
            flex-direction: column;
            gap: var(--space-8);
            margin-top: var(--space-10);
            padding-bottom: 160px; /* 一覧の最下部に十分な余白を確保 */
        }
        .template-item {
            border: 1px solid #e0e0e0;
            border-radius: 6px;
            padding: 12px;
            cursor: pointer;
            background: #fff;
            transition: background-color 0.15s, border-color 0.15s;
        }
        .template-item:hover {
            background: #f8fbff;
            border-color: #cfe2ff;
        }
        .template-item.is-deleted {
            background: #f5f5f5;
            color: #999;
            border-color: #e5e5e5;
        }
        .template-item.is-deleted .template-title { color: #9a9a9a; }
        .template-item.is-deleted .template-body { color: #9a9a9a; }
        .template-item.is-deleted { cursor: default; }
        .template-item.is-deleted .template-item-main { pointer-events: none; }
        .template-item.is-current { background: #f0f8ff; border-color: #b6d8ff; }
        .template-item.is-current:hover { background: #e8f2ff; border-color: #a6cfff; }
        /* 直前に編集していたテンプレートの強調表示 */
        .template-item.was-edited { background: #fff8e1; border-color: #ffd699; }
        /* ハイライト解除時のフェード専用（通常のホバー速度に影響させない） */
        .template-item.highlight-fade { transition: background-color 0.35s ease, border-color 0.35s ease; }
        
        /* ドラッグ&ドロップ UX 改善 */
        .template-item {
            transition: all 0.2s ease;
            position: relative;
        }
        .template-item.dragging {
            opacity: 0.3;
            transform: rotate(2deg);
            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
            z-index: var(--z-card-float);
        }
        .template-item.drag-over {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }
        
        /* ドロップゾーンインジケーター */
        .drop-indicator {
            height: 3px;
            background: #007bff;
            border-radius: 2px;
            margin: 2px 0;
            opacity: 0;
            transform: scaleX(0);
            transform-origin: left center;
            transition: opacity 0.15s ease, transform 0.15s ease;
            box-shadow: 0 0 8px rgba(0, 123, 255, 0.3);
            pointer-events: none;
        }
        .drop-indicator.active {
            opacity: 1;
            transform: scaleX(1);
        }
        
        /* ドラッグハンドル改善 */
        .template-item .drag-handle {
            opacity: 0.4;
            transition: opacity 0.2s ease;
            cursor: grab;
        }
        .template-item:hover .drag-handle {
            opacity: 0.8;
        }
        .template-item .drag-handle:active {
            cursor: grabbing;
        }
        
        /* ドラッグアクティブ時のリスト状態 */
        .drag-active {
            background: rgba(0, 123, 255, 0.02);
            border-radius: 4px;
        }
        .drag-active .template-item:not(.dragging) {
            transition: all 0.2s ease;
        }
        
        /* キーボードナビゲーション */
        .template-item:focus-within {
            outline: 2px solid #007bff;
            outline-offset: 2px;
        }
        .template-item .current-badge { display:inline-block; margin-left:8px; padding:2px 6px; font-size: 0.8em; color: #0d6efd; background: #e7f1ff; border: 1px solid #d0e3ff; border-radius: 999px; vertical-align: middle; }
        .template-item-row { display:flex; align-items:flex-start; gap: var(--space-8); }
        .template-item-main { flex: 1 1 auto; min-width: 0; }
        .template-item-actions { flex: 0 0 auto; position: relative; }
        .template-item-actions .more-btn { border:none; background: transparent; padding:4px; cursor: pointer; border-radius:4px; }
        .template-item-actions .more-btn:hover { background: #f0f4ff; }
        .template-item-actions .more-menu {
            position: absolute;
            top: 0;
            right: 100%; /* ボタンの左側へ表示 */
            transform: translateX(-8px); /* 少し間隔を空ける */
            background: #fff;
            border: 1px solid var(--color-border);
            border-radius: 6px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.12);
            display: none;
            min-width: 120px;
            z-index: var(--z-menu);
        }
        .template-item-actions .more-menu.open { display:block; }
        .template-item-actions .more-menu button {
            width: 100%;
            text-align: left;
            background: transparent;
            border: none;
            padding: 8px 10px;
            cursor: pointer;
        }
        .template-item-actions .more-menu button:hover { background: #f5f5f5; }
        .template-title {
            font-weight: bold;
        /* コピー選択モード: 新規ボタンと各行のアクション（3点リーダー含む）を隠す */
        #templateModal[data-mode="copy"] .header-right #templateNewButton { display: none !important; }
        #templateModal[data-mode="copy"] .template-item-actions { display: none !important; }
            margin: 0 0 6px 0;
            color: #333;
        }
        .template-desc {
            margin: 0;
            color: #666;
            font-size: 0.95em;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .template-meta {
            color: #666;
            font-size: 0.9em;
            margin-bottom: 8px;
        }
        .template-meta-row {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 8px;
        }
        /* テンプレート記法のヘルプ */
        .template-help {
            margin-top: 10px;
            padding: 10px 12px;
            border: 1px solid #e6e6e6;
            border-radius: 6px;
            background: #fafafa;
            color: #555;
            font-size: 0.92em;
            line-height: 1.6;
        }
        /* テンプレート作成/編集では構造上のヘルプ容器を強制的に非表示（余白も消す） */
        #templateSyntaxHelp { display: none !important; margin: 0 !important; padding: 0 !important; border: none !important; }
        .template-help b { color: #333; }
        .template-help code {
            background: #fff;
            border: 1px solid #eee;
            padding: 0 4px;
            border-radius: 3px;
            font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
        }
        
        .template-search-row {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 12px;
        }
        .template-search {
            width: 100%;
            height: 40px;
            padding: 0 12px;
            line-height: 40px; /* 垂直中央 */
            margin: 0;
            border: 1px solid var(--color-border);
            border-radius: 4px;
            font-size: 0.95em;
            box-sizing: border-box;
            flex: 1 1 auto;
        }
        .template-search:focus {
            outline: none;
            border-color: #007BFF;
            box-shadow: 0 0 5px rgba(0, 123, 255, 0.3);
        }
        .clear-button {
            padding: 0;
            background-color: transparent;
            color: #333;
            border: none;
            cursor: pointer;
            font-size: 1.2em;
            position: relative;
            height: auto;
        }
        /* アイコンボタン共通スタイル（全体の大きさを統一） */
        /* アイコンボタンの統一（外観/サイズ/ホバー） */
        .search-button,
        .clear-button,
        .new-button,
        .edit-button,
        .var-gear-btn {
            width: 36px;
            height: 36px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            line-height: 1;
            box-sizing: border-box;
            padding: 0;
            border-radius: 6px;
            border: 1px solid transparent;
            background-color: #fff;
            color: #333;
            font-size: 18px;
            cursor: pointer;
            transition: background 0.2s ease, border-color 0.2s ease, filter 0.2s ease;
        }
        .search-button:hover,
        .clear-button:hover,
        .new-button:hover,
        .edit-button:hover,
        .var-gear-btn:hover { background-color: #f1f3f5; }
        /* SVGアイコンの共通サイズ */
        .icon {
            width: 20px;
            height: 20px;
            display: block;
        }

        /* 新規作成ボタン */
        .new-button {
            padding: 0;
            background-color: transparent;
            color: #333;
            border: none;
            cursor: pointer;
            font-size: 1.2em;
            position: relative;
        }
        .template-item.hidden {
            display: none;
        }
        .template-pagination {
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 10px;
            margin-top: 12px;
            padding: 8px;
        }
        .pagination-info {
            color: #666;
            font-size: 0.9em;
        }
        .pagination-button {
            padding: 4px 8px;
            background-color: #007BFF;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 0.85em;
        }
        .pagination-button:hover {
            background-color: #0056b3;
        }
        .pagination-button:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }
        .search-loading {
            text-align: center;
            color: #666;
            font-style: italic;
            padding: 20px;
        }
        .no-results {
            padding: 16px;
            color: #666;
            text-align: center;
            border: 1px dashed #ddd;
            border-radius: 6px;
            background: #fafafa;
        }
        .template-body {
            margin: 4px 0 0 0;
            color: #444;
            font-size: 0.95em;
            white-space: normal;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .footer {
            position: fixed;
            inset: auto 0 0 0; /* 下端にピッタリ固定 */
            width: 100%;
            background-color: var(--color-bg-subtle);
            height: var(--footer-h);
            padding: 10px 10px; /* 下余白は @supports 内で安全域分を追加 */
            box-shadow: var(--shadow-top);
            display: flex;
            justify-content: flex-end;
            align-items: center;
            z-index: var(--z-footer); /* モーダル（--z-modal）より前、トースト（--z-toast）より下 */
            box-sizing: border-box;
            margin: 0; /* フッター外側のマージンを無効化 */
            opacity: 1;
            transform: translateY(0);
            transition: opacity 0.3s ease, transform 0.3s ease;
        }

        .footer.hidden {
            opacity: 0;
            transform: translateY(100%);
            pointer-events: none;
        }

        /* iOS等のセーフエリア下に本文が覗くのを防止 */
        @supports (padding: max(0px)) {
            .footer {
                padding-bottom: calc(var(--space-10) + env(safe-area-inset-bottom));
            }
            .column {
                padding-bottom: calc(var(--footer-h) + env(safe-area-inset-bottom) + var(--space-8));
            }
        }
        /* セーフエリアで底に空白が見えるのを避けるため、追加の下パディングは付けない */

        .footer-content {
            width: 100%;
            max-width: 1200px; /* コンテナーと同じ最大幅 */
            margin: 0; /* 外側マージンを無効化 */
            display: flex;
            justify-content: flex-end;
            padding: 10px; /* modal-buttons と同一の左右/上下パディングに合わせる */
        }
        .footer .button {
            margin-left: 10px;
        }
        
        /**
         * ========================================
         * タイポグラフィ正規化・テーマ設定
         * ========================================
         * 
         * 含まれる要素:
         * - フォントサイズ・行間の統一
         * - カラーテーマ
         * - レスポンシブ対応の文字サイズ
         * - アクセシビリティ考慮
         */
        :root {
            --font-size-base: 14px;
            --font-size-sm: 13px;
            --font-size-heading: 16px;
            --font-size-title: 18px;
            --line-height-base: 1.6;
        }
        
        html { font-size: 16px; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
        body {
            font-size: var(--font-size-base);
            line-height: var(--line-height-base);
        }
        /* Text blocks */
        .display-text,
        .content-area,
        .template-body,
        .template-desc,
        .template-meta,
        .pagination-info,
        #display-text {
            font-size: var(--font-size-base);
            line-height: var(--line-height-base);
        }
        /* renderUI メタ（控えめ表示） */
        #render-meta {
            margin-top: 4px;
            color: #6c757d; /* muted */
            font-size: 12px;
            line-height: 1.4;
            text-align: right;
            white-space: normal;
            user-select: text;
        }
        
        /* Headings */
        .title-section h3,
        .column h3,
        .modal-content h3 {
            font-size: var(--font-size-heading);
            line-height: 1.4;
        }
        
        /* Title emphasis (template list etc.) */
        .template-title { font-size: var(--font-size-heading); }
        
        /* Labels */
        label,
        .modal-field-label,
        .persist-label {
            font-size: var(--font-size-base);
            line-height: 1.4;
        }
        /* Form controls */
        input,
        select,
        button,
        textarea {
            font-size: var(--font-size-base);
            line-height: var(--line-height-base);
        }
        .template-search { font-size: var(--font-size-base); }
        .variable-type { font-size: var(--font-size-base); height: 32px; }
        
        /* Buttons */
        .button { font-size: var(--font-size-base); }
        .search-button,
        .edit-button,
        .clear-button { font-size: 1rem; }

        /* Variable inputs width/appearance (unify with textarea) */
        .variable-input:not([type="checkbox"]) {
            width: 100%;
            box-sizing: border-box;
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid var(--color-border);
            border-radius: 4px;
            background-color: #fefefe;
            line-height: 1.4;
        }
        .variable-input:not([type="checkbox"]):focus {
            outline: none;
            border-color: #007BFF;
            box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
        }

        /* チェックボックス専用のスタイル */
        .variable-input[type="checkbox"] {
            width: 18px;
            height: 18px;
            margin: 8px 0;
            cursor: pointer;
        }

        .checkbox-container {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 12px;
            border: 1px solid var(--color-border);
            border-radius: 6px;
            background-color: #fefefe;
            margin-bottom: 15px;
            transition: border-color 0.2s, background-color 0.2s;
        }

        .checkbox-container:hover {
            border-color: #007BFF;
            background-color: #f8f9ff;
        }

        .checkbox-container.checked {
            border-color: #28a745;
            background-color: #f0fff4;
        }
        .checkbox-label {
            font-weight: normal;
            cursor: pointer;
            user-select: none;
            flex: 1;
            color: #495057;
        }
        .checkbox-status {
            font-size: 0.9em;
            font-weight: 500;
            padding: 2px 8px;
            border-radius: 12px;
            background-color: #e9ecef;
            color: #6c757d;
            transition: all 0.2s;
        }
        .checkbox-container.checked .checkbox-status {
            background-color: #d4edda;
            color: #155724;
        }
        /* Missing variable visuals */
        .clickable-placeholder.missing {
            color: #d93025;
        }
        .variable-row .variable-input.is-empty:not([type="checkbox"]) {
            border-color: #d93025;
            box-shadow: 0 0 0 1px rgba(217, 48, 37, 0.15) inset;
        }

        .variable-row .checkbox-container.is-empty {
            border-color: #d93025;
            box-shadow: 0 0 0 1px rgba(217, 48, 37, 0.15);
        }

        /* [[]] チェックボックス項目の表示スタイル */
        .bracket-checkbox-display {
            font-weight: 600;
            padding: 4px 8px;
            border-radius: 4px;
            display: inline-block;
            margin: 2px;
            transition: background-color 0.2s, box-shadow 0.2s, transform 0.1s;
            cursor: pointer;
            user-select: none;
        }
        .bracket-checkbox-display:hover {
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            transform: translateY(-1px);
        }
        
        .bracket-checkbox-display:active {
            transform: translateY(0);
        }

        .bracket-checkbox-display.checked {
            background-color: #d4edda;
            color: #155724;
        }

        .bracket-checkbox-display.unchecked {
            background-color: #f8f9fa;
            color: #6c757d;
        }

        /* [[]] チェックボックス行のスタイル */
        .bracket-checkbox-row {
            border-left: 3px solid #17a2b8;
            padding-left: 10px;
            margin-bottom: 15px;
        }

        .bracket-checkbox-row .checkbox-container {
            margin-bottom: 0;
        }
        .variable-row.missing > div > label {
            color: #d93025;
        }
        /* =============================
           Unified input appearance
           （入力欄の見た目を統一）
           ============================= */
        
        .inline-popover .variable-input:not([type="checkbox"]),
        .modal-content input[type="text"],
        .modal-content textarea,
        #global-note-textarea,
        #direct-input,
        #original-textarea {
            display: block;
            width: 100%;
            box-sizing: border-box;
            font-size: var(--font-size-base);
            line-height: 1.6; /* 固定化: 同じline-heightで最低高さが一致するように */
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid var(--color-border);
            border-radius: 6px;
            background-color: #fefefe;
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
        }
        /* 検索入力は行内でアイコンと揃えるため下余白を無効化 */
        #templateSearch { margin: 0; }
        /* #form-container .variable-input:not([type="checkbox"]):focus, */
        .inline-popover .variable-input:not([type="checkbox"]):focus,
        .modal-content input[type="text"]:focus,
        .modal-content textarea:focus,
        #global-note-textarea:focus,
        #direct-input:focus,
        #original-textarea:focus {
            outline: none;
            border-color: #007BFF;
            box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
        }
        /* テキスト/数値/日付/時刻/日時の1行コントロールの高さ・パディングを統一（全体） */
        .variable-input[type="text"],
        .variable-input[type="number"],
        .variable-input[type="date"],
        .variable-input[type="time"],
        .variable-input[type="datetime-local"],
        .modal-content input[type="text"] {
            height: 36px;
            padding: 6px 10px; /* 元に戻す: 上下6px */
            line-height: 24px; /* テキストをわずかに上寄せにする */
        }

        /* WebKit系(date/time/datetime-local)の内側テキストを上下中央に揃える */
        .variable-input[type="date"],
        .variable-input[type="time"],
        .variable-input[type="datetime-local"] {
            -webkit-appearance: none;
            appearance: none;
        }
        .variable-input[type="date"]::-webkit-datetime-edit,
        .variable-input[type="time"]::-webkit-datetime-edit,
        .variable-input[type="datetime-local"]::-webkit-datetime-edit {
            padding: 0;
            height: 24px;
            line-height: 24px;
            margin-top: -3px; /* さらに1px上へ持ち上げる */
        }
        .variable-input[type="date"]::-webkit-datetime-edit-fields-wrapper,
        .variable-input[type="time"]::-webkit-datetime-edit-fields-wrapper,
        .variable-input[type="datetime-local"]::-webkit-datetime-edit-fields-wrapper {
            display: flex;
            align-items: center;
        }

        /* テキストエリアの最小高さを軽く確保（モーダルや備考も含め） */
        
        .inline-popover textarea.variable-input,
        .modal-content textarea,
        #direct-input,
        #original-textarea,
        #global-note-textarea {
            min-height: 96px; /* 統一: 6行前後ベース（line-height依存） */
            resize: vertical; /* 手動リサイズを許可（備考と同様） */
            overflow: auto;
        }
        /* 非常に長い単語やURLが入力された場合でも枠内に収める */
        #direct-input,
        #original-textarea {
            overflow-wrap: anywhere;
            word-break: break-word;
            hyphens: auto;
        }
        /* プレースホルダの色を統一（薄め） */
        
        .inline-popover .variable-input::placeholder,
        .modal-content input[type="text"]::placeholder,
        .modal-content textarea::placeholder,
        #global-note-textarea::placeholder,
        #direct-input::placeholder,
        #original-textarea::placeholder {
            color: #9aa0a6;
        }
        /* 無効状態の見た目統一 */
        .modal-content input[type="text"][disabled],
        .modal-content textarea[disabled],
        .inline-popover .variable-input[disabled] {
            background-color: #f8f9fa;
            color: #6c757d;
        }

        /* Variable settings gear and panel */
        .variable-row { position: relative; }
        .variable-header { display:flex; align-items:center; gap: var(--space-10); margin-bottom:5px; min-height: 36px; }
        .input-with-quick { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 15px; }
        .input-with-quick .variable-input { margin-bottom: 0 !important; flex: 1 1 auto; width: auto; }
        /* ポップオーバー内でも同じ整列を適用 */
        .inline-popover .input-with-quick { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 0; }
        .inline-popover .input-with-quick .variable-input { flex: 1 1 auto; width: auto; }
        .quick-set-btn {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            height: 36px; /* 入力(height:36px)と揃える */
            padding: 0 10px;
            font-size: 12px;
            line-height: 1; /* 日本語の文字上端/下端の切れを防ぐ */
            border: 1px solid #ccd;
            border-radius: 4px;
            background: #f3f6ff;
            color: #333;
            cursor: pointer;
            box-sizing: border-box; /* 入力と同じ箱モデルに */
            -webkit-appearance: none; /* iOS/Safariのデフォルトスタイル抑止 */
            appearance: none; /* 標準 */
        }
        .quick-set-btn:hover { background: #e8efff; }
        .var-gear-btn {
            padding: 0;
            background-color: transparent;
            color: #333;
            border: none;
            cursor: pointer;
            font-size: 1.2em;
            position: relative;
        }

        .variable-settings-panel {
            position: absolute;
            right: 0;
            top: 34px;
            display: none;
            background: #fff;
            border: 1px solid var(--color-border);
            box-shadow: 0 4px 12px rgba(0,0,0,0.12);
            border-radius: 6px;
            padding: 10px;
            z-index: var(--z-drag);
            min-width: 240px;
            max-width: min(360px, 90%);
        }
        .variable-settings-panel .settings-row { display:flex; align-items:center; gap: var(--space-8); margin-bottom: var(--space-8); }

        /* インラインの吹き出し（ポップオーバー） */
        .inline-popover {
            position: fixed;
            display: none;
            background: #fff;
            border: 1px solid var(--color-border);
            box-shadow: var(--shadow-popover);
            border-radius: 8px;
            padding: 0;
            z-index: var(--z-modal); /* フッターより前面 */
            min-width: 280px;
            /* 幅は固定相当（画面に応じて一定）。横方向はユーザー操作で変えられない */
            width: min(520px, 92vw);
            max-width: min(520px, 92vw);
            /* 縦方向も画面内に収め、必要時は内部スクロール */
            max-height: 85vh;
            overflow: auto;
            /* cursor: moveはヘッダーのみに適用（下記参照） */
        }
        /* アンカーの下側に表示する場合の余白を付与 */
        .inline-popover.is-below { padding-top: var(--space-6); }

        /* フレーズポップオーバーはテンプレートモーダルと同じ3行構造にする */
        .phrase-popover {
            display: grid;
            grid-template-rows: auto minmax(0, 1fr) auto;
            overflow: hidden; /* 本文のみスクロール */
            max-height: 85vh;
        }
        .phrase-popover .popover-scroll-body,
        .phrase-popover .modal-scroll-body {
            overflow: auto;
            overflow-y: auto; /* 明示: 縦スクロール */
            -webkit-overflow-scrolling: touch;
            min-height: 0;
            max-height: 100%;
            overscroll-behavior: contain;
            scrollbar-gutter: stable both-edges;
            padding: var(--space-16) var(--space-12) 0; /* 左右に12px、下は0 */
        }
        .phrase-popover .modal-buttons > .bar { display:flex; gap:10px; justify-content:flex-end; width:100%; }
        .phrase-popover .modal-header .header-right { display:flex; align-items:center; gap:8px; }
        /* フレーズ選択モーダルのヘッダー右側コンテナ */
        #phraseModal .modal-header .header-right { display:flex; align-items:center; gap:8px; }
        .phrase-popover .template-modal-body { padding-top: 12px; padding-bottom: 160px; }
        /* ドラッグ移動時のスムーズな追従アニメーション（フレーズピッカー等） */
        .inline-popover.drag-animate {
            transition: left 120ms ease-out, top 120ms ease-out;
            will-change: left, top;
        }
        /* フレーズピッカー（phraseModal）のカードを更に小さく＆ドラッグ用に絶対配置 */
        #phraseModal .modal-content {
            position: absolute;
            max-width: 360px;
            width: min(92vw, 360px);
            max-height: 60vh;
            margin: 0; /* ドラッグと競合する中央寄せマージンを無効化 */
        }
        /* モード表示バッジ */
        /* #phraseModal .mode-badge { display:none; } */
        /* フレーズピッカーは暗幕なし（背景を透明） */
        #phraseModal { background-color: transparent; pointer-events: none; }
        /* カード本体のみ操作可能（背面はクリック可） */
        #phraseModal .modal-content { pointer-events: auto; }
        /* ドラッグ可能なカードヘッダーのカーソルを統一（grab/grabbing） */
        .modal .modal-header.is-draggable { cursor: grab; }
        .modal .modal-header.is-draggable.grabbing { cursor: grabbing; }
        /* フレーズカードのヘッダー: タイトルを上下中央に配置 */
        #phraseModal .modal-header { display: flex; align-items: center; }
        /* 見出しの下余白を除去して正確に中央寄せ */
        #phraseModal #phraseModalTitle { margin: 0; }
        /* 全画面モーダル共通の本文パディングは原則0だが、テンプレ/フレーズは左右余白を付与する */
        .modal-content.fullscreen .modal-scroll-body.template-modal-body { padding: 0 !important; }
        /* テンプレ/フレーズの全画面時: 左右余白を統一（下部は個別指定） */
        #templateModal .modal-content.fullscreen .modal-scroll-body.template-modal-body,
        #phraseModal   .modal-content.fullscreen .modal-scroll-body.template-modal-body {
            padding: var(--space-16) var(--space-16) 0 !important;
        }
        /* テンプレは固定フッター分の下余白も確保 */
        #templateModal .modal-content.fullscreen .modal-scroll-body.template-modal-body { padding-bottom: calc(var(--footer-h) + 100px) !important; }
        /* 全画面モーダル共通: コンテナ自体のパディングも0に強制 */
        .modal-content.fullscreen { padding: 0 !important; }
        /* 編集モードのヘッダースタイル: 色は変更せず（見出しテキストのみ切替） */
        /* ルール不要のため削除（JSでタイトル文言のみ切替） */
        /* フレーズ一覧ではタイトル非表示にし、本文だけ見せる */
        #phraseModal .template-title { display: none !important; }
        #phraseModal .template-body { font-size: 0.92em; line-height: 1.5; margin-top: 0; }
        /* フレーズ一覧の各行で本文を上下中央に配置 */
        #phraseModal .template-item-row { align-items: center; }
        /* フレーズ選択: クリックで選択中を薄い黄色で強調 */
        #phraseModal .template-item.is-selected { background: #fff8e1; border-color: #ffd699; }
        /* ホバー時も選択色を維持して視認性を担保 */
        #phraseModal .template-item.is-selected:hover { background: #fff8e1; border-color: #ffd699; }
        /* iPhone 用: 全面カードウインドウ（固定、ドラッグ不可） */
        .inline-popover.fullscreen {
            inset: 0;
            width: 100% !important;
            height: 100% !important;
            min-width: 0 !important;
            max-width: 100% !important;
            max-height: 100% !important;
            border-radius: 0 !important;
            border: none !important;
            box-shadow: none !important;
            box-sizing: border-box;
            padding: 0; /* 余白なし: 完全にウインドウ全体を占有 */
            display: grid;
            grid-template-rows: auto 1fr auto; /* ヘッダー + 本文 + フッター */
            overflow: hidden; /* 本文のみスクロール */
        }
        .inline-popover.fullscreen .popover-scroll-body { overflow: auto; -webkit-overflow-scrolling: touch; min-height: 0; padding: var(--space-16) var(--space-12) 84px; }
        .inline-popover.fullscreen .popover-actions {
            position: fixed;
            left: 0;
            right: 0;
            bottom: 0;
            margin-top: 0;
            padding: var(--space-10) calc(12px + env(safe-area-inset-right)) calc(12px + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left));
            background: #fff;
            border-top: 1px solid #eee;
            z-index: var(--z-layer-3);
        }
        /* フルスクリーンの変数ポップオーバーでは、アクション行はフッターに移すため非表示 */
        .inline-popover.fullscreen .popover-actions { display: none !important; }
        /* フルスクリーンの変数ポップオーバーでは、内蔵のボタン行(フッター風)も非表示 */
        .inline-popover.fullscreen .modal-buttons { display: none !important; }
        .inline-popover.fullscreen .popover-header { flex-wrap: wrap; min-width: 0; }
        .inline-popover.fullscreen .popover-field-name { white-space: normal; word-break: break-word; overflow-wrap: anywhere; }
        .inline-popover.fullscreen .popover-row { flex-direction: column; align-items: stretch; gap: var(--space-6); }
        .inline-popover.fullscreen .popover-row label { min-width: 0; width: 100%; height: auto; align-items: flex-start; }
        .inline-popover.fullscreen #popoverValueContainer { min-width: 0; width: 100%; }
        .inline-popover.fullscreen .variable-input { min-width: 0 !important; width: 100% !important; flex: 1 1 auto; }
        .inline-popover.fullscreen .input-with-quick { flex-wrap: wrap; }
        .inline-popover.fullscreen .popover-header { cursor: default; }
        .inline-popover .popover-row { 
            display:flex; 
            align-items:flex-start; /* テキストエリアのリサイズに対応 */
            gap: var(--space-8); 
            margin-bottom: var(--space-10);
            padding-left: var(--space-12);
            padding-right: var(--space-12);
        }
        /* プルダウンが余白に食い込まないように横幅を内側に収める */
        .inline-popover .popover-row { min-width: 0; }
        .inline-popover .popover-row .variable-type { width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; }
        .inline-popover .popover-row label {
            min-width: 64px;
            font-weight: 600;
            height: 36px; /* 入力と同じ高さ */
            display: flex;
            align-items: center; /* 垂直中央 */
        }
        .inline-popover #popoverValueContainer {
            display: flex;
            align-items: flex-start; /* テキストエリアのリサイズに対応 */
            flex: 1 1 auto;
        }
        .inline-popover .popover-actions { display:none !important; }
        .inline-popover .variable-input { 
            width: auto; 
            flex: 0 1 auto; /* ユーザーのリサイズを尊重しつつ縮小も可能に */
            min-width: 160px; /* 最小幅を確保しつつ取り回しを改善 */
            box-sizing: border-box;
        }
        /* テキストエリアの横方向のリサイズは無効化（縦のみ） */
        .inline-popover textarea.variable-input {
            resize: vertical !important;
            overflow: auto;
        }
        .inline-popover .popover-header { display:flex; align-items:center; justify-content:space-between; gap: var(--space-8); font-weight: 700; margin-bottom: var(--space-8); color: #333; position: sticky; top: 0; z-index: var(--z-layer-2); cursor: grab; background: #fff; border-bottom: 1px solid #eee; padding-top: var(--space-4); padding-bottom: var(--space-4); padding-left: var(--space-12); padding-right: var(--space-12); }
        .inline-popover .popover-header.grabbing { cursor: grabbing; }
        /* スクロールバーは表示しない（スクロール自体は可） */
        .inline-popover,
        .inline-popover .popover-scroll-body {
            -ms-overflow-style: none; /* IE/Edge */
            scrollbar-width: none;     /* Firefox */
        }
        .inline-popover::-webkit-scrollbar,
        .inline-popover .popover-scroll-body::-webkit-scrollbar {
            width: 0;
            height: 0;
            display: none;            /* Chrome/Safari */
        }
        .inline-popover.dragging-state { box-shadow: 0 12px 28px rgba(0,0,0,0.22); opacity: 0.995; }
        .inline-popover .popover-field-name { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .inline-popover .popover-type-badge { font-weight: 600; font-size: 0.85em; color: #495057; background: #f1f3f5; border: 1px solid #dee2e6; border-radius: 10px; padding: 2px 8px; white-space: nowrap; }
        .inline-popover .var-gear-btn { width: 28px; height: 28px; padding: 0; background-color: transparent; border: none; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
        .inline-popover .var-gear-btn .icon { width: 20px; height: 20px; }
        /* 歯車/Xの切替（data-open属性で制御） */
        .var-gear-btn .icon .icon-gear { display: inline; }
        .var-gear-btn .icon .icon-close { display: none; }
        .var-gear-btn[data-open="true"] .icon .icon-gear { display: none; }
        .var-gear-btn[data-open="true"] .icon .icon-close { display: inline; }
        /* インライン吹き出しヘッダー用の疑似Xボタン（歯車オープン時のみ表示） */
        .inline-popover .popover-close-x { display: none; width: 28px; height: 28px; padding: 0; background-color: transparent; border: none; cursor: pointer; align-items: center; justify-content: center; }
        .inline-popover .popover-close-x .icon { width: 20px; height: 20px; }
        /* 歯車が開いているときは歯車自体を隠し、隣のXを表示 */
        #popoverGearBtn[data-open="true"] { visibility: hidden; pointer-events: none; }
        #popoverGearBtn[data-open="true"] + .popover-close-x { display: inline-flex; position: absolute; right: 0; top: 0; }
        /* 設定吹き出しの幅調整 */
        #variableSettingsPopover { 
            min-width: 260px; 
            max-width: 400px; 
            cursor: auto;
            /* 位置変更時のスムーズなアニメーション */
            transition: top 0.2s ease-out, left 0.2s ease-out, transform 0.2s ease-out;
        }
        /* 吹き出し矢印は非表示（デザイン簡素化） */
        #variableSettingsPopover::before,
        #variableSettingsPopover::after { display: none !important; content: none !important; }
        /* インタラクティブ要素は通常カーソル */
        .modal-content input,
        .modal-content textarea,
        .modal-content select,
        .modal-content button,
        .modal-content .modal-buttons,
        .modal-content .input-with-quick,
        .modal-content .variable-input,
        .inline-popover input,
        .inline-popover textarea,
        .inline-popover select,
        .inline-popover button,
        .inline-popover .popover-actions,
        .inline-popover .input-with-quick,
        .inline-popover .variable-input { cursor: auto; }

        /* 変数入力ポップオーバー用オーバーレイ */
        .popover-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.01); /* 透明度99% */
            z-index: var(--z-overlay-dim); /* 吹き出し（--z-modal）より下、他の要素より上 */
            display: none; /* 初期は非表示 */
        }

        /* 設定吹き出し用オーバーレイ（完全透明） */
        #settingsPopoverOverlay {
            background-color: rgba(0, 0, 0, 0); /* 完全透明 */
        }
        /* Dim page headers while edit modal is open */
        .header-dimmed .title-section {
            filter: brightness(0.6);
            transition: filter 0.2s ease-in-out;
            pointer-events: none;
        }
        /* Hide footer (Cancel/転記) while card window (template modal) is open */
        /* フッターのアニメーション制御は .footer.hidden クラスで行う */
    </style>
    <!-- ============================= -->
    <!-- Section: Document Body Start  -->
    <!-- ============================= -->
</head>
<body>
    <!--
      レイアウト概要（主要領域）
      - .page-body: コンテンツ全体のスクロール領域（固定フッター分の余白を確保）
      - .container: 2カラムのグリッド（左: 入力/編集、右: プレビュー/補助）
      - 固定要素:
        - .footer: 画面下部固定の操作バー（保存/クリア/コピー など）
        - .modal: 画面全体を覆うダイアログ（確認/メッセージ/編集）
      - アクセシビリティ: aria-* 属性とロールを適宜付与、タブ順序を維持
    -->
    <div class="page-body">
        <div class="container">
        <div class="column" id="original-text">
            <div class="title-section">
                <div class="edit-section">
                    <div class="title-left">
                        <h3>備考</h3>
                    </div>
                    <div class="title-actions">
                        <button type="button" id="searchButton" class="search-button" title="テンプレートを選択" aria-label="テンプレートを選択">
                            <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <rect x="3" y="4" width="8" height="6" rx="1" fill="currentColor"/>
                                <rect x="13" y="4" width="8" height="6" rx="1" fill="currentColor"/>
                                <rect x="3" y="14" width="8" height="6" rx="1" fill="currentColor"/>
                                <rect x="13" y="14" width="8" height="6" rx="1" fill="currentColor"/>
                            </svg>
                        </button>
                        <button type="button" id="copyContentButton" class="edit-button" title="内容をコピー" aria-label="内容をコピー">
                            <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v12h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 14H8V7h11v12z"/>
                            </svg>
                        </button>
                        <button type="button" id="phrasePickerButton" class="edit-button" title="フレーズを挿入" aria-label="フレーズを挿入">
                            <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <path fill="currentColor" d="M3 6h18v2H3V6zm0 10h12v2H3v-2z"/>
                            </svg>
                        </button>
                    </div>
                </div>
            </div>
            <div class="content-area">
                <div class="flex-container" id="instruction-container">
                    <label for="original-textarea">内容:</label>
                    <div class="action-buttons">
                        <button type="button" id="editButton" class="edit-button" title="テンプレートを編集する" aria-label="テンプレートを編集する">
                            <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zm2.92 2.33H5v-.92l9.06-9.06.92.92L5.92 19.58zM20.71 7.04a1.003 1.003 0 000-1.42l-2.34-2.34a1.003 1.003 0 00-1.42 0l-1.83 1.83 3.75 3.75 1.84-1.82z"/>
                            </svg>
                        </button>
                    </div>
                </div>
                <div style="margin:6px 0 8px 0; display:flex; align-items:center;">
                    <label style="display:inline-flex; align-items:center; gap:6px; cursor:pointer; user-select:none;">
                        <input id="toggleShowMemos" type="checkbox" style="margin:0;">
                        <span>メモを表示する</span>
                    </label>
                </div>
                <div id="display-text" class="display-text"></div>
                <textarea id="direct-input" rows="4" placeholder="内容を入力してください..." style="display: none;"></textarea>
                <textarea id="original-textarea" rows="4" placeholder="内容を入力してください..." style="display: none;"></textarea>
                <div class="global-note-section">
                    <div class="flex-container">
                        <label for="global-note-textarea">備考:</label>
                    </div>
                    <textarea id="global-note-textarea" rows="4" placeholder="備考を入力してください..."></textarea>
                    <div id="render-meta" class="render-meta" aria-live="polite"></div>
                </div>
            </div>
        </div>
        
        </div>
    </div>

    <div class="footer u-bg-subtle u-shadow-top">
            <div class="footer-content u-flex-end u-gap-10">
            <div class="change-nav" style="margin-right:auto; display:none; align-items:center; gap:4px;">
                <button type="button" id="changeNavPrev" class="search-button" title="前へ" aria-label="前へ">
                    <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                        <path fill="currentColor" d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
                    </svg>
                </button>
                <span id="changeNavStatus" class="change-status" aria-live="polite" aria-atomic="true">0 / 0</span>
                <button type="button" id="changeNavNext" class="search-button" title="次へ" aria-label="次へ">
                    <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                        <path fill="currentColor" d="M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z"/>
                    </svg>
                </button>
            </div>
            <button id="footerCancelButton" class="button button-secondary">キャンセル</button>
            <button id="footerQueryButton" class="button">転記</button>
            <button id="footerEditCancel" class="button button-secondary" style="display:none;">キャンセル</button>
            <button id="footerEditSave" class="button" style="display:none;">変更を反映する</button>
        </div>
    </div>

    <!-- 変数入力ポップオーバー用オーバーレイ -->
    <div id="variablePopoverOverlay" class="popover-overlay" style="display: none;"></div>

    <!-- インライン吹き出し（変数入力・設定） -->
    <div id="variableInlinePopover" class="inline-popover" role="dialog" aria-hidden="true" aria-label="変数入力">
        <div class="popover-header">
            <span id="popoverFieldName" class="popover-field-name"></span>
            <span id="popoverTypeBadge" class="popover-type-badge" aria-live="polite"></span>
            <button type="button" class="var-gear-btn" id="popoverGearBtn" title="設定を編集" aria-label="設定を編集" data-open="false">
                <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                    <g class="icon-gear">
                        <path fill="currentColor" d="M19.14,12.94a7.49,7.49,0,0,0,.05-.94,7.49,7.49,0,0,0-.05-.94l2.11-1.65a.5.5,0,0,0,.12-.64l-2-3.46a.5.5,0,0,0-.6-.22l-2.49,1a7.27,7.27,0,0,0-1.63-.94l-.38-2.65A.5.5,0,0,0,13.67,2H10.33a.5.5,0,0,0-.5.42L9.45,5.07a7.27,7.27,0,0,0-1.63.94l-2.49-1a.5.5,0,0,0-.6.22l-2,3.46a.5.5,0,0,0,.12.64L4.86,11.06a7.49,7.49,0,0,0-.05.94,7.49,7.49,0,0,0,.05.94L2.75,14.59a.5.5,0,0,0,.12.64l2,3.46a.5.5,0,0,0,.6.22l2.49-1a7.27,7.27,0,0,0,1.63.94l.38,2.65a.5.5,0,0,0,.5.42h3.34a.5.5,0,0,0,.5-.42l.38-2.65a7.27,7.27,0,0,0,1.63-.94l2.49,1a.5.5,0,0,0,.6-.22l2-3.46a.5.5,0,0,0-.12-.64ZM12,15.5A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"/>
                    </g>
                    <g class="icon-close">
                        <path fill="currentColor" d="M18.3 5.71a1 1 0 00-1.41 0L12 10.59 7.11 5.7A1 1 0 105.7 7.11L10.59 12l-4.9 4.89a1 1 0 101.41 1.41L12 13.41l4.89 4.9a1 1 0 001.41-1.41L13.41 12l4.9-4.89a1 1 0 000-1.4z"/>
                    </g>
                </svg>
            </button>
            <button type="button" class="popover-close-x" id="popoverHeaderCloseX" aria-label="閉じる（外側クリック扱い）" title="閉じる">
                <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                    <path fill="currentColor" d="M18.3 5.71a1 1 0 00-1.41 0L12 10.59 7.11 5.7A1 1 0 105.7 7.11L10.59 12l-4.9 4.89a1 1 0 101.41 1.41L12 13.41l4.89 4.9a1 1 0 001.41-1.41L13.41 12l4.9-4.89a1 1 0 000-1.4z"/>
                </svg>
            </button>
        </div>
        <div class="popover-scroll-body">
            <div class="popover-row">
                <label>値</label>
                <div class="input-with-quick" style="flex:1 1 auto;">
                    <div id="popoverValueContainer" style="flex:1 1 auto;"></div>
                    <button type="button" id="popoverClear" class="quick-set-btn" style="display:none;">クリア</button>
                    <button type="button" id="popoverNow" class="quick-set-btn" style="display:none;">今</button>
                </div>
            </div>
            <div class="popover-row">
                <label>メモ</label>
                <textarea id="popoverMemo" class="variable-input" rows="2" placeholder="この変数に関するメモ"></textarea>
            </div>
        </div>
        <div class="modal-buttons u-flex-end u-gap-10">
            <button type="button" id="popoverCancel" class="button button-secondary">キャンセル</button>
            <button type="button" id="popoverSave" class="button">反映</button>
        </div>
    </div>

    <!-- 設定用の小さなインライン吹き出し用オーバーレイ -->
    <div id="settingsPopoverOverlay" class="popover-overlay" style="display: none;"></div>

    <!-- 設定用の小さなインライン吹き出し（タイプ/次回も） -->
    <div id="variableSettingsPopover" class="inline-popover" role="dialog" aria-hidden="true" aria-label="変数設定" style="max-width: 360px;">
        <div class="popover-inner" style="padding:10px;">
        <div class="popover-header" style="display:none;"></div>
        <div class="popover-row">
            <label>タイプ</label>
            <select id="settingsPopoverType" class="variable-type" style="flex:1 1 auto;">
                <option value="multiline">テキスト(複数行)</option>
                <option value="text">テキスト(1行)</option>
                <option value="number">数値</option>
                <option value="date">日付</option>
                <option value="time">時刻</option>
                <option value="timestamp">タイムスタンプ</option>
            </select>
        </div>
        <div class="popover-row">
            <label>次回も</label>
            <label style="display:inline-flex; align-items:center; gap:6px; font-weight:normal;">
                <input type="checkbox" id="settingsPopoverPersist"> 使用
            </label>
        </div>
        </div>
    </div>

        <!-- ============================= -->
        <!-- Section: Modals - Edit       -->
        <!-- ============================= -->
        <!-- 内容のモーダル -->
    <div id="editModal" class="modal" aria-hidden="true">
        <div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="editModalTitle">
            <div class="modal-header">
                <div class="header-left">
                    <h3 id="editModalTitle">内容を編集</h3>
                </div>
                <div class="header-right">
                    <span id="syntaxHelpTriggerEdit" class="help-icon" title="テンプレート記法の説明">?</span>
                </div>
            </div>
            <div class="modal-scroll-body">
             <div class="modal-field-row" style="margin-bottom: 10px;">
                 <label for="modalTemplateName" class="modal-field-label" style="display:block; font-weight:bold; margin-bottom:6px;">テンプレート名:</label>
                 <input id="modalTemplateName" type="text" placeholder="テンプレート名を入力">
             </div>
            <textarea id="modalTextarea" rows="15" placeholder="内容を入力してください..."></textarea>
             <div class="modal-option-row" style="margin-top:10px; display:flex; align-items:center; gap:8px;">
                 <label style="display:inline-flex; align-items:center; gap:6px;">
                     <input type="checkbox" id="modalUpdateTemplateCheckbox"> テンプレートも変更する
                 </label>
             </div>
            
            </div>
        </div>
    </div>

        <!-- ============================= -->
        <!-- Section: Modals - Template   -->
        <!-- ============================= -->
        <!-- テンプレート選択のモーダル（カードウインドウ） -->
        <div id="templateModal" class="modal" aria-hidden="true">
            <div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="templateModalTitle">
                <div class="modal-header">
                    <div class="header-left">
                        <h3 id="templateModalTitle">テンプレートを選択</h3>
                    </div>
                    <div class="header-right">
                        <span id="syntaxHelpTriggerTemplateEdit" class="help-icon" title="テンプレート記法の説明">?</span>
                        <button type="button" id="templateSearchToggle" class="search-button" title="検索" aria-label="検索">
                            <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <path fill="currentColor" d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
                            </svg>
                        </button>
                        <div id="templateSearchRow" class="template-search-row" style="display:none;">
                            <input type="text" id="templateSearch" class="template-search" placeholder="テンプレートを検索...">
                            <button type="button" id="templateSearchExec" class="search-button" title="検索実行" aria-label="検索実行">
                                <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                    <path fill="currentColor" d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
                                </svg>
                            </button>
                        </div>
                        <!-- コピー専用の検索行（テンプレートとは独立） -->
                        <div id="copySearchRow" class="template-search-row" style="display:none;">
                            <input type="text" id="copySearch" class="template-search" placeholder="検索...">
                            <button type="button" id="copySearchExec" class="search-button" title="検索実行" aria-label="検索実行">
                                <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                    <path fill="currentColor" d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
                                </svg>
                            </button>
                        </div>
                        <button type="button" id="templateSearchClose" class="clear-button" title="検索を閉じる" aria-label="検索を閉じる" style="display:none;">
                            <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <path fill="currentColor" d="M18.3 5.71a1 1 0 00-1.41 0L12 10.59 7.11 5.7A1 1 0 105.7 7.11L10.59 12l-4.9 4.89a1 1 0 101.41 1.41L12 13.41l4.89 4.9a1 1 0 001.41-1.41L13.41 12l4.9-4.89a1 1 0 000-1.4z"/>
                            </svg>
                        </button>
                        <button type="button" id="templateNewButton" class="new-button" title="新規作成" aria-label="新規テンプレートを作成">
                            <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
                                <path fill="currentColor" d="M19 11h-6V5a1 1 0 10-2 0v6H5a1 1 0 100 2h6v6a1 1 0 102 0v-6h6a1 1 0 100-2z"/>
                            </svg>
                        </button>
                    </div>
                </div>
                <div class="template-modal-body">
                <!-- 新規テンプレート作成セクション（初期は非表示） -->
                <div id="templateCreateSection" style="display:none;">
                    <div class="modal-field-row" style="margin-bottom: 10px;">
                        <label for="createTemplateName" class="modal-field-label" style="display:block; font-weight:bold; margin-bottom:6px;">テンプレート名:</label>
                        <input id="createTemplateName" type="text" placeholder="テンプレート名を入力">
                    </div>
                    <div class="modal-field-row" style="margin-bottom: 10px;">
                        <label for="createTemplateContent" class="modal-field-label" style="display:block; font-weight:bold; margin-bottom:6px;">内容:</label>
                        <textarea id="createTemplateContent" rows="12" placeholder="テンプレートの内容を入力してください..."></textarea>
                        <!-- テンプレート選択/作成ではヘルプは非表示に変更 -->
                        <div id="templateSyntaxHelp" class="template-help"><!-- 統一テンプレ挿入先（JSで上書き） --></div>
                    </div>
                </div>
                <div id="templateList" class="template-list"></div>
                <div id="templatePagination" class="template-pagination" style="display: none;"></div>
                <!-- コピー専用の一覧・ページング（テンプレートとは独立） -->
                <div id="copyList" class="template-list" style="display:none;"></div>
                <div id="copyPagination" class="template-pagination" style="display:none;"></div>
                </div>
            </div>
        </div>

        <!-- ============================= -->
        <!-- Section: Modals - VarSettings -->
        <!-- ============================= -->
        <!-- 変数設定のモーダル（カードウインドウ） -->
        <div id="varSettingsModal" class="modal" aria-hidden="true">
            <div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="varSettingsModalTitle">
                <div class="modal-header">
                    <h3 id="varSettingsModalTitle">変数の設定</h3>
                </div>
                
                <div class="modal-scroll-body">
                <div class="modal-field-row" style="margin-bottom: 10px;">
                    <label class="modal-field-label" style="display:block; font-weight:bold; margin-bottom:6px;">タイプ:</label>
                    <select id="varSettingsType">
                        <option value="multiline">テキスト(複数行)</option>
                        <option value="text">テキスト(1行)</option>
                        <option value="number">数値</option>
                        <option value="date">日付</option>
                        <option value="time">時刻</option>
                        <option value="timestamp">タイムスタンプ</option>
                    </select>
                </div>
                <div class="modal-option-row" id="varSettingsQuickRow" style="margin-top:8px; display:none; align-items:center; gap:8px;">
                    <label style="display:inline-flex; align-items:center; gap:6px; font-weight:normal;">値</label>
                    <button type="button" id="varSettingsNow" class="quick-set-btn">今</button>
                </div>
                <div class="modal-option-row" style="margin-top:10px; display:flex; align-items:center; gap:8px;">
                    <label style="display:inline-flex; align-items:center; gap:6px;">
                        <input type="checkbox" id="varSettingsPersist"> 次回も使用する
                    </label>
                </div>
                <div class="modal-field-row" style="margin-top:10px;">
                    <label class="modal-field-label" style="display:block; font-weight:bold; margin-bottom:6px;">メモ:</label>
                    <textarea id="varSettingsMemo" class="variable-input" placeholder="この変数に関するメモを記載"></textarea>
                </div>
                </div>
                <div class="modal-buttons u-flex-end u-gap-10">
                    <button id="varSettingsCancel" class="button button-secondary">キャンセル</button>
                    <button id="varSettingsSave" class="button">変更</button>
                </div>
            </div>
        </div>

        <!-- ============================= -->
        <!-- Section: Modals - Confirm    -->
        <!-- ============================= -->
        <!-- カスタム確認ダイアログのモーダル -->
        <div id="confirmModal" class="modal" aria-hidden="true">
            <div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="confirmModalTitle" aria-describedby="confirmModalMessage">
                <div class="modal-header">
                    <h3 id="confirmModalTitle">確認</h3>
                </div>
                <div class="modal-scroll-body">
                    <p id="confirmModalMessage">変更が破棄されます。よろしいですか？</p>
                </div>
            <div class="modal-buttons u-flex-end u-gap-10">
                    <button id="confirmModalCancel" class="button button-secondary">キャンセル</button>
                    <button id="confirmModalConfirm" class="button">削除</button>
                </div>
            </div>
        </div>

        <!-- ============================= -->
        <!-- Section: Modals - Message    -->
        <!-- ============================= -->
        <!-- メッセージ表示モーダル（カードウインドウ） -->
        <div id="messageModal" class="modal" aria-hidden="true">
            <div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="messageModalTitle" aria-describedby="messageModalMessage">
                <div class="modal-header">
                    <h3 id="messageModalTitle">お知らせ</h3>
                </div>
                <div class="modal-scroll-body">
                    <p id="messageModalMessage"></p>
                </div>
                <div class="modal-buttons u-flex-end u-gap-10">
                    <button id="messageModalOk" class="button">OK</button>
                </div>
            </div>
        </div>

        <!-- ============================= -->
        <!-- Section: Modals - PhraseEdit -->
        <!-- ============================= -->
        <!-- フレーズ編集モーダル -->
        <div id="phraseEditModal" class="modal" aria-hidden="true">
            <div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="phraseEditModalTitle">
                <div class="modal-header">
                    <h3 id="phraseEditModalTitle">フレーズを編集</h3>
                </div>
                <div class="modal-scroll-body">
                    <textarea id="phraseEditTextarea" rows="8" placeholder="フレーズを入力してください..."></textarea>
                </div>
                <div class="modal-buttons">
                    <button id="phraseEditCancel" class="button button-secondary">キャンセル</button>
                    <button id="phraseEditSave" class="button">保存</button>
                </div>
            </div>
        </div>
    <!-- ============================= -->
    <!-- Section: Application Script   -->
    <!-- ============================= -->
    <script>
        /**
         * ========================================
         * 文章作成支援アドオン - メインスクリプト
         * ========================================
         * 
         * 機能概要:
         * - テンプレート管理機能（作成、編集、削除、検索）
         * - 変数入力機能（{{}} プレースホルダー、[[]] チェックボックス）
         * - モーダルウィンドウ管理（ドラッグ可能、レスポンシブ対応）
         * - Claris FileMakerとの連携機能
         * - XSS対策とセキュリティ機能
         * 
         * 主要コンポーネント:
         * 1. ユーティリティ関数群（エスケープ、フォーマット等）
         * 2. 変数管理システム（入力、保存、復元）
         * 3. テンプレート管理システム
         * 4. UI制御（モーダル、ポップオーバー等）
         * 5. イベントハンドリング
         */

        /**
         * 目次（スクリプト内の道しるべ）
         * A. データ構造の概要
         * B. 主なビューとDOM構造の関連
         * C. 処理フロー（初期化→入力→レンダリング→保存/外部送信）
         * D. 主イベント一覧（発火点と役割）
         * E. 外部連携（ Claris FileMaker 連携方針）
         * F. データ永続化/ロード方針
         */

        /**
         * A. データ構造の概要
         * - templates: Array<Template>
         *   Template = { id: string, title: string, body: string, updatedAt: number, ... }
         * - variables: Record<string, string | boolean>
         * - phrases: string[]（定型句）
         * - settings: { 各種表示/動作設定 }
         * これらは UI の各ビュー（一覧/編集/入力）により読み書きされる。
         */

        /**
         * B. 主なビューとDOM構造
         * - テンプレート一覧ビュー: 検索/並び替え/選択、編集ビューへの遷移
         * - テンプレート編集ビュー: タイトル/本文編集、変数のハイライト補助
         * - 変数入力ビュー: 入力値→プレビュー→コピー/送信
         * - 共通: モーダル（確認/メッセージ）、トースト、ポップオーバー
         */

        /**
         * C. 処理フロー（概要）
         * 1) 初期化: DOM 準備 → 状態ロード（ローカル/外部）→ 初回レンダリング
         * 2) 入力: ユーザー操作 → 内部状態更新 → 差分レンダリング → 検証
         * 3) 出力: 保存（ローカル）/エクスポート/外部送信（ Claris FileMaker ）
         * 4) 例外: エラー → ユーザ通知（トースト/モーダル）→ リカバリ指針
         */

        /**
         * D. 主イベント一覧（発火点）
         * - click: 各種ボタン（保存/削除/適用/コピー など）
         * - input/change: フォーム入力（タイトル/本文/変数/チェックボックス）
         * - keydown: ショートカット（例: Escape でモーダル閉）
         * - custom: トースト表示/非表示、モーダル開閉、ドラッグ移動
         * デリゲーションを活用し、バインド箇所を最小化している。
         */

        /**
         * E. 外部連携（ Claris FileMaker ）
         * - window.FileMaker.PerformScriptWithOption(name, param, 5) を利用
         * - 未注入時はリトライ/フォールバック（ユーザ通知）を行う
         * - 渡す param は文字列化し、必要に応じて JSON 形式を使用
         */

        /**
         * F. 永続化/ロード
         * - ローカル保存（必要な場合）では例外捕捉し、破損検知時は初期化/退避
         * - バージョン/互換性を考慮し、将来のキー変更に備える
         */

        /**
         * 正規表現メタ文字をエスケープする関数
         * @param {string} text - エスケープ対象の文字列
         * @returns {string} エスケープされた文字列
         */
        function escapeRegExp(text) {
            return String(text).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        }

        /**
         * HTMLエスケープ関数（XSS対策）
         * 
         * セキュリティ目的:
         * - ユーザー入力を安全にHTMLに表示するため
         * - スクリプトインジェクション攻撃を防止
         * - HTMLタグの無効化
         * 
         * エスケープ対象文字:
         * & → &amp; (最初に処理する必要がある)
         * < → &lt; (HTMLタグ開始)
         * > → &gt; (HTMLタグ終了)
         * " → &quot; (属性値の囲み文字)
         * ' → &#039; (属性値の囲み文字)
         * / → &#x2F; (終了タグ用)
         * 
         * @param {string|null|undefined} unsafe - エスケープ対象の文字列
         * @returns {string} エスケープされた安全な文字列
         */
        function escapeHtml(unsafe) {
            if (unsafe == null) return '';
            return String(unsafe)
                .replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;")
                .replace(/"/g, "&quot;")
                .replace(/'/g, "&#039;")
                .replace(/\//g, "&#x2F;");
        }

        /**
         * HTMLエスケープを解除するヘルパー関数
         * 
         * 用途:
         * - エスケープされた文字列を元の形に戻す
         * - 内部処理で元の文字列が必要な場合に使用
         * 
         * 注意事項:
         * - セキュリティ上の理由で使用は最小限にする
         * - 解除後の文字列を再度HTMLに出力する際は必ずescapeHtml()を通す
         * - 処理順序はescapeHtml()の逆順で行う（&amp;を最後に処理）
         * 
         * @param {string|null|undefined} safe - 解除対象のエスケープ済み文字列
         * @returns {string} 元の文字列
         */
        function unescapeHtml(safe) {
            if (safe == null) return '';
            return String(safe)
                .replace(/&#x2F;/g, '/')
                .replace(/&#039;/g, "'")
                .replace(/&quot;/g, '"')
                .replace(/&gt;/g, '>')
                .replace(/&lt;/g, '<')
                .replace(/&amp;/g, '&');
        }

        // HTML属性値エスケープ関数
        function escapeAttr(unsafe) {
            if (unsafe == null) return '';
            return String(unsafe)
                .replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;")
                .replace(/"/g, "&quot;")
                .replace(/'/g, "&#039;");
        }

        // DOMユーティリティ: 子要素を安全に全削除
        function clearChildren(el) {
            try {
                if (!el) return;
                while (el.firstChild) el.removeChild(el.firstChild);
            } catch(_) {}
        }

        // SVGアイコン生成（innerHTMLを使わず生成）
        function createIcon(name) {
            const NS = 'http://www.w3.org/2000/svg';
            const svg = document.createElementNS(NS, 'svg');
            svg.setAttribute('class', 'icon');
            svg.setAttribute('viewBox', '0 0 24 24');
            svg.setAttribute('aria-hidden', 'true');
            svg.setAttribute('focusable', 'false');
            const path = document.createElementNS(NS, 'path');
            path.setAttribute('fill', 'currentColor');
            if (name === 'search') {
                path.setAttribute('d', 'M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z');
            } else if (name === 'close') {
                path.setAttribute('d', 'M18.3 5.71a1 1 0 00-1.41 0L12 10.59 7.11 5.7A1 1 0 105.7 7.11L10.59 12l-4.9 4.89a1 1 0 101.41 1.41L12 13.41l4.89 4.9a1 1 0 001.41-1.41L13.41 12l4.9-4.89a1 1 0 000-1.4z');
            } else if (name === 'plus') {
                path.setAttribute('d', 'M19 11h-6V5a1 1 0 10-2 0v6H5a1 1 0 100 2h6v6a1 1 0 102 0v-6h6a1 1 0 100-2z');
            } else if (name === 'drag-handle') {
                path.setAttribute('d', 'M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z');
            }
            svg.appendChild(path);
            return svg;
        }

        // アクションメニュー生成（innerHTMLを使わず生成）
        function createActionsMenu() {
            const actionsEl = document.createElement('div');
            actionsEl.className = 'template-item-actions';
            // copyモードではアクション群を生成しない（UIから完全に隠す）
            try {
                const modal = document.getElementById('templateModal');
                if (modal && modal.dataset.mode === 'copy') {
                    actionsEl.style.display = 'none';
                    return actionsEl;
                }
            } catch(_) {}
            const moreBtn = document.createElement('button');
            moreBtn.type = 'button';
            moreBtn.className = 'more-btn';
            moreBtn.setAttribute('aria-label', 'その他の操作');
            moreBtn.setAttribute('title', 'その他の操作');
            moreBtn.textContent = '⋯';
            const moreMenu = document.createElement('div');
            moreMenu.className = 'more-menu';
            moreMenu.setAttribute('role', 'menu');
            const editBtn = document.createElement('button');
            editBtn.type = 'button';
            editBtn.className = 'menu-edit';
            editBtn.setAttribute('role', 'menuitem');
            editBtn.textContent = '編集';
            const deleteBtn = document.createElement('button');
            deleteBtn.type = 'button';
            deleteBtn.className = 'menu-delete';
            deleteBtn.setAttribute('role', 'menuitem');
            deleteBtn.textContent = '削除';
            moreMenu.appendChild(editBtn);
            moreMenu.appendChild(deleteBtn);
            actionsEl.appendChild(moreBtn);
            actionsEl.appendChild(moreMenu);
            return actionsEl;
        }

        /**
         * クリップボードへテキストをコピーする共通ユーティリティ
         * - navigator.clipboard API を優先し、失敗時は一時 textarea + execCommand によるフォールバックを実行
         * - エラー時は reportError へ記録し、呼び出し元が成否を判断できるよう boolean を返す
         *
         * @param {string} text コピー対象の文字列
         * @returns {Promise<boolean>} コピーが成功した場合 true
         */
        async function copyTextToClipboard(text) {
            const value = String(text == null ? '' : text);

            // Clipboard API が利用可能なら最優先で利用
            try {
                if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
                    await navigator.clipboard.writeText(value);
                    return true;
                }
            } catch (err) {
                try { reportError('clipboard_write_api', err, { level: 'warn' }); } catch(_) {}
            }

            // フォールバック: 一時的な textarea を使って execCommand('copy') を実行
            let textarea = null;
            let selection = null;
            let storedRange = null;
            try {
                textarea = document.createElement('textarea');
                textarea.value = value;
                textarea.setAttribute('readonly', '');
                textarea.style.position = 'fixed';
                textarea.style.top = '-1000px';
                textarea.style.left = '-1000px';
                textarea.style.width = '0';
                textarea.style.height = '0';
                textarea.style.opacity = '0';
                textarea.style.pointerEvents = 'none';
                document.body.appendChild(textarea);

                try { selection = typeof document.getSelection === 'function' ? document.getSelection() : null; } catch(_) {}
                if (selection && typeof selection.rangeCount === 'number' && selection.rangeCount > 0) {
                    try { storedRange = selection.getRangeAt(0); } catch(_) { storedRange = null; }
                }

                try { textarea.focus({ preventScroll: true }); } catch(_) { try { textarea.focus(); } catch(__) {} }
                textarea.select();
                textarea.setSelectionRange(0, textarea.value.length);

                const succeeded = (typeof document.execCommand === 'function') ? document.execCommand('copy') : false;

                if (selection && typeof selection.removeAllRanges === 'function') {
                    selection.removeAllRanges();
                    if (storedRange && typeof selection.addRange === 'function') {
                        selection.addRange(storedRange);
                    }
                }

                return !!succeeded;
            } catch (err) {
                try { reportError('clipboard_write_fallback', err, { level: 'warn' }); } catch(_) {}
                return false;
            } finally {
                if (textarea && textarea.parentNode) {
                    try { textarea.parentNode.removeChild(textarea); } catch(_) {}
                }
            }
        }

        /**
         * fieldData の値表示に使う文字列を整形するヘルパー
         * - 改行やタブをスペースに変換し、連続スペースを単一化
         * - 先頭/末尾の空白を除去
         *
         * @param {*} value 任意の値
         * @returns {string} 表示用に整形された文字列
         */
        function normalizeDisplayValue(value) {
            try {
                if (value == null) return '';
                if (typeof value === 'string') {
                    return value.replace(/[\r\n\t]+/g, ' ').replace(/\s{2,}/g, ' ').trim();
                }
                if (typeof value === 'number' || typeof value === 'boolean') {
                    return String(value);
                }
                if (Array.isArray(value)) {
                    const parts = value.map(item => normalizeDisplayValue(item)).filter(Boolean);
                    return parts.join(', ');
                }
                if (typeof value === 'object') {
                    const parts = Object.keys(value).map(key => `${key}=${normalizeDisplayValue(value[key])}`).filter(Boolean);
                    return parts.join(', ');
                }
                return String(value);
            } catch (_) {
                return String(value == null ? '' : value);
            }
        }

        /**
         * fieldData の値を展開し、配列や JSON 文字列内の値を個別行として扱えるようにする
         * - JSON 文字列 ({...},{...}, [...], {...}) を解析し、最下層のプリミティブ値を抽出
         * - 配列要素ごと、オブジェクトの各キーごとに { subLabel, value } を返す
         *
         * @param {string} rawText fieldData に格納されている文字列
         * @returns {Array<{ subLabel: string, value: string }>} 展開後の表示用データ
         */
        function expandEntryForDisplay(rawText) {
            const source = String(rawText == null ? '' : rawText).trim();
            if (!source) return [];

            const attempts = [];
            // FileMaker から `{...},{...}` として渡されるケースを配列 JSON に変換
            if (/^\s*\{/.test(source) && /\}\s*,\s*\{/.test(source)) {
                attempts.push(`[${source}]`);
            }
            attempts.push(source);

            let parsed = null;
            for (const candidate of attempts) {
                try {
                    parsed = JSON.parse(candidate);
                    break;
                } catch (_) {
                    parsed = null;
                }
            }
            if (parsed == null) return [];

            const results = [];

            const pushValue = (pathSegments, val) => {
                const normalized = normalizeDisplayValue(val);
                if (!normalized) return;
                const subLabel = pathSegments.filter(Boolean).join('.');
                results.push({ subLabel, value: normalized });
            };

            const traverse = (val, path = []) => {
                if (val == null) {
                    pushValue(path, '');
                    return;
                }
                if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
                    pushValue(path, val);
                    return;
                }
                if (Array.isArray(val)) {
                    val.forEach((item, index) => {
                        const containsObject = item && typeof item === 'object';
                        const nextPath = containsObject ? path.concat(String(index + 1)) : path;
                        traverse(item, nextPath);
                    });
                    return;
                }
                if (typeof val === 'object') {
                    const keys = Object.keys(val);
                    if (!keys.length) {
                        pushValue(path, '');
                        return;
                    }
                    keys.forEach((key) => {
                        traverse(val[key], path.concat(String(key)));
                    });
                    return;
                }
                pushValue(path, val);
            };

            traverse(parsed, []);
            return results;
        }

        // 安全なHTMLレンダラ（DOMParser + ホワイトリストサニタイズ）
        function renderUpdatedContent(container, htmlString) {
            try {
                if (!container) return;
                const parser = new DOMParser();
                const doc = parser.parseFromString(String(htmlString == null ? '' : htmlString), 'text/html');
                const root = doc.body;

                // 許可タグを最小化（必要最小限のみ）
                // - BR: 改行のため
                // - SELECT/OPTION: 置換UI
                // - SPAN/STRONG: テキスト装飾・クリック対象
                // - SVG/PATH: 小アイコン描画（G/TITLE は除外し、SVG 内の TITLE は使用しない）
                const allowedTags = new Set(['BR','SELECT','OPTION','SPAN','STRONG','SVG','PATH']);
                const allowedAttrs = {
                    'SELECT': new Set(['class','data-field','aria-label']),
                    'OPTION': new Set(['value','selected']),
                    'SPAN': new Set(['class','role','tabindex','data-field']),
                    'STRONG': new Set(['class','role','tabindex','data-item']),
                    'SVG': new Set(['class','viewBox','aria-hidden','focusable']),
                    'PATH': new Set(['fill','d','stroke','stroke-width','stroke-linecap']),
                    'BR': new Set([])
                };
                const allowedClasses = {
                    'SPAN': new Set(['clickable-placeholder','missing','persist','has-memo','type-icon-wrapper','memo-indicator']),
                    'STRONG': new Set(['bracket-checkbox-display','checked','unchecked']),
                    'SELECT': new Set(['curly-select','bracket-select']),
                    'SVG': new Set(['placeholder-type-icon'])
                };

                function sanitizeNode(node) {
                    if (node.nodeType === 3) return node; // Text
                    if (node.nodeType !== 1) { return doc.createTextNode(''); }
                    const tag = node.tagName.toUpperCase();
                    if (!allowedTags.has(tag)) {
                        return doc.createTextNode(node.textContent || '');
                    }
                    // Clone allowed element and copy safe attributes
                    const isSvgTag = (tag === 'SVG' || tag === 'PATH');
                    const clone = isSvgTag
                        ? document.createElementNS('http://www.w3.org/2000/svg', tag.toLowerCase())
                        : doc.createElement(tag.toLowerCase());
                    // Attributes
                    const attrAllow = allowedAttrs[tag] || new Set();
                    for (const attr of Array.from(node.attributes || [])) {
                        const name = attr.name;
                        if (!attrAllow.has(name)) continue;
                        if (name === 'class') {
                            const clsAllow = allowedClasses[tag] || new Set();
                            const filtered = String(attr.value || '').split(/\s+/).filter(c => clsAllow.has(c)).join(' ');
                            if (filtered) clone.setAttribute('class', filtered);
                        } else if ((name === 'data-field' || name === 'data-item' || name === 'role' || name === 'tabindex' || name === 'aria-label' || name === 'viewBox' || name === 'aria-hidden' || name === 'focusable' || name === 'value' || name === 'selected' || name === 'fill' || name === 'd' || name === 'stroke' || name === 'stroke-width' || name === 'stroke-linecap')) {
                            clone.setAttribute(name, String(attr.value || ''));
                        }
                    }
                    // Children（SVG <title> は許可しないため通常と同処理）
                    for (const child of Array.from(node.childNodes)) {
                        const safeChild = sanitizeNode(child);
                        if (safeChild) clone.appendChild(safeChild);
                    }
                    return clone;
                }

                const frag = document.createDocumentFragment();
                for (const child of Array.from(root.childNodes)) {
                    const safe = sanitizeNode(child);
                    if (safe) frag.appendChild(safe);
                }
                clearChildren(container);
                container.appendChild(frag);
            } catch (_) {
                // フォールバック: 生テキストで描画（<br>は改行に戻す）
                try {
                    clearChildren(container);
                    container.textContent = String(htmlString == null ? '' : htmlString).replace(/<br\s*\/?>/gi, '\n');
                } catch(_) {}
            }
        }

        /**
         * 共通ヘルパー: テキスト内の範囲を抽出
         * @param {string} text
         * @param {RegExp} regex - g フラグ必須
         * @returns {Array<[number, number]>}
         */
        function collectRanges(text, regex) {
            const ranges = [];
            try {
                let m;
                while ((m = regex.exec(text)) !== null) {
                    ranges.push([m.index, m.index + m[0].length]);
                }
            } catch(_) {}
            return ranges;
        }

        /**
         * 共通ヘルパー: 範囲内かどうか
         * @param {Array<[number, number]>} ranges
         * @param {number} start
         * @param {number} len
         */
        function isInsideRanges(ranges, start, len) {
            return ranges.some(([a, b]) => start >= a && start + len <= b);
        }

        /**
         * 共通ヘルパー: ガード付き置換
         * @param {string} text
         * @param {RegExp} pattern - g フラグ必須
         * @param {(match: string, ...args:any[]) => string} replacer
         * @param {Array<[number, number]>} guardRanges - 置換を行わない範囲
         */
        function replaceWithGuard(text, pattern, replacer, guardRanges) {
            return String(text).replace(pattern, function(match) {
                const offset = arguments[arguments.length - 2];
                if (Array.isArray(guardRanges) && guardRanges.length > 0) {
                    if (isInsideRanges(guardRanges, offset, match.length)) return match;
                }
                return replacer.apply(this, arguments);
            });
        }

        // デバウンス/スロットル/rAFユーティリティとロガー
        const raf = (cb) => (window.requestAnimationFrame ? requestAnimationFrame(cb) : setTimeout(cb, 16));
        function debounce(fn, wait = 100) {
            let t = null;
            return function(...args){
                if (t) clearTimeout(t);
                t = setTimeout(() => fn.apply(this, args), wait);
            };
        }
        function throttle(fn, wait = 100) {
            let last = 0, timer = null, lastArgs = null, lastThis = null;
            const invoke = () => { last = Date.now(); timer = null; fn.apply(lastThis, lastArgs); };
            return function(...args){
                const now = Date.now();
                lastArgs = args; lastThis = this;
                if (now - last >= wait) invoke();
                else if (!timer) timer = setTimeout(invoke, wait - (now - last));
            };
        }
        // 単一登録保証付きイベントバインダ（要素ごと・イベント種別＋キー）
        function bindSingleEvent(target, type, key, handler, options) {
            try {
                if (!target || !type || !key || !handler) return;
                const bucketKey = '__listenerBucket';
                const slot = type + ':' + key;
                if (!target[bucketKey]) { try { target[bucketKey] = {}; } catch(_) {} }
                const bucket = target[bucketKey] || {};
                const prev = bucket[slot];
                if (prev && prev.type && prev.handler) {
                    try { target.removeEventListener(prev.type, prev.handler, prev.options || false); } catch(_) {}
                }
                target.addEventListener(type, handler, options);
                try { bucket[slot] = { type: type, handler: handler, options: (options || false) }; } catch(_) {}
                try { target[bucketKey] = bucket; } catch(_) {}
            } catch(_) {}
        }
        // 単一登録の解除
        function unbindSingleEvent(target, type, key) {
            try {
                if (!target || !type || !key) return;
                const bucket = target['__listenerBucket'] || {};
                const slot = type + ':' + key;
                const prev = bucket[slot];
                if (prev && prev.type && prev.handler) {
                    try { target.removeEventListener(prev.type, prev.handler, prev.options || false); } catch(_) {}
                }
                try { delete bucket[slot]; } catch(_) {}
            } catch(_) {}
        }
        // スコープ管理（イベント/デタッチャ/オブザーバ等を一括管理）
        function createScope() {
            const cleanups = [];
            return {
                addEvent(target, type, handler, options) {
                    try {
                        if (!target || !type || !handler) return;
                        target.addEventListener(type, handler, options);
                        cleanups.push(() => { try { target.removeEventListener(type, handler, options || false); } catch(_) {} });
                    } catch(_) {}
                },
                addDetach(fn) {
                    try { if (typeof fn === 'function') cleanups.push(fn); } catch(_) {}
                },
                cleanup() {
                    try {
                        while (cleanups.length) {
                            const fn = cleanups.pop();
                            try { fn && fn(); } catch(_) {}
                        }
                    } catch(_) {}
                }
            };
        }
        const logger = (() => {
            const enabled = (typeof window !== 'undefined' && (window.DEBUG_LOG === true || /[?&]debug=1\b/.test(location.search)));
            const safe = (method) => (...args) => { try { if (enabled && console && console[method]) console[method](...args); } catch(_) {} };
            return { debug: safe('log'), info: safe('info'), warn: safe('warn'), error: safe('error') };
        })();

        // 最下行Enter時の自動スクロールは削除

        // エラーハンドリング方針の明確化
        // - 収集: logger へ送出（debug=1 時のみコンソール）
        // - 通知: 重大時のみトースト表示（既存の showToast を利用可能な場合）
        // - 失敗は握りつぶさず、最低限ログに残す
        // 安定性向上: エラーバッファと重複抑制（短時間の同一エラー氾濫を抑える）
        (function setupErrorBuffer(){
            try {
                const root = (typeof window !== 'undefined') ? window : this;
                root.__errorBuffer = root.__errorBuffer || [];
                root.__errorDedup = root.__errorDedup || { key: '', ts: 0 };
                root.__errorConfig = root.__errorConfig || { max: 100, dedupWindowMs: 5000 };
            } catch(_) {}
        })();
        function reportError(context, error, options) {
            try {
                const opts = options || {};
                const level = opts.level || 'error';
                const msg = (error && error.message) ? error.message : String(error);
                const stack = (error && error.stack) ? String(error.stack) : '';

                // シグネチャ生成と重複抑制
                const key = `${String(context)}|${msg}|${stack.split('\n')[0] || ''}`;
                try {
                    const now = Date.now();
                    const root = (typeof window !== 'undefined') ? window : this;
                    const conf = (root.__errorConfig || { dedupWindowMs: 5000, max: 100 });
                    const last = root.__errorDedup || { key: '', ts: 0 };
                    const isDup = (last.key === key && (now - last.ts) < Number(conf.dedupWindowMs || 0));
                    // バッファに積む（最大長は切り詰め）
                    try {
                        root.__errorBuffer = root.__errorBuffer || [];
                        root.__errorBuffer.push({ ts: now, level, context: String(context), message: msg, stack });
                        if (root.__errorBuffer.length > Number(conf.max || 100)) {
                            root.__errorBuffer.splice(0, root.__errorBuffer.length - Number(conf.max || 100));
                        }
                    } catch(_) {}
                    // App が存在すれば state にも露出
                    try { if (root.App && root.App.state) { root.App.state.errorBuffer = root.__errorBuffer; } } catch(_) {}
                    // 重複はログを控えめに
                    if (!isDup) {
                        try { (logger[level] || logger.error)(`[${context}]`, msg, error); } catch(_) {}
                        try { root.__errorDedup = { key, ts: now }; } catch(_) {}
                    } else {
                        try { (logger.debug || logger.info)(`[${context}] duplicate suppressed`, msg); } catch(_) {}
                    }
                } catch(_) {
                    // フォールバックログ
                    try { (logger[level] || logger.error)(`[${context}]`, msg, error); } catch(_) {}
                }
                if (opts.toast && typeof showToast === 'function') {
                    try { showToast(opts.userMessage || '処理中にエラーが発生しました。', { role: 'alert', duration: 2200 }); } catch(_) {}
                }
            } catch(_) {}
        }

        // グローバル未処理例外/拒否の収集（ユーザー通知は行わず、ログのみ）
        try {
            window.addEventListener('error', (e) => { try { reportError('window_error', e && (e.error || e.message || e)); } catch(_) {} });
            window.addEventListener('unhandledrejection', (e) => { try { reportError('unhandledrejection', e && (e.reason || e)); } catch(_) {} });
        } catch(_) {}

        // FileMaker呼び出しの安全ラッパ
        (function setupFileMakerBridge(){
            const queue = [];
            function isReady(){
                try { return !!(window.FileMaker && typeof window.FileMaker.PerformScriptWithOption === 'function'); } catch(_) { return false; }
            }
            function drain(){
                if (!isReady()) return;
                let item;
                while ((item = queue.shift())) {
                    const [name, param, opt] = item;
                    try { window.FileMaker.PerformScriptWithOption(String(name || ''), String(param || ''), Number(opt || 5)); } catch(e) { logger.warn('FileMaker 呼び出し失敗(再送しません):', name, e); }
                }
            }
            function ensureTimer(){
                if (call.__timer) return;
                call.__timer = setInterval(() => {
                    try {
                        drain();
                        if (isReady() && queue.length === 0) { clearInterval(call.__timer); call.__timer = null; }
                    } catch(err) {
                        try { reportError('fmCall_drain', err, { level: 'warn' }); } catch(_) {}
                    }
                }, 250);
            }
            function call(name, param, option){
                try {
                    const payload = (param == null) ? '' : (typeof param === 'string' ? param : JSON.stringify(param));
                    if (isReady()) {
                        try { window.FileMaker.PerformScriptWithOption(String(name || ''), String(payload), Number(option || 5)); } catch(e) { logger.warn('FileMaker 呼び出し失敗:', name, e); }
                        return true;
                    }
                    queue.push([name, payload, option]);
                    ensureTimer();
                    return false;
                } catch(e) {
                    logger.error('FileMaker 呼び出し準備で例外:', e);
                    return false;
                }
            }
            try { window.fmCall = call; } catch(err) { try { reportError('fmCall_export', err, { level: 'warn' }); } catch(_) {} }
        })();

        /**
         * 共通ヘルパー: モーダル外側クリックで閉じるハンドラをアタッチ
         * @param {HTMLElement} modalEl - オーバーレイ要素（背景）
         * @param {() => void} onClose - 閉じる処理
         * @returns {() => void} detach - リスナ解除関数
         */
        function attachOutsideClickClose(modalEl, onClose) {
            if (!modalEl || typeof onClose !== 'function') return () => {};
            const handleOutsideClick = (e) => {
                if (e.target === modalEl) {
                    try { onClose(); } catch(err) { try { reportError('modal_outside_click_close', err, { level: 'warn' }); } catch(_) {} }
                }
            };
            modalEl.addEventListener('click', handleOutsideClick);
            return () => { try { modalEl.removeEventListener('click', handleOutsideClick); } catch(err) { try { reportError('modal_outside_click_detach', err, { level: 'warn' }); } catch(_) {} } };
        }

        /**
         * 共通ヘルパー: ESCキーで閉じるハンドラをアタッチ
         * @param {() => void} onClose
         * @param {Document} doc
         * @returns {() => void} detach
         */
        function attachEscToClose(onClose, doc) {
            const d = doc || document;
            if (typeof onClose !== 'function' || !d) return () => {};
            const handleEsc = (e) => { if (e.key === 'Escape') { try { onClose(); } catch(err) { try { reportError('modal_esc_close', err, { level: 'warn' }); } catch(_) {} } } };
            d.addEventListener('keydown', handleEsc);
            return () => { try { d.removeEventListener('keydown', handleEsc); } catch(err) { try { reportError('modal_esc_detach', err, { level: 'warn' }); } catch(_) {} } };
        }

        /**
         * モーダルA11yユーティリティ
         * - aria-hiddenの更新
         * - フォーカストラップ（Tab/Shift+Tab循環）
         * - フォーカス復帰（トリガへ）
         * - 背景の一時不可視化（aria-hidden）
         */
        (function setupModalA11y(){
            const FOCUSABLE_SELECTOR = [
                'a[href]','area[href]','input:not([disabled])','select:not([disabled])','textarea:not([disabled])',
                'button:not([disabled])','iframe','audio[controls]','video[controls]','[contenteditable]','[tabindex]:not([tabindex="-1"])'
            ].join(',');

            const appRoot = document.querySelector('.page-body');

            function getFocusable(container){
                return Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR))
                    .filter(el => (el.offsetParent !== null || el === container));
            }

            function setAriaHiddenBackground(hidden){
                try {
                    if (!appRoot) return;
                    if (hidden) appRoot.setAttribute('aria-hidden', 'true');
                    else appRoot.removeAttribute('aria-hidden');
                } catch(_) {}
            }

            function trapFocus(e, modalContent){
                if (e.key !== 'Tab') return;
                const focusables = getFocusable(modalContent);
                if (focusables.length === 0) return;
                const first = focusables[0];
                const last = focusables[focusables.length - 1];
                const active = document.activeElement;
                if (e.shiftKey) {
                    if (active === first || !modalContent.contains(active)) {
                        try { e.preventDefault(); } catch(_) {}
                        try { last && last.focus && last.focus({ preventScroll: true }); } catch(_) {}
                    }
                } else {
                    if (active === last || !modalContent.contains(active)) {
                        try { e.preventDefault(); } catch(_) {}
                        try { first && first.focus && first.focus({ preventScroll: true }); } catch(_) {}
                    }
                }
            }

            function enhanceModal(modal){
                if (!modal || modal.__modalA11ySetup) return;
                try { modal.__modalA11ySetup = true; } catch(err) { try { reportError('modal_a11y_flag', err, { level: 'warn' }); } catch(_) {} }
                const content = modal.querySelector('.modal-content');
                if (!content) return;
                try { if (!content.getAttribute('role')) content.setAttribute('role', 'dialog'); } catch(err) { try { reportError('modal_role_set', err, { level: 'warn' }); } catch(_) {} }
                try { if (!content.getAttribute('aria-modal')) content.setAttribute('aria-modal', 'true'); } catch(err) { try { reportError('modal_aria_modal_set', err, { level: 'warn' }); } catch(_) {} }

                let restoreFocusEl = null;
                const onKeydown = (e) => trapFocus(e, content);
                const onShow = () => {
                    try { restoreFocusEl = document.activeElement; } catch(err) { try { reportError('modal_restore_focus_capture', err, { level: 'warn' }); } catch(_) {} }
                    modal.setAttribute('aria-hidden', 'false');
                    setAriaHiddenBackground(true);
                    const f = getFocusable(content);
                    const target = f[0] || content;
                    raf(() => { try { target.focus({ preventScroll: true }); } catch(err) { try { reportError('modal_initial_focus', err, { level: 'warn' }); } catch(_) {} } });
                    document.addEventListener('keydown', onKeydown, true);
                };
                const onHide = () => {
                    modal.setAttribute('aria-hidden', 'true');
                    setAriaHiddenBackground(false);
                    document.removeEventListener('keydown', onKeydown, true);
                    raf(() => { try { restoreFocusEl && restoreFocusEl.focus({ preventScroll: true }); } catch(err) { try { reportError('modal_restore_focus', err, { level: 'warn' }); } catch(_) {} } });
                };

                const mo = new MutationObserver(() => {
                    const visible = (modal.style.display === 'block');
                    if (visible) onShow(); else onHide();
                });
                try { mo.observe(modal, { attributes: true, attributeFilter: ['style'] }); } catch(err) { try { reportError('modal_observe_attach', err, { level: 'warn' }); } catch(_) {} }

                // 初期状態同期
                try {
                    const visible = (modal.style.display === 'block');
                    if (visible) onShow(); else onHide();
                } catch(err) { try { reportError('modal_initial_sync', err, { level: 'warn' }); } catch(_) {} }

                try { modal.__modalA11yCleanup = () => { try { mo.disconnect(); } catch(err) { try { reportError('modal_observe_disconnect', err, { level: 'warn' }); } catch(_) {} } document.removeEventListener('keydown', onKeydown, true); }; } catch(err) { try { reportError('modal_cleanup_export', err, { level: 'warn' }); } catch(_) {} }
            }

            // 既存モーダルに適用
            try { Array.from(document.querySelectorAll('.modal')).forEach(enhanceModal); } catch(err) { try { reportError('modal_enhance_all', err, { level: 'warn', toast: false }); } catch(_) {} }

            // 動的追加にも対応
            try {
                const rootMo = new MutationObserver(() => {
                    try { Array.from(document.querySelectorAll('.modal')).forEach(enhanceModal); } catch(err) { try { reportError('modal_enhance_dynamic', err, { level: 'warn', toast: false }); } catch(_) {} }
                });
                rootMo.observe(document.body, { childList: true, subtree: true });
            } catch(err) { try { reportError('modal_root_observer_attach', err, { level: 'warn', toast: false }); } catch(_) {} }
        })();

        // テンプレートモーダルのヘッダー表示を data-mode に合わせて強制同期
        /**
         * ヘッダー表示同期
         * - #templateModal の data-mode (create/edit) に応じてヘッダー右側の表示を同期する
         * - DOMは固定し、visibilityのみ変更する（将来の保守性/自動テスト容易性のため）
         */
        function updateTemplateHeaderForMode() {
            try {
                const modal = document.getElementById('templateModal');
                if (!modal) return;
                const mode = modal.dataset.mode || '';
                const help = document.getElementById('syntaxHelpTriggerTemplateEdit');
                const searchToggle = document.getElementById('templateSearchToggle');
                const searchClose = document.getElementById('templateSearchClose');
                const titleEl = document.getElementById('templateModalTitle');
                const newBtn = document.getElementById('templateNewButton');
                const searchInput = document.getElementById('templateSearch');
                const copySearchRow = document.getElementById('copySearchRow');
                const templateSearchRow = document.getElementById('templateSearchRow');
                const copyList = document.getElementById('copyList');
                const templateList = document.getElementById('templateList');
                if (mode === 'create' || mode === 'edit') {
                    if (help) help.style.display = '';
                    if (searchToggle) searchToggle.style.display = 'none';
                    if (searchClose) searchClose.style.display = 'none';
                    if (copySearchRow) copySearchRow.style.display = 'none';
                    if (templateSearchRow) templateSearchRow.style.display = 'none';
                    if (copyList) copyList.style.display = 'none';
                    if (templateList) {
                        // 新規／編集モードではフォームのみを見せるため一覧を確実に非表示化
                        templateList.style.display = 'none';
                    }
                } else {
                    if (help) help.style.display = 'none';
                    // select/copy の表示切替
                    const isCopy = mode === 'copy';
                    // 検索アイコン: copy時も表示
                    if (searchToggle) searchToggle.style.display = '';
                    if (searchClose) searchClose.style.display = 'none';
                    // 検索行は初期非表示（トグルで表示）
                    if (copySearchRow) copySearchRow.style.display = 'none';
                    if (templateSearchRow) templateSearchRow.style.display = 'none';
                    if (copyList) copyList.style.display = isCopy ? '' : 'none';
                    if (templateList) templateList.style.display = isCopy ? 'none' : '';
                }
                // タイトルもモードに合わせて同期（create/edit はそれぞれの処理で設定されるため触らない）
                if (titleEl) {
                    if (mode === 'copy') {
                        titleEl.textContent = 'コピー';
                    } else if (mode !== 'create' && mode !== 'edit') {
                        titleEl.textContent = 'テンプレートを選択';
                    }
                }
                // [+] ボタンの表示/非表示もモードに合わせて強制
                if (newBtn) {
                    if (mode === 'copy') newBtn.style.display = 'none';
                    else if (mode !== 'create' && mode !== 'edit') newBtn.style.display = '';
                }
                // 検索プレースホルダーをモードに応じて同期
                if (searchInput) {
                    if (mode === 'copy') {
                        searchInput.placeholder = '検索...';
                    } else if (mode !== 'create' && mode !== 'edit') {
                        searchInput.placeholder = 'テンプレートを検索...';
                    }
                }
            } catch(_) {}
        }

        // data-mode 変更を監視してヘッダー表示を自動同期
        (function observeTemplateHeaderMode(){
            try {
                const modal = document.getElementById('templateModal');
                if (!modal) return;
                try { updateTemplateHeaderForMode(); } catch(_) {}
                const mo = new MutationObserver(() => { try { updateTemplateHeaderForMode(); } catch(_) {} });
                mo.observe(modal, { attributes: true, attributeFilter: ['data-mode'] });
                try { modal.__detachHeaderModeObserver = () => { try { mo.disconnect(); } catch(_) {} }; } catch(_) {}
            } catch(_) {}
        })();

        /**
         * 共通ヘルパー: ドラッグ移動（要素のleft/topを更新）をアタッチ
         * options: {
         *   handleEl?: HTMLElement,
         *   clampToViewport?: boolean,
         *   padding?: number,
         *   blockSelector?: string,           // このセレクタにマッチする子要素ではドラッグ開始しない
         *   disabledPredicate?: () => boolean,// trueならドラッグ無効
         *   disallowSelection?: boolean,      // mousedown時にe.preventDefault()
         *   smoothing?: number,               // 0..1 慣性風の追従（0=無し）
         *   onStart?: () => void,
         *   onEnd?: () => void,
         * }
         * @returns {() => void} detach
         */
        function attachDragMove(targetEl, options) {
            const el = targetEl;
            const opts = options || {};
            if (!el) return () => {};
            const handle = opts.handleEl || el;
            let isDragging = false;
            let startX = 0, startY = 0, origLeft = 0, origTop = 0;

            const onMouseDown = (e) => {
                if (e.button !== 0) return;
                if (typeof opts.disabledPredicate === 'function' && opts.disabledPredicate()) return;
                if (opts.blockSelector) {
                    const blocked = e.target.closest && e.target.closest(opts.blockSelector);
                    if (blocked) return;
                }
                if (opts.disallowSelection) { try { e.preventDefault(); } catch(_) {} }
                const rect = el.getBoundingClientRect();
                startX = e.clientX; startY = e.clientY;
                origLeft = rect.left; origTop = rect.top;
                isDragging = true;
                try { if (opts.onStart) opts.onStart(); } catch(err) { try { reportError('drag_onStart', err, { level: 'warn' }); } catch(_) {} }
            };

            const onMouseMove = (e) => {
                if (!isDragging) return;
                if (typeof opts.disabledPredicate === 'function' && opts.disabledPredicate()) return;
                const dx = e.clientX - startX;
                const dy = e.clientY - startY;
                const sm = Math.min(Math.max(Number(opts.smoothing || 0), 0), 1);
                let newLeft = Math.round(origLeft + dx * (1 - sm));
                let newTop  = Math.round(origTop  + dy * (1 - sm));

                if (opts.clampToViewport) {
                    const pad = Number(opts.padding || 8);
                    const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                    const vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
                    const r = el.getBoundingClientRect();
                    const w = r.width || 0; const h = r.height || 0;
                    if (newLeft + w + pad > vw) newLeft = vw - w - pad;
                    if (newLeft < pad) newLeft = pad;
                    if (newTop + h + pad > vh) newTop = vh - h - pad;
                    if (newTop < pad) newTop = pad;
                }
                el.style.left = `${newLeft}px`;
                el.style.top  = `${newTop}px`;
                try { if (typeof opts.onMove === 'function') opts.onMove(newLeft, newTop); } catch(err) { try { reportError('drag_onMove', err, { level: 'warn' }); } catch(_) {} }
            };

            const onMouseUp = () => {
                if (!isDragging) return;
                isDragging = false;
                try { if (opts.onEnd) opts.onEnd(); } catch(err) { try { reportError('drag_onEnd', err, { level: 'warn' }); } catch(_) {} }
            };

            handle.addEventListener('mousedown', onMouseDown);
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);

            return () => {
                try { handle.removeEventListener('mousedown', onMouseDown); } catch(err) { try { reportError('drag_detach_mousedown', err, { level: 'warn' }); } catch(_) {} }
                try { document.removeEventListener('mousemove', onMouseMove); } catch(err) { try { reportError('drag_detach_mousemove', err, { level: 'warn' }); } catch(_) {} }
                try { document.removeEventListener('mouseup', onMouseUp); } catch(err) { try { reportError('drag_detach_mouseup', err, { level: 'warn' }); } catch(_) {} }
            };
        }

        /**
         * 共通ヘルパー: {{名前:A|B|C}} の候補一覧を抽出（最初の出現のみ）
         * @param {string} text
         * @returns {Record<string, string[]>}
         */
        function buildSelectSpecMapFromText(text) {
            const map = {};
            try {
                const src = String(text == null ? '' : text);
                const re = /\{\{\s*([^:}]+?)\s*:\s*([^}]+?)\s*\}\}/g;
                let m;
                while ((m = re.exec(src)) !== null) {
                    const nm = String(m[1] || '').trim();
                    if (!nm || Object.prototype.hasOwnProperty.call(map, nm)) continue;
                    const opts = String(m[2] || '')
                        .split('|')
                        .map(s => s.trim())
                        .filter(s => s !== '');
                    if (opts.length > 0) map[nm] = opts;
                }
            } catch(_) {}
            return map;
        }

        /**
         * 共通ヘルパー: {{...}} 範囲収集
         * @param {string} text
         * @returns {Array<[number, number]>}
         */
        function collectBraceRanges(text) {
            return collectRanges(String(text == null ? '' : text), /\{\{[\s\S]*?\}\}/g);
        }

        /**
         * 共通ヘルパー: [[...]] 範囲収集
         * @param {string} text
         * @returns {Array<[number, number]>}
         */
        function collectBracketRanges(text) {
            return collectRanges(String(text == null ? '' : text), /\[\[[\s\S]*?\]\]/g);
        }

        /**
         * 共通ヘルパー: 表示用HTML内の {{...}} 由来セレクト範囲収集
         * @param {string} html
         * @returns {Array<[number, number]>}
         */
        function collectCurlySelectRangesFromHtml(html) {
            return collectRanges(String(html == null ? '' : html), /<select\s+class="curly-select"[\s\S]*?<\/select>/g);
        }

        /**
         * 共通ヘルパー: タイプキー→日本語ラベル
         */
        function getTypeLabel(typeKey) {
            const map = { multiline: '複数行', text: 'テキスト', number: '数値', date: '日付', time: '時刻', timestamp: '日時' };
            const k = String(typeKey || '').toLowerCase();
            return map[k] || k || '';
        }

        /**
         * 共通ヘルパー: イベントリスナ等をリセットするために要素をクローンで置換
         * @param {HTMLElement|null} el
         * @returns {HTMLElement|null} 新しい要素（置換後）
         */
        function replaceWithClone(el) {
            if (!el) return el;
            const clone = el.cloneNode(true);
            if (el.parentNode) el.parentNode.replaceChild(clone, el);
            return clone;
        }

        /**
         * 共通ヘルパー: モーダルに標準の閉じる挙動（外側クリック/ESC）を付与
         * @param {HTMLElement} modalEl
         * @param {() => void} onClose
         * @returns {() => void} detach
         */
        function attachStandardModalClose(modalEl, onClose) {
            if (!modalEl || typeof onClose !== 'function') return () => {};
            const detachers = [];
            detachers.push(attachOutsideClickClose(modalEl, onClose));
            detachers.push(attachEscToClose(onClose, document));
            return () => { detachers.forEach(fn => { try { fn && fn(); } catch(_) {} }); };
        }

        /**
         * Claris FileMaker 等から渡される UTCMilliseconds 正規化
         * - Unix Epoch(ms) そのままの場合は無変換
         * - 0001-01-01 起点の ms（例: 63892566392227）の場合は
         *   1970-01-01 との差分 62135596800000 を減算して Unix ms へ変換
         */
        function normalizeUtcMilliseconds(input) {
            if (input == null) return null;
            const n = Number(input);
            if (!isFinite(n)) return null;
            const FILEMAKER_EPOCH_OFFSET_MS = 62135596800000; // 0001-01-01 → 1970-01-01
            // 13桁を大きく超える値は 0001 起点の可能性が高い
            if (n > 1e13) return n - FILEMAKER_EPOCH_OFFSET_MS;
            return n;
        }

        /**
         * ========================================
         * 日付・時刻フォーマット関数群
         * ========================================
         */

        /**
         * 日付を yyyy/mm/dd 形式（表示用）へ整形
         * 
         * 対応入力形式:
         * - yyyy-mm-dd (ISO形式)
         * - yyyy/mm/dd, yyyy.mm.dd, yyyy-mm-dd
         * - mm/dd/yyyy (米国形式)
         * - その他Date()で解析可能な形式
         * 
         * @param {string|Date|null|undefined} value - 変換対象の日付値
         * @returns {string} yyyy/mm/dd形式の文字列、変換失敗時は元の値
         */
        function formatDateYMD(value) {
            if (!value) return '';
            const v = String(value).trim();
            let y = '', m = '', d = '';
            if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(v)) {
                const [yy, mm, dd] = v.split('-');
                y = yy; m = mm; d = dd;
            } else if (/^\d{4}[\/\-.]\d{1,2}[\/\-.]\d{1,2}$/.test(v)) {
                const parts = v.split(/[\/\-.]/);
                y = parts[0]; m = parts[1]; d = parts[2];
            } else if (/^\d{1,2}[\/\-.]\d{1,2}[\/\-.]\d{4}$/.test(v)) {
                const parts = v.split(/[\/\-.]/);
                y = parts[2]; m = parts[0]; d = parts[1];
            } else {
                const dt = new Date(v);
                if (!isNaN(dt.getTime())) {
                    y = String(dt.getFullYear());
                    m = String(dt.getMonth() + 1);
                    d = String(dt.getDate());
                }
            }
            if (!y || !m || !d) return v;
            return `${y}/${String(m).padStart(2,'0')}/${String(d).padStart(2,'0')}`;
        }
        /**
         * 日付を yyyy/mm/dd(曜) 形式（表示用）へ整形
         * - formatDateYMD() の結果が YYYY/MM/DD のときのみ曜日を付与
         * - 解析失敗時は元の値を返す（曜日は付けない）
         * @param {string|Date|null|undefined} value
         * @returns {string}
         */
        function formatDateYMDWithWeekday(value) {
            const ymd = formatDateYMD(value);
            if (!ymd) return '';
            const m = ymd.match(/^(\d{4})\/(\d{2})\/(\d{2})$/);
            if (!m) return ymd;
            const y = Number(m[1]);
            const mo = Number(m[2]);
            const d = Number(m[3]);
            const dt = new Date(y, mo - 1, d);
            if (isNaN(dt.getTime())) return ymd;
            const jpWeekFull = ['日曜日','月曜日','火曜日','水曜日','木曜日','金曜日','土曜日'];
            const wd = jpWeekFull[dt.getDay()] || '';
            return `${ymd} ( ${wd} )`;
        }
        /**
         * 全角文字→半角文字変換関数（数値・記号用）
         * 
         * 変換対象:
         * - 全角数字 (０-９) → 半角数字 (0-9)
         * - 全角記号 (＋－．：／等) → 半角記号 (+-.:/等)
         * - 全角スペース (　) → 半角スペース ( )
         * - 桁区切りコンマ (，,) → 削除
         * 
         * 用途:
         * - 数値入力フィールドの正規化
         * - 日付・時刻入力の統一
         * 
         * @param {string|null|undefined} input - 変換対象の文字列
         * @returns {string} 半角に変換された文字列
         */
        function toHalfWidthNumeric(input) {
            if (input == null) return '';
            let s = String(input);
            // 全角数字
            s = s.replace(/[０-９]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 0xFF10 + 0x30));
            // 記号類
            s = s.replace(/[＋]/g, '+')
                 .replace(/[－−―ー]/g, '-')
                 .replace(/[．｡]/g, '.')
                 .replace(/[：]/g, ':')
                 .replace(/[／]/g, '/')
                 .replace(/[　]/g, ' ');
            // 桁区切りコンマを除去（半角/全角）
            s = s.replace(/[，,]/g, '');
            return s.trim();
        }

        // 特定のタイプでのみ全角→半角変換を行う関数
        function toHalfWidthForType(input, fieldType) {
            const typesWithConversion = ['number', 'date', 'time', 'timestamp'];
            if (typesWithConversion.includes(fieldType)) {
                return toHalfWidthNumeric(input);
            } else {
                // 変換対象外のタイプの場合は、そのまま返す
                return input == null ? '' : String(input);
            }
        }
// 型表記の正規化（外部から渡される多様な表記を内部の統一表記へ揃える）
function normalizeFieldType(type) {
    const s = String(type == null ? '' : type).trim().toLowerCase();
    if (!s) return undefined;
    if (['multiline','text','number','date','time','timestamp'].includes(s)) return s;
    if (s === 'datetime-local' || s === 'datetime' || s === 'date-time' || s === 'date_time') return 'timestamp';
    if (s === 'bool' || s === 'boolean' || s === 'checkbox') return undefined; // チェックボックスは型を設定しない
    return undefined;
}
        // 日付を yyyy-MM-dd（input[type=date] 用）へ整形
        function formatDateISO(value) {
            if (!value) return '';
            const v = toHalfWidthNumeric(value);
            let y = '', m = '', d = '';
            if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(v)) {
                const [yy, mm, dd] = v.split('-');
                y = yy; m = mm; d = dd;
            } else if (/^\d{4}[\/\-.]\d{1,2}[\/\-.]\d{1,2}$/.test(v)) {
                const parts = v.split(/[\/\-.]/);
                y = parts[0]; m = parts[1]; d = parts[2];
            } else if (/^\d{1,2}[\/\-.]\d{1,2}[\/\-.]\d{4}$/.test(v)) {
                const parts = v.split(/[\/\-.]/);
                y = parts[2]; m = parts[0]; d = parts[1];
            } else {
                const dt = new Date(v);
                if (!isNaN(dt.getTime())) {
                    y = String(dt.getFullYear());
                    m = String(dt.getMonth() + 1);
                    d = String(dt.getDate());
                }
            }
            if (!y || !m || !d) return '';
            return `${y}-${String(m).padStart(2,'0')}-${String(d).padStart(2,'0')}`;
        }

        // 日時を yyyy-MM-ddTHH:mm（input[type=datetime-local] 用）へ整形
        function formatDateTimeLocalISO(value) {
            if (!value) return '';
            const v = toHalfWidthNumeric(value);
            // 既に ISO 形式（datetime-local）
            if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(v)) return v;
            // スペース区切りの ISO 近似
            if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}$/.test(v)) return v.replace(' ', 'T');
            // スラッシュやドット区切り: YYYY/MM/DD HH:mm など
            const m = v.match(/^(\d{4})[\/\-.](\d{1,2})[\/\-.](\d{1,2})\s+(\d{1,2}):(\d{1,2})$/);
            if (m) {
                const [, yy, mm, dd, HH, MM] = m;
                const y = yy;
                const mo = String(mm).padStart(2, '0');
                const d = String(dd).padStart(2, '0');
                const h = String(HH).padStart(2, '0');
                const mi = String(MM).padStart(2, '0');
                return `${y}-${mo}-${d}T${h}:${mi}`;
            }
            // Date に投げてみる（ロケール依存を避け最小限）
            const dt = new Date(v);
            if (!isNaN(dt.getTime())) {
                const y = String(dt.getFullYear());
                const mo = String(dt.getMonth() + 1).padStart(2, '0');
                const d = String(dt.getDate()).padStart(2, '0');
                const h = String(dt.getHours()).padStart(2, '0');
                const mi = String(dt.getMinutes()).padStart(2, '0');
                return `${y}-${mo}-${d}T${h}:${mi}`;
            }
            return '';
        }

        // 日時を yyyy/MM/dd HH:mm で表示用に整形
        function formatDateTimeDisplay(value) {
            const iso = formatDateTimeLocalISO(value);
            if (!iso) return '';
            const [datePart, timePart] = iso.split('T');
            const [y, m, d] = datePart.split('-');
            return `${y}/${m}/${d} ${timePart}`;
        }

        // 型別の入出力フォーマット統一: 入力用(ISO) ⇄ 保存用(表示フォーマット)
        function toInputValueByType(value, type) {
            const t = normalizeFieldType(type) || 'multiline';
            const v = value == null ? '' : String(value);
            if (t === 'date') return formatDateISO(v);
            if (t === 'timestamp') return formatDateTimeLocalISO(v);
            if (t === 'time') {
                const m = String(v).match(/^(\d{1,2}):(\d{2})/);
                return m ? `${m[1].padStart(2,'0')}:${m[2]}` : '';
            }
            if (t === 'number') {
                const n = toHalfWidthNumeric(v).replace(/[^+\-\.\d]/g, '');
                if (n === '' || isNaN(Number(n))) return '';
                return String(Number(n));
            }
            return v;
        }

        function fromInputToStorageByType(value, type) {
            const t = normalizeFieldType(type) || 'multiline';
            const v = value == null ? '' : String(value);
            if (t === 'date') return formatDateYMD(v);
            if (t === 'timestamp') return formatDateTimeDisplay(v);
            if (t === 'time') {
                const m = String(v).match(/^(\d{1,2}):(\d{2})/);
                return m ? `${m[1].padStart(2,'0')}:${m[2]}` : '';
            }
            if (t === 'number') {
                const n = toHalfWidthNumeric(v).replace(/[^+\-\.\d]/g, '');
                if (n === '' || isNaN(Number(n))) return '';
                return String(Number(n));
            }
            return v;
        }

        function nowValueByType(type) {
            const t = normalizeFieldType(type) || 'multiline';
            const now = new Date();
            const y = now.getFullYear();
            const m = String(now.getMonth() + 1).padStart(2, '0');
            const d = String(now.getDate()).padStart(2, '0');
            const hh = String(now.getHours()).padStart(2, '0');
            const mm = String(now.getMinutes()).padStart(2, '0');
            if (t === 'date') return `${y}/${m}/${d}`;
            if (t === 'time') return `${hh}:${mm}`;
            if (t === 'timestamp') return `${y}/${m}/${d} ${hh}:${mm}`;
            return '';
        }

        // CSS属性セレクタ用の値エスケープ（querySelectorで[name="..."]を安全に扱う）
        function escapeForCssAttr(value) {
            const str = String(value == null ? '' : value);
            try {
                if (window.CSS && typeof window.CSS.escape === 'function') {
                    return window.CSS.escape(str);
                }
            } catch (_) {}
            // フォールバック: CSS属性セレクタで特別扱いされる文字をバックスラッシュでエスケープ
            return str.replace(/([\\\"'\[\]\.#:>+~*=|^$!\()])/g, '\\$1');
        }



        /* 
         * セキュリティ対策について:
         * 1. 全てのユーザー入力はescapeHtml()でエスケープ
         * 2. HTML属性値はescapeAttr()でエスケープ  
         * 3. innerHTML使用時は必ずエスケープ済みデータを使用
         * 4. Claris FileMaker からのデータも同様に検証・エスケープ
         * 5. 可能な限りtextContentやcreateElementを使用
         */
        // 真偽値に寄せる簡易変換
        function coerceBoolean(value) {
            if (typeof value === 'boolean') return value;
            if (typeof value === 'number') return value !== 0;
            if (typeof value === 'string') {
                const v = value.trim().toLowerCase();
                return v === 'true' || v === '1' || v === 'yes' || v === 'on' || v === '☑';
            }
            return false;
        }
        // 検索欄フォーカス（簡素化ガード付き）とUI更新を一元化
        let __searchGuardTimer = null;
        let __searchGuardEndAt = 0;
        let __searchGuardListenerAttached = false;
        function focusSearchWithGuard(durationMs = 2000) {
            const input = document.getElementById('templateSearch');
            const modal = document.getElementById('templateModal');
            if (!input) return;
            input.focus({ preventScroll: true });
            const len = input.value.length;
            try { input.setSelectionRange(len, len); } catch (_) {}
            if (!modal || modal.style.display !== 'block') return;
            __searchGuardEndAt = Date.now() + durationMs;
            if (__searchGuardTimer) clearInterval(__searchGuardTimer);
            __searchGuardTimer = setInterval(() => {
                if (Date.now() > __searchGuardEndAt) {
                    clearInterval(__searchGuardTimer);
                    __searchGuardTimer = null;
                    return;
                }
                if (document.activeElement !== input) {
                    input.focus({ preventScroll: true });
                    const l = input.value.length;
                    try { input.setSelectionRange(l, l); } catch (_) {}
                }
            }, 150);
            if (!__searchGuardListenerAttached) {
                document.addEventListener('focusin', (e) => {
                    const inp = document.getElementById('templateSearch');
                    const md = document.getElementById('templateModal');
                    if (!inp || !md || md.style.display !== 'block') return;
                    if (Date.now() > __searchGuardEndAt) return;
                    if (e.target !== inp) {
                        setTimeout(() => {
                            if (Date.now() <= __searchGuardEndAt) {
                                inp.focus({ preventScroll: true });
                                const l2 = inp.value.length;
                                try { inp.setSelectionRange(l2, l2); } catch (_) {}
                            }
                        }, 0);
                    }
                }, true);
                __searchGuardListenerAttached = true;
            }
        }
        function focusSearchAndUpdateUI() {
            const input = document.getElementById('templateSearch');
            const clearBtn = document.getElementById('templateSearchClear');
            if (clearBtn && input) {
                const hasText = input.value.trim().length > 0;
                clearBtn.style.display = hasText ? 'inline-block' : 'none';
            }
            requestAnimationFrame(() => {
                focusSearchWithGuard(2000);
                enforceTemplateSelectionFooterState();
            });
        }

        // テンプレート選択モードのとき、下部の「更新/登録/キャンセル」ボタン状態を強制的に選択モード用に戻す
        function enforceTemplateSelectionFooterState() {
            try {
                const titleEl = document.getElementById('templateModalTitle');
                if (!titleEl) return;
                // 作成フォームが表示されている場合は何もしない（競合回避）
                try {
                    const createSectionEl = document.getElementById('templateCreateSection');
                    if (createSectionEl && getComputedStyle(createSectionEl).display === 'block') return;
                } catch(_) {}
                // モードフラグで明示的に除外
                try {
                    const modalEl = document.getElementById('templateModal');
                    if (modalEl && (modalEl.dataset.mode === 'create' || modalEl.dataset.mode === 'edit')) return;
                } catch(_) {}
                // タイトルでの判定に加え、copyモードも選択扱いにする
                const modalEl2 = document.getElementById('templateModal');
                const isCopyMode = !!(modalEl2 && modalEl2.dataset.mode === 'copy');
                const isSelectionMode = isCopyMode || (String(titleEl.textContent || '').trim() === 'テンプレートを選択');
                if (!isSelectionMode) return;

                const submitBottomEl = null;
                const cancelBottomEl = null;
                const closeBtnEl = null;

                if (cancelBottomEl) cancelBottomEl.style.display = 'none';
                if (submitBottomEl) { submitBottomEl.style.display = 'none'; submitBottomEl.textContent = '登録'; submitBottomEl.disabled = false; }
                if (closeBtnEl) closeBtnEl.style.display = 'inline-flex';

                // テンプレート選択モードではヘルプ「?」を非表示
                try {
                    const help = document.getElementById('syntaxHelpTriggerTemplateEdit');
                    if (help) help.style.display = 'none';
                } catch(_) {}
            } catch (_) {}
        }

        // 作成モードのフッター表示を強制（冪等）
        function enforceTemplateCreateFooterState() {
            try {
                const titleEl = document.getElementById('templateModalTitle');
                const cancelBottomEl = null;
                const submitBottomEl = null;
                const closeBtnEl = null;
                const createSectionEl = document.getElementById('templateCreateSection');
                if (titleEl) titleEl.textContent = 'テンプレートを新規作成';
                if (createSectionEl) createSectionEl.style.display = 'block';
                if (cancelBottomEl) cancelBottomEl.style.display = '';
                if (submitBottomEl) submitBottomEl.style.display = '';
                if (closeBtnEl) closeBtnEl.style.display = 'none';
                // モードフラグ
                const modalEl = document.getElementById('templateModal');
                if (modalEl) modalEl.dataset.mode = 'create';
            } catch(_) {}
        }

        // 検索フォーカスガードは簡素化し、focusSearchWithGuard に統合済み

        // 取り除き: 外部フォーカス関数（focusTemplateSearch）
        // フィールド名から対応する入力要素を探す（用途別）


        // 指定要素を、その最寄りのスクロール可能コンテナ内で中央付近にスムーズスクロール
        // クリック可能プレースホルダーとチェックボックス表示のクリック処理
        (function attachDisplayClickDelegation() {
            const container = document.getElementById('display-text');
            if (!container) return;
            
            // クリックイベント処理
            const handleInteraction = (e) => {
                // 先に [[]] チェックボックス項目のクリック処理を優先
                const checkboxTarget = e.target.closest ? e.target.closest('.bracket-checkbox-display') : null;
                if (checkboxTarget) {
                    const itemName = checkboxTarget.getAttribute('data-item');
                    if (!itemName) return;
                    const fieldKey = `checkbox_${itemName}`;
                    const current = !!persistedDefaults.values[fieldKey];
                    persistedDefaults.values = { ...persistedDefaults.values, [fieldKey]: !current };
                    try { e.stopPropagation(); } catch(_) {}
                    updateDisplayText();
                    return;
                }

                // {{}} プレースホルダーのクリック処理
                const placeholderTarget = e.target.closest ? e.target.closest('.clickable-placeholder') : null;
                if (placeholderTarget) {
                    const fieldName = placeholderTarget.getAttribute('data-field');
                    if (!fieldName) return;

                    // Shift+クリックで、タイプが date/time/timestamp の場合は「今」を即時記録
                    if (e.shiftKey) {
                        try {
                            const currentType = (persistedDefaults.types && persistedDefaults.types[fieldName]) || 'multiline';
                            const isDateLike = currentType === 'date' || currentType === 'time' || currentType === 'timestamp';
                            if (isDateLike) {
                                const now = new Date();
                                let newVal = '';
                                if (currentType === 'date') {
                                    const y = now.getFullYear();
                                    const m = String(now.getMonth() + 1).padStart(2, '0');
                                    const d = String(now.getDate()).padStart(2, '0');
                                    newVal = `${y}/${m}/${d}`;
                                } else if (currentType === 'time') {
                                    const hh = String(now.getHours()).padStart(2, '0');
                                    const mm = String(now.getMinutes()).padStart(2, '0');
                                    newVal = `${hh}:${mm}`;
                                } else if (currentType === 'timestamp') {
                                    const y = now.getFullYear();
                                    const m = String(now.getMonth() + 1).padStart(2, '0');
                                    const d = String(now.getDate()).padStart(2, '0');
                                    const hh = String(now.getHours()).padStart(2, '0');
                                    const mm = String(now.getMinutes()).padStart(2, '0');
                                    newVal = `${y}/${m}/${d} ${hh}:${mm}`;
                                }
                                persistedDefaults.values = { ...(persistedDefaults.values || {}), [fieldName]: newVal };
                                updateDisplayText();
                                return;
                            }
                        } catch(_) {}
                    }

                    // 通常クリックはカードウインドウを開く
                    openInlineVariablePopover(fieldName, placeholderTarget);
                    return;
                }
            };
            
            container.addEventListener('click', handleInteraction);
            
            // キーボードイベント処理
            container.addEventListener('keydown', (e) => {
                if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    handleInteraction(e);
                }
            });
        })();
        // Function to generate a form where {{}} placeholders become inputs with persist checkboxes and type select
        // 変数名の妥当性チェック（入れ子/異常なデリミタ混在を除外）
        function isValidVarToken(raw) {
            const s = String(raw || '').trim();
            if (!s) return false;
            // デリミタ文字を含む名前は無効
            return !(/[{}\[\]]/.test(s));
        }
        function generateDynamicForm(text, existingValues = {}, existingPersist = {}, existingTypes = {}) {
            // {{}} プレースホルダーと [[]] チェックボックス項目を出現順に検出
            const items = [];
            
            // {{}} プレースホルダーを位置と共に抽出
            const placeholderRegex = /\{\{(.*?)\}\}/g;
            let match;
            while ((match = placeholderRegex.exec(text)) !== null) {
                const fieldName = match[1].trim();
                if (fieldName && isValidVarToken(fieldName)) {
                    items.push({
                        type: 'placeholder',
                        name: fieldName,
                        position: match.index
                    });
                }
            }
            
            // [[]] チェックボックス項目を位置と共に抽出
            const checkboxRegex = /\[\[(.*?)\]\]/g;
            while ((match = checkboxRegex.exec(text)) !== null) {
                const itemName = match[1].trim();
                if (itemName && isValidVarToken(itemName)) {
                    items.push({
                        type: 'checkbox',
                        name: itemName,
                        position: match.index
                    });
                }
            }
            
            if (!items.length) {
                // 項目がない場合は空のHTMLを返す
                return '';
            }
            
            // 出現順でソート
            items.sort((a, b) => a.position - b.position);
            
            // 重複を除去（最初の出現を優先）
            const seenNames = new Set();
            const uniqueItems = items.filter(item => {
                const key = `${item.type}_${item.name}`;
                if (seenNames.has(key)) {
                    return false;
                }
                seenNames.add(key);
                return true;
            });
            
            const typeOptions = [
                ['multiline', 'テキスト(複数行)'],
                ['text', 'テキスト(1行)'],
                ['number', '数値'],
                ['date', '日付'],
                ['time', '時刻'],
                ['timestamp', 'タイムスタンプ']
            ];

            // 統一されたアイテムリストから出現順にHTML生成
            const formHtml = uniqueItems.map(item => {
                if (item.type === 'placeholder') {
                    // {{}} プレースホルダー項目の処理
                    const fieldName = item.name;
                    const value = existingValues[fieldName] || '';
                    const persistChecked = existingPersist[fieldName] ? 'checked' : '';
                    const type = existingTypes[fieldName] || 'multiline';
                    const savedHeight = typeof fieldHeights[fieldName] === 'number' && fieldHeights[fieldName] > 0
                        ? `height:${Math.round(fieldHeights[fieldName])}px;`
                        : '';
                    const safeFieldName = escapeAttr(fieldName);
                    const typeSelectHtml = `<select class='variable-type' data-field='${safeFieldName}' aria-label='型選択'>` +
                        typeOptions.map(([v, label]) => `<option value='${escapeAttr(v)}' ${v === type ? 'selected' : ''}>${escapeHtml(label)}</option>`).join('') +
                        `</select>`;

                    const memoText = (persistedDefaults.memos && persistedDefaults.memos[fieldName]) || '';
                    const labelTitleAttr = memoText ? ` title="${escapeAttr(memoText)}"` : '';
                    let inputHtml = '';
                    const safeValue = escapeAttr(value);
                    const safeFieldNameHtml = escapeHtml(fieldName);
                    
                    if (type === 'multiline') {
                        inputHtml = `<textarea class='variable-input' name='${safeFieldName}' placeholder='${safeFieldName}' rows='3' data-field='${safeFieldName}' data-source='placeholder' style='${savedHeight}'>${escapeHtml(value)}</textarea>`;
                    } else if (type === 'text') {
                        inputHtml = `<input class='variable-input' type='text' name='${safeFieldName}' placeholder='${safeFieldName}' data-field='${safeFieldName}' data-source='placeholder' value='${safeValue}' />`;
                    } else if (type === 'number') {
                        inputHtml = `<input class='variable-input' type='text' inputmode='decimal' pattern='[0-9]*' data-x-type='number' name='${safeFieldName}' placeholder='${safeFieldName}' data-field='${safeFieldName}' data-source='placeholder' value='${safeValue}' />`;
                    } else if (type === 'date') {
                        const isoVal = formatDateISO(value || '');
                        inputHtml = `<span class='input-with-quick'>
                            <input class='variable-input' type='date' name='${safeFieldName}' data-field='${safeFieldName}' data-source='placeholder' value='${escapeAttr(isoVal)}' />
                            <button type='button' class='quick-set-btn quick-set-today' data-field='${safeFieldName}' title='今日の日付をセット'>今</button>
                        </span>`;
                    } else if (type === 'time') {
                        inputHtml = `<span class='input-with-quick'>
                            <input class='variable-input' type='time' name='${safeFieldName}' data-field='${safeFieldName}' data-source='placeholder' value='${safeValue}' />
                            <button type='button' class='quick-set-btn quick-set-now' data-field='${safeFieldName}' title='現在時刻をセット'>今</button>
                        </span>`;
                    } else if (type === 'timestamp') {
                        const dtVal = formatDateTimeLocalISO(value || '');
                        inputHtml = `<span class='input-with-quick'>
                            <input class='variable-input' type='datetime-local' name='${safeFieldName}' data-field='${safeFieldName}' data-source='placeholder' value='${escapeAttr(dtVal)}' />
                            <button type='button' class='quick-set-btn quick-set-now-datetime' data-field='${safeFieldName}' title='現在日時をセット'>今</button>
                        </span>`;
                    } else {
                        inputHtml = `<input class='variable-input' type='text' name='${safeFieldName}' placeholder='${safeFieldName}' data-field='${safeFieldName}' data-source='placeholder' value='${safeValue}' />`;
                    }

                    return `<div class='variable-row' data-field='${safeFieldName}'>
                                <div class='variable-header'>
                                    <label${labelTitleAttr}>${safeFieldNameHtml}: </label>
                                    <button type='button' class='var-gear-btn' title='設定' aria-label='変数設定' data-field='${safeFieldName}' data-open='false'>
                                        <svg class='icon' viewBox='0 0 24 24' aria-hidden='true' focusable='false'>
                                            <g class='icon-gear'>
                                                <path fill='currentColor' d='M19.14,12.94a7.49,7.49,0,0,0,.05-.94,7.49,7.49,0,0,0-.05-.94l2.11-1.65a.5.5,0,0,0,.12-.64l-2-3.46a.5.5,0,0,0-.6-.22l-2.49,1a7.27,7.27,0,0,0-1.63-.94l-.38-2.65A.5.5,0,0,0,13.67,2H10.33a.5.5,0,0,0-.5.42L9.45,5.07a7.27,7.27,0,0,0-1.63.94l-2.49-1a.5.5,0,0,0-.6.22l-2,3.46a.5.5,0,0,0,.12.64L4.86,11.06a7.49,7.49,0,0,0-.05.94,7.49,7.49,0,0,0,.05.94L2.75,14.59a.5.5,0,0,0,.12.64l2,3.46a.5.5,0,0,0,.6.22l2.49-1a7.27,7.27,0,0,0,1.63.94l.38,2.65a.5.5,0,0,0,.5.42h3.34a.5.5,0,0,0,.5-.42l.38-2.65a7.27,7.27,0,0,0,1.63-.94l2.49,1a.5.5,0,0,0,.6-.22l2-3.46a.5.5,0,0,0-.12-.64ZM12,15.5A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z'/>
                                            </g>
                                            <g class='icon-close'>
                                                <path fill='currentColor' d='M18.3 5.71a1 1 0 00-1.41 0L12 10.59 7.11 5.7A1 1 0 105.7 7.11L10.59 12l-4.9 4.89a1 1 0 101.41 1.41L12 13.41l4.89 4.9a1 1 0 001.41-1.41L13.41 12l4.9-4.89a1 1 0 000-1.4z'/>
                                            </g>
                                        </svg>
                                    </button>
                                    <div class='variable-settings-panel' data-field='${safeFieldName}'>
                                        <div class='settings-row'>
                                            <label style='min-width:64px;'>タイプ</label>
                                            ${typeSelectHtml}
                                        </div>
                                        <div class='settings-row'>
                                            <label class='persist-label' style='display:inline-flex;align-items:center;gap:6px;'>
                                                <input type='checkbox' class='persist-checkbox' data-field='${safeFieldName}' ${persistChecked} /> 次回も使用
                                            </label>
                                        </div>
                                    </div>
                                </div>
                                ${inputHtml}
                            </div>`;
                } else if (item.type === 'checkbox') {
                    // [[]] チェックボックス項目の処理
                    const itemName = item.name;
                    const fieldKey = `checkbox_${itemName}`;
                    const value = existingValues[fieldKey] || false;
                    const checked = coerceBoolean(value) ? 'checked' : '';
                    const checkedClass = coerceBoolean(value) ? 'checked' : '';
                    const statusText = coerceBoolean(value) ? 'チェック済み' : 'チェックなし';
                    const safeFieldKey = escapeAttr(fieldKey);
                    const safeItemName = escapeAttr(itemName);
                    const safeItemNameHtml = escapeHtml(itemName);
                    
                    return `<div class='variable-row bracket-checkbox-row' data-field='${safeFieldKey}'>
                                <div class='checkbox-container ${checkedClass}' data-field='${safeFieldKey}'>
                                    <input class='variable-input bracket-checkbox' type='checkbox' id='checkbox-${safeFieldKey}' name='${safeFieldKey}' data-field='${safeFieldKey}' data-source='bracket' data-item-name='${safeItemName}' ${checked} />
                                    <label class='checkbox-label' for='checkbox-${safeFieldKey}'>${safeItemNameHtml}</label>
                                    <span class='checkbox-status'>${statusText}</span>
                                </div>
                            </div>`;
                }
                return '';
            }).join('');
            
            return formHtml;
        }

        /**
         * ========================================
         * 変数管理システム - 表示更新機能
         * ========================================
         */
        /**
         * 表示テキスト内のプレースホルダーを入力値で置換する関数
         * 
         * 処理対象:
         * - {{変数名}} プレースホルダー → 入力された値に置換
         * - [[項目名]] チェックボックス → ☑/☐記号に置換
         * 
         * セキュリティ対策:
         * - 全ての入力値をescapeHtml()でエスケープ
         * - HTMLタグの無効化
         * - 改行は<br>タグに変換（安全な範囲で）
         * 
         * メモ機能:
         * - 変数にメモが設定されている場合、ツールチップ表示用の目印を追加
         */
        // 変数パネルトグルの表示/非表示を現在の本文に応じて更新
        function hasVariablesInText(text) {
            if (!text) return false;
            return /\{\{\s*[^}]+\s*\}\}/.test(text) || /\[\[\s*[^\]]+\s*\]\]/.test(text);
        }
        
        // 外部（ Claris FileMaker ）からJSON文字列/オブジェクトを渡された場合は、それを反映してから表示を更新
        // 入れ子 {{}} を安全に解決するヘルパー
        function replacePlaceholdersOnce(text, valueGetter) {
            if (!text) return text;
            return String(text).replace(/\{\{\s*(.*?)\s*\}\}/g, (m, raw) => {
                const key = String(raw || '').trim();
                if (!key) return m;
                const val = valueGetter ? valueGetter(key) : undefined;
                return (val != null && val !== '') ? String(val) : m;
            });
        }
        function resolveNestedPlaceholders(text, valueGetter, maxDepth = 10) {
            let cur = String(text == null ? '' : text);
            for (let i = 0; i < maxDepth; i++) {
                const next = replacePlaceholdersOnce(cur, valueGetter);
                if (next === cur) break;
                cur = next;
            }
            return cur;
        }
        function updateDisplayText() {
            const originalTextarea = document.querySelector('#original-textarea');
            const directInput = document.querySelector('#direct-input');
            const displayText = document.querySelector('#display-text');
            // メモ表示トグルの状態をグローバルへ反映
            try {
                const cb = document.getElementById('toggleShowMemos');
                if (cb) {
                    window.__showMemosInline = !!cb.checked;
                    document.body.classList.toggle('show-memos-inline', window.__showMemosInline);
                    // 永続化は行わない（localStorageは使用しない方針）
                }
                updateMemoToggleVisibility();
            } catch(_) {}
            
            // 内容が空の場合
            if (!originalTextarea.value.trim()) {
                originalTextarea.style.display = 'none';
                displayText.style.display = 'none';
                directInput.style.display = 'block';
                // 表示直後に自動フィット
                raf(() => { try { fitTextareaToContent(directInput); } catch(_) {} });
            } else {
                originalTextarea.style.display = 'none';
                directInput.style.display = 'none';
                displayText.style.display = 'block';
                
                // 入力値マップは永続デフォルトから構築
                const valuesMap = { ...(persistedDefaults.values || {}) };

                

                // 元のテンプレートテキストを安全にエスケープ（HTMLタグを無害化）
                let baseText = escapeHtml(originalTextarea.value);
                // エスケープ後に改行を<br>に変換
                baseText = baseText.replace(/\n/g, '<br>');

                // 事前に {{名前:A|B|C}} の最初の出現のみを採用したマッピングを作る（共通関数）
                const selectSpecMap = buildSelectSpecMapFromText(originalTextarea.value);

                // {{}} プレースホルダーを置換（エスケープ済みテキストに対して）
                // ただし [[...]] の内側は置換対象から除外
                const __bracketRangesDisplay = collectBracketRanges(baseText);
                const __isInsideBracketDisplay = (start, len) => isInsideRanges(__bracketRangesDisplay, start, len);

                let updatedText = replaceWithGuard(baseText, /\{\{(.*?)\}\}/g, (match, rawName) => {
                    // rawNameは既にエスケープされているため、HTMLデコードしてから処理
                    const raw = unescapeHtml(rawName || '').trim();
                    if (!raw) return match;
                    // 新記法: {{名前:A|B|C}} → セレクト（variables.select を優先）
                    if (raw.includes(':')) {
                        const idx = raw.indexOf(':');
                        const name = raw.slice(0, idx).trim();
                        const optionsPart = raw.slice(idx + 1);
                        let options = optionsPart.split('|').map(s => s.trim()).filter(s => s !== '');
                        if (!name) return match;
                        if (persistedDefaults.selects && persistedDefaults.selects[name] && Array.isArray(persistedDefaults.selects[name].options)) {
                            options = persistedDefaults.selects[name].options;
                        }
                        if (options.length === 0) return match;
                        const selected = (persistedDefaults.selects && persistedDefaults.selects[name] && persistedDefaults.selects[name].value)
                            || (persistedDefaults.values && persistedDefaults.values[name]) || options[0] || '';
                        const optsHtml = options.map(opt => {
                            const sel = (opt === selected) ? ' selected' : '';
                            return `<option value="${escapeAttr(opt)}"${sel}>${escapeHtml(opt)}</option>`;
                        }).join('');
                        const safeName = escapeAttr(name);
                        return `<select class="curly-select" data-field="${safeName}">${optsHtml}</select>`;
                    }
                    const fieldName = raw;
                    if (!isValidVarToken(fieldName)) return match;
                    // マッピングがある場合は、素の {{名前}} もセレクトに統一
                    if (Object.prototype.hasOwnProperty.call(selectSpecMap, fieldName)) {
                        let options = selectSpecMap[fieldName] || [];
                        // 受信済みの variables.select を優先
                        if (persistedDefaults.selects && persistedDefaults.selects[fieldName] && Array.isArray(persistedDefaults.selects[fieldName].options)) {
                            options = persistedDefaults.selects[fieldName].options;
                        }
                        if (options.length > 0) {
                            const selected = (persistedDefaults.selects && persistedDefaults.selects[fieldName] && persistedDefaults.selects[fieldName].value)
                                || (persistedDefaults.values && persistedDefaults.values[fieldName]) || options[0] || '';
                            const optsHtml = options.map(opt => {
                                const sel = (opt === selected) ? ' selected' : '';
                                return `<option value="${escapeAttr(opt)}"${sel}>${escapeHtml(opt)}</option>`;
                            }).join('');
                            const safeName = escapeAttr(fieldName);
                            return `<select class="curly-select" data-field="${safeName}">${optsHtml}</select>`;
                        }
                    }
                    const valueFromMap = valuesMap[fieldName];
                    
                    // チェックボックス（☑☐記号）の場合は常に値ありとして扱う
                    const isCheckboxValue = valueFromMap === '☑' || valueFromMap === '☐';
                    const isMissing = !isCheckboxValue && !(valueFromMap && String(valueFromMap).length > 0);
                    
                    // 値を安全にエスケープ（改行は<br>に変換）
                    let display;
                    if (isMissing) {
                        // 未入力の場合は既にエスケープ済みのマッチ文字列をそのまま使用
                        display = match;
                    } else {
                        // 型に応じて画面表示用に整形（date は曜日付き）
                        const tDisplay = (persistedDefaults.types && persistedDefaults.types[fieldName]) || 'multiline';
                        let raw = valueFromMap;
                        try { raw = toHalfWidthForType(raw, tDisplay); } catch(_) {}
                        let value = '';
                        if (tDisplay === 'date') value = formatDateYMDWithWeekday(raw || '').trim();
                        else if (tDisplay === 'timestamp') value = formatDateTimeDisplay(raw || '').trim();
                        else if (tDisplay === 'time') { const mm = String(raw || '').match(/^(\d{1,2}):(\d{2})/); value = mm ? `${mm[1].padStart(2,'0')}:${mm[2]}` : ''; }
                        else {
                            const resolved = resolveNestedPlaceholders(String(raw || ''), (k) => valuesMap[k]);
                            value = String(resolved);
                        }
                        if (value === '') {
                            display = match;
                        } else {
                            const safeValue = String(value).replace(/\\n/g, '').replace(/\n/g, '<br>');
                            display = escapeHtml(safeValue).replace(/&lt;br&gt;/g, '<br>'); // <br>のみ許可
                        }
                    }
                    
                    const memoText = (persistedDefaults.memos && persistedDefaults.memos[fieldName]) || '';
                    const tooltipText = memoText 
                        ? `変数名: ${fieldName}\n${memoText}`
                        : `変数名: ${fieldName}`;
                    const titleAttr = ` title="${escapeAttr(tooltipText)}"`;
                    const safeFieldName = escapeAttr(fieldName);
                    // メモがある場合: 黄色いバッジへ内容を表示（トグルで文言切替）
                    const showMemos = !!(window.__showMemosInline);
                    const memoBadge = memoText
                        ? (showMemos ? `<span class="memo-indicator" title="${escapeAttr(memoText)}">${escapeHtml(memoText)}</span>` : '')
                        : '';

                    // タイプと「次回も」の状態を取得
                    const t = (persistedDefaults.types && persistedDefaults.types[fieldName]) || 'multiline';
                    const isPersist = !!(persistedDefaults.persist && persistedDefaults.persist[fieldName]);

                    // タイプ別の小さなSVGアイコン（ツールチップ付き）
                    const typeIconSvgInner = (() => {
                        const typeLabel = getTypeLabel(t);
                        const common = `class="placeholder-type-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" title="タイプ: ${escapeAttr(typeLabel)}"`;
                        if (t === 'date') {
                            return `<svg ${common}><path fill="currentColor" d="M7 2v2H5a2 2 0 00-2 2v11a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2h-2V2h-2v2H9V2H7zm12 7H5v8h14V9zM5 7h14V6H5v1z"/></svg>`;
                        } else if (t === 'time') {
                            return `<svg ${common}><path fill="currentColor" d="M12 2a10 10 0 100 20 10 10 0 000-20zm1 6h-2v5l4.25 2.52.75-1.23-3-1.79V8z"/></svg>`;
                        } else if (t === 'timestamp') {
                            return `<svg ${common}><path fill="currentColor" d="M7 2v2H5a2 2 0 00-2 2v3h18V6a2 2 0 00-2-2h-2V2h-2v2H9V2H7zm14 9H3v6a2 2 0 002 2h14a2 2 0 002-2v-6zM13 8h-2v3h2V8z"/></svg>`;
                        } else if (t === 'number') {
                            // 視認性が高く欠けにくいハッシュ（#）記号をストロークで描画
                            return `<svg ${common}><g>
                                <path d="M7 4v16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                                <path d="M13 4v16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                                <path d="M4 8h16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                                <path d="M4 16h16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                            </g></svg>`;
                        } else if (t === 'text') {
                            // 1行テキスト: 1本の横線
                            return `<svg ${common}><path fill="currentColor" d="M4 11h16v2H4z"/></svg>`;
                        } else if (t === 'multiline') {
                            // 複数行: 3本の横線
                            return `<svg ${common}><path fill="currentColor" d="M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"/></svg>`;
                        }
                        return '';
                    })();
                    const typeIconSvg = typeIconSvgInner ? `<span class="type-icon-wrapper">${typeIconSvgInner}</span>` : '';

                    const persistClass = isPersist ? ' persist' : '';
                    const memoClass = memoText ? ' has-memo' : '';
                    return `<span class="clickable-placeholder${isMissing ? ' missing' : ''}${persistClass}${memoClass}" data-field="${safeFieldName}"${titleAttr} role="button" tabindex="0">${display}${typeIconSvg}</span>${memoBadge}`;
                });
                
                // [[]] を置換（エスケープ済みテキストに対して）
                // 1) [[A|B|C]] → ドロップダウン
                // 2) [[項目]] → チェックボックス表示
                (function() {
                    try { if (!window.__bracketSelectState) window.__bracketSelectState = {}; } catch(_) {}
                    let __bracketSelectIndex = 0;
                    // {{...}} のセレクト要素内では [[...]] を解決しない
                    const __curlySelectRanges = collectCurlySelectRangesFromHtml(updatedText);
                    const __isInsideCurlySelect = (start, len) => __curlySelectRanges.some(([a, b]) => start >= a && start + len <= b);
                    updatedText = replaceWithGuard(updatedText, /\[\[(.*?)\]\]/g, (match, itemName) => {
                        // itemNameは既にエスケープされているため、HTMLデコードしてから処理
                        const raw = unescapeHtml(itemName || '').trim();
                        if (!raw) return match;
                        // パイプ区切りがあればドロップダウン
                        if (raw.includes('|')) {
                            // 先頭に {{変数名}} がある場合はキーに採用し、'|' は任意
                            let varName = null;
                            let optionsPart = raw;
                            const mVar = raw.match(/^\{\{\s*([^}]+?)\s*\}\}/);
                            if (mVar) {
                                varName = (mVar[1] || '').trim();
                                optionsPart = raw.slice(mVar[0].length);
                                if (optionsPart.startsWith('|')) optionsPart = optionsPart.slice(1);
                            }
                            const options = optionsPart.split('|').map(s => s.trim()).filter(s => s !== '');
                            if (options.length === 0) return match;
                            // キー: 明示名があれば select:名前、無ければ通し番号
                            const key = varName ? `select:${varName}` : `select#${++__bracketSelectIndex}`;
                            const safeKey = escapeAttr(key);
                            let selected = '';
                            try { selected = (window.__bracketSelectState && window.__bracketSelectState[key]) || options[0] || ''; } catch(_) { selected = options[0] || ''; }
                            const optsHtml = options.map(opt => {
                                const sel = (opt === selected) ? ' selected' : '';
                                return `<option value="${escapeAttr(opt)}"${sel}>${escapeHtml(opt)}</option>`;
                            }).join('');
                            return `<select class="bracket-select" data-key="${safeKey}">${optsHtml}</select>`;
                        }
                        // それ以外はチェックボックス
                        if (!isValidVarToken(raw)) return match;
                        const fieldKey = `checkbox_${raw}`;
                        const isChecked = !!valuesMap[fieldKey];
                        const symbol = isChecked ? '☑' : '☐';
                        const safeItemName = escapeHtml(raw);
                        const display = `${symbol} ${safeItemName}`;
                        const safeDataItem = escapeAttr(raw);
                        return `<strong class="bracket-checkbox-display ${isChecked ? 'checked' : 'unchecked'}" data-item="${safeDataItem}" role="button" tabindex="0">${display}</strong>`;
                    }, __curlySelectRanges);
                })();

                renderUpdatedContent(displayText, updatedText);
                // {{名前:A|B|C}} のセレクトにイベントをバインド
                try {
                    const selects = displayText.querySelectorAll('.curly-select');
                    selects.forEach(sel => {
                        if (sel.__bound) return;
                        sel.addEventListener('change', () => {
                            try {
                                const name = sel.getAttribute('data-field') || '';
                                const val = sel.value || '';
                                if (name) {
                                    // 値は values と selects の両方へ反映（OK時は selects.value を優先するため）
                                    persistedDefaults.values = { ...(persistedDefaults.values || {}), [name]: val };
                                    try {
                                        const opts = Array.from(sel.options).map(o => String(o.value || ''));
                                        const next = { options: opts, value: val };
                                        persistedDefaults.selects = { ...(persistedDefaults.selects || {}), [name]: next };
                                    } catch(_) {}
                                    // 同名の他のセレクトも即座に同期
                                    try {
                                        document.querySelectorAll(`.curly-select[data-field="${CSS.escape(name)}"]`).forEach(other => {
                                            if (other !== sel) { try { other.value = val; } catch(_) {} }
                                        });
                                    } catch(_) {}
                                    // 表示全体を再描画（{{変数}} 側も同期）
                                    try { updateDisplayText(); } catch(_) {}
                                }
                            } catch(_) {}
                        });
                        sel.__bound = true;
                    });
                } catch(_) {}
                // メモ表示トグルがある場合は、変更時に即時更新
                try {
                    const cb = document.getElementById('toggleShowMemos');
                    if (cb && !cb.__bound) {
                        cb.addEventListener('change', () => updateDisplayText());
                        cb.__bound = true;
                    }
                    updateMemoToggleVisibility();
                } catch(_) {}
            }
        }

        // フッターの表示/非表示をオーバーレイの有無に応じて更新
        function updateFooterHiddenState() {
            try {
                const footer = document.querySelector('.footer');
                if (!footer) return;

                const isDisplayed = (sel) => {
                    const el = document.querySelector(sel);
                    return !!(el && el.style && el.style.display === 'block');
                };
                const isEditModalOpen = isDisplayed('#editModal');

                // テンプレート選択モーダルが開いている場合は既存フッターを利用するため常に表示
                try {
                    const isTemplateModalOpen = isDisplayed('#templateModal');
                    if (isTemplateModalOpen) {
                        footer.classList.remove('hidden');
                        document.body.classList.remove('header-hidden');
                        return;
                    }
                } catch(_) {}

                // 編集モーダルが開いている間は必ずフッターを表示（他のポップアップがあっても隠さない）
                if (isEditModalOpen) {
                    footer.classList.remove('hidden');
                    document.body.classList.remove('header-hidden');
                    return;
                }

                // 変数ポップオーバーがフルスクリーンで開いている場合もフッターを表示
                try {
                    const inline = document.getElementById('variableInlinePopover');
                    const inlineFullscreenOpen = !!(inline && inline.style && inline.style.display === 'block' && inline.classList && inline.classList.contains('fullscreen'));
                    if (inlineFullscreenOpen) {
                        footer.classList.remove('hidden');
                        document.body.classList.remove('header-hidden');
                        return;
                    }
                } catch(_) {}

                const hasOtherModalOpen = (
                    isDisplayed('#varSettingsModal') ||
                    isDisplayed('#confirmModal') ||
                    isDisplayed('#messageModal') ||
                    isDisplayed('#phraseEditModal')
                );
                const hasInlinePopoverOpen = (() => {
                    const pop = document.getElementById('variableInlinePopover');
                    return !!(pop && pop.style && pop.style.display === 'block');
                })();
                const hasSettingsPopoverOpen = (() => {
                    const settingsPopover = document.getElementById('variableSettingsPopover');
                    return !!(settingsPopover && settingsPopover.style && settingsPopover.style.display === 'block');
                })();

                const shouldHide = hasOtherModalOpen || hasInlinePopoverOpen || hasSettingsPopoverOpen;
                if (shouldHide) {
                    footer.classList.add('hidden');
                    document.body.classList.add('header-hidden');
                } else {
                    footer.classList.remove('hidden');
                    document.body.classList.remove('header-hidden');
                }
            } catch (_) {}
        }

        // 共通モーダル開閉API
        function openModal(target) {
            try {
                const el = (typeof target === 'string') ? document.getElementById(target) : target;
                if (!el) return;
                el.style.display = 'block';
                try { el.classList.add('is-open'); } catch(_) {}
                updateFooterHiddenState();
            } catch(_) {}
        }
        function closeModal(target) {
            try {
                const el = (typeof target === 'string') ? document.getElementById(target) : target;
                if (!el) return;
                el.style.display = 'none';
                try { el.classList.remove('is-open'); } catch(_) {}
                updateFooterHiddenState();
            } catch(_) {}
        }

        const modalState = {
            template: {
                isOpen: false,
                mode: 'select',
                lastOpenedAt: 0
            },
            phrase: {
                isOpen: false,
                recordId: '',
                lastOpenedAt: 0
            }
        };
        try { window.__modalState = modalState; } catch(_) {}

        function markTemplateModalOpen(mode) {
            try { modalState.template.isOpen = true; } catch(_) {}
            try { modalState.template.mode = mode || modalState.template.mode || 'select'; } catch(_) {}
            try { modalState.template.lastOpenedAt = Date.now(); } catch(_) {}
            try { document.body.classList.add('template-modal-open'); } catch(_) {}
            debugLog('info', 'templateModal: open', { mode: modalState.template.mode });
        }
        function markTemplateModalClosed() {
            try { modalState.template.isOpen = false; } catch(_) {}
            try { modalState.template.mode = 'select'; } catch(_) {}
            try { document.body.classList.remove('template-modal-open'); } catch(_) {}
            try { document.body.classList.remove('copy-mode'); } catch(_) {}
            debugLog('info', 'templateModal: close', {});
        }
        function markPhraseModalOpen(recordId) {
            try { modalState.phrase.isOpen = true; } catch(_) {}
            try { modalState.phrase.recordId = String(recordId || ''); } catch(_) {}
            try { modalState.phrase.lastOpenedAt = Date.now(); } catch(_) {}
            try { document.body.classList.add('phrase-modal-open'); } catch(_) {}
            debugLog('info', 'phraseModal: open', { recordId });
        }
        function markPhraseModalClosed() {
            try { modalState.phrase.isOpen = false; } catch(_) {}
            try { modalState.phrase.recordId = ''; } catch(_) {}
            try { document.body.classList.remove('phrase-modal-open'); } catch(_) {}
            debugLog('info', 'phraseModal: close', {});
        }
        function isTemplateModalOpen() {
            try { return !!modalState.template.isOpen; } catch(_) { return false; }
        }

        // テンプレートモーダル用の状態管理ラッパ（開閉とフッター切替を一元化）
        const templateModalUI = (function createTemplateModalUI(){
            function getModal(){ return document.getElementById('templateModal'); }
            function ensureOpen(mode){
                const modal = getModal();
                if (!modal) return;
                openModal(modal);
                markTemplateModalOpen(mode || 'select');
            }
            function ensureClose(){
                const modal = getModal();
                if (!modal) return;
                closeModal(modal);
                markTemplateModalClosed();
            }
            function setMode(mode){
                try { window.setFooterForTemplateMode && window.setFooterForTemplateMode(mode); } catch(_) {}
                try { modalState.template.mode = mode; } catch(_) {}
            }
            function resetFooter(){
                try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {}
            }
            return {
                openSelect(){ ensureOpen('select'); setMode('select'); },
                openCreate(){ ensureOpen('create'); setMode('create'); },
                openEdit(){ ensureOpen('edit'); setMode('edit'); },
                close(){ ensureClose(); resetFooter(); },
            };
        })();

        // クリック相当のキーボード操作（role="button" 用）と aria-pressed のトグル
        (function setupRoleButtonKeyboardActivation(){
            const shouldIgnore = (t) => {
                if (!t) return true;
                if (t.isContentEditable) return true;
                const tag = (t.tagName || '').toUpperCase();
                return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
            };
            document.addEventListener('keydown', (e) => {
                try {
                    if (e.isComposing) return;
                    const target = e.target;
                    if (shouldIgnore(target)) return;
                    const btn = target.closest && target.closest('[role="button"]');
                    if (!btn) return;
                    if (e.key === 'Enter' || e.key === ' ') {
                        try { e.preventDefault(); } catch(_) {}
                        try { btn.click(); } catch(_) {}
                    }
                } catch(_) {}
            }, true);

            // aria-pressed を持つボタンはクリック時にトグル（明示的な実装が無い場合のフォールバック）
            document.addEventListener('click', (e) => {
                try {
                    const el = e.target && e.target.closest && e.target.closest('[role="button"][aria-pressed]');
                    if (!el) return;
                    const cur = String(el.getAttribute('aria-pressed') || 'false') === 'true';
                    el.setAttribute('aria-pressed', cur ? 'false' : 'true');
                } catch(_) {}
            }, true);
        })();

        // テンプレートモーダル用: 既存フッターをモード別に切り替え
        window.setFooterForTemplateMode = function(mode) {
            try {
                const footerCancel = document.getElementById('footerCancelButton');
                const footerOk = document.getElementById('footerQueryButton');
                const footerEditCancel = document.getElementById('footerEditCancel');
                const footerEditSave = document.getElementById('footerEditSave');
                const changeNav = document.querySelector('.footer .change-nav');
                const templateModal = document.getElementById('templateModal');

				// 既定のボタンは隠す。change-nav はテンプレート選択モードでは非表示（ユーザー要望変更）
                if (footerCancel) footerCancel.style.display = 'none';
                if (footerOk) footerOk.style.display = 'none';
				// changeNav の表示制御はモード別分岐で行う
                

                // 既存の一時ハンドラを解除
                try {
                    if (templateModal && templateModal.__footerCancelHandler && footerEditCancel) footerEditCancel.removeEventListener('click', templateModal.__footerCancelHandler);
                } catch(_) {}
                try {
                    if (templateModal && templateModal.__footerSaveHandler && footerEditSave) footerEditSave.removeEventListener('click', templateModal.__footerSaveHandler);
                } catch(_) {}
                try { if (templateModal) { delete templateModal.__footerCancelHandler; delete templateModal.__footerSaveHandler; } } catch(_) {}

                const ensureFooterVisible = () => { try { updateFooterHiddenState(); } catch(_) {} };

                if (mode === 'select') {
                    if (footerEditCancel) {
                        footerEditCancel.textContent = '戻る';
                        footerEditCancel.style.display = '';
                        templateModal.__footerCancelHandler = () => {
                            try {
                                // 統一クローズ: 状態も含めて確実に終了
                                if (window.templateModalUI && typeof window.templateModalUI.close === 'function') {
                                    window.templateModalUI.close();
                                } else {
                                    const tm = document.getElementById('templateModal');
                                    if (tm) { try { closeModal(tm); } catch(_) {} }
                                    try { markTemplateModalClosed(); } catch(_) {}
                                }
                                // copy-mode の取り残しを必ず解除し、モードを初期化
                                try { document.body.classList.remove('copy-mode'); } catch(_) {}
                                try { const tm2 = document.getElementById('templateModal'); if (tm2 && tm2.dataset) { tm2.dataset.mode = 'select'; delete tm2.dataset.__wasOpenByCopy; } } catch(_) {}
                                // 一時データをクリア
                                try { if (Array.isArray(window.copySelectionItems)) window.copySelectionItems = []; } catch(_) {}
                            } catch(_) {}
                            // フッターを通常状態へ復帰（即時 + 次フレームの二重フェイルセーフ）
                            try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {}
                            ensureFooterVisible();
                            try {
                                setTimeout(() => {
                                    try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {}
                                    try { updateFooterHiddenState(); } catch(_) {}
                                }, 0);
                            } catch(_) {}
                        };
                        bindSingleEvent(footerEditCancel, 'click', 'templateModal:cancel', templateModal.__footerCancelHandler, false);
                    }
                    if (footerEditSave) footerEditSave.style.display = 'none';
					// テンプレート選択画面では常に変更ナビ（[<] m/n [>]）を非表示
					try {
						if (changeNav) {
							if (!changeNav.dataset.prevDisplay) { changeNav.dataset.prevDisplay = changeNav.style.display || ''; }
							changeNav.style.display = 'none';
						}
					} catch(_) {}
                    // 選択モードに戻る際は他画面での利用に備えて無効状態を必ず解除
                    try {
                        if (footerEditSave) {
                            footerEditSave.disabled = false;
                            footerEditSave.removeAttribute('disabled');
                            footerEditSave.setAttribute('aria-disabled', 'false');
                            footerEditSave.title = '';
                        }
                    } catch(_) {}
					// change-nav はテンプレート選択画面では常時非表示
                } else if (mode === 'create') {
                    if (footerEditCancel) {
                        footerEditCancel.textContent = 'キャンセル';
                        footerEditCancel.style.display = '';
                        templateModal.__footerCancelHandler = () => {
                            // クリック直後にフッターを選択モードへ切替（閉じるのみ表示）
                            try { window.setFooterForTemplateMode && window.setFooterForTemplateMode('select'); } catch(_) {}
                            try { const modal = document.getElementById('templateModal'); if (modal && modal.__createCancelFn) modal.__createCancelFn(); else if (window.hideTemplateCreateForm) window.hideTemplateCreateForm(); } catch(_) {}
                        };
                        bindSingleEvent(footerEditCancel, 'click', 'templateModal:cancel', templateModal.__footerCancelHandler, false);
                    }
                    if (footerEditSave) {
                        footerEditSave.textContent = '登録';
                        footerEditSave.style.display = '';
                        templateModal.__footerSaveHandler = () => {
                            // クリック直後にフッターを選択モードへ切替（閉じるのみ表示）
                            try { window.setFooterForTemplateMode && window.setFooterForTemplateMode('select'); } catch(_) {}
                            try { const modal = document.getElementById('templateModal'); if (modal && modal.__createSubmitFn) modal.__createSubmitFn(); } catch(_) {}
                        };
                        bindSingleEvent(footerEditSave, 'click', 'templateModal:save', templateModal.__footerSaveHandler, false);
                    }
                    if (changeNav) changeNav.style.display = 'none';
                } else if (mode === 'edit') {
                    if (footerEditCancel) {
                        footerEditCancel.textContent = 'キャンセル';
                        footerEditCancel.style.display = '';
                        templateModal.__footerCancelHandler = () => {
                            // クリック直後にフッターを選択モードへ切替（閉じるのみ表示）
                            try { window.setFooterForTemplateMode && window.setFooterForTemplateMode('select'); } catch(_) {}
                            try { const modal = document.getElementById('templateModal'); if (modal && modal.__editCancelFn) modal.__editCancelFn(); } catch(_) {}
                        };
                        bindSingleEvent(footerEditCancel, 'click', 'templateModal:cancel', templateModal.__footerCancelHandler, false);
                    }
                    if (footerEditSave) {
                        footerEditSave.textContent = '更新';
                        footerEditSave.style.display = '';
                        templateModal.__footerSaveHandler = () => {
                            // クリック直後にフッターを選択モードへ切替（閉じるのみ表示）
                            try { window.setFooterForTemplateMode && window.setFooterForTemplateMode('select'); } catch(_) {}
                            try { const modal = document.getElementById('templateModal'); if (modal && modal.__editSaveFn) modal.__editSaveFn(); } catch(_) {}
                        };
                        bindSingleEvent(footerEditSave, 'click', 'templateModal:save', templateModal.__footerSaveHandler, false);
                    }
                    if (changeNav) changeNav.style.display = 'none';
                }

                ensureFooterVisible();
            } catch(_) {}
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('setFooterForTemplateMode', window.setFooterForTemplateMode); } catch(_) {}

        // テンプレートモーダル終了時: フッター表示を通常に戻す
        window.resetFooterForTemplate = function() {
            try {
                // ヘッダーのタイトルを元に戻す
                try {
                    const titleElement = document.querySelector('.title-section h3');
                    if (titleElement && titleElement.dataset.originalTitle) {
                        titleElement.textContent = titleElement.dataset.originalTitle;
                        delete titleElement.dataset.originalTitle;
                    }
                } catch(_) {}
                
                const footerCancel = document.getElementById('footerCancelButton');
                const footerOk = document.getElementById('footerQueryButton');
                const footerEditCancel = document.getElementById('footerEditCancel');
                const footerEditSave = document.getElementById('footerEditSave');
                const changeNav = document.querySelector('.footer .change-nav');
                const templateModal = document.getElementById('templateModal');

                // 一時ハンドラ解除（単一登録バケットから）
                try { if (footerEditCancel) unbindSingleEvent(footerEditCancel, 'click', 'templateModal:cancel'); } catch(_) {}
                try { if (footerEditSave) unbindSingleEvent(footerEditSave, 'click', 'templateModal:save'); } catch(_) {}
                try { if (templateModal) { delete templateModal.__footerCancelHandler; delete templateModal.__footerSaveHandler; } } catch(_) {}

                // ラベル復元と表示状態復元
                if (footerEditCancel) { footerEditCancel.textContent = 'キャンセル'; footerEditCancel.style.display = 'none'; }
                if (footerEditSave) {
                    footerEditSave.textContent = '変更を反映する';
                    footerEditSave.style.display = 'none';
                    // モーダル終了時は無効状態をクリアして他画面に影響しないようにする
                    try { footerEditSave.disabled = false; footerEditSave.removeAttribute('disabled'); footerEditSave.setAttribute('aria-disabled', 'false'); footerEditSave.title = ''; } catch(_) {}
                }
                if (footerCancel) footerCancel.style.display = '';
                if (footerOk) footerOk.style.display = '';
                try { window.restoreChangeNavDisplay && window.restoreChangeNavDisplay(); } catch(_) {}
                

                updateFooterHiddenState();
            } catch(_) {}
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('resetFooterForTemplate', window.resetFooterForTemplate); } catch(_) {}

        // フッター復元のフェイルセーフ: テンプレートモーダルが閉じられたら必ず通常フッターに戻す
        (function installTemplateModalObserver(){
            try {
                const tm = document.getElementById('templateModal');
                if (!tm || tm.__footerObserverInstalled) return;
                const observer = new MutationObserver(() => {
                    try {
                        const isOpen = (tm.style && tm.style.display === 'block');
                        if (!isOpen) {
                            // すぐ復帰 + 次フレームでもう一度復帰（タイミング差のフェイルセーフ）
                            try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {}
                            setTimeout(() => { try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {} }, 0);
                        }
                    } catch(_) {}
                });
                observer.observe(tm, { attributes: true, attributeFilter: ['style'] });
                try { tm.__footerObserverInstalled = true; } catch(_) {}
                try { tm.__footerObserver = observer; } catch(_) {}
            } catch(_) {}
        })();

            function closePhraseModalIfOpen(force) {
                try {
                    const pm = document.getElementById('phraseModal');
                    const templateActive = isTemplateModalOpen();
                    if (!force && templateActive) return;
                    if (pm && pm.parentNode) pm.parentNode.removeChild(pm);
                    markPhraseModalClosed();
                } catch (_) {}
            }

            function isPhraseModalOpen() {
                try { return !!modalState.phrase.isOpen; } catch(_) { return false; }
            }

        // 永続デフォルト（外部から適用するための受け皿）
        let persistedDefaults = { values: {}, persist: {}, types: {}, memos: {}, selects: {} };
        // 変数適用後のプレーンテキストを生成
        function generateAppliedPlainText(templateText) {
            try {
                const source = String(templateText == null ? '' : templateText);
                if (!source) return '';
                const valuesMap = { ...(persistedDefaults.values || {}) };
                const selectsMap = { ...(persistedDefaults.selects || {}) };

                // {{名前:A|B|C}} の候補一覧を抽出（最初の出現のみ）
                const selectSpecMap = buildSelectSpecMapFromText(source);

                const normalizeVal = (val) => String(val == null ? '' : val).replace(/\\n/g, '\n');

                // {{}} をテキストで解決
                let text = source.replace(/\{\{(.*?)\}\}/g, (match, rawName) => {
                    const raw = String(rawName || '').trim();
                    if (!raw) return match;
                    if (raw.includes(':')) {
                        const idx = raw.indexOf(':');
                        const name = raw.slice(0, idx).trim();
                        let options = raw.slice(idx + 1).split('|').map(s => s.trim()).filter(s => s !== '');
                        if (!name) return match;
                        if (selectsMap[name] && Array.isArray(selectsMap[name].options)) {
                            options = selectsMap[name].options;
                        }
                        if (options.length === 0) return match;
                        const selected = (selectsMap[name] && selectsMap[name].value)
                            || valuesMap[name] || options[0] || '';
                        return normalizeVal(resolveNestedPlaceholders(selected, (k) => valuesMap[k]));
                    }
                    const fieldName = raw;
                    if (!isValidVarToken(fieldName)) return match;
                    if (Object.prototype.hasOwnProperty.call(selectSpecMap, fieldName)) {
                        let options = selectSpecMap[fieldName] || [];
                        if (selectsMap[fieldName] && Array.isArray(selectsMap[fieldName].options)) {
                            options = selectsMap[fieldName].options;
                        }
                        if (options.length > 0) {
                            const selected = (selectsMap[fieldName] && selectsMap[fieldName].value)
                                || valuesMap[fieldName] || options[0] || '';
                            return normalizeVal(resolveNestedPlaceholders(selected, (k) => valuesMap[k]));
                        }
                    }
                    const value = valuesMap[fieldName];
                    if (value == null || String(value).length === 0) return match;
                    const resolved = resolveNestedPlaceholders(String(value || ''), (k) => valuesMap[k]);
                    return normalizeVal(resolved);
                });

                // [[]] をテキストで解決
                let bracketIndex = 0;
                const bracketState = (window.__bracketSelectState || {});
                text = text.replace(/\[\[(.*?)\]\]/g, (match, itemName) => {
                    const raw = String(itemName || '').trim();
                    if (!raw) return match;
                    if (raw.includes('|')) {
                        let varName = null;
                        let optionsPart = raw;
                        const mVar = raw.match(/^\{\{\s*([^}]+?)\s*\}\}/);
                        if (mVar) {
                            varName = (mVar[1] || '').trim();
                            optionsPart = raw.slice(mVar[0].length);
                            if (optionsPart.startsWith('|')) optionsPart = optionsPart.slice(1);
                        }
                        const options = optionsPart.split('|').map(s => s.trim()).filter(s => s !== '');
                        if (options.length === 0) return match;
                        const key = varName ? `select:${varName}` : `select#${++bracketIndex}`;
                        const selected = bracketState[key] || options[0] || '';
                        return normalizeVal(selected);
                    }
                    // チェックボックス
                    if (!isValidVarToken(raw)) return match;
                    const fieldKey = `checkbox_${raw}`;
                    const isChecked = !!valuesMap[fieldKey];
                    const symbol = isChecked ? '☑' : '☐';
                    return `${symbol} ${raw}`;
                });

                return text;
            } catch(_) {
                return String(templateText || '');
            }
        }
        // 現在表示されている内容のプレーンテキストを取得
        function getDisplayedTextPlain() {
            try {
                const original = document.getElementById('original-textarea');
                const display = document.getElementById('display-text');
                const direct = document.getElementById('direct-input');
                const isDisplayVisible = !!(display && display.style && display.style.display !== 'none');
                const isDirectVisible = !!(direct && direct.style && direct.style.display !== 'none');
                if (isDisplayVisible && original && typeof generateAppliedPlainText === 'function') {
                    return generateAppliedPlainText(original.value || '');
                }
                if (isDirectVisible && direct && typeof direct.value === 'string') {
                    return String(direct.value || '');
                }
                if (original && typeof original.value === 'string') {
                    return (typeof generateAppliedPlainText === 'function') ? generateAppliedPlainText(original.value || '') : String(original.value || '');
                }
                return '';
            } catch(_) { return ''; }
        }
        // フッターを既定状態へ復帰（change-nav含む）。タイミング差のフェイルセーフ付き
        function restoreFooterToDefaultState() {
            try {
                const footerCancel = document.getElementById('footerCancelButton');
                const footerOk = document.getElementById('footerQueryButton');
                const footerEditCancel = document.getElementById('footerEditCancel');
                const footerEditSave = document.getElementById('footerEditSave');
                if (footerCancel) footerCancel.style.display = '';
                if (footerOk) footerOk.style.display = '';
                if (footerEditCancel) footerEditCancel.style.display = 'none';
                if (footerEditSave) footerEditSave.style.display = 'none';
                try { window.restoreChangeNavDisplay && window.restoreChangeNavDisplay(); } catch(_) {}
                try { updateFooterHiddenState(); } catch(_) {}
                setTimeout(() => {
                    try { window.restoreChangeNavDisplay && window.restoreChangeNavDisplay(); } catch(_) {}
                    try { updateFooterHiddenState(); } catch(_) {}
                }, 0);
            } catch(_) {}
        }
        // 長押しコピーを指定ボタンへ付与
        function installLongPressCopy(targetButton, getTextFn, longPressMs) {
            try {
                if (!targetButton) return;
                const LONG_PRESS_MS = Math.max(0, Number(longPressMs) || 600);
                let longPressTimer = null;
                let longPressTriggered = false;
                const clearLongPressTimer = () => { try { if (longPressTimer) { clearTimeout(longPressTimer); longPressTimer = null; } } catch(_) {} };
                const performInstantCopy = async () => {
                    try {
                        const text = (typeof getTextFn === 'function') ? String(getTextFn() || '') : getDisplayedTextPlain();
                        let ok = false;
                        try { ok = await copyTextToClipboard(text); } catch (err) { ok = false; try { reportError && reportError('longpress_copy_failed', err, { level: 'warn' }); } catch(_) {} }
                        try {
                            if (typeof showToast === 'function') {
                                showToast(ok ? 'クリップボードへコピーしました。' : 'コピーに失敗しました。', {
                                    role: ok ? 'status' : 'alert',
                                    duration: ok ? 2200 : 2600
                                });
                            }
                        } catch(_) {}
                        longPressTriggered = true;
                    } catch(_) { longPressTriggered = true; }
                };
                targetButton.addEventListener('pointerdown', () => {
                    try {
                        clearLongPressTimer();
                        longPressTriggered = false;
                        longPressTimer = setTimeout(() => { performInstantCopy(); }, LONG_PRESS_MS);
                    } catch(_) {}
                });
                ['pointerup', 'pointerleave', 'pointercancel'].forEach((type) => {
                    try { targetButton.addEventListener(type, () => { clearLongPressTimer(); }, { once: false }); } catch(_) {}
                });
                // ロングプレス成立直後のクリックは既存処理を抑止
                targetButton.addEventListener('click', (ev) => {
                    try {
                        if (longPressTriggered) {
                            ev.preventDefault();
                            ev.stopImmediatePropagation();
                            longPressTriggered = false;
                        }
                    } catch(_) {}
                }, true);
            } catch(_) {}
        }
        // 吹き出し内での未保存の編集中値（フィールド単位のドラフト）
        let inProgressDrafts = {};
        // 直前に吹き出しから保存したフィールド（既存値の上書きを防ぐための優先適用）
        let lastSavedFieldName = null;
        // IME 変換中フラグ（数値入力の全角→半角で多重反映を避ける）
        let isComposingNumberInput = false;
        let fieldHeights = {};
        // 行数ベース同期は幅差でズレるため使用しない（後方互換のため残置）
        let fieldRows = {};
        // 選択されたテンプレートのメタ情報（keyなど）を保持
        let selectedTemplateMeta = { key: null, title: '', is_blank: 0 };
        // ユーザー選択後に key を固定（外部からの renderUI による上書きを防ぐ）
        let isSelectedTemplateKeyLocked = false;

        // メモの有無に応じて「メモを表示する」トグルの可視性を制御
        function updateMemoToggleVisibility() {
            try {
                const wrapper = document.getElementById('toggleShowMemos')?.closest('div');
                if (!wrapper) return;
                const isBlankHeader = Number(selectedTemplateMeta && selectedTemplateMeta.is_blank) === 1;
                const memos = (persistedDefaults && persistedDefaults.memos) || {};
                let hasAnyMemo = false;
                for (const key in memos) {
                    if (!Object.prototype.hasOwnProperty.call(memos, key)) continue;
                    const v = memos[key];
                    if ((typeof v === 'string' ? v.trim() !== '' : !!v)) { hasAnyMemo = true; break; }
                }
                wrapper.style.display = (!isBlankHeader && hasAnyMemo) ? '' : 'none';
            } catch(_) {}
        }

        // ヘッダー表示を未選択状態にリセット（表示のみ、データは変更しない）
        function resetHeaderView() {
            try {
                const titleElement = document.querySelector('.title-section h3');
                if (titleElement) titleElement.textContent = '(テンプレート未選択)';
            } catch (_) {}
            try {
                const memoToggleWrapper = document.getElementById('toggleShowMemos')?.closest('div');
                if (memoToggleWrapper) memoToggleWrapper.style.display = 'none';
            } catch (_) {}
        }

        // テンプレートが未選択のときは鉛筆ボタンを隠す
        function updateEditButtonVisibility() {
            try {
                const btn = document.getElementById('editButton');
                if (!btn) return;
                const hasTemplate = !!(selectedTemplateMeta && selectedTemplateMeta.key != null);
                // template_key が存在する場合は is_blank に関係なく鉛筆ボタンを表示
                btn.style.display = hasTemplate ? '' : 'none';
            } catch (_) {}
        }

        // 初期状態（未選択）は is_blank=1 相当としてUI反映
        try {
            const memoToggleWrapperInit = document.getElementById('toggleShowMemos')?.closest('div');
            if (memoToggleWrapperInit) memoToggleWrapperInit.style.display = 'none';
        } catch(_) {}
        updateEditButtonVisibility();
        updateMemoToggleVisibility();
        // 変更追跡用のグローバル変数
        let initialValues = {
            originalTextarea: '',
            directInput: '',
            modalTextarea: '',
            modalTemplateName: '',
            variables: {}
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('positionTemplateMoreMenu', window.positionTemplateMoreMenu); } catch(_) {}
        // 3点メニューをビューポート内に収める配置調整
        window.positionTemplateMoreMenu = function(moreMenu) {
            try {
                if (!moreMenu) return;
                // 初期化
                moreMenu.style.left = '';
                moreMenu.style.right = '';
                moreMenu.style.top = '';
                moreMenu.style.bottom = '';
                moreMenu.style.transform = '';
                // スクロールコンテナ（カードボディ）基準で計算
                const container = moreMenu.closest('.template-modal-body');
                const viewportRect = container ? container.getBoundingClientRect() : { width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0), top: 0, left: 0, right: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), bottom: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) };
                moreMenu.style.maxHeight = (Math.max(160, (viewportRect.height || window.innerHeight) - 16)) + 'px';
                moreMenu.style.overflowY = 'auto';

                const vw = viewportRect.right - viewportRect.left;
                const vh = viewportRect.bottom - viewportRect.top;
                let rect = moreMenu.getBoundingClientRect();

                // 横方向のはみ出し調整
                if (rect.right > viewportRect.right) {
                    moreMenu.style.left = 'auto';
                    moreMenu.style.right = '100%';
                    moreMenu.style.transform = 'translateX(-8px)';
                } else if (rect.left < viewportRect.left) {
                    moreMenu.style.left = '100%';
                    moreMenu.style.right = 'auto';
                    moreMenu.style.transform = 'translateX(8px)';
                }

                // 再計測
                rect = moreMenu.getBoundingClientRect();
                // 縦方向のはみ出し調整（下にはみ出す場合は上に出す）
                if (rect.bottom > viewportRect.bottom) {
                    moreMenu.style.top = 'auto';
                    moreMenu.style.bottom = '0';
                }
                // 再計測後、まだ上にはみ出す場合はtop:0に固定
                rect = moreMenu.getBoundingClientRect();
                if (rect.top < viewportRect.top) {
                    moreMenu.style.top = '0';
                    moreMenu.style.bottom = 'auto';
                }
            } catch(_) {}
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('highlightAndScrollTemplateEl', window.highlightAndScrollTemplateEl); } catch(_) {}
        // 任意のテンプレート行要素を強調しスクロールする
        window.highlightAndScrollTemplateEl = function(target) {
            try {
                if (!target) return false;
                try { target.classList.add('was-edited'); } catch(_) {}
                try {
                    setTimeout(() => {
                        try {
                            target.classList.add('highlight-fade');
                            requestAnimationFrame(() => {
                                try { target.classList.remove('was-edited'); } catch(_) {}
                                setTimeout(() => { try { target.classList.remove('highlight-fade'); } catch(_) {} }, 400);
                            });
                        } catch(_) {}
                    }, 2000);
                } catch(_) {}
                // スクロール制御は行わない（ユーザー操作を尊重）
                return true;
            } catch(_) { return false; }
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('scrollAndHighlightTemplateByKey', window.scrollAndHighlightTemplateByKey); } catch(_) {}
        // key指定でテンプレート行を探して強調しスクロールする
        window.scrollAndHighlightTemplateByKey = function(key) {
            try {
                const k = String(key || '');
                if (!k) return false;
                const listEl = document.getElementById('templateList');
                if (!listEl) return false;
                const target = document.querySelector(`#templateList .template-item[data-key="${CSS.escape(k)}"]`);
                if (!target) return false;
                return window.highlightAndScrollTemplateEl(target);
            } catch(_) { return false; }
        };

        // renderUI時点の送信内容スナップショット(JSON; キーソート済み)
        let initialOkPayloadString = '';

        // ページ表示からの経過時間計測用の開始時刻
        const __pageShownTimes = { perf: (typeof performance !== 'undefined' && typeof performance.now === 'function') ? performance.now() : null, epoch: Date.now() };
        function getTimeToOkMs() {
            try {
                const nowPerf = (typeof performance !== 'undefined' && typeof performance.now === 'function') ? performance.now() : null;
                if (__pageShownTimes.perf != null && nowPerf != null) return Math.max(0, Math.round(nowPerf - __pageShownTimes.perf));
                return Math.max(0, Math.round(Date.now() - __pageShownTimes.epoch));
            } catch(_) { return null; }
        }

        // オブジェクトをキー名で再帰ソートし、安定化したJSON文字列へ
        function stableStringify(value) {
            const seen = new WeakSet();
            function _sort(v) {
                if (v === null || typeof v !== 'object') return v;
                if (seen.has(v)) return v;
                seen.add(v);
                if (Array.isArray(v)) return v.map(_sort);
                const out = {};
                Object.keys(v).sort().forEach(k => { out[k] = _sort(v[k]); });
                return out;
            }
            try { return JSON.stringify(_sort(value)); } catch(_) { return ''; }
        }
        // 現在のUI状態から addon_remarks_web_template_ok へ渡すデータを構築
        function buildCurrentOkPayload() {
            const originalTextarea = document.getElementById('original-textarea');
            const directInput = document.getElementById('direct-input');
            const globalNoteEl = document.getElementById('global-note-textarea');
            const originalTemplate = originalTextarea ? originalTextarea.value : '';
            const globalNote = globalNoteEl ? globalNoteEl.value : '';

            let instructions = (originalTextarea && originalTextarea.value.trim())
                ? originalTextarea.value
                : (directInput ? directInput.value : '');

            // {{...}} 範囲収集（[[...]] 置換対象から除外）
            const __braceRanges = collectBraceRanges(instructions);
            const __isInsideBrace = (start, len) => __braceRanges.some(([a, b]) => start >= a && start + len <= b);

            if (instructions.trim()) {
                // [[...]] 内は {{...}} を置換しないための事前範囲収集
                const __bracketRangesForOk = collectBracketRanges(instructions);
                const __isInsideBracketForOk = (start, len) => __bracketRangesForOk.some(([a, b]) => start >= a && start + len <= b);
                // 0) {{名前:A|B|C}} を選択値で置換（variables.select優先）
                try {
                    instructions = instructions.replace(/\{\{\s*([^:}]+?)\s*:\s*([^}]+?)\s*\}\}/g, (m, nameRaw, optsRaw, offset) => {
                        if (__isInsideBracketForOk(offset, m.length)) return m;
                        const name = String(nameRaw || '').trim();
                        let options = String(optsRaw || '').split('|').map(s => s.trim()).filter(s => s !== '');
                        if (!name) return m;
                        // 複数出現時は、本文内で最初に現れた {{名前:...}} の候補を採用
                        try {
                            const reSpec = new RegExp(`\\{\\{\\s*${escapeRegExp(name)}\\s*:(.*?)\\}\\}`);
                            const mm = (originalTextarea ? originalTextarea.value : '').match(reSpec);
                            if (mm && mm[1]) options = String(mm[1]).split('|').map(s => s.trim()).filter(s => s !== '');
                        } catch(_) {}
                        if (persistedDefaults.selects && persistedDefaults.selects[name] && Array.isArray(persistedDefaults.selects[name].options)) {
                            options = persistedDefaults.selects[name].options;
                        }
                        if (options.length === 0) return m;
                        const chosen = (persistedDefaults.selects && persistedDefaults.selects[name] && persistedDefaults.selects[name].value)
                            || (persistedDefaults.values && persistedDefaults.values[name]) || options[0];
                        return String(chosen ?? options[0]);
                    });
                } catch(_) {}

                // 1) {{field}} を persistedDefaults から置換
                try {
                    const reFields = /\{\{\s*(.*?)\s*\}\}/g;
                    const fieldNames = new Set();
                    let m;
                    while ((m = reFields.exec(instructions)) !== null) {
                        const fieldName = String(m[1] || '').trim();
                        // コロン含み（新セレクト記法）はスキップ済み
                        if (fieldName.includes(':')) continue;
                        if (isValidVarToken(fieldName)) fieldNames.add(fieldName);
                    }
                    fieldNames.forEach(fieldName => {
                        const type = (persistedDefaults.types && persistedDefaults.types[fieldName]) || 'multiline';
                        let raw = (persistedDefaults.values && persistedDefaults.values[fieldName]) || '';
                        try { raw = toHalfWidthForType(raw, type); } catch(_) {}
                        let value = '';
                        if (type === 'date') value = formatDateYMDWithWeekday(raw || '').trim();
                        else if (type === 'timestamp') value = formatDateTimeDisplay(raw || '').trim();
                        else if (type === 'time') { const mm = String(raw || '').match(/^(\d{1,2}):(\d{2})/); value = mm ? `${mm[1].padStart(2,'0')}:${mm[2]}` : ''; }
                        else value = String(raw || '').trim();
                        if (value !== '') {
                            const regex = new RegExp(`\\{\\{\\s*${escapeRegExp(fieldName)}\\s*\\}}`, 'g');
                            instructions = instructions.replace(regex, (mm0, offset) => __isInsideBracketForOk(offset, mm0.length) ? mm0 : value);
                        }
                    });
                } catch(_) {}

                // 2) [[]] を置換（{{}} 内は置換しない）
                const updatedBraceRanges = collectBraceRanges(instructions);
                const isInsideUpdatedBrace = (start, len) => updatedBraceRanges.some(([a, b]) => start >= a && start + len <= b);
                // 元テンプレートの {{...}} の中に現れた [[...]] は常にリテラル扱い
                const __bracketsInsideCurlySet = (() => {
                    const set = new Set();
                    try {
                        const src = originalTextarea ? (originalTextarea.value || '') : '';
                        const reCurly = /\{\{[\s\S]*?\}\}/g;
                        let m;
                        while ((m = reCurly.exec(src)) !== null) {
                            const body = m[0];
                            const reB = /\[\[\s*(.*?)\s*\]\]/g;
                            let mb;
                            while ((mb = reB.exec(body)) !== null) {
                                const token = String(mb[1] || '').trim();
                                if (token) set.add(token);
                            }
                        }
                    } catch(_) {}
                    return set;
                })();
                let __bracketSelectIndexOut = 0;
                instructions = instructions.replace(/\[\[\s*(.*?)\s*\]\]/g, (mtch, inner, offset) => {
                    const raw = String(inner || '').trim();
                    if (!raw) return mtch;
                    if (isInsideUpdatedBrace(offset, mtch.length)) return mtch;
                    if (__bracketsInsideCurlySet.has(raw)) return mtch;
                    // それ以外は従来どおりチェック記号
                    if (!isValidVarToken(raw)) return mtch;
                    const key = `checkbox_${raw}`;
                    const checked = !!(persistedDefaults.values && persistedDefaults.values[key]);
                    return `${checked ? '☑' : '☐'} ${raw}`;
                });
            }
            // 送信用の変数構築（本文に出現する項目のみ）
            const variables = { checkbox: {}, fields: {}, select: {} };
            const variableTypes = {};
            try {
                const reFields2 = /\{\{\s*(.*?)\s*\}\}/g;
                let m1; const seenFields = new Set();
                while ((m1 = reFields2.exec(originalTextarea ? originalTextarea.value || '' : '')) !== null) {
                    const raw = String(m1[1] || '').trim();
                    if (!raw) continue;
                    // 新記法: {{名前:A|B|C}} は select へ（variables.select を優先）
                    if (raw.includes(':')) {
                        const idx = raw.indexOf(':');
                        const name = raw.slice(0, idx).trim();
                        const optionsPart = raw.slice(idx + 1);
                        let options = optionsPart.split('|').map(s => s.trim()).filter(s => s !== '');
                        if (!name) continue;
                        // 受信済みの variables.select を優先
                        if (persistedDefaults.selects && persistedDefaults.selects[name] && Array.isArray(persistedDefaults.selects[name].options)) {
                            options = persistedDefaults.selects[name].options;
                        }
                        if (options.length === 0) continue;
                        const val = (persistedDefaults.selects && persistedDefaults.selects[name] && persistedDefaults.selects[name].value) || (persistedDefaults.values && persistedDefaults.values[name]) || options[0] || '';
                        variables.select[name] = { value: val, options: options };
                        continue;
                    }
                    const name = raw;
                    if (!isValidVarToken(name) || seenFields.has(name)) continue;
                    seenFields.add(name);
                    // 先頭出現の {{名前:...}} があれば、その options を採用
                    try {
                        const reSpec = new RegExp(`\\{\\{\\s*${escapeRegExp(name)}\\s*:(.*?)\\}\\}`);
                        const mm = (originalTextarea ? (originalTextarea.value || '') : '').match(reSpec);
                        if (mm && mm[1]) {
                            let options = String(mm[1] || '').split('|').map(s => s.trim()).filter(s => s !== '');
                            if (persistedDefaults.selects && persistedDefaults.selects[name] && Array.isArray(persistedDefaults.selects[name].options)) {
                                options = persistedDefaults.selects[name].options;
                            }
                            if (options.length > 0) {
                                const val = (persistedDefaults.selects && persistedDefaults.selects[name] && persistedDefaults.selects[name].value) || (persistedDefaults.values && persistedDefaults.values[name]) || options[0] || '';
                                variables.select[name] = { value: val, options: options };
                                continue;
                            }
                        }
                    } catch(_) {}
                    const val = (persistedDefaults.values && persistedDefaults.values[name]) || '';
                    const persist = !!(persistedDefaults.persist && persistedDefaults.persist[name]);
                    variables.fields[name] = { value: val, type: (persistedDefaults.types && persistedDefaults.types[name]) || 'multiline', persist: persist };
                    if (persistedDefaults.types && persistedDefaults.types[name]) variableTypes[name] = persistedDefaults.types[name];
                }
                const reBoxes2 = /\[\[\s*(.*?)\s*\]\]/g;
                let m2; const seenBoxes = new Set();
                const selectCounterMap2 = {};
                while ((m2 = reBoxes2.exec(originalTextarea ? originalTextarea.value || '' : '')) !== null) {
                    const raw = String(m2[1] || '').trim();
                    if (!raw) continue;
                    // [[A|B|C]] は select として収集
                    if (raw.includes('|')) {
                        const options = raw.split('|').map(s => s.trim()).filter(s => s !== '');
                        if (options.length === 0) continue;
                        const count = (selectCounterMap2[raw] || 0) + 1;
                        selectCounterMap2[raw] = count;
                        const key = `${raw}#${count}`;
                        let value = options[0];
                        try { if (window.__bracketSelectState && window.__bracketSelectState[key]) value = window.__bracketSelectState[key]; } catch(_) {}
                        variables.select[key] = { value: value, options: options };
                        continue;
                    }
                    // それ以外はチェックボックス
                    const item = raw;
                    if (!isValidVarToken(item) || seenBoxes.has(item)) continue;
                    seenBoxes.add(item);
                    const key = `checkbox_${item}`;
                    const val = !!(persistedDefaults.values && persistedDefaults.values[key]);
                    const persist = !!(persistedDefaults.persist && persistedDefaults.persist[key]);
                    variables.checkbox[item] = { value: val, persist: persist };
                    if (persistedDefaults.types && persistedDefaults.types[key]) variableTypes[key] = persistedDefaults.types[key];
                }
            } catch(_) {}

            const filteredVariableTypes = {};
            const filteredVariableMemos = {};
            Object.keys(variables.fields).forEach(fieldName => {
                if (variableTypes[fieldName]) filteredVariableTypes[fieldName] = variableTypes[fieldName];
                if (persistedDefaults.memos && persistedDefaults.memos[fieldName]) {
                    filteredVariableMemos[fieldName] = persistedDefaults.memos[fieldName];
                }
            });
            Object.keys(variables.checkbox).forEach(itemName => {
                const key = `checkbox_${itemName}`;
                if (variableTypes[key]) filteredVariableTypes[key] = variableTypes[key];
                if (persistedDefaults.memos && persistedDefaults.memos[key]) {
                    filteredVariableMemos[key] = persistedDefaults.memos[key];
                }
            });
            // select は types/memos を持たないのでフィルタ不要

            // グローバルノート高さ
            let globalNoteHeight = null;
            try {
                if (globalNoteEl) {
                    const rect = globalNoteEl.getBoundingClientRect();
                    globalNoteHeight = Math.round(rect.height);
                }
            } catch(_) {}

            // インストラクション（本文）テキストエリア高さ
            let instructionsHeight = null;
            try {
                const instructionsSourceEl = (originalTextarea && (originalTextarea.value || '').trim()) ? originalTextarea : directInput;
                if (instructionsSourceEl) {
                    const rect = instructionsSourceEl.getBoundingClientRect();
                    instructionsHeight = Math.round(rect.height);
                }
            } catch(_) {}

            return {
                instructions: instructions,
                originalTemplate: originalTemplate,
                template_key: selectedTemplateMeta.key,
                templateName: selectedTemplateMeta.title,
                is_blank: Number(selectedTemplateMeta.is_blank) || 0,
                variables,
                variableTypes: filteredVariableTypes,
                variableMemos: filteredVariableMemos,
                globalNote: globalNote,
                globalNoteHeight: globalNoteHeight,
                instructionsHeight: instructionsHeight,
                timeToOkMs: getTimeToOkMs()
            };
        }

        // 変更検知用に不安定な値（時間など）を除外したペイロードを返す
        function normalizeOkPayloadForChangeDetection(payload) {
            try {
                const clone = JSON.parse(JSON.stringify(payload || {}));
                // 時間経過で毎回変わるため、変更検知からは除外
                delete clone.timeToOkMs;
                return clone;
            } catch(_) { return payload || {}; }
        }

        function updateInitialOkSnapshot() {
            try { initialOkPayloadString = stableStringify(normalizeOkPayloadForChangeDetection(buildCurrentOkPayload())); } catch(_) { initialOkPayloadString = ''; }
        }

        function hasPendingChanges() {
            try { return stableStringify(normalizeOkPayloadForChangeDetection(buildCurrentOkPayload())) !== initialOkPayloadString; } catch(_) { return false; }
        }
        // 初期値を保存する関数
        function saveInitialValues() {
            const originalTextarea = document.getElementById('original-textarea');
            const directInput = document.getElementById('direct-input');
            const modalTextarea = document.getElementById('modalTextarea');
            const modalTemplateName = document.getElementById('modalTemplateName');
            
            if (originalTextarea) initialValues.originalTextarea = originalTextarea.value;
            if (directInput) initialValues.directInput = directInput.value;
            if (modalTextarea) initialValues.modalTextarea = modalTextarea.value;
            if (modalTemplateName) initialValues.modalTemplateName = modalTemplateName.value;
            
            // 変数の初期値を保存
            const inputs = [];
            initialValues.variables = {};
            inputs.forEach(input => {
                const fieldName = input.getAttribute('name');
                if (fieldName) {
                    if (input instanceof HTMLInputElement && input.type === 'checkbox') {
                        initialValues.variables[fieldName] = input.checked;
                    } else {
                        initialValues.variables[fieldName] = input.value;
                    }
                }
            });
        }



        // 現在の変数値を取得する関数
        function getCurrentVariableValues() {
            const currentVariables = {};
            const inputs = [];
            inputs.forEach(input => {
                const fieldName = input.getAttribute('name');
                if (fieldName) {
                    if (input instanceof HTMLInputElement && input.type === 'checkbox') {
                        currentVariables[fieldName] = input.checked;
                    } else {
                        currentVariables[fieldName] = input.value;
                    }
                }
            });
            return currentVariables;
        }

        // モーダル内で変更があるかどうかチェックする関数
        function hasModalChanges() {
            const modalTextarea = document.getElementById('modalTextarea');
            const modalTemplateName = document.getElementById('modalTemplateName');
            
            if (modalTextarea && modalTextarea.value !== initialValues.modalTextarea) {
                return true;
            }
            if (modalTemplateName && modalTemplateName.value !== initialValues.modalTemplateName) {
                return true;
            }
            
            return false;
        }
        // カスタム確認ダイアログを表示する関数
        function showConfirmModal(message, onConfirm, onCancel, options) {
            const confirmModal = document.getElementById('confirmModal');
            const confirmModalMessage = document.getElementById('confirmModalMessage');
            const confirmModalConfirm = document.getElementById('confirmModalConfirm');
            const confirmModalCancel = document.getElementById('confirmModalCancel');
            
            if (!confirmModal || !confirmModalMessage || !confirmModalConfirm || !confirmModalCancel) {
                return;
            }
            
            // メッセージ/ボタン文言を設定
            confirmModalMessage.textContent = message;
            try {
                const opts = options || {};
                if (opts.cancelLabel) confirmModalCancel.textContent = String(opts.cancelLabel);
                if (opts.confirmLabel) confirmModalConfirm.textContent = String(opts.confirmLabel);
                // 危険操作の時はボタン色を赤へ
                confirmModalConfirm.classList.remove('button-danger');
                if (opts.confirmClass === 'button-danger') {
                    confirmModalConfirm.classList.add('button-danger');
                }
            } catch (_) {}
            
            // スコープ管理
            try { confirmModal.__scope && confirmModal.__scope.cleanup && confirmModal.__scope.cleanup(); } catch(_) {}
            try { confirmModal.__scope = createScope(); } catch(_) {}
            // モーダル外クリックで閉じる（共通関数）
            try { confirmModal.__scope && confirmModal.__scope.addDetach && confirmModal.__scope.addDetach(attachOutsideClickClose(confirmModal, () => { hideConfirmModal(); if (onCancel) onCancel(); })); } catch(_) {}
            
            // ボタンイベント設定
            const handleConfirm = () => {
                hideConfirmModal();
                if (onConfirm) onConfirm();
            };
            
            const handleCancel = () => {
                hideConfirmModal();
                if (onCancel) onCancel();
            };
            
            // 既存のイベントリスナーを削除（重複登録防止）
            replaceWithClone(confirmModalConfirm);
            replaceWithClone(confirmModalCancel);
            
            // 新しい要素を取得直し
            const newConfirmButton = document.getElementById('confirmModalConfirm');
            const newCancelButton = document.getElementById('confirmModalCancel');
            // 念のためボタン文言を再適用（置換後の要素に）
            try {
                const opts = options || {};
                if (opts.cancelLabel) newCancelButton.textContent = String(opts.cancelLabel);
                if (opts.confirmLabel) newConfirmButton.textContent = String(opts.confirmLabel);
                newConfirmButton.classList.remove('button-danger');
                if (opts.confirmClass === 'button-danger') {
                    newConfirmButton.classList.add('button-danger');
                }
            } catch (_) {}
            
            // イベントリスナーを追加
            newConfirmButton.addEventListener('click', handleConfirm);
            newCancelButton.addEventListener('click', handleCancel);
            // 外側クリックは共通関数により付与済み
            // ESCで閉じる（共通関数）
            try { confirmModal.__scope && confirmModal.__scope.addDetach && confirmModal.__scope.addDetach(attachEscToClose(handleCancel, document)); } catch(_) {}
            
            // モーダルを表示
            openModal(confirmModal);
            updateFooterHiddenState();
            
            // フォーカスをキャンセルボタン（編集に戻る）に設定
            setTimeout(() => {
                try { newCancelButton && newCancelButton.focus && newCancelButton.focus({ preventScroll: true }); } catch (_) {}
            }, 100);
        }
        
        // カスタム確認ダイアログを非表示にする関数
        function hideConfirmModal() {
            const confirmModal = document.getElementById('confirmModal');
            if (confirmModal) {
                closeModal(confirmModal);
                updateFooterHiddenState();
                
                // イベント/リソースを削除
                try { confirmModal.__scope && confirmModal.__scope.cleanup && confirmModal.__scope.cleanup(); } catch(_) {}
                const clonedModal = confirmModal.cloneNode(true);
                confirmModal.parentNode.replaceChild(clonedModal, confirmModal);
            }
        }

        // 単純なメッセージ（情報/警告）をカードウインドウで表示する関数
        function showMessageModal(message, title) {
            const modal = document.getElementById('messageModal');
            const messageEl = document.getElementById('messageModalMessage');
            const okButton = document.getElementById('messageModalOk');
            const titleEl = document.getElementById('messageModalTitle');
            if (!modal || !messageEl || !okButton || !titleEl) return;

            if (title) titleEl.textContent = String(title);
            messageEl.textContent = String(message || '');

            // 既存のイベントリスナーをクリア
            replaceWithClone(okButton);
            const newOkButton = document.getElementById('messageModalOk');

            // スコープ管理
            try { modal.__scope && modal.__scope.cleanup && modal.__scope.cleanup(); } catch(_) {}
            try { modal.__scope = createScope(); } catch(_) {}
            // 外側クリックは共通関数で付与
            try { modal.__scope && modal.__scope.addDetach && modal.__scope.addDetach(attachOutsideClickClose(modal, hideMessageModal)); } catch(_) {}

            newOkButton.addEventListener('click', () => {
                hideMessageModal();
            });
            // 外側クリックは共通関数により付与済み
            // ESCで閉じる（共通関数）
            try { modal.__scope && modal.__scope.addDetach && modal.__scope.addDetach(attachEscToClose(hideMessageModal, document)); } catch(_) {}

            openModal(modal);
            updateFooterHiddenState();
            setTimeout(() => { try { newOkButton && newOkButton.focus && newOkButton.focus({ preventScroll: true }); } catch (_) {} }, 50);
        }

        function hideMessageModal() {
            const modal = document.getElementById('messageModal');
            if (!modal) return;
            closeModal(modal);
            updateFooterHiddenState();
            try { modal.__scope && modal.__scope.cleanup && modal.__scope.cleanup(); } catch(_) {}
            const cloned = modal.cloneNode(true);
            modal.parentNode.replaceChild(cloned, modal);
        }
        // トースト通知ユーティリティ
        function showToast(message, options) {
            try {
                const container = document.getElementById('toastContainer');
                if (!container) return;
                const opts = options || {};
                const duration = Number(opts.duration || 1800);
                const role = opts.role || 'status';
                container.classList.remove('is-hidden');

                const item = document.createElement('div');
                item.className = 'toast-message';
                item.setAttribute('role', role);
                item.textContent = String(message || '');
                container.appendChild(item);
                // アニメーション表示
                requestAnimationFrame(() => { item.classList.add('show'); });

                const cleanup = () => {
                    try {
                        item.classList.remove('show');
                        setTimeout(() => {
                            if (item.parentNode) item.parentNode.removeChild(item);
                            if (!container.children.length) container.classList.add('is-hidden');
                        }, 180);
                    } catch(_) {}
                };
                setTimeout(cleanup, duration);
                return cleanup;
            } catch(_) {}
        }
        function synchronizeForm() {
            const originalTextarea = document.querySelector('#original-textarea');
            const currentText = originalTextarea ? originalTextarea.value : '';
            const formContainer = document.getElementById('form-container');
            if (!formContainer) return;

            const existingInputs = Array.from(formContainer.querySelectorAll('.variable-input[data-source="placeholder"], .variable-input[data-source="bracket"]'));

            const existingValues = {};
            existingInputs.forEach(input => {
                const fieldName = input.getAttribute('name') || input.getAttribute('data-field');
                if (!fieldName) return;
                if (input instanceof HTMLInputElement && input.type === 'checkbox') {
                    existingValues[fieldName] = !!input.checked;
                } else if ('value' in input) {
                    existingValues[fieldName] = input.value;
                }
            });

            const existingPersist = {};
            const existingCheckboxes = Array.from(formContainer.querySelectorAll('.persist-checkbox'));
            existingCheckboxes.forEach(cb => {
                const fieldName = cb.getAttribute('data-field');
                if (!fieldName) return;
                existingPersist[fieldName] = !!cb.checked;
            });

            const existingTypes = {};
            const existingTypeSelects = Array.from(formContainer.querySelectorAll('.variable-type'));
            existingTypeSelects.forEach(sel => {
                const fieldName = sel.getAttribute('data-field');
                if (!fieldName) return;
                existingTypes[fieldName] = sel.value;
            });

            // 永続デフォルトをマージ（既存のユーザー入力を優先）
            const mergedValues = { ...persistedDefaults.values, ...existingValues };
            // 直前の吹き出し保存で上書きされないように、最後に確実に適用
            if (lastSavedFieldName && Object.prototype.hasOwnProperty.call(persistedDefaults.values, lastSavedFieldName)) {
                mergedValues[lastSavedFieldName] = persistedDefaults.values[lastSavedFieldName];
            }
            const mergedPersist = { ...persistedDefaults.persist, ...existingPersist };
            // 型は外部適用（persistedDefaults.types）を優先してUIへ反映したいので順序を逆にする
            const mergedTypes = { ...existingTypes, ...persistedDefaults.types };

            formContainer.innerHTML = generateDynamicForm(currentText, mergedValues, mergedPersist, mergedTypes);
            try {
                if (typeof bindFormEvents === 'function') {
                    bindFormEvents(formContainer);
                }
            } catch (_) {}

            // 生成直後に、最後に保存したフィールドの値をDOMにも強制適用（input[type=date/time/datetime]など）
            if (lastSavedFieldName) {
                let savedVal = persistedDefaults.values[lastSavedFieldName];
                const fieldType = (persistedDefaults.types && persistedDefaults.types[lastSavedFieldName]) || 'multiline';
                savedVal = toHalfWidthForType(savedVal, fieldType);
                const lastFieldEl = formContainer.querySelector(`.variable-input[data-field="${CSS.escape(lastSavedFieldName)}"], .variable-input[name="${CSS.escape(lastSavedFieldName)}"]`);
                if (lastFieldEl) {
                    if (lastFieldEl instanceof HTMLInputElement && lastFieldEl.type === 'date') {
                        lastFieldEl.value = formatDateISO(savedVal);
                    } else if (lastFieldEl instanceof HTMLInputElement && lastFieldEl.type === 'datetime-local') {
                        lastFieldEl.value = formatDateTimeLocalISO(savedVal);
                    } else if ('value' in lastFieldEl) {
                        lastFieldEl.value = savedVal == null ? '' : String(savedVal);
                    }
                }
            }

            // type=date を使用するためフォーマッタは不要
        }
        /**
         * ========================================
         * 外部変数データ適用システム
         * ========================================
         * 
         * 外部（ Claris FileMaker 等）から渡された変数データを内部システムに反映する
         * 
         * 対応データ形式:
         * 1. 新形式: { checkbox: {項目名: {value, persist, type, memo}}, fields: {変数名: {value, persist, type, memo}} }
         * 2. 旧形式: { 変数名: {value, persist, type, memo} } (後方互換)
         * 3. ラップ形式: { variables: {上記のいずれか} }
         * 
         * 入力形式:
         * - 直接オブジェクト
         * - JSON文字列（自動パース）
         * 
         * 処理内容:
         * - 永続デフォルト値への保存
         * - 現在のフォームへの値反映
         * - UI状態の更新
         * - 型変換（日付、数値等）
         * 
         * エラーハンドリング:
         * - 不正データでもアプリケーションが停止しないよう安全に処理
         */
        window.applyVariables = function(variablesInput) {
            try {
                let payload = variablesInput;
                if (typeof payload === 'string') {
                    payload = JSON.parse(payload);
                }
                const vars = payload && payload.variables ? payload.variables : payload;
                if (!vars || typeof vars !== 'object') return;

                // 新形式か旧形式かを判定
                const isNewFormat = vars.checkbox || vars.fields || vars.select;
                let fieldsToProcess = {};

                if (isNewFormat) {
                    // 新形式: checkbox と fields を分けて処理
                    fieldsToProcess = {};
                    
                    // fields カテゴリ（{{}} プレースホルダー）の処理
                    if (vars.fields) {
                        Object.keys(vars.fields).forEach(fieldName => {
                            fieldsToProcess[fieldName] = vars.fields[fieldName];
                        });
                    }
                    
                    // checkbox カテゴリ（[[]] チェックボックス）は別途処理
                    // select カテゴリ（{{名前:..}}）は別途処理
                } else {
                    // 旧形式: そのまま使用（後方互換）
                    fieldsToProcess = vars;
                }

                // 永続デフォルトに反映（次回のフォーム再生成時にも保持）
                const newValues = { ...persistedDefaults.values };
                const newPersist = { ...persistedDefaults.persist };
                const newTypes = { ...persistedDefaults.types };
                const newMemos = { ...persistedDefaults.memos };
                Object.keys(fieldsToProcess).forEach((field) => {
                    const entry = fieldsToProcess[field] || {};
                    if (entry.value !== undefined) newValues[field] = entry.value;
                    if (entry.persist !== undefined) newPersist[field] = !!entry.persist;
                    if (entry.type) newTypes[field] = entry.type;
                    if (entry.memo !== undefined) newMemos[field] = entry.memo;
                });
                persistedDefaults = { values: newValues, persist: newPersist, types: newTypes, memos: newMemos };

                // 現在のフォームに適用
                synchronizeForm();

                // fields カテゴリ（{{}} プレースホルダー）の値設定
                Object.keys(fieldsToProcess).forEach((field) => {
                    const entry = fieldsToProcess[field] || {};
                    // チェックボックス以外の要素を対象とする（textarea, input[type="text"/"date"]など）
                const el = null;
                    if (el && entry.value !== undefined) {
                        // 日付/日時入力の場合は input 用の ISO に整形してから反映
                        if (el instanceof HTMLInputElement && el.type === 'date') {
                            el.value = formatDateISO(entry.value);
                        } else if (el instanceof HTMLInputElement && el.type === 'datetime-local') {
                            el.value = formatDateTimeLocalISO(entry.value);
                        } else {
                            el.value = entry.value;
                        }
                    }
                });
                
                // checkbox カテゴリ（[[]] チェックボックス）の値設定
                if (isNewFormat && vars.checkbox) {
                    Object.keys(vars.checkbox).forEach(itemName => {
                        const entry = vars.checkbox[itemName];
                        const fieldName = `checkbox_${itemName}`;
                        // data-source="bracket" のチェックボックス要素のみを対象とする
                        
                        // 永続デフォルトにも反映
                        if (entry.value !== undefined) newValues[fieldName] = entry.value;
                        if (entry.persist !== undefined) newPersist[fieldName] = !!entry.persist;
                        if (entry.type) newTypes[fieldName] = entry.type;
                        if (entry.memo !== undefined) newMemos[fieldName] = entry.memo;
                    });
                }

                // select カテゴリ（{{名前:..}}）の取り込みと永続反映
                if (isNewFormat && vars.select) {
                    const newSelects = { ...(persistedDefaults.selects || {}) };
                    Object.keys(vars.select).forEach((name) => {
                        const entry = vars.select[name] || {};
                        const value = entry.value == null ? '' : String(entry.value);
                        const options = Array.isArray(entry.options) ? entry.options.map(v => String(v == null ? '' : v)) : [];
                        newSelects[name] = { value, options };
                        if (value !== undefined) newValues[name] = value;
                    });
                    persistedDefaults.selects = newSelects;
                }

                updateDisplayText();
                
                // チェックボックスのUI更新（値設定後に必要）
                document.querySelectorAll('.checkbox-container input[type="checkbox"]').forEach(checkbox => {
                    updateCheckboxUI(checkbox);
                });
            } catch (e) {
                // 受信データが不正でもアプリが落ちないように無視
            }
        }
        /**
         * ========================================
         * モーダルウィンドウ管理システム
         * ========================================
         * 
         * 管理対象モーダル:
         * 1. テンプレート選択モーダル（検索、作成、編集機能付き）
         * 2. 内容編集モーダル（テンプレート更新機能付き）
         * 3. 変数設定モーダル（型選択、永続化設定）
         * 4. インライン変数入力ポップオーバー（ドラッグ可能）
         * 5. 確認ダイアログ（カスタム）
         * 6. メッセージ表示モーダル
         * 
         * 主要機能:
         * - ドラッグ&ドロップによる移動
         * - レスポンシブ対応（画面サイズ自動調整）
         * - キーボードナビゲーション（ESCキー等）
         * - 重複初期化防止
         * - 外側クリックでの閉じる機能
         * - フォーカス管理
         * 
         * セキュリティ考慮:
         * - XSS対策（全入力値のエスケープ）
         * - イベント伝播制御
         * - メモリリーク防止（イベントリスナー適切な削除）
         */
        function initializeModal() {
            // 重複初期化を防止
            if (document.body.dataset.modalInitialized === 'true') return;
            document.body.dataset.modalInitialized = 'true';
            const editButton = document.getElementById('editButton');
            const copyContentButton = document.getElementById('copyContentButton');
            const searchButton = document.getElementById('searchButton');
            const modal = document.getElementById('editModal');
            const modalTextarea = document.getElementById('modalTextarea');
            // 既存フッターの編集ボタンを優先し、なければモーダル内ボタン（後方互換）
            const cancelButton = document.getElementById('footerEditCancel') || document.getElementById('cancelButton');
            const saveButton = document.getElementById('footerEditSave') || document.getElementById('saveButton');
            const originalTextarea = document.getElementById('original-textarea');
            const directInput = document.getElementById('direct-input');
            const displayText = document.getElementById('display-text');
            const templateModal = document.getElementById('templateModal');
            const templateCancelButton = document.getElementById('templateCancelButton');
            const templateNewButton = document.getElementById('templateNewButton');
            const templateCreateSection = document.getElementById('templateCreateSection');
            const templateListEl = document.getElementById('templateList');
            const templatePaginationEl = document.getElementById('templatePagination');
            const templateCreateCancel = document.getElementById('templateCreateCancel');
            const templateCreateSubmit = document.getElementById('templateCreateSubmit');
            const templateSearchRow = document.getElementById('templateSearchRow');
            const templateActionsRow = document.getElementById('templateActionsRow');
            const templateModalTitle = document.getElementById('templateModalTitle');
            // 行のデフォルトdisplayを保持（復元用）
            const defaultSearchRowDisplay = templateSearchRow ? getComputedStyle(templateSearchRow).display || 'flex' : 'flex';
            const defaultActionsRowDisplay = templateActionsRow ? getComputedStyle(templateActionsRow).display || 'flex' : 'flex';
            // 他スコープからも参照できるよう dataset に保存
            try { if (templateSearchRow) templateSearchRow.dataset.defaultDisplay = defaultSearchRowDisplay; } catch(_) {}
            try { if (templateActionsRow) templateActionsRow.dataset.defaultDisplay = defaultActionsRowDisplay; } catch(_) {}
            // メモ表示トグル 初期化
            try {
                const cb = document.getElementById('toggleShowMemos');
                // localStorageを使わず、要素の既定状態（checked属性）を初期値として採用
                const initial = cb ? !!cb.checked : false;
                if (cb) cb.checked = initial;
                window.__showMemosInline = initial;
                document.body.classList.toggle('show-memos-inline', window.__showMemosInline);
                updateMemoToggleVisibility();
            } catch(_) {}

            // コピー用選択モード起動（テンプレートを選択 と同じ構造、ただし [+]/編集/削除/3点を非表示）
            if (copyContentButton) {
                copyContentButton.addEventListener('click', () => {
                    // ヘッダーのタイトルを一時的に変更
                    try {
                        const titleElement = document.querySelector('.title-section h3');
                        if (titleElement) {
                            // 元のタイトルを保存（まだ保存されていない場合のみ）
                            if (!titleElement.dataset.originalTitle) {
                                titleElement.dataset.originalTitle = titleElement.textContent || '';
                            }
                            titleElement.textContent = 'コピー選択中...';
                        }
                    } catch(_) {}
                    try {
                        const modal = document.getElementById('templateModal');
                        if (modal) {
                            modal.dataset.mode = 'copy';
                            try { updateTemplateHeaderForMode(); } catch(_) {}
                            try { 
                                const titleEl = document.getElementById('templateModalTitle'); 
                                if (titleEl) {
                                    titleEl.textContent = 'コピー'; 
                                    // 検索中で非表示になっている場合は表示状態に戻す
                                    titleEl.style.display = '';
                                }
                            } catch(_) {}
                        }
                        // コピー検索行を閉じる（検索中の状態をリセット）
                        try {
                            const copySearchRow = document.getElementById('copySearchRow');
                            const copySearchInput = document.getElementById('copySearch');
                            const templateSearchToggle = document.getElementById('templateSearchToggle');
                            const templateSearchClose = document.getElementById('templateSearchClose');
                            const templateNewButton = document.getElementById('templateNewButton');
                            if (copySearchRow) copySearchRow.style.display = 'none';
                            if (copySearchInput) copySearchInput.value = '';
                            if (templateSearchToggle) templateSearchToggle.style.display = '';
                            if (templateSearchClose) templateSearchClose.style.display = 'none';
                            // コピーモードでは[+]ボタンは非表示のまま
                            if (templateNewButton) templateNewButton.style.display = 'none';
                        } catch(_) {}
                    } catch(_) {}
                    try { isSearching = false; } catch(_) {}
                    try { performTemplateSearch('', 1, false); } catch(_) {}
                    // ページオープン時に FileMaker スクリプトを実行
                    try { fmCall && fmCall('addon_remarks_web_copy_prepare', '', 5); } catch(_) {}
                });
                // 長押しコピーを共通関数で付与（現在表示内容をコピー）
                try { installLongPressCopy(copyContentButton, getDisplayedTextPlain, 600); } catch(_) {}
            }
            const templateSearchEl = document.getElementById('templateSearch');
            // 変数設定モーダル要素
            const varSettingsModal = document.getElementById('varSettingsModal');
            const varSettingsType = document.getElementById('varSettingsType');
            const varSettingsPersist = document.getElementById('varSettingsPersist');
            const varSettingsMemo = document.getElementById('varSettingsMemo');
            const varSettingsCancel = document.getElementById('varSettingsCancel');
            const varSettingsSave = document.getElementById('varSettingsSave');

            // インライン吹き出し要素
            const inlinePopover = document.getElementById('variableInlinePopover');
            // 参照をグローバルにも保持（他処理から閉じる用途）
            window.variableInlinePopover = inlinePopover;
            const popoverValueContainer = document.getElementById('popoverValueContainer');
            const popoverNow = document.getElementById('popoverNow');
            const popoverClear = document.getElementById('popoverClear');

            // 全画面化の判定（{{変数}}の編集ダイアログ専用）
            function decideInlineFullscreen() {
                try {
                    const ua = (navigator.userAgent || '').toLowerCase();
                    const isIPhone = /iphone/.test(ua) && !/ipad/.test(ua);
                    const vwNow = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                    const vhNow = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
                    const widthThreshold = 480; // 幅がこの値未満なら全面
                    const computedCardWidth = Math.min(520, Math.floor(vwNow * 0.92));
                    const minCardWidth = 280; // CSSのmin-widthと同値
                    const heightThreshold = 420; // 高さが極端に低い場合も全面
                    const tooShort = vhNow < heightThreshold;
                    return isIPhone || (vwNow < widthThreshold) || (computedCardWidth < minCardWidth) || tooShort;
                } catch(_) { return false; }
            }
            // ポップオーバーをドラッグ移動可能にする（ヘッダをハンドル）
            (function enableInlinePopoverDrag() {
                if (!inlinePopover) return;
                const handle = inlinePopover.querySelector('.popover-header');
                if (!handle) return;
                const detach = attachDragMove(inlinePopover, {
                    handleEl: handle,
                    clampToViewport: true,
                    padding: 8,
                    blockSelector: 'button, .var-gear-btn, .popover-close-x',
                    disallowSelection: true,
                    onStart: () => {
                        try { document.body.classList.add('dragging'); handle.classList.add('grabbing'); } catch(_) {}
                    // まだロックされていなければ現在位置をロック座標として採用
                        try {
                            const rect = inlinePopover.getBoundingClientRect();
                    if (!inlinePopover.__lockPosition) {
                        inlinePopover.__lockedLeft = rect.left;
                        inlinePopover.__lockedTop = rect.top;
                    }
                        } catch(_) {}
                        try { inlinePopover.__lockPosition = true; } catch(_) {}
                    },
                    onMove: (left, top) => {
                        try {
                            inlinePopover.__lockedLeft = left;
                            inlinePopover.__lockedTop = top;
                            inlinePopover.__lockPosition = true;
                    } catch(_) {}
                    },
                    onEnd: () => {
                        try { document.body.classList.remove('dragging'); } catch(_) {}
                    },
                });
                inlinePopover.__dragCleanup = () => { try { detach(); } catch(_) {} };
            })();
            const popoverFieldName = document.getElementById('popoverFieldName');
            const popoverType = document.getElementById('popoverType');
            const popoverPersist = document.getElementById('popoverPersist');
            const popoverMemo = document.getElementById('popoverMemo');
            const popoverCancel = document.getElementById('popoverCancel');
            const popoverSave = document.getElementById('popoverSave');
            const popoverGearBtn = document.getElementById('popoverGearBtn');
            const popoverHeaderCloseX = document.getElementById('popoverHeaderCloseX');
            // 設定用ポップオーバー
            const settingsPopover = document.getElementById('variableSettingsPopover');
            // 変数名タイトルは非表示化
            const settingsPopoverTitle = null;
            const settingsPopoverType = document.getElementById('settingsPopoverType');
            const settingsPopoverPersist = document.getElementById('settingsPopoverPersist');
            // バッジ（タイプ/次回も）の表示更新
            function updatePopoverTypeBadgeWithPersist(typeKey, persistBool) {
                try {
                    const badge = document.getElementById('popoverTypeBadge');
                    if (!badge) return;
                    const label = getTypeLabel(typeKey);
                    badge.textContent = persistBool ? `タイプ: ${label}・次回も` : `タイプ: ${label}`;
                    badge.style.display = 'inline-block';
                } catch(_) {}
            }
            // グローバル制御フラグ（初期化）
            try {
                if (typeof window.__suppressInlineCloseOnce !== 'boolean') window.__suppressInlineCloseOnce = false;
            } catch(_) {}
            // すべての保存済み高さをカードウインドウへ適用


            function applySavedTextareaHeight(fieldName, el) {
                // 高さ復元は停止（内部スクロールに統一）
            }

            function propagateTextareaHeight(fieldName, heightPx, sourceEl) {
                const h = Math.max(0, Math.round(heightPx || 0));
                if (!fieldName || h <= 0) return;
                fieldHeights = { ...fieldHeights, [fieldName]: h };
                // ポップオーバー側
                // 相互反映は停止
            }

            function bindTextareaHeightSync(fieldName, el) {
                // オートリサイズは無効化（イベント/Observerを貼らない）
            }
            // メモ欄など高さ同期不要のテキストエリアでも、
            // リサイズに合わせてカードの位置/サイズを再計算する
            function bindPopoverAutoLayoutOnTextarea(el) {
                // レイアウト追従は無効化（サイズ変化に依存しない）
                if (!(el instanceof HTMLTextAreaElement)) return;
                if (el.__autoLayoutBound) return;
                try { el.__autoLayoutBound = true; } catch(_) {}
            }
            // 幅同期ロジックは撤回（ユーザー要望）

            window.openInlineVariablePopover = function(fieldName, anchorEl) {
                if (!inlinePopover || !fieldName || !anchorEl) return;
                // 他のカードウインドウ（例: フレーズピッカー）が開いていれば先に閉じる
                    // フレーズモーダルとは独立運用するため、クロス閉鎖はしない
                // 再オープン時の残留状態をクリア
                try {
                    if (typeof closeSmallSettingsPopover === 'function') closeSmallSettingsPopover();
                    window.__suppressInlineCloseOnce = false;
                } catch(_) {}
                // 前回のスコープをクリーンアップし、新規スコープを作成
                try { inlinePopover.__scope && inlinePopover.__scope.cleanup && inlinePopover.__scope.cleanup(); } catch(_) {}
                try { inlinePopover.__scope = createScope(); } catch(_) {}
                // iPhone 判定（iOS Safari 等を対象）。iPadOSデスクトップ表示は除外
                {
                    const shouldFullscreen = decideInlineFullscreen();
                    inlinePopover.classList.toggle('fullscreen', shouldFullscreen);
                    // クリック時の判定をロック（以降のリサイズでは切り替えない）
                    try {
                        inlinePopover.__lockFullscreenDecision = true;
                        inlinePopover.__isFullscreenAtOpen = shouldFullscreen;
                        if (shouldFullscreen) {
                            // 全画面時は位置決めを無効化し、クリック位置に依存しない
                            inlinePopover.__lockPosition = true;
                            inlinePopover.__anchorEl = null;
                            inlinePopover.style.top = '';
                            inlinePopover.style.left = '';
                        } else {
                            inlinePopover.__lockPosition = false;
                        }
                    } catch(_) {}
                }
                // フルスクリーン時は既存フッターを利用し、[キャンセル][反映]を表示（既存の[キャンセル][転記]とナビを非表示）
                try {
                    if (inlinePopover.classList.contains('fullscreen')) {
                        const footerCancel = document.getElementById('footerCancelButton');
                        const footerOk = document.getElementById('footerQueryButton');
                        const footerEditCancel = document.getElementById('footerEditCancel');
                        const footerEditSave = document.getElementById('footerEditSave');
                        const changeNav = document.querySelector('.footer .change-nav');
                        if (footerCancel) footerCancel.style.display = 'none';
                        if (footerOk) footerOk.style.display = 'none';
                        if (changeNav) { changeNav.dataset.prevDisplay = changeNav.style.display || ''; changeNav.style.display = 'none'; }
                        if (footerEditCancel) footerEditCancel.style.display = '';
                        if (footerEditSave) {
                            // ラベルを一時的に「反映」に変更
                            try { footerEditSave.dataset.prevLabel = footerEditSave.textContent || ''; } catch(_) {}
                            footerEditSave.textContent = '反映';
                            footerEditSave.style.display = '';
                            // 変数ポップオーバーでは未入力可のため常に有効化
                            try { footerEditSave.disabled = false; footerEditSave.removeAttribute('disabled'); footerEditSave.setAttribute('aria-disabled', 'false'); footerEditSave.title = ''; } catch(_) {}
                        }
                        // フッターのボタンに一時ハンドラを付与（単一登録保証）
                        try { if (footerEditCancel) unbindSingleEvent(footerEditCancel, 'click', 'inlinePopover:cancel'); } catch(_) {}
                        try { if (footerEditSave) unbindSingleEvent(footerEditSave, 'click', 'inlinePopover:save'); } catch(_) {}
                        inlinePopover.__footerCancelHandler = () => { try { const btn = document.getElementById('popoverCancel'); if (btn) btn.click(); else closeInlineVariablePopover(); } catch(_) { try { closeInlineVariablePopover(); } catch(__) {} } };
                        inlinePopover.__footerSaveHandler = () => { try { const btn = document.getElementById('popoverSave'); if (btn) btn.click(); } catch(_) {} };
                        if (footerEditCancel) bindSingleEvent(footerEditCancel, 'click', 'inlinePopover:cancel', inlinePopover.__footerCancelHandler, false);
                        if (footerEditSave) bindSingleEvent(footerEditSave, 'click', 'inlinePopover:save', inlinePopover.__footerSaveHandler, false);
                        try {
                            inlinePopover.__scope && inlinePopover.__scope.addDetach && inlinePopover.__scope.addDetach(() => {
                                try { unbindSingleEvent(footerEditCancel, 'click', 'inlinePopover:cancel'); } catch(_) {}
                                try { unbindSingleEvent(footerEditSave, 'click', 'inlinePopover:save'); } catch(_) {}
                            });
                        } catch(_) {}
                        // フッターを必ず表示
                        updateFooterHiddenState();
                        try { inlinePopover.__scope && inlinePopover.__scope.addDetach && inlinePopover.__scope.addDetach(() => { try { updateFooterHiddenState(); } catch(_) {} }); } catch(_) {}
                    }
                } catch(_) {}
                // クリック時の判定は初期値として使用（リサイズ時は再評価で切替）
                // 保存: どの要素に追従するか（カード型のときのみ）
                if (!inlinePopover.classList.contains('fullscreen')) {
                    inlinePopover.__anchorEl = anchorEl;
                    inlinePopover.__lockedTop = null;
                    inlinePopover.__lockedLeft = null;
                    inlinePopover.__lockPosition = false; // スクロールに追従する
                }
                // 初期の上下配置（スクロール中に切り替えない）と初期座標のロック
                if (!inlinePopover.classList.contains('fullscreen')) {
                    try {
                        const rect0 = anchorEl.getBoundingClientRect();
                        const vw0 = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                        const vh0 = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
                        const padding0 = 8;
                        const minHeight0 = 220;
                        inlinePopover.__placement = (rect0.bottom + minHeight0 + padding0 <= vh0) ? 'below' : 'above';
                        const maxW0 = Math.min(520, Math.floor(vw0 * 0.92));
                        let top0 = (inlinePopover.__placement === 'below') ? (rect0.bottom + 6) : Math.max(padding0, rect0.top - minHeight0 - 6);
                        let left0 = Math.round(rect0.left);
                        if (left0 + maxW0 + padding0 > vw0) left0 = vw0 - maxW0 - padding0;
                        if (left0 < padding0) left0 = padding0;
                        inlinePopover.style.left = left0 + 'px';
                        inlinePopover.style.top = top0 + 'px';
                        inlinePopover.__lockedLeft = left0;
                        inlinePopover.__lockedTop = top0;
                        inlinePopover.__lockPosition = true;
                    } catch(_) { inlinePopover.__placement = 'below'; }
                }
                // クリック元（または最初の一致要素）をハイライト
                try {
                    document.querySelectorAll('.clickable-placeholder.editing').forEach(el => el.classList.remove('editing'));
                    let highlightTargets = [];
                    const container = document.getElementById('display-text');
                    if (container && fieldName) {
                        try { highlightTargets = Array.from(container.querySelectorAll(`.clickable-placeholder[data-field="${CSS.escape(fieldName)}"]`)); } catch(_) { highlightTargets = []; }
                    }
                    if ((!highlightTargets || highlightTargets.length === 0) && anchorEl && anchorEl.classList && anchorEl.classList.contains('clickable-placeholder')) {
                        highlightTargets = [anchorEl];
                    }
                    inlinePopover.__highlightEls = highlightTargets || [];
                    (highlightTargets || []).forEach((target) => {
                        if (!target || !target.classList) return;
                        try { target.classList.add('editing'); } catch(_) {}
                        try { target.setAttribute('aria-current', 'true'); } catch(_) {}
                    });
                } catch(_) {}
                // ヘッダーに「編集中: 変数名」を表示
                if (popoverFieldName) popoverFieldName.textContent = `編集中: ${fieldName}`;
                // ヘッダーにタイプバッジを表示（「次回も」を反映）
                try {
                    const badge = document.getElementById('popoverTypeBadge');
                    if (badge) {
                        const t = (persistedDefaults.types && persistedDefaults.types[fieldName]) || 'multiline';
                        const persistVal0 = !!(persistedDefaults.persist && persistedDefaults.persist[fieldName]);
                        const label0 = getTypeLabel(t);
                        badge.textContent = persistVal0 ? `タイプ: ${label0}・次回も` : `タイプ: ${label0}`;
                        badge.style.display = 'inline-block';
                    }
                } catch(_) {}

                // 歯車クリック時に「タイプ」「次回も」を編集できる状態へ切替
                if (popoverGearBtn) {
                    try { popoverGearBtn.onclick = null; } catch(_) {}
                    popoverGearBtn.addEventListener('click', (e) => {
                        if (!settingsPopover) return;
                        try { e.preventDefault(); } catch(_) {}
                        // pending値があればそれを、なければ現在値で初期化
                        try {
                            const pendingType = inlinePopover.__pendingType;
                            const typeToUse = (typeof pendingType === 'string' && pendingType) ? pendingType : ((typeof currentType === 'string' && currentType) ? currentType : 'multiline');
                            if (settingsPopoverType) settingsPopoverType.value = typeToUse;
                        } catch(_) {}
                        try {
                            const pendingPersist = inlinePopover.__pendingPersist;
                            const persistToUse = (typeof pendingPersist === 'boolean') ? pendingPersist : !!currentPersist;
                            if (settingsPopoverPersist) settingsPopoverPersist.checked = persistToUse;
                        } catch(_) {}
                        // 先にXを表示、歯車を非表示にしてからXをアンカーにする
                        try {
                            const gearBtn = document.getElementById('popoverGearBtn');
                            const headerClose = document.getElementById('popoverHeaderCloseX');
                            if (gearBtn) gearBtn.style.display = 'none';
                            if (headerClose) headerClose.style.display = 'inline-flex';
                        } catch(_) {}
                        // Xボタン直下に配置（Xが可視状態なので正しい座標が取得できる）
                        try {
                            const headerClose = document.getElementById('popoverHeaderCloseX');
                            const anchorEl = (headerClose && headerClose.offsetParent !== null) ? headerClose : popoverGearBtn;
                            settingsPopover.__anchorBtn = anchorEl;
                            openSmallSettingsPopover(anchorEl);
                        } catch(_) {
                            // フォールバック
                            settingsPopover.__anchorBtn = popoverGearBtn;
                                openSmallSettingsPopover(popoverGearBtn);
                        }
                        try { settingsPopover.style.display = 'block'; } catch(_) {}
                        // バッジの表示も pendingType/次回も に追従
                        try {
                            const pendingType = inlinePopover.__pendingType;
                            const typeKey = (typeof pendingType === 'string' && pendingType) ? pendingType : (persistedDefaults.types[fieldName] || 'multiline');
                            const pendingPersist = inlinePopover.__pendingPersist;
                            const persistVal = (typeof pendingPersist === 'boolean') ? pendingPersist : !!currentPersist;
                            const badge = document.getElementById('popoverTypeBadge');
                            if (badge) {
                                const label = getTypeLabel(typeKey);
                                badge.textContent = persistVal ? `タイプ: ${label}・次回も` : `タイプ: ${label}`;
                            }
                        } catch(_) {}
                        try { e.stopPropagation(); } catch(_) {}
                    });
                }

                // ヘッダーのXは表示のみ（クリック機能なし）

                // 現在の型・値・永続・メモを取得
                const currentType = persistedDefaults.types[fieldName] || 'multiline';
                const currentPersist = !!(persistedDefaults.persist && persistedDefaults.persist[fieldName]);
                // バッジも再確認（型/次回も）
                try {
                    const badge = document.getElementById('popoverTypeBadge');
                    if (badge) {
                        const label = getTypeLabel(currentType);
                        const persistVal = !!currentPersist;
                        badge.textContent = persistVal ? `タイプ: ${label}・次回も` : `タイプ: ${label}`;
                    }
                } catch(_) {}
                const currentMemo = (persistedDefaults.memos && persistedDefaults.memos[fieldName]) || '';
                const currentValue = persistedDefaults.values[fieldName] || '';

                // 値入力UIを型に合わせて再生成
                clearChildren(popoverValueContainer);
                let valueInputEl = null;
                if (currentType === 'multiline') {
                    valueInputEl = document.createElement('textarea');
                    valueInputEl.rows = 3;
                } else if (currentType === 'text' || currentType === 'number') {
                    valueInputEl = document.createElement('input');
                    if (currentType === 'number') {
                        valueInputEl.type = 'text';
                        valueInputEl.setAttribute('inputmode', 'decimal');
                        valueInputEl.setAttribute('pattern', '[0-9]*');
                        valueInputEl.dataset.xType = 'number';
                    } else {
                        valueInputEl.type = 'text';
                    }
                } else if (currentType === 'date') {
                    valueInputEl = document.createElement('input');
                    valueInputEl.type = 'date';
                } else if (currentType === 'time') {
                    valueInputEl = document.createElement('input');
                    valueInputEl.type = 'time';
                } else if (currentType === 'timestamp') {
                    valueInputEl = document.createElement('input');
                    valueInputEl.type = 'datetime-local';
                } else {
                    valueInputEl = document.createElement('input');
                    valueInputEl.type = 'text';
                }
                valueInputEl.className = 'variable-input';
                valueInputEl.name = fieldName;
                // Draftは、歯車などプレースホルダー以外から開いた場合のみ優先。
                // プレースホルダー（{{変数}}）クリック時は、常に現在表示の値で初期化。
                try {
                    const draftKey = String(fieldName || '');
                    const isFromPlaceholder = !!(anchorEl && anchorEl.classList && anchorEl.classList.contains('clickable-placeholder'));
                    if (!isFromPlaceholder && draftKey && Object.prototype.hasOwnProperty.call(inProgressDrafts, draftKey)) {
                        valueInputEl.value = inProgressDrafts[draftKey] ?? '';
                    } else {
                        // 型に応じて既存値を入力用(ISO等)に整形
                        valueInputEl.value = toInputValueByType(currentValue || '', currentType);
                    }
                } catch(_) {
                    // フォールバック
                    valueInputEl.value = toInputValueByType(currentValue || '', currentType);
                }
                popoverValueContainer.appendChild(valueInputEl);
                // 数字入力の整形（パネル非表示モードでも同等挙動に）
                try { if (valueInputEl.dataset && valueInputEl.dataset.xType === 'number') attachNumberInputSanitizers(valueInputEl); } catch(_){}
                // レイアウト追従をバインド
                if (valueInputEl instanceof HTMLTextAreaElement) {
                    // 高さ同期（複数行フィールド）
                    applySavedTextareaHeight(fieldName, valueInputEl);
                    bindTextareaHeightSync(fieldName, valueInputEl);
                } else {
                    // 1行入力でも横幅変更が起きた場合に備えて
                    // カード位置再計算だけは有効化
                    bindPopoverAutoLayoutOnTextarea(valueInputEl);
                }
                // 「タイプ」「次回も」は親UIからは非表示のため、内部同期のみ
                try {
                    // pending値があればそれを使用、なければ保存済み値を使用
                    const pendingType = inlinePopover.__pendingType;
                    const pendingPersist = inlinePopover.__pendingPersist;
                    
                    const effectiveType = (typeof pendingType === 'string' && pendingType) ? pendingType : currentType;
                    const effectivePersist = (typeof pendingPersist === 'boolean') ? pendingPersist : currentPersist;
                    
                    if (popoverType) popoverType.value = effectiveType;
                    if (popoverPersist) popoverPersist.checked = effectivePersist;
                    
                    // pending値がまだ設定されていない場合のみ初期化
                    if (typeof inlinePopover.__pendingType !== 'string') {
                        inlinePopover.__pendingType = currentType;
                    }
                    if (typeof inlinePopover.__pendingPersist !== 'boolean') {
                        inlinePopover.__pendingPersist = currentPersist;
                    }
                    
                    // オリジナル値を記録（キャンセル時の復帰用、初回のみ設定）
                    if (typeof inlinePopover.__originalType !== 'string') {
                        inlinePopover.__originalType = currentType;
                    }
                    if (typeof inlinePopover.__originalPersist !== 'boolean') {
                        inlinePopover.__originalPersist = currentPersist;
                    }
                } catch(_) {}
                popoverMemo.value = currentMemo;
                // メモ欄のリサイズに追随（カード位置/サイズ再計算）
                bindPopoverAutoLayoutOnTextarea(popoverMemo);
                // 幅同期は無効化（ユーザー要望により撤回）
                // 幅同期は無効化（ユーザー要望により撤回）

                // 「今」/「クリア」ボタンの表示制御（パネル非表示時のポップオーバー）
                try {
                    const resolvedType = currentType;
                    const isDateLike = resolvedType === 'date' || resolvedType === 'time' || resolvedType === 'timestamp';
                    if (popoverNow) {
                        popoverNow.style.display = isDateLike ? 'inline-flex' : 'none';
                        popoverNow.dataset.field = fieldName;
                        popoverNow.dataset.type = resolvedType;
                    }
                    if (popoverClear) {
                        const hasValue = isDateLike && String(((valueInputEl && valueInputEl.value) || '')).trim() !== '';
                        popoverClear.style.display = hasValue ? 'inline-flex' : 'none';
                        popoverClear.dataset.field = fieldName;
                        popoverClear.dataset.type = resolvedType;
                    }
                } catch(_) {}

                // オーバーレイを表示
                const overlay = document.getElementById('variablePopoverOverlay');
                if (overlay) overlay.style.display = 'block';

                // 表示前にフィールド名を設定し、不可視状態でDOMに載せてから正確にレイアウト
                inlinePopover.dataset.field = fieldName;
                inlinePopover.style.visibility = 'hidden';
                inlinePopover.style.display = 'block';
                // フッターを非表示にする
                updateFooterHiddenState();
                // 実計測で「カード幅 > 画面幅」なら全面化に切替（初期描画時の保険）
                try {
                    if (!inlinePopover.__lockFullscreenDecision && !inlinePopover.classList.contains('fullscreen')) {
                        const vwNow2 = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                        const actualW = Math.ceil(inlinePopover.getBoundingClientRect().width || 0);
                        const widthThreshold = 480;
                        if (vwNow2 < widthThreshold || actualW > vwNow2 || decideInlineFullscreen()) {
                            inlinePopover.classList.add('fullscreen');
                        }
                    }
                } catch(_) {}
                // 全面でなければ配置、全面なら配置不要
                if (!inlinePopover.classList.contains('fullscreen')) {
                    try { positionInlinePopover(); } catch(_) {}
                    try { requestAnimationFrame(() => { try { positionInlinePopover(); } catch(_) {} }); } catch(_) {}
                }
                // 最後に可視化
                try { inlinePopover.style.visibility = ''; } catch(_) {}
                try {
                    const ua = (navigator.userAgent || '').toLowerCase();
                    const isIPhone = /iphone/.test(ua) && !/ipad/.test(ua);
                    const t = (valueInputEl && valueInputEl.type) || '';
                    const isDateLikeInput = (t === 'date' || t === 'time' || t === 'datetime-local');
                    if (!(isIPhone && isDateLikeInput)) { valueInputEl.focus(); }
                } catch(_) {}
                // 入力中の値をドラフトに保持
                try {
                    const draftKey = String(fieldName || '');
                    valueInputEl.addEventListener('input', () => {
                        try { inProgressDrafts[draftKey] = valueInputEl.value; } catch(_) {}
                        // 入力値に応じた「クリア」ボタンの表示制御
                        try {
                            const resolvedType = (inlinePopover && typeof inlinePopover.__pendingType === 'string' && inlinePopover.__pendingType) ? inlinePopover.__pendingType : currentType;
                            const isDateLike = resolvedType === 'date' || resolvedType === 'time' || resolvedType === 'timestamp';
                            if (popoverClear) {
                                const hasValue = isDateLike && String(((valueInputEl && valueInputEl.value) || '')).trim() !== '';
                                popoverClear.style.display = hasValue ? 'inline-flex' : 'none';
                            }
                        } catch(_) {}
                    });
                } catch(_) {}
            }
            // 歯車ボタン（data-field付き）クリックでカードウインドウを開く
            // どこにあっても .var-gear-btn[data-field] を押せば、そのフィールドの吹き出しを表示
            document.addEventListener('click', (e) => {
                const gearBtn = e.target.closest && e.target.closest('.var-gear-btn[data-field]');
                if (!gearBtn) return;
                const field = gearBtn.getAttribute('data-field') || '';
                if (!field) return;
                try { openInlineVariablePopover(field, gearBtn); } catch(_) {}
                try { e.stopPropagation(); } catch(_) {}
                try { e.preventDefault(); } catch(_) {}
            });

            // ウインドウリサイズ時は位置のみ再計算（属性は変更しない）
            window.addEventListener('resize', () => {
                if (!inlinePopover) return;
                if (inlinePopover.style.display !== 'block') return;
                if (!inlinePopover.classList.contains('fullscreen')) {
                    try { positionInlinePopover(); } catch(_) {}
                }
            });
            window.addEventListener('orientationchange', () => {
                if (!inlinePopover) return;
                if (inlinePopover.style.display !== 'block') return;
                if (!inlinePopover.classList.contains('fullscreen')) {
                    try { positionInlinePopover(); } catch(_) {}
                }
            });

            // 小さな設定ポップオーバーの位置決め＆表示
            function openSmallSettingsPopover(anchorEl) {
                if (!settingsPopover || !anchorEl) return;
                // スコープ管理
                try { settingsPopover.__scope && settingsPopover.__scope.cleanup && settingsPopover.__scope.cleanup(); } catch(_) {}
                try { settingsPopover.__scope = createScope(); } catch(_) {}
                // 設定吹き出し用オーバーレイを表示
                const settingsOverlay = document.getElementById('settingsPopoverOverlay');
                if (settingsOverlay) settingsOverlay.style.display = 'block';
                try { settingsPopover.__scope && settingsPopover.__scope.addDetach && settingsPopover.__scope.addDetach(() => { const o = document.getElementById('settingsPopoverOverlay'); if (o) o.style.display = 'none'; }); } catch(_) {}
                const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                const vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
                const padding = 8;
                const width = Math.min(220, Math.floor(vw * 0.9));
                settingsPopover.style.width = width + 'px';
                // 歯車のSVGアイコン中心を優先して参照（なければボタン全体）
                const btnRect = anchorEl.getBoundingClientRect();
                // レイアウトが未確定で 0,0 のときは次フレームで再試行
                if (btnRect.width === 0 && btnRect.height === 0 && btnRect.top === 0 && btnRect.left === 0) {
                    try { return requestAnimationFrame(() => openSmallSettingsPopover(anchorEl)); } catch(_) {}
                }
                const rect = btnRect;
                const popRect = settingsPopover.getBoundingClientRect();
                const popH = Math.max(140, popRect.height || 160);
                // デフォルトは右横に出す
                let placement = 'right';
                let top = rect.top + (rect.height - popH) / 2; // 歯車ボタンの中央に合わせる
                let left = rect.right + 8; // 歯車ボタンの右側に配置
                
                // 右側に入りきらない場合は上側に配置
                if (left + width + padding > vw) {
                    placement = 'above';
                    top = rect.top - popH - 8;
                    left = Math.round(rect.right - width);
                    
                    // 上側にも入りきらない場合は下側に配置
                    if (top < padding) {
                        placement = 'below';
                        top = rect.bottom + 8;
                        left = Math.round(rect.right - width);
                        
                        // 下側にも入りきらない場合は左側に配置（最終フォールバック）
                        if (top + popH + padding > vh) {
                            placement = 'left';
                            top = rect.top + (rect.height - popH) / 2;
                            left = rect.left - width - 8;
                        }
                    }
                }
                // 矢印位置の基準値を先に計算（クランプ前の位置を基準にする）
                const originalTop = top;
                
                // 画面端はクランプ（左右）
                if (left + width + padding > vw) left = vw - width - padding;
                if (left < padding) left = padding;
                if (top + popH + padding > vh) top = vh - popH - padding;
                if (top < padding) top = padding;
                
                settingsPopover.style.top = Math.round(top) + 'px';
                settingsPopover.style.left = Math.round(left) + 'px';
                settingsPopover.classList.remove('is-below', 'is-above', 'is-right', 'is-left');
                
                // 矢印関連ロジックは撤廃
                try { settingsPopover.style.removeProperty('--arrow-left'); settingsPopover.style.removeProperty('--arrow-top'); } catch(_) {}
                // 初回表示時はアニメーションを無効化
                const wasVisible = settingsPopover.style.display === 'block';
                if (!wasVisible) {
                    settingsPopover.style.transition = 'none';
                }
                
                settingsPopover.style.display = 'block';
                updateFooterHiddenState();
                
                // 初回表示後にアニメーションを有効化
                if (!wasVisible) {
                    // 次フレームでtransitionを復活させる
                    requestAnimationFrame(() => {
                        settingsPopover.style.transition = 'top 0.2s ease-out, left 0.2s ease-out, transform 0.2s ease-out';
                    });
                }
                
                // 親カードに追従（リサイズ/スクロール）
                attachSettingsFollow(anchorEl);
                try { settingsPopover.__scope && settingsPopover.__scope.addDetach && settingsPopover.__scope.addDetach(() => { try { detachSettingsFollow(); } catch(_) {} }); } catch(_) {}
                // アイコン状態は変更しない（常に歯車表示のまま）
                // 次フレームで再配置（初回レイアウト未確定対策）
                try { requestAnimationFrame(() => { try { attachSettingsFollow(anchorEl); } catch(_) {} }); } catch(_) {}
            }

            function closeSmallSettingsPopover() {
                if (!settingsPopover) return;
                // 設定吹き出し用オーバーレイを非表示
                const settingsOverlay = document.getElementById('settingsPopoverOverlay');
                if (settingsOverlay) settingsOverlay.style.display = 'none';
                
                settingsPopover.style.display = 'none';
                try { settingsPopover.__anchorBtn = null; } catch(_) {}
                // 位置クラスや矢印位置は、再表示時に計算し直す
                try { settingsPopover.classList.remove('is-below', 'is-above', 'is-right', 'is-left'); } catch(_) {}
                try { settingsPopover.style.removeProperty('--arrow-left'); } catch(_) {}
                try { settingsPopover.style.removeProperty('--arrow-top'); } catch(_) {}
                updateFooterHiddenState();
                detachSettingsFollow();
                // スコープ一括解除
                try { settingsPopover.__scope && settingsPopover.__scope.cleanup && settingsPopover.__scope.cleanup(); } catch(_) {}
                // 設定吹き出しを閉じたら、ヘッダーの表示を歯車に戻す（親カードは残す）
                try {
                    const gearBtn = document.getElementById('popoverGearBtn');
                    const headerClose = document.getElementById('popoverHeaderCloseX');
                    if (gearBtn) gearBtn.style.display = 'inline-flex';
                    if (headerClose) headerClose.style.display = 'none';
                } catch(_) {}
            }

            // 親カードに追従させる（スクロールや親のドラッグ中）
            let followRAF = null;
            let followAnchor = null;
            function attachSettingsFollow(anchorEl) {
                followAnchor = anchorEl;
                if (followRAF) cancelAnimationFrame(followRAF);
                const tick = () => {
                    if (!settingsPopover || settingsPopover.style.display !== 'block' || !followAnchor) { followRAF = null; return; }
                    // 位置を再計算
                    try { openSmallSettingsPopover(followAnchor); } catch(_) {}
                    followRAF = requestAnimationFrame(tick);
                };
                followRAF = requestAnimationFrame(tick);
            }
            function detachSettingsFollow() {
                if (followRAF) cancelAnimationFrame(followRAF);
                followRAF = null;
                followAnchor = null;
            }
            // タイプ変更で即時反映
                if (settingsPopoverType) settingsPopoverType.addEventListener('change', () => {
                const fieldName = inlinePopover ? (inlinePopover.dataset.field || '') : '';
                // カードウインドウ外から開いた場合はデータフィールドを取得して適用
                let effectiveField = fieldName;
                if (!effectiveField) {
                    try {
                        const title = settingsPopoverTitle ? String(settingsPopoverTitle.textContent || '') : '';
                        effectiveField = title;
                    } catch(_) {}
                }
                if (!effectiveField) { closeSmallSettingsPopover(); return; }
                // 型は即保存せず、見た目だけ切替。保存は[反映]時に行う
                if (settingsPopoverType) {
                    // カードウインドウが開いている場合はセレクト反映のみ（内部pendingに保持）
                    const selectedType = settingsPopoverType.value;
                    try { if (popoverType) popoverType.value = selectedType; } catch(_) {}
                    // pendingに保持（反映時に適用）。永続保存は[反映]押下時のみ
                    try { inlinePopover.__pendingType = selectedType; } catch(_) {}
                    // カードウインドウが開いていれば入力再生成
                    if (inlinePopover && inlinePopover.style.display === 'block' && inlinePopover.dataset.field === effectiveField) {
                        const oldEl = popoverValueContainer.querySelector('.variable-input');
                        const oldVal = oldEl ? oldEl.value : '';
                        clearChildren(popoverValueContainer);
                        let valueInputEl = null;
                        if (selectedType === 'multiline') {
                            valueInputEl = document.createElement('textarea');
                            valueInputEl.rows = 3;
                        } else if (selectedType === 'text' || selectedType === 'number') {
                            valueInputEl = document.createElement('input');
                            if (selectedType === 'number') {
                                valueInputEl.type = 'text';
                                valueInputEl.setAttribute('inputmode', 'decimal');
                                valueInputEl.setAttribute('pattern', '[0-9]*');
                                valueInputEl.dataset.xType = 'number';
                            } else {
                                valueInputEl.type = 'text';
                            }
                        } else if (selectedType === 'date') {
                            valueInputEl = document.createElement('input');
                            valueInputEl.type = 'date';
                        } else if (selectedType === 'time') {
                            valueInputEl = document.createElement('input');
                            valueInputEl.type = 'time';
                        } else if (selectedType === 'timestamp') {
                            valueInputEl = document.createElement('input');
                            valueInputEl.type = 'datetime-local';
                        } else {
                            valueInputEl = document.createElement('input');
                            valueInputEl.type = 'text';
                        }
                        valueInputEl.className = 'variable-input';
                        valueInputEl.name = effectiveField;
                        // 旧型の入力欄の値を保存形式に一旦変換し、新しい型の入力用値に変換
                        try {
                            const sourceType = inlinePopover.__pendingType || selectedType;
                            const savedVal = fromInputToStorageByType(oldVal || '', sourceType);
                            valueInputEl.value = toInputValueByType(savedVal, selectedType);
                        } catch(_) { valueInputEl.value = oldVal || ''; }
                        popoverValueContainer.appendChild(valueInputEl);
                        try { if (valueInputEl.dataset && valueInputEl.dataset.xType === 'number') attachNumberInputSanitizers(valueInputEl); } catch(_) {}
                        if (valueInputEl instanceof HTMLTextAreaElement) {
                            applySavedTextareaHeight(effectiveField, valueInputEl);
                            bindTextareaHeightSync(effectiveField, valueInputEl);
                        } else {
                            bindPopoverAutoLayoutOnTextarea(valueInputEl);
                        }
                        // 幅同期は無効化（ユーザー要望により撤回）
                        try {
                            const ua = (navigator.userAgent || '').toLowerCase();
                            const isIPhone = /iphone/.test(ua) && !/ipad/.test(ua);
                            const t = (valueInputEl && valueInputEl.type) || '';
                            const isDateLikeInput = (t === 'date' || t === 'time' || t === 'datetime-local');
                            if (!(isIPhone && isDateLikeInput)) { valueInputEl.focus(); }
                        } catch(_) {}
                        // タイプに応じて「今」ボタンの表示/非表示とデータ属性を更新
                    try {
                        const isDateLike = selectedType === 'date' || selectedType === 'time' || selectedType === 'timestamp';
                        if (popoverNow) {
                            popoverNow.style.display = isDateLike ? 'inline-flex' : 'none';
                            popoverNow.dataset.field = effectiveField;
                            popoverNow.dataset.type = selectedType;
                        }
                        if (popoverClear) {
                            const hasValue = isDateLike && String(((valueInputEl && valueInputEl.value) || '')).trim() !== '';
                            popoverClear.style.display = hasValue ? 'inline-flex' : 'none';
                            popoverClear.dataset.field = effectiveField;
                            popoverClear.dataset.type = selectedType;
                        }
                    } catch(_) {}
                        // ヘッダーのタイプバッジも更新（次回も併記）
                        try {
                            const badge = document.getElementById('popoverTypeBadge');
                            if (badge) {
                                const label = getTypeLabel(selectedType);
                                const persistVal = (typeof inlinePopover.__pendingPersist === 'boolean') ? inlinePopover.__pendingPersist : !!(settingsPopoverPersist && settingsPopoverPersist.checked);
                                badge.textContent = persistVal ? `タイプ: ${label}・次回も` : `タイプ: ${label}`;
                            }
                        } catch(_) {}
                    }
                }
            });
            // 「次回も」変更で即時反映
                if (settingsPopoverPersist) settingsPopoverPersist.addEventListener('change', () => {
                const fieldName = inlinePopover ? (inlinePopover.dataset.field || '') : '';
                let effectiveField = fieldName;
                // titleは使わない
                if (!effectiveField) { closeSmallSettingsPopover(); return; }
                    const newVal = !!settingsPopoverPersist.checked;
                    if (popoverPersist) popoverPersist.checked = newVal;
                    try { inlinePopover.__pendingPersist = newVal; } catch(_) {}
                    // バッジ更新（pendingType 優先）
                    try {
                        const typeKey = (typeof inlinePopover.__pendingType === 'string' && inlinePopover.__pendingType) ? inlinePopover.__pendingType : ((persistedDefaults.types && persistedDefaults.types[effectiveField]) || (popoverType && popoverType.value) || 'multiline');
                        const badge = document.getElementById('popoverTypeBadge');
                        if (badge) {
                            const label = getTypeLabel(typeKey);
                            badge.textContent = newVal ? `タイプ: ${label}・次回も` : `タイプ: ${label}`;
                        }
                    } catch(_) {}
                    // 永続保存は[反映]押下時のみ
            });

            // ページ全体クリックで設定ポップオーバーを閉じる（ただし親カードは閉じない）
            document.addEventListener('click', (e) => {
                if (!settingsPopover || settingsPopover.style.display !== 'block') return;
                const inside = e.target.closest && e.target.closest('#variableSettingsPopover, #popoverGearBtn, .var-gear-btn');
                if (inside) return;
                // 親カードは閉じない（ここで closeInlineVariablePopover は呼ばない）
                closeSmallSettingsPopover();
            }, true);

            // 親カードのX（閉じる）操作で設定吹き出しも閉じる
            (function bindParentCloseToSettingsClose(){
                try {
                    const modalHeaderClose = document.getElementById('cancelButton');
                    if (modalHeaderClose) {
                        modalHeaderClose.addEventListener('click', () => { try { closeSmallSettingsPopover(); } catch(_) {} });
                    }
                } catch(_) {}
            })();

            function positionInlinePopover() {
                if (!inlinePopover) return;
                if (inlinePopover.classList && inlinePopover.classList.contains('fullscreen')) return;
                // iOS系端末で入力フォーカス中は、ソフトウェアキーボード表示に伴う
                // visualViewportの変化で位置が動かないよう再配置を抑止する
                try {
                    const ua = (navigator.userAgent || '').toLowerCase();
                    const isIOS = /(iphone|ipad|ipod)/.test(ua) || (ua.indexOf('macintosh') !== -1 && (navigator.maxTouchPoints || 0) > 1);
                    const hasFocusInside = inlinePopover && inlinePopover.contains(document.activeElement);
                    if (isIOS && hasFocusInside) {
                        return;
                    }
                } catch(_) {}
                const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                const vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
            const padding = 8;
            const maxWidth = Math.floor(vw * 0.92);
            // 横幅はCSSで固定相当。ここでは上限のみ再設定
            inlinePopover.style.maxWidth = Math.min(520, maxWidth) + 'px';
            const width = Math.min(520, maxWidth);
                if (inlinePopover.__lockPosition && inlinePopover.__lockedTop != null && inlinePopover.__lockedLeft != null) {
                    const estimatedHeight = Math.max(220, inlinePopover.getBoundingClientRect().height || 0);
                    let top = inlinePopover.__lockedTop;
                    let left = inlinePopover.__lockedLeft;
                    // ビューポート内にクランプ
                    if (left + width + padding > vw) left = vw - width - padding;
                    if (left < padding) left = padding;
                    if (top + estimatedHeight + padding > vh) top = vh - estimatedHeight - padding;
                    if (top < padding) top = padding;
                    // コンテナ(#original-text)内にクランプ
                    try {
                        const containerEl = inlinePopover && inlinePopover.__anchorEl ? inlinePopover.__anchorEl.closest('#original-text') : null;
                        if (containerEl) {
                            const cr = containerEl.getBoundingClientRect();
                            const minLeft = Math.max(padding, cr.left + padding);
                            const maxLeft = Math.min(vw - width - padding, cr.right - width - padding);
                            const minTop = Math.max(padding, cr.top + padding);
                            const maxTop = Math.min(vh - estimatedHeight - padding, cr.bottom - estimatedHeight - padding);
                            if (left > maxLeft) left = maxLeft;
                            if (left < minLeft) left = minLeft;
                            if (top > maxTop) top = maxTop;
                            if (top < minTop) top = minTop;
                        }
                    } catch(_) {}
                    inlinePopover.style.top = Math.round(top) + 'px';
                    inlinePopover.style.left = Math.round(left) + 'px';
                    return;
                }
                // カードウインドウ時は画面中央に配置
                const popRect = inlinePopover.getBoundingClientRect();
                const popH = Math.max(220, popRect.height || 0);
                let top = Math.round((vh - popH) / 2);
                let left = Math.round((vw - width) / 2);
                // まずはビューポート内にクランプ
                if (left + width + padding > vw) left = vw - width - padding;
                if (left < padding) left = padding;
                if (top + popH + padding > vh) top = vh - popH - padding;
                if (top < padding) top = padding;
                inlinePopover.style.top = Math.round(top) + 'px';
                inlinePopover.style.left = Math.round(left) + 'px';
            }
            function closeInlineVariablePopover() {
                if (!inlinePopover) return;
                // スコープの一括解除
                try { inlinePopover.__scope && inlinePopover.__scope.cleanup && inlinePopover.__scope.cleanup(); } catch(_) {}
                // オーバーレイを非表示
                const overlay = document.getElementById('variablePopoverOverlay');
                if (overlay) overlay.style.display = 'none';
                
                inlinePopover.style.display = 'none';
                try { inlinePopover.classList.remove('fullscreen'); } catch(_) {}
                inlinePopover.dataset.field = '';
                inlinePopover.__anchorEl = null;
                inlinePopover.__lockedTop = null;
                inlinePopover.__lockedLeft = null;
                inlinePopover.__lockPosition = false;
                // フッターの一時的なボタン表示/ハンドラを元に戻す
                try {
                    const footerCancel = document.getElementById('footerCancelButton');
                    const footerOk = document.getElementById('footerQueryButton');
                    const footerEditCancel = document.getElementById('footerEditCancel');
                    const footerEditSave = document.getElementById('footerEditSave');
                    const changeNav = document.querySelector('.footer .change-nav');
                    if (footerEditCancel) footerEditCancel.style.display = 'none';
                    if (footerEditSave) {
                        if (footerEditSave.dataset && footerEditSave.dataset.prevLabel) {
                            footerEditSave.textContent = footerEditSave.dataset.prevLabel;
                            try { delete footerEditSave.dataset.prevLabel; } catch(_) {}
                        }
                        footerEditSave.style.display = 'none';
                    }
                    if (footerCancel) footerCancel.style.display = '';
                    if (footerOk) footerOk.style.display = '';
                    try { window.restoreChangeNavDisplay && window.restoreChangeNavDisplay(); } catch(_) {}
                    // ハンドラ解除（単一登録バケットから）
                    try { if (footerEditCancel) unbindSingleEvent(footerEditCancel, 'click', 'inlinePopover:cancel'); } catch(_) {}
                    try { if (footerEditSave) unbindSingleEvent(footerEditSave, 'click', 'inlinePopover:save'); } catch(_) {}
                    try { delete inlinePopover.__footerCancelHandler; } catch(_) {}
                    try { delete inlinePopover.__footerSaveHandler; } catch(_) {}
                } catch(_) {}
                // クリック元のハイライトを遅延解除（iPhone/全面カードで見やすく）
                try {
                    const hs = inlinePopover.__highlightEls || (inlinePopover.__highlightEl ? [inlinePopover.__highlightEl] : []);
                    const delayMs = 1000; // 端末問わず1秒残す
                    (hs || []).forEach(h => {
                        if (!h || !h.classList) return;
                        // 念のためハイライトを付与（編集中表示が失われている場合の保険）
                        try { if (!h.classList.contains('editing')) h.classList.add('editing'); } catch(_) {}
                        try { h.classList.add('flash-highlight'); } catch(_) {}
                        setTimeout(() => {
                            try { h.classList.remove('flash-highlight'); } catch(_) {}
                            try { h.classList.remove('editing'); } catch(_) {}
                            try { h.removeAttribute('aria-current'); } catch(_) {}
                        }, delayMs);
                    });
                } catch(_) {}
                try { delete inlinePopover.__highlightEls; } catch(_) {}
                try { delete inlinePopover.__highlightEl; } catch(_) {}
                // 親を閉じるときは設定吹き出しも閉じる
                try { if (typeof closeSmallSettingsPopover === 'function') closeSmallSettingsPopover(); } catch(_) {}
                // 吹き出しを閉じたらフッターの表示状態を更新
                updateFooterHiddenState();
                // 一時変更（pendingType/pendingPersist）は保持（閉じるだけでは破棄しない）
                // ヘッダーの歯車/X 表示を元に戻す（歯車を表示、Xを隠す）
                try {
                    const gearBtn = document.getElementById('popoverGearBtn');
                    const headerClose = document.getElementById('popoverHeaderCloseX');
                    if (gearBtn) gearBtn.style.display = 'inline-flex';
                    if (headerClose) headerClose.style.display = 'none';
                } catch(_) {}
            }
            if (popoverType) {
                // 親UI側のタイプUIは非表示のため、内部同期だけ維持（設定吹き出し側の変更に追従）
                popoverType.addEventListener('change', () => {
                    const fieldName = inlinePopover ? (inlinePopover.dataset.field || '') : '';
                    if (!fieldName) return;
                    const oldEl = popoverValueContainer.querySelector('.variable-input');
                    const oldVal = oldEl ? oldEl.value : '';
                    // 再生成（見た目だけ。保存は[反映]時）
                    clearChildren(popoverValueContainer);
                    let valueInputEl = null;
                    const selectedType = popoverType.value;
                    if (selectedType === 'multiline') {
                        valueInputEl = document.createElement('textarea');
                        valueInputEl.rows = 3;
                    } else if (selectedType === 'text' || selectedType === 'number') {
                        valueInputEl = document.createElement('input');
                        if (selectedType === 'number') {
                            valueInputEl.type = 'text';
                            valueInputEl.setAttribute('inputmode', 'decimal');
                            valueInputEl.setAttribute('pattern', '[0-9]*');
                            valueInputEl.dataset.xType = 'number';
                        } else {
                            valueInputEl.type = 'text';
                        }
                    } else if (selectedType === 'date') {
                        valueInputEl = document.createElement('input');
                        valueInputEl.type = 'date';
                    } else if (selectedType === 'time') {
                        valueInputEl = document.createElement('input');
                        valueInputEl.type = 'time';
                    } else if (selectedType === 'timestamp') {
                        valueInputEl = document.createElement('input');
                        valueInputEl.type = 'datetime-local';
                    } else {
                        valueInputEl = document.createElement('input');
                        valueInputEl.type = 'text';
                    }
                    valueInputEl.className = 'variable-input';
                    valueInputEl.name = fieldName;
                    // 既存値を型に合わせて整形
                    try {
                        let v = oldVal || '';
                        if (selectedType === 'date') v = formatDateISO(v);
                        else if (selectedType === 'timestamp') v = formatDateTimeLocalISO(v);
                        else if (selectedType === 'time') {
                            const m = String(v).match(/^(\d{1,2}):(\d{2})/);
                            v = m ? `${m[1].padStart(2,'0')}:${m[2]}` : '';
                        }
                        valueInputEl.value = v;
                    } catch(_) { valueInputEl.value = oldVal || ''; }
                    popoverValueContainer.appendChild(valueInputEl);
                    try { if (valueInputEl.dataset && valueInputEl.dataset.xType === 'number') attachNumberInputSanitizers(valueInputEl); } catch(_){}
                    applySavedTextareaHeight(fieldName, valueInputEl);
                    bindTextareaHeightSync(fieldName, valueInputEl);
                    // 型変更は即保存しない（[反映]時に適用）。pendingに保持
                    try { inlinePopover.__pendingType = selectedType; } catch(_) {}
                    try {
                        const ua = (navigator.userAgent || '').toLowerCase();
                        const isIPhone = /iphone/.test(ua) && !/ipad/.test(ua);
                        const t = (valueInputEl && valueInputEl.type) || '';
                        const isDateLikeInput = (t === 'date' || t === 'time' || t === 'datetime-local');
                        if (!(isIPhone && isDateLikeInput)) { valueInputEl.focus(); }
                    } catch(_) {}
                    // 型変更に応じて「今」ボタンの表示も更新
                    try {
                        const isDateLike = selectedType === 'date' || selectedType === 'time' || selectedType === 'timestamp';
                        if (popoverNow) {
                            popoverNow.style.display = isDateLike ? 'inline-flex' : 'none';
                            popoverNow.dataset.field = fieldName;
                            popoverNow.dataset.type = selectedType;
                        }
                        if (popoverClear) {
                            const hasValue = isDateLike && String(((valueInputEl && valueInputEl.value) || '')).trim() !== '';
                            popoverClear.style.display = hasValue ? 'inline-flex' : 'none';
                            popoverClear.dataset.field = fieldName;
                            popoverClear.dataset.type = selectedType;
                        }
                    } catch(_) {}
                });
            }
            // ポップオーバーの「今」ボタン挙動
            if (popoverNow) {
                popoverNow.addEventListener('click', () => {
                    if (!inlinePopover) return;
                    const fieldName = popoverNow.dataset.field || '';
                    const currentType = popoverNow.dataset.type || '';
                    if (!fieldName || !currentType) return;
                    // ポップオーバー内の入力要素
                    const valueEl = popoverValueContainer.querySelector('.variable-input');
                    if (!valueEl) return;
                    if (currentType === 'date' || currentType === 'time' || currentType === 'timestamp') {
                        const nowDisplay = nowValueByType(currentType);
                        valueEl.value = toInputValueByType(nowDisplay, currentType);
                        // 入力が入ったためクリアボタンを表示
                        try {
                            if (popoverClear) popoverClear.style.display = 'inline-flex';
                        } catch(_) {}
                        // バッジのタイプ表記を維持
                        try {
                            const badge = document.getElementById('popoverTypeBadge');
                            if (badge) {
                                const pendingPersist = (inlinePopover && typeof inlinePopover.__pendingPersist === 'boolean')
                                    ? inlinePopover.__pendingPersist
                                    : !!(popoverPersist && popoverPersist.checked);
                                const lbl = getTypeLabel(currentType);
                                badge.textContent = pendingPersist ? `タイプ: ${lbl}・次回も` : `タイプ: ${lbl}`;
                            }
                        } catch(_) {}
                    }

                    // 「反映」ボタンと同じ処理を実行
                    // 型を保存
                    const typeToSave = (inlinePopover && inlinePopover.__pendingType) ? inlinePopover.__pendingType : (popoverType && popoverType.value);
                    if (typeToSave) persistedDefaults.types = { ...persistedDefaults.types, [fieldName]: typeToSave };
                    // 永続チェック反映（pending優先で保存）
                    const persistToSave = (inlinePopover && typeof inlinePopover.__pendingPersist === 'boolean')
                        ? inlinePopover.__pendingPersist
                        : !!(popoverPersist && popoverPersist.checked);
                    // 次回も を永続へ保存
                    persistedDefaults.persist = { ...(persistedDefaults.persist || {}), [fieldName]: persistToSave };
                    
                    // 保存後はpending値とoriginal値をクリア（次回正しく初期化するため）
                    try { delete inlinePopover.__pendingType; } catch(_) {}
                    try { delete inlinePopover.__pendingPersist; } catch(_) {}
                    try { delete inlinePopover.__originalType; } catch(_) {}
                    try { delete inlinePopover.__originalPersist; } catch(_) {}
                    // メモ保存
                    if (popoverMemo) {
                        const memoText = popoverMemo.value || '';
                        persistedDefaults.memos = { ...(persistedDefaults.memos || {}), [fieldName]: memoText };
                        updateMemoToggleVisibility();
                    }
                    // 値を型に合わせて正規化（保存形式に一元化）
                    const rawVal = valueEl ? valueEl.value : '';
                    const selType = (inlinePopover && inlinePopover.__pendingType) ? inlinePopover.__pendingType : ((popoverType && popoverType.value) || (persistedDefaults.types[fieldName] || 'multiline'));
                    const normalizedVal = fromInputToStorageByType(rawVal, selType);

                    // 値反映（フォームが非表示でも内部DOMと既定値に反映）
                    // 直接既定値へ反映
                    persistedDefaults.values = { ...persistedDefaults.values, [fieldName]: normalizedVal };
                    lastSavedFieldName = fieldName;
                    // 保存後はドラフトをクリア
                    try { delete inProgressDrafts[fieldName]; } catch(_) {}
                    // フォーム再生成・表示更新
                    synchronizeForm();
                    updateDisplayText();
                    // 今ボタン時も一時ハイライト（反映と同様に）
                    try {
                        const container = document.getElementById('display-text');
                        if (container && fieldName) {
                            const list = container.querySelectorAll(`.clickable-placeholder[data-field="${CSS.escape(fieldName)}"]`);
                            list.forEach(newEl => {
                                if (!newEl || !newEl.classList) return;
                                newEl.classList.add('editing');
                                newEl.classList.add('flash-highlight');
                                try { newEl.setAttribute('aria-current', 'true'); } catch(_) {}
                                setTimeout(() => {
                                    try { newEl.classList.remove('flash-highlight'); } catch(_) {}
                                    try { newEl.classList.remove('editing'); } catch(_) {}
                                    try { newEl.removeAttribute('aria-current'); } catch(_) {}
                                }, 1000);
                            });
                        }
                    } catch(_) {}
                    closeInlineVariablePopover();
                });
            }

            // クリアボタン挙動（date/time/timestamp のときのみ表示）
            if (popoverClear) {
                popoverClear.addEventListener('click', () => {
                    if (!inlinePopover) return;
                    const fieldName = popoverClear.dataset.field || '';
                    const currentType = popoverClear.dataset.type || '';
                    if (!fieldName || !currentType) return;
                    const valueEl = popoverValueContainer.querySelector('.variable-input');
                    if (!valueEl) return;
                    // 入力欄クリア
                    valueEl.value = '';
                    // クリアしたのでボタンを非表示
                    try { popoverClear.style.display = 'none'; } catch(_) {}
                    // 保存も空に（反映と同等の挙動）
                    // 型は pendingType があればそれを用いて空文字保存
                    const selType = (inlinePopover && inlinePopover.__pendingType) ? inlinePopover.__pendingType : currentType;
                    const normalizedVal = fromInputToStorageByType('', selType);
                    persistedDefaults.values = { ...persistedDefaults.values, [fieldName]: normalizedVal };
                    lastSavedFieldName = fieldName;
                    // 表示更新（フォームは値入力だけなので再生成不要）
                    updateDisplayText();
                });
            }

            // 数字入力用の整形ハンドラ（パネル非表示時のポップオーバー内）
            function attachNumberInputSanitizers(target) {
                if (!(target instanceof HTMLInputElement)) return;
                target.addEventListener('compositionstart', () => { isComposingNumberInput = true; });
                target.addEventListener('compositionend', () => {
                    isComposingNumberInput = false;
                    let v = toHalfWidthNumeric(target.value).replace(/[^+\-\.\d]/g, '');
                    const parts = v.split('.');
                    if (parts.length > 2) v = parts.shift() + '.' + parts.join('');
                    v = v.replace(/^([+\-])+/,'$1');
                    if (/^[+-]?\d+\.$/.test(v)) v = v.slice(0, -1);
                    target.value = /\d/.test(v) ? v : '';
                });
                target.addEventListener('input', () => {
                    if (isComposingNumberInput) return;
                    const before = target.value;
                    let after = toHalfWidthNumeric(before).replace(/[^+\-\.\d]/g, '');
                    const parts = after.split('.');
                    if (parts.length > 2) after = parts.shift() + '.' + parts.join('');
                    after = after.replace(/^([+\-])+/,'$1');
                    if (before !== after) target.value = after;
                });
                target.addEventListener('blur', () => {
                    if (isComposingNumberInput) return;
                    let v = toHalfWidthNumeric(target.value).replace(/[^+\-\.\d]/g, '');
                    if (/^[+-]?\d+\.$/.test(v)) v = v.slice(0, -1);
                    target.value = /\d/.test(v) ? v : '';
                });
            }
                if (popoverCancel) popoverCancel.addEventListener('click', () => {
                    if (!inlinePopover) return;
                    const fieldName = inlinePopover.dataset.field || '';
                    // pendingの変更を破棄し、内部セレクトをオリジナルへ戻す
                    try {
                        const originalType = inlinePopover.__originalType;
                        const originalPersist = inlinePopover.__originalPersist;
                        if (typeof originalType === 'string') {
                        
                            if (popoverType) popoverType.value = originalType;
                        }
                        if (typeof originalPersist === 'boolean') {
                        
                            if (popoverPersist) popoverPersist.checked = originalPersist;
                        }
                    } catch(_) {}
                    try { delete inlinePopover.__pendingType; } catch(_) {}
                    try { delete inlinePopover.__pendingPersist; } catch(_) {}
                    try { delete inlinePopover.__originalType; } catch(_) {}
                    try { delete inlinePopover.__originalPersist; } catch(_) {}
                    closeInlineVariablePopover();
                });
            if (popoverSave) popoverSave.addEventListener('click', () => {
                if (!inlinePopover) return;
                const fieldName = inlinePopover.dataset.field || '';
                if (!fieldName) return;
                const valueEl = popoverValueContainer.querySelector('.variable-input');
                // 型を保存
                const typeToSave = (inlinePopover && inlinePopover.__pendingType) ? inlinePopover.__pendingType : (popoverType && popoverType.value);
                if (typeToSave) persistedDefaults.types = { ...persistedDefaults.types, [fieldName]: typeToSave };
                // 永続チェック反映（pending優先で保存）
                const persistToSave = (inlinePopover && typeof inlinePopover.__pendingPersist === 'boolean')
                    ? inlinePopover.__pendingPersist
                    : !!(popoverPersist && popoverPersist.checked);
            // 次回も を永続へ保存
            persistedDefaults.persist = { ...(persistedDefaults.persist || {}), [fieldName]: persistToSave };
                
                // 保存後はpending値とoriginal値をクリア（次回正しく初期化するため）
                try { delete inlinePopover.__pendingType; } catch(_) {}
                try { delete inlinePopover.__pendingPersist; } catch(_) {}
                try { delete inlinePopover.__originalType; } catch(_) {}
                try { delete inlinePopover.__originalPersist; } catch(_) {}
                // メモ保存
                if (popoverMemo) {
                    const memoText = popoverMemo.value || '';
                    persistedDefaults.memos = { ...(persistedDefaults.memos || {}), [fieldName]: memoText };
                    updateMemoToggleVisibility();
                }
                // 値を型に合わせて正規化（保存形式に一元化）
                const rawVal = valueEl ? valueEl.value : '';
                const selType = (inlinePopover && inlinePopover.__pendingType) ? inlinePopover.__pendingType : ((popoverType && popoverType.value) || (persistedDefaults.types[fieldName] || 'multiline'));
                const normalizedVal = fromInputToStorageByType(rawVal, selType);

                // 値反映（フォームが非表示でも内部DOMと既定値に反映）
                // 直接既定値へ反映
                persistedDefaults.values = { ...persistedDefaults.values, [fieldName]: normalizedVal };
                lastSavedFieldName = fieldName;
                // 保存後はドラフトをクリア
                try { delete inProgressDrafts[fieldName]; } catch(_) {}
                // フォーム再生成・表示更新
                synchronizeForm();
                updateDisplayText();
                // 反映後もどの変数だったか分かるよう、新しいプレースホルダを一時的に強調
                try {
                    const container = document.getElementById('display-text');
                    if (container && fieldName) {
                        const list = container.querySelectorAll(`.clickable-placeholder[data-field="${CSS.escape(fieldName)}"]`);
                        list.forEach(newEl => {
                            if (!newEl || !newEl.classList) return;
                            newEl.classList.add('editing');
                            newEl.classList.add('flash-highlight');
                            try { newEl.setAttribute('aria-current', 'true'); } catch(_) {}
                            setTimeout(() => {
                                try { newEl.classList.remove('flash-highlight'); } catch(_) {}
                                try { newEl.classList.remove('editing'); } catch(_) {}
                                try { newEl.removeAttribute('aria-current'); } catch(_) {}
                            }, 1000);
                        });
                    }
                } catch(_) {}
                closeInlineVariablePopover();
            });

            // インラインポップオーバーの外側クリック処理は templateModal のイベントハンドラーに統合
            // Escで閉じる、⌘Enterで[反映]
            document.addEventListener('keydown', (e) => {
                if (e.key === 'Escape') closeInlineVariablePopover();
                // Mac: ⌘+Enter で [反映] 実行（必要なら Ctrl+Enter も対応）
                if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
                    if (!inlinePopover || inlinePopover.style.display !== 'block') return;
                    const isInside = e.target && inlinePopover.contains ? inlinePopover.contains(e.target) : false;
                    if (!isInside) return;
                    try { e.preventDefault(); } catch(_) {}
                    try { if (popoverSave) popoverSave.click(); } catch(_) {}
                }
            });
            // オーバーレイクリックで [キャンセル] と同じ動作を行う（pending を破棄して閉じる）
            const popoverOverlay = document.getElementById('variablePopoverOverlay');
            if (popoverOverlay) {
                popoverOverlay.addEventListener('click', () => {
                    if (!inlinePopover) { closeInlineVariablePopover(); return; }
                    // キャンセルと同じ処理
                    try {
                        const originalType = inlinePopover.__originalType;
                        const originalPersist = inlinePopover.__originalPersist;
                        if (typeof originalType === 'string') {
                            if (popoverType) popoverType.value = originalType;
                        }
                        if (typeof originalPersist === 'boolean') {
                            if (popoverPersist) popoverPersist.checked = originalPersist;
                        }
                    } catch(_) {}
                    try { delete inlinePopover.__pendingType; } catch(_) {}
                    try { delete inlinePopover.__pendingPersist; } catch(_) {}
                    try { delete inlinePopover.__originalType; } catch(_) {}
                    try { delete inlinePopover.__originalPersist; } catch(_) {}
                    closeInlineVariablePopover();
                });
            }

            // 設定吹き出し用オーバーレイクリックで閉じる
            const settingsOverlay = document.getElementById('settingsPopoverOverlay');
            if (settingsOverlay) {
                settingsOverlay.addEventListener('click', () => {
                    closeSmallSettingsPopover();
                });
            }

            // スクロール時の再計算は廃止（スクロール制御を削除）
            window.addEventListener('resize', () => {
                if (!inlinePopover) return;
                if (inlinePopover.style.display !== 'block') return;
                if (!inlinePopover.classList.contains('fullscreen')) {
                    try { positionInlinePopover(); } catch(_) {}
                }
            });
            window.addEventListener('orientationchange', () => {
                if (!inlinePopover) return;
                if (inlinePopover.style.display !== 'block') return;
                if (!inlinePopover.classList.contains('fullscreen')) {
                    try { positionInlinePopover(); } catch(_) {}
                }
            });

            // 内容のモーダルのイベントリスナー
            editButton.addEventListener('click', () => {
                // フレーズ選択カードが開いていれば先に閉じる
                // フレーズモーダルとは独立運用するため、クロス閉鎖はしない
                modalTextarea.value = originalTextarea.value;
                // 可変高さ: flexの残余高を占有させる（fitは解除）
                // 注意: 自動リサイズ無効化のためコメントアウト
                // try { modalTextarea.style.height = 'auto'; } catch(_) {}
                const modalTemplateName = document.getElementById('modalTemplateName');
                if (modalTemplateName) {
                    // 現在のタイトルを初期表示
                    modalTemplateName.value = selectedTemplateMeta.title || '';
                    // デフォルトは編集不可（チェックで有効化）
                    modalTemplateName.disabled = true;
                }
                // モーダルを開いた時点での初期値を保存
                initialValues.modalTextarea = modalTextarea.value;
                initialValues.modalTemplateName = modalTemplateName ? modalTemplateName.value : '';
                
                const modalUpdateTemplateCheckbox = document.getElementById('modalUpdateTemplateCheckbox');
                if (modalUpdateTemplateCheckbox) {
                    modalUpdateTemplateCheckbox.checked = false;
                    // チェックに応じてテンプレ名の編集可否を切り替え
                    modalUpdateTemplateCheckbox.addEventListener('change', () => {
                        const nameInput = document.getElementById('modalTemplateName');
                        if (nameInput) {
                            nameInput.disabled = !modalUpdateTemplateCheckbox.checked;
                            if (modalUpdateTemplateCheckbox.checked) {
                                nameInput.focus();
                                try { const len = nameInput.value.length; nameInput.setSelectionRange(len, len); } catch (_) {}
                            }
                        }
                    });
                }
                openModal(modal);
                try { document.body.classList.add('header-dimmed'); } catch (_) {}
                updateFooterHiddenState();
                // モーダル位置を初期化（中央配置）し、ドラッグ可能に
                try {
                    const mc = modal.querySelector('.modal-content');
                    if (mc) {
                        // 常に全画面表示（高さはCSSのfullscreenに委譲）
                        mc.classList.add('fullscreen');
                        try { mc.style.height = ''; mc.style.maxHeight = ''; } catch(_) {}
                        // スクロール位置は維持（強制移動しない）
                        // テンプレート選択はドラッグ不可: 既定のセンタリングのみ（positionは相対のまま）
                        // 何も変更しないことで CSS の中央配置/スクロール動作を利用
                    }
                } catch (_) {}
                // 既存フッターのボタン表示を編集モード用に切替
                try {
                    const footerCancel = document.getElementById('footerCancelButton');
                    const footerOk = document.getElementById('footerQueryButton');
                    const footerEditCancel = document.getElementById('footerEditCancel');
                    const footerEditSave = document.getElementById('footerEditSave');
                    const changeNav = document.querySelector('.footer .change-nav');
                    if (footerCancel) footerCancel.style.display = 'none';
                    if (footerOk) footerOk.style.display = 'none';
                    if (changeNav) { changeNav.dataset.prevDisplay = changeNav.style.display || ''; changeNav.style.display = 'none'; }
                    if (footerEditCancel) footerEditCancel.style.display = '';
                    if (footerEditSave) footerEditSave.style.display = '';
                } catch(_) {}
                // 表示直後は本文にフォーカス（複数回リトライ）
                const focusCancel = () => {
                    try {
                        // スクロール位置は維持（強制移動しない）
                    } catch(_) {}
                    // 全画面時は本文に直接フォーカスの方が自然
                    try {
                        if (modalTextarea) {
                            modalTextarea.focus({ preventScroll: true });
                            const len = modalTextarea.value.length;
                            try { modalTextarea.setSelectionRange(len, len); } catch(_) {}
                        }
                    } catch(_) {}
                };
                // 即時 + rAF + 遅延フォールバック
                raf(focusCancel);
                try { requestAnimationFrame(focusCancel); } catch(_) {}
                setTimeout(focusCancel, 50);
                setTimeout(focusCancel, 150);
            });
            // モーダルカードをドラッグ可能にする汎用関数（h3がハンドル）
            function makeModalDraggable(overlayEl, cardEl, handleEl) {
                if (!overlayEl || !cardEl) return;
                const handle = handleEl || cardEl;
                const detach = attachDragMove(cardEl, {
                    handleEl: handle,
                    clampToViewport: true,
                    padding: 8,
                    blockSelector: 'input, textarea, select, button, .modal-buttons, .input-with-quick, .variable-input',
                    disallowSelection: true,
                    onStart: () => { try { document.body.classList.add('dragging'); } catch(_) {} },
                    onEnd: () => { try { document.body.classList.remove('dragging'); } catch(_) {} },
                });
                // キャンセル / 保存時に解除（既存フッターの編集ボタンにバインド）
                const cleanup = () => { try { detach(); } catch(_) {} };
                const cancelBtn = document.getElementById('footerEditCancel');
                if (cancelBtn) cancelBtn.addEventListener('click', cleanup, { once: true });
                const saveBtn = document.getElementById('footerEditSave');
                if (saveBtn) saveBtn.addEventListener('click', cleanup, { once: true });
            }

            // 検索ボタン押下で Claris FileMaker に検索を依頼
            if (searchButton) {
                searchButton.addEventListener('click', () => {
                    // ヘッダーのタイトルを一時的に変更
                    try {
                        const titleElement = document.querySelector('.title-section h3');
                        if (titleElement) {
                            // 元のタイトルを保存（まだ保存されていない場合のみ）
                            if (!titleElement.dataset.originalTitle) {
                                titleElement.dataset.originalTitle = titleElement.textContent || '';
                            }
                            titleElement.textContent = 'テンプレート選択中...';
                        }
                    } catch(_) {}
                    // 通常のテンプレート選択モードに戻す
                    try {
                        const modal = document.getElementById('templateModal');
                        if (modal) {
                            delete modal.dataset.mode;
                            updateTemplateHeaderForMode();
                        }
                        const titleEl = document.getElementById('templateModalTitle');
                        if (titleEl) {
                            titleEl.textContent = 'テンプレートを選択';
                            // 検索中で非表示になっている場合は表示状態に戻す
                            titleEl.style.display = '';
                        }
                        // 検索行を閉じる（検索中の状態をリセット）
                        try {
                            const templateSearchRow = document.getElementById('templateSearchRow');
                            const templateSearchInput = document.getElementById('templateSearch');
                            const templateSearchToggle = document.getElementById('templateSearchToggle');
                            const templateSearchClose = document.getElementById('templateSearchClose');
                            const templateNewButton = document.getElementById('templateNewButton');
                            if (templateSearchRow) templateSearchRow.style.display = 'none';
                            if (templateSearchInput) templateSearchInput.value = '';
                            if (templateSearchToggle) templateSearchToggle.style.display = '';
                            if (templateSearchClose) templateSearchClose.style.display = 'none';
                            // [+]ボタンも表示状態に戻す
                            if (templateNewButton) templateNewButton.style.display = '';
                        } catch(_) {}
                    } catch(_) {}
                    // ヘッダーの表示状態をリセット
                    try { if (typeof resetHeaderView === 'function') resetHeaderView(); } catch(_) {}
                    // 以前の状態が残っていても検索できるよう解除
                    isSearching = false;
                    // 初期検索を実行（毎回リモート）: モーダル表示・フォーカスは performTemplateSearch 側で一元管理
                    performTemplateSearch('', 1, true);
                });
            }
            // 新規テンプレート作成の表示切替
            function showTemplateCreateForm() {
                if (!templateCreateSection) return;
                if (templateListEl) templateListEl.style.display = 'none';
                if (templatePaginationEl) templatePaginationEl.style.display = 'none';
                if (templateSearchRow) templateSearchRow.style.display = 'none';
                try {
                    const templateSearchToggleEl = document.getElementById('templateSearchToggle');
                    const templateSearchCloseEl = document.getElementById('templateSearchClose');
                    if (templateSearchToggleEl) templateSearchToggleEl.style.display = 'none';
                    if (templateSearchCloseEl) templateSearchCloseEl.style.display = 'none';
                } catch(_) {}
                if (templateActionsRow) templateActionsRow.style.display = 'none';
                if (templateNewButton) templateNewButton.style.display = 'none';
                if (templateModalTitle) templateModalTitle.textContent = 'テンプレートを新規作成';
                templateCreateSection.style.display = 'block';
                try { const modalEl = document.getElementById('templateModal'); if (modalEl) { modalEl.dataset.mode = 'create'; updateTemplateHeaderForMode(); } } catch(_) {}
                // 本文を確実にスクロール可能に（ヘッダー/フッターを除いた高さに制限）
                try {
                    const modal = document.getElementById('templateModal');
                    const mc = modal ? modal.querySelector('.modal-content') : null;
                    const headerEl = mc ? mc.querySelector('.modal-header') : null;
                    const bodyEl = mc ? mc.querySelector('.template-modal-body') : null;
                    const contentTextarea = document.getElementById('createTemplateContent');
                    // 擬似フッター廃止に伴い、実フッター高さを使用
                    const footerEl = document.querySelector('.footer');
                    const adjust = () => {
                        if (!mc || !bodyEl) return;
                        const totalH = mc.clientHeight;
                        const hh = headerEl ? headerEl.offsetHeight : 0;
                        const fh = footerEl ? footerEl.offsetHeight : 0;
                        const avail = Math.max(120, totalH - hh - fh - 24);
                        bodyEl.style.maxHeight = avail + 'px';
                        bodyEl.style.overflowY = 'auto';
                        bodyEl.style.webkitOverflowScrolling = 'touch';
                        // フッターに隠れないよう、ボディ下余白をフッター高ぶん確保
                        try { bodyEl.style.paddingBottom = Math.max(0, fh + 100) + 'px'; } catch(_) {}
                        if (contentTextarea) {
                            const nameRow = contentTextarea.closest('.modal-field-row')?.previousElementSibling;
                            const nameRowH = nameRow && nameRow.matches('.modal-field-row') ? nameRow.offsetHeight : 0;
                            const labelH = 24; // ラベル分の目安
                            const innerAvail = Math.max(80, avail - nameRowH - labelH - 32);
                            contentTextarea.style.height = innerAvail + 'px';
                            contentTextarea.style.minHeight = '120px';
                            contentTextarea.style.overflow = 'auto';
                        }
                    };
                    adjust();
                    // リサイズ/回転時も再計算
                    const onResize = () => { try { adjust(); } catch(_) {} };
                    window.addEventListener('resize', onResize);
                    window.addEventListener('orientationchange', onResize);
                    try { if (window.visualViewport) { window.visualViewport.addEventListener('resize', onResize); } } catch(_) {}
                    try { modal.__detachCreateResize = () => { window.removeEventListener('resize', onResize); window.removeEventListener('orientationchange', onResize); try { if (window.visualViewport) { window.visualViewport.removeEventListener('resize', onResize); } } catch(_) {} }; } catch(_) {}
                } catch(_) {}
                // ヘルプ（はてな）を表示（固定DOMを利用）
                try { const help = document.getElementById('syntaxHelpTriggerTemplateEdit'); if (help) help.style.display = ''; } catch(_) {}
                // 擬似フッターは廃止：既存フッターを使用
                const templateCreateCancelBottom = null;
                const templateCreateSubmitBottom = null;
                const nameEl = document.getElementById('createTemplateName');
                const contentEl = document.getElementById('createTemplateContent');
                if (nameEl) nameEl.value = '';
                if (contentEl) contentEl.value = '';
                raf(() => { try { nameEl && nameEl.focus(); } catch(_){} });
                // 追い強制（UI競合のフェイルセーフ）
                // デバウンスで1回に集約
                (window.__enforceFooterDebounced || (window.__enforceFooterDebounced = debounce(() => enforceTemplateCreateFooterState(), 80)))();

                // 既存フッターを作成モードに切替
                try { window.setFooterForTemplateMode && window.setFooterForTemplateMode('create'); } catch(_) {}
                // フッター表示切替後に高さ再計算（フッター高さ反映）: リサイズ発火をスロットル
                ;(window.__throttledResize || (window.__throttledResize = throttle(() => { try { window.dispatchEvent(new Event('resize')); } catch(_) {} }, 120)))();

                // フッターから直接呼べるように作成モード用の関数を登録
                try {
                    const modal = document.getElementById('templateModal');
                    if (modal) {
                        modal.__createCancelFn = () => { try { hideTemplateCreateForm(); } catch(_) {} };
                        modal.__createSubmitFn = () => {
                            try {
                                const nameEl2 = document.getElementById('createTemplateName');
                                const contentEl2 = document.getElementById('createTemplateContent');
                                const name2 = nameEl2 ? String(nameEl2.value || '').trim() : '';
                                const content2 = contentEl2 ? String(contentEl2.value || '').trim() : '';
                                if (!name2 || !content2) { showMessageModal('テンプレート名と内容を入力してください。', '入力が必要です'); return; }
                                const payload = { title: name2, content: content2 };
                                const jsonData = JSON.stringify(payload);
                                fmCall && fmCall('addon_remarks_web_template_create', jsonData, 5);
                                hideTemplateCreateForm();
                            } catch(_) {}
                        };
                        // 初期は保存ボタンを無効化
                        try { const saveBtn = document.getElementById('footerEditSave'); if (saveBtn) saveBtn.disabled = true; } catch(_) {}
                        // 入力監視で有効/無効を切り替え
                        const recomputeCreateValidity = () => {
                            try {
                                const nameVal = String((document.getElementById('createTemplateName') || {}).value || '').trim();
                                const contentVal = String((document.getElementById('createTemplateContent') || {}).value || '').trim();
                                const save = document.getElementById('footerEditSave');
                                if (save) save.disabled = !(nameVal && contentVal);
                            } catch(_) {}
                        };
                        try {
                            const nameEl = document.getElementById('createTemplateName');
                            const contentEl = document.getElementById('createTemplateContent');
                            if (nameEl) nameEl.addEventListener('input', recomputeCreateValidity);
                            if (contentEl) contentEl.addEventListener('input', recomputeCreateValidity);
                            recomputeCreateValidity();
                        } catch(_) {}
                    }
                } catch(_) {}

                // 擬似フッター構築は不要
            }
            function hideTemplateCreateForm() {
                if (!templateCreateSection) return;
                templateCreateSection.style.display = 'none';
                        if (templateListEl) templateListEl.style.display = '';
                        if (templatePaginationEl) templatePaginationEl.style.display = '';
                        if (templateSearchRow) templateSearchRow.style.display = (templateSearchRow.dataset && templateSearchRow.dataset.defaultDisplay) || 'flex';
                try {
                    const templateSearchToggleEl = document.getElementById('templateSearchToggle');
                    const templateSearchCloseEl = document.getElementById('templateSearchClose');
                    if (templateSearchToggleEl) templateSearchToggleEl.style.display = '';
                    if (templateSearchCloseEl) templateSearchCloseEl.style.display = 'none';
                } catch(_) {}
                if (templateActionsRow) templateActionsRow.style.display = (templateActionsRow.dataset && templateActionsRow.dataset.defaultDisplay) || 'flex';
                if (templateModalTitle) templateModalTitle.textContent = 'テンプレートを選択';
                // 選択モードではヘルプ「?」を非表示
                try { const help = document.getElementById('syntaxHelpTriggerTemplateEdit'); if (help) help.style.display = 'none'; } catch(_) {}
                // 本文高さ調整の後始末
                try {
                    const modal = document.getElementById('templateModal');
                    const mc = modal ? modal.querySelector('.modal-content') : null;
                    const bodyEl = mc ? mc.querySelector('.template-modal-body') : null;
                    if (bodyEl) { bodyEl.style.maxHeight = ''; bodyEl.style.overflowY = ''; bodyEl.style.webkitOverflowScrolling = ''; }
                    if (modal && modal.__detachCreateResize) { modal.__detachCreateResize(); modal.__detachCreateResize = null; }
                } catch(_) {}
                // 擬似フッター復元は不要（既存フッターで閉じるを提供）
                try { const modalEl = document.getElementById('templateModal'); if (modalEl) delete modalEl.dataset.mode; } catch(_) {}
                // 画面幅の変動（キーボード表示/回転）等でズレた配置を復元
                try { relocateTemplateNewButton(); } catch(_) {}
                focusSearchAndUpdateUI();
                if (templateNewButton) templateNewButton.style.display = '';
            }

            // 新規作成ボタン
            // 新規作成ボタン: クリックの取りこぼし防止（イベントデリゲーション + フェイルセーフ）
            (function bindNewButtonRobustly(){
                const triggerShow = () => {
                    try { showTemplateCreateForm(); } catch(_) {}
                    // フェイルセーフ: 次フレームで強制表示状態を再適用
                    requestAnimationFrame(() => {
                        try {
                            const modalEl = document.getElementById('templateModal');
                            if (modalEl) { modalEl.dataset.mode = 'create'; updateTemplateHeaderForMode(); }
                            const cancelBottom = document.getElementById('templateCreateCancelBottom');
                            const submitBottom = document.getElementById('templateCreateSubmitBottom');
                            const closeBtn = document.getElementById('templateCancelButton');
                            const titleEl = document.getElementById('templateModalTitle');
                            if (titleEl) titleEl.textContent = 'テンプレートを新規作成';
                            if (cancelBottom) cancelBottom.style.display = '';
                            if (submitBottom) submitBottom.style.display = '';
                            if (closeBtn) closeBtn.style.display = 'none';
                            const createSection = document.getElementById('templateCreateSection');
                            if (createSection) createSection.style.display = 'block';
                        } catch(_) {}
                    });
                };
                if (templateNewButton) {
                    templateNewButton.addEventListener('click', triggerShow);
                }
                try {
                    const actionsRow = document.getElementById('templateActionsRow');
                    if (actionsRow) {
                        actionsRow.addEventListener('click', (e) => {
                            const t = e.target;
                            const btn = (t && (t.closest ? t.closest('#templateNewButton') : null)) || null;
                            if (btn) {
                                e.preventDefault();
                                triggerShow();
                            }
                        });
                    }
                } catch(_) {}
            })();

            if (templateCreateCancel) {
                templateCreateCancel.addEventListener('click', () => {
                    hideTemplateCreateForm();
                });
            }
            // 擬似フッターのキャンセルは廃止

            if (templateCreateSubmit) {
                templateCreateSubmit.addEventListener('click', () => {
                    const nameEl = document.getElementById('createTemplateName');
                    const contentEl = document.getElementById('createTemplateContent');
                    const name = nameEl ? String(nameEl.value || '').trim() : '';
                    const content = contentEl ? String(contentEl.value || '').trim() : '';
                    if (!name) {
                        showMessageModal('テンプレート名を入力してください。', '入力が必要です');
                        return;
                    }
                    const payload = { title: name, content: content };
                    const jsonData = JSON.stringify(payload);
                    fmCall && fmCall('addon_remarks_web_template_create', jsonData, 5);
                    // 送信後は作成フォームを閉じ、検索結果画面へ戻る
                    hideTemplateCreateForm();
                });
            }
            // 擬似フッターの登録は廃止
            // 変数設定モーダル操作
            window.openVarSettingsModal = function(fieldName) {
                if (!varSettingsModal || !fieldName) return;
                // 現在の値をUIへ反映
                const varSettingsTitle = document.getElementById('varSettingsModalTitle');
                if (varSettingsTitle) varSettingsTitle.textContent = `${fieldName}`;
                if (varSettingsType) varSettingsType.value = (persistedDefaults.types[fieldName] || 'multiline');
                if (varSettingsPersist) varSettingsPersist.checked = !!persistedDefaults.persist[fieldName];
                if (varSettingsMemo) varSettingsMemo.value = (persistedDefaults.memos && persistedDefaults.memos[fieldName]) || '';

                // 型に応じて「今」ボタンの行を表示/非表示
                try {
                    const quickRow = document.getElementById('varSettingsQuickRow');
                    const nowBtn = document.getElementById('varSettingsNow');
                    const currentType = varSettingsType ? varSettingsType.value : 'multiline';
                    const isDateLike = currentType === 'date' || currentType === 'time' || currentType === 'timestamp';
                    if (quickRow) quickRow.style.display = isDateLike ? 'flex' : 'none';
                    if (nowBtn) {
                        nowBtn.dataset.field = fieldName;
                        nowBtn.dataset.type = currentType;
                    }
                } catch(_) {}

                openModal(varSettingsModal);
                updateFooterHiddenState();
                try {
                    if (varSettingsModal.__detachStandardClose) { varSettingsModal.__detachStandardClose(); }
                    varSettingsModal.__detachStandardClose = attachStandardModalClose(varSettingsModal, () => {
                        try { closeModal(varSettingsModal); updateFooterHiddenState(); } catch(_) {}
                    });
                } catch(_) {}
                // 中央配置し、ドラッグ可能に
                try {
                    const mc = varSettingsModal.querySelector('.modal-content');
                    if (mc) {
                        // iPhone/小画面では全面化
                        const ua = (navigator.userAgent || '').toLowerCase();
                        const isIPhone = /iphone/.test(ua) && !/ipad/.test(ua);
                        const vwNow = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                        const isSmallViewport = vwNow <= 480;
                        if (isIPhone || isSmallViewport) {
                            mc.classList.add('fullscreen');
                        } else {
                            mc.classList.remove('fullscreen');
                        }
                        const rect = mc.getBoundingClientRect();
                        mc.style.margin = '0';
                        mc.style.position = 'absolute';
                        mc.style.left = `${Math.max(16, Math.floor((window.innerWidth - rect.width) / 2))}px`;
                        mc.style.top = `${Math.max(16, Math.floor((window.innerHeight - rect.height) / 2))}px`;
                        mc.style.right = 'auto';
                        mc.style.bottom = 'auto';
                        // フルスクリーン時はドラッグ無効
                        if (!mc.classList.contains('fullscreen')) {
                            makeModalDraggable(varSettingsModal, mc);
                        }
                    }
                } catch (_) {}
                raf(() => { try { varSettingsType && varSettingsType.focus(); } catch (_) {} });
            }

            if (varSettingsCancel && varSettingsModal) {
                varSettingsCancel.addEventListener('click', () => {
                    closeModal(varSettingsModal);
                    updateFooterHiddenState();
                    try { if (varSettingsModal.__detachStandardClose) { varSettingsModal.__detachStandardClose(); varSettingsModal.__detachStandardClose = null; } } catch(_) {}
                });
            }
            if (varSettingsSave && varSettingsModal) {
                varSettingsSave.addEventListener('click', () => {
                    const titleEl = document.getElementById('varSettingsModalTitle');
                    const fieldName = titleEl ? String(titleEl.textContent || '').replace(/^.* - /, '') : '';
                    if (!fieldName) return;
                    // 型反映
                    if (varSettingsType) {
                        persistedDefaults.types = { ...persistedDefaults.types, [fieldName]: varSettingsType.value };
                    }
                    // 永続チェック反映
                    // 永続フラグを直接保存
                    persistedDefaults.persist = { ...(persistedDefaults.persist || {}), [fieldName]: !!(varSettingsPersist && varSettingsPersist.checked) };
                    // メモ保存
                    if (varSettingsMemo) {
                        const memoText = varSettingsMemo.value || '';
                        persistedDefaults.memos = { ...(persistedDefaults.memos || {}), [fieldName]: memoText };
                        updateMemoToggleVisibility();
                    }
                    // フォーム再生成・表示更新
                    synchronizeForm();
                    updateDisplayText();

                    closeModal(varSettingsModal);
                    updateFooterHiddenState();
                });
            }

            // 型変更時にも「今」ボタン行の表示を更新
            if (varSettingsType) {
                varSettingsType.addEventListener('change', () => {
                    try {
                        const quickRow = document.getElementById('varSettingsQuickRow');
                        const nowBtn = document.getElementById('varSettingsNow');
                        const titleEl = document.getElementById('varSettingsModalTitle');
                        const fieldName = titleEl ? String(titleEl.textContent || '') : '';
                        const currentType = varSettingsType.value;
                        const isDateLike = currentType === 'date' || currentType === 'time' || currentType === 'timestamp';
                        if (quickRow) quickRow.style.display = isDateLike ? 'flex' : 'none';
                        if (nowBtn) {
                            nowBtn.dataset.field = fieldName;
                            nowBtn.dataset.type = currentType;
                        }
                    } catch(_) {}
                });
            }
            // 「今」ボタンの挙動: 型に応じてフォームの値を現在時刻等で更新
            const varSettingsNowBtn = document.getElementById('varSettingsNow');
            if (varSettingsNowBtn) {
                varSettingsNowBtn.addEventListener('click', () => {
                    const fieldName = varSettingsNowBtn.dataset.field || '';
                    const currentType = varSettingsNowBtn.dataset.type || '';
                    if (!fieldName || !currentType) return;
                    const container = document.getElementById('form-container');
                    if (!container) return;
                    if (currentType === 'date') {
                        const el = container.querySelector(`input.variable-input[type="date"][name="${CSS.escape(fieldName)}"]`);
                        if (el) {
                            const now = new Date();
                            const y = now.getFullYear();
                            const m = String(now.getMonth() + 1).padStart(2, '0');
                            const d = String(now.getDate()).padStart(2, '0');
                            el.value = `${y}-${m}-${d}`;
                            updateDisplayText();
                        }
                    } else if (currentType === 'time') {
                        const el = container.querySelector(`input.variable-input[type="time"][name="${CSS.escape(fieldName)}"]`);
                        if (el) {
                            const now = new Date();
                            const hh = String(now.getHours()).padStart(2, '0');
                            const mm = String(now.getMinutes()).padStart(2, '0');
                            el.value = `${hh}:${mm}`;
                            try { el.value = toHalfWidthForType(el.value, 'time'); } catch(_) {}
                            updateDisplayText();
                        }
                    } else if (currentType === 'timestamp') {
                        const el = container.querySelector(`input.variable-input[type="datetime-local"][name="${CSS.escape(fieldName)}"]`);
                        if (el) {
                            const now = new Date();
                            const y = String(now.getFullYear());
                            const m = String(now.getMonth() + 1).padStart(2, '0');
                            const d = String(now.getDate()).padStart(2, '0');
                            const hh = String(now.getHours()).padStart(2, '0');
                            const mm = String(now.getMinutes()).padStart(2, '0');
                            el.value = `${y}-${m}-${d}T${hh}:${mm}`;
                            try { el.value = toHalfWidthForType(el.value, 'timestamp'); } catch(_) {}
                            updateDisplayText();
                        }
                    }
                });
            }
            // モーダル外クリックで閉じる
            if (varSettingsModal) {
                varSettingsModal.addEventListener('click', (e) => {
                    if (e.target === varSettingsModal) {
                        closeModal(varSettingsModal);
                        updateFooterHiddenState();
                    }
                });
            }

            cancelButton.addEventListener('click', () => {
                // このハンドラは内容編集モーダルが開いているときのみ有効
                if (!modal || modal.style.display !== 'block') return;
                // モーダル内で変更があるかチェック
                if (hasModalChanges()) {
                    showConfirmModal(
                        '変更が破棄されます。よろしいですか？',
                        () => {
                            // 破棄する場合
                            closeModal(modal);
                            try { document.body.classList.remove('header-dimmed'); } catch (_) {}
                            updateFooterHiddenState();
                            // フッターボタン表示と change-nav を既定に復帰
                            try { restoreFooterToDefaultState(); } catch(_) {}
                        },
                        () => {
                            // 編集に戻る場合（何もしない）
                        },
                        { cancelLabel: 'キャンセル', confirmLabel: '破棄' }
                    );
                } else {
                    closeModal(modal);
                    try { document.body.classList.remove('header-dimmed'); } catch (_) {}
                    updateFooterHiddenState();
                    // フッターボタン表示と change-nav を既定に復帰
                    try { restoreFooterToDefaultState(); } catch(_) {}
                }
            });
            saveButton.addEventListener('click', () => {
                // このハンドラは内容編集モーダルが開いているときのみ有効
                if (!modal || modal.style.display !== 'block') return;
                const previousInstructions = originalTextarea.value;
                const newInstructions = modalTextarea.value;
                const modalTemplateName = document.getElementById('modalTemplateName');
                const newTemplateName = modalTemplateName ? modalTemplateName.value.trim() : '';
                const modalUpdateTemplateCheckbox = document.getElementById('modalUpdateTemplateCheckbox');
                const updateTemplateFlag = modalUpdateTemplateCheckbox ? !!modalUpdateTemplateCheckbox.checked : false;

                manageUndoRedo(originalTextarea); // モーダルからの値を設定する前に履歴を管理
                // モーダルからの値を設定
                originalTextarea.value = newInstructions;
                raf(() => fitTextareaToContent(originalTextarea));
                // 新記法 {{名前:A|B|C}} の初期値を selects/values へ反映（編集直後の再描画で反映されるように）
                try {
                    const reCurlySelect = /\{\{\s*([^:}]+?)\s*:\s*([^}]+?)\s*\}\}/g;
                    let m; const seenNames = new Set();
                    while ((m = reCurlySelect.exec(newInstructions)) !== null) {
                        const name = String(m[1] || '').trim();
                        const parsedOptions = String(m[2] || '').split('|').map(s => s.trim()).filter(s => s !== '');
                        if (!name || parsedOptions.length === 0 || seenNames.has(name)) continue;
                        seenNames.add(name);
                        // 現在の選択値を保持しつつ、新しい options に無ければ先頭へ丸める
                        const prevVal = (persistedDefaults.selects && persistedDefaults.selects[name] && persistedDefaults.selects[name].value)
                            || (persistedDefaults.values && persistedDefaults.values[name]) || '';
                        const nextVal = parsedOptions.includes(prevVal) ? prevVal : (parsedOptions[0] || '');
                        const entry = { options: parsedOptions, value: nextVal };
                        persistedDefaults.selects = { ...(persistedDefaults.selects || {}), [name]: entry };
                        persistedDefaults.values = { ...(persistedDefaults.values || {}), [name]: nextVal };
                    }
                } catch(_) {}
                closeModal(modal);
                try { document.body.classList.remove('header-dimmed'); } catch (_) {}
                updateFooterHiddenState();
                // フッターボタン表示を通常に戻す
                try {
                    const footerCancel = document.getElementById('footerCancelButton');
                    const footerOk = document.getElementById('footerQueryButton');
                    const footerEditCancel = document.getElementById('footerEditCancel');
                    const footerEditSave = document.getElementById('footerEditSave');
                    const changeNav = document.querySelector('.footer .change-nav');
                    if (footerCancel) footerCancel.style.display = '';
                    if (footerOk) footerOk.style.display = '';
                    if (footerEditCancel) footerEditCancel.style.display = 'none';
                    if (footerEditSave) footerEditSave.style.display = 'none';
                    if (changeNav) {
                        const prev = changeNav.dataset && typeof changeNav.dataset.prevDisplay !== 'undefined' ? changeNav.dataset.prevDisplay : '';
                        changeNav.style.display = (prev && prev !== 'none') ? prev : '';
                    }
                } catch(_) {}
                synchronizeForm();
                updateDisplayText();

                // テンプレート名はチェック時のみ更新
                const titleElement = document.querySelector('.title-section h3');
                if (updateTemplateFlag && newTemplateName) {
                    if (titleElement) {
                        titleElement.textContent = newTemplateName;
                    }
                    selectedTemplateMeta.title = newTemplateName;
                }

                // 変更情報を Claris FileMaker へ通知
                const placeholderMatches = newInstructions.match(/\{\{(.*?)\}\}/g) || [];
                const placeholderNames = Array.from(
                    new Set(placeholderMatches.map(p => p.replace(/\{\{|\}\}/g, '').trim()))
                );
                const templateNameToSend = (updateTemplateFlag && newTemplateName)
                    ? newTemplateName
                    : (selectedTemplateMeta.title || '');
                const payload = {
                    event: 'template_saved',
                    previousInstructions: previousInstructions,
                    newInstructions: newInstructions,
                    placeholders: placeholderNames,
                    template_key: selectedTemplateMeta.key,
                    templateName: templateNameToSend,
                    updateTemplate: updateTemplateFlag
                };
                const jsonData = JSON.stringify(payload);
                fmCall && fmCall('addon_remarks_web_template_saved', jsonData, 5);
            });

            // テンプレート選択モーダルの閉じる
            if (templateCancelButton) {
                templateCancelButton.addEventListener('click', () => {
                    templateModalUI.close();
                    try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {}
                    updateFooterHiddenState();
                    // 閉じた後も再検索できるようフラグ解除
                    isSearching = false;
                });
            }

            // カードウインドウ（テンプレート選択）は枠外クリックでは閉じない
            // 既存の外側クリックで閉じる処理は削除し、明示操作（閉じるボタン等）のみに限定する
            // テンプレート検索機能（Enterキー／ボタン実行）
            const templateSearch = document.getElementById('templateSearch');
            // 統一テンプレート記法説明はヘッダーの[?]ポップオーバーのみで表示

            // ヘルプポップオーバー（はてなアイコン）
            (function setupSyntaxHelpPopover(){
                const fallbackHtml = (
                    '<h4>テンプレート記法の説明</h4>'+
                    '<ul>'+
                    '<li><b>テキスト入力</b>: <code>{{氏名}}</code>、<code>{{訪問日}}</code> のように <code>{{変数名}}</code> を書くと入力欄ができます。</li>'+
                    '<li><b>チェックボックス</b>: <code>[[初診]] [[再診]]</code> のように <code>[[項目名]]</code> を並べるとチェックボックスになります。</li>'+
                    '<li><b>プルダウン</b>: <code>{{性別:男|女|その他}}</code> のように候補を <code>|</code> 区切りで指定します。本文の <code>{{性別}}</code> も同じ候補でプルダウン化されます。</li>'+
                    '<li><b>同名変数の統一</b>: 同じ変数名は1箇所で選んだ値が全箇所に反映されます。</li>'+
                    '<li><b>日付/時刻型</b>: 変数の型は設定から変更できます（例: 日付、時刻、日時）。</li>'+
                    '</ul>'
                );
                function getSyntaxHelpHtml(){
                    // 静的ヘルプのみ許可（ユーザー入力は混入させない）
                    try {
                        const id = (window.__lastHelpTrigger && window.__lastHelpTrigger.id) || '';
                        if (id === 'syntaxHelpTriggerPhrase') {
                            return (
                                '<div style="max-width:560px">'+
                                '<p>よく使うフレーズを登録/編集できます。<br/>'+
                                'フレーズは削除できますので、ご自由にお使いください。</p>'+
                                '</div>'
                            );
                        }
                    } catch(_) {}
                    return fallbackHtml;
                }
                function ensurePopover(){
                    let pop = document.getElementById('syntaxHelpPopover');
                    if (!pop) {
                        pop = document.createElement('div');
                        pop.id = 'syntaxHelpPopover';
                        pop.className = 'syntax-help-popover';
                        clearChildren(pop);
                        pop.insertAdjacentHTML('afterbegin', getSyntaxHelpHtml());
                        document.body.appendChild(pop);
                        document.addEventListener('click', (e) => {
                            const t = e.target;
                            const isTrigger = (t && (t.id === 'syntaxHelpTriggerEdit' || t.id === 'syntaxHelpTriggerTemplate' || t.id === 'syntaxHelpTriggerTemplateEdit' || t.id === 'syntaxHelpTriggerPhrase'));
                            if (!isTrigger && pop.style.display === 'block' && !pop.contains(t)) {
                                pop.style.display = 'none';
                            }
                        });
                    }
                    return pop;
                }
                function positionAndShow(pop, trigger){
                    const rect = trigger.getBoundingClientRect();
                    const top = rect.bottom + window.scrollY + 8;
                    const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                    const margin = 12;
                    const maxPopoverWidth = 600;
                    // 一度表示状態にして幅を正確に測る（フラッシュしないよう visibility を利用）
                    const prevDisplay = pop.style.display;
                    const prevVisibility = pop.style.visibility;
                    pop.style.visibility = 'hidden';
                    pop.style.display = 'block';
                    const measuredWidth = pop.offsetWidth | 0;
                    const popWidth = Math.min(maxPopoverWidth, measuredWidth || (vw - margin * 2));
                    // 右端を越える場合はトリガーの右端に合わせて左寄せを調整
                    const viewportLeft = window.scrollX + margin;
                    const viewportRight = window.scrollX + vw - margin;
                    let left = rect.left + window.scrollX;
                    if (left + popWidth > viewportRight) {
                        left = rect.right + window.scrollX - popWidth;
                    }
                    left = Math.max(viewportLeft, Math.min(left, viewportRight - popWidth));
                    // 元の表示状態に戻す（open時に最終的な表示切替を行う）
                    pop.style.display = prevDisplay;
                    pop.style.visibility = prevVisibility;
                    pop.style.top = Math.round(top) + 'px';
                    pop.style.left = Math.round(left) + 'px';
                }
                function openSyntaxHelpPopover(trigger){
                    if (!trigger) return;
                    try { window.__lastHelpTrigger = trigger; } catch(_) {}
                    const pop = ensurePopover();
                    clearChildren(pop);
                    pop.insertAdjacentHTML('afterbegin', getSyntaxHelpHtml());
                    const willShow = !(pop.style.display === 'block');
                    positionAndShow(pop, trigger);
                    pop.style.display = willShow ? 'block' : 'none';
                }
                try { window.openSyntaxHelpPopover = openSyntaxHelpPopover; } catch(_) {}
                const t1 = document.getElementById('syntaxHelpTriggerEdit');
                if (t1) t1.addEventListener('click', (e) => { e.stopPropagation(); openSyntaxHelpPopover(t1); });
                // 動的追加された[?]にも対応するイベントデリゲーション
                document.addEventListener('click', (e) => {
                    const t = e.target;
                    if (!t) return;
                    const btn = (t.id === 'syntaxHelpTriggerEdit' || t.id === 'syntaxHelpTriggerTemplate' || t.id === 'syntaxHelpTriggerTemplateEdit' || t.id === 'syntaxHelpTriggerPhrase') ? t : null;
                    if (btn) { e.stopPropagation(); openSyntaxHelpPopover(btn); }
                });
            })();
            const templateSearchExec = document.getElementById('templateSearchExec');
            const templateSearchToggle = document.getElementById('templateSearchToggle');
            const templateSearchClose = document.getElementById('templateSearchClose');
            function updateSearchButtonsVisibility() {
                // フレーズUI準拠: x は検索枠の閉じるボタンとして動作
                // テンプレートでは入力クリアボタンは非表示運用に変更
                return;
            }
            function runSearchByTerm(term) {
                const searchTerm = (term ?? '').trim();
                isSearching = false; // 前回の検索状態でブロックしないよう解除
                if (lastExecutedSearchTerm === searchTerm) {
                    return;
                }
                if (searchTerm === '') {
                    // 空入力時は必ずリモート初期取得（ページング復帰のため）
                    lastExecutedSearchTerm = '';
                    performTemplateSearch('', 1, true);
                    return;
                }
                try {
                    const modal = document.getElementById('templateModal');
                    const isCopy = !!(modal && modal.dataset.mode === 'copy');
                    if (isCopy) {
                        // コピー専用: copySelectionItems から絞り込み、copyList に描画
                        const termLower = searchTerm.toLowerCase();
                        const filtered = termLower
                            ? copySelectionItems.filter(item => {
                                const t = String(item.title || '').toLowerCase();
                                const c = String(item.content || '').toLowerCase();
                                return t.includes(termLower) || c.includes(termLower);
                            })
                            : copySelectionItems.slice();
                        displayFilteredTemplates(filtered, searchTerm);
                        lastExecutedSearchTerm = searchTerm;
                        return;
                    }
                } catch(_) {}

                if (totalTemplateCount <= 100 && allTemplatesCache.length > 0) {
                    displayFilteredTemplates(filterTemplatesLocally(searchTerm), searchTerm);
                    lastExecutedSearchTerm = searchTerm;
                } else {
                    lastExecutedSearchTerm = searchTerm;
                    performTemplateSearch(searchTerm, 1);
                }
            }
            if (templateSearch) {
                templateSearch.addEventListener('keydown', (e) => {
                    if (e.isComposing) return; // IME変換中は無視
                    if (e.key !== 'Enter') return;
                    e.preventDefault();
                    runSearchByTerm(templateSearch.value);
                });
                // 入力時の特別なUI更新は不要（フレーズUI準拠）
            }
            if (templateSearchExec && templateSearch) {
                templateSearchExec.addEventListener('click', () => runSearchByTerm(templateSearch.value));
            }
            // コピー専用検索のEnter/ボタン
            try {
                const copySearch = document.getElementById('copySearch');
                const copySearchExec = document.getElementById('copySearchExec');
                if (copySearch) {
                    copySearch.addEventListener('keydown', (e) => {
                        if (e.isComposing) return;
                        if (e.key !== 'Enter') return;
                        e.preventDefault();
                        runSearchByTerm(copySearch.value);
                    });
                }
                if (copySearchExec && copySearch) {
                    copySearchExec.addEventListener('click', () => runSearchByTerm(copySearch.value));
                }
            } catch(_) {}
            // 検索トグル（虫眼鏡 → 入力欄）- モード別に正しい検索行を表示
            try {
                const templateSearchRow = document.getElementById('templateSearchRow');
                const copySearchRow = document.getElementById('copySearchRow');
                const copySearch = document.getElementById('copySearch');
                const templateNewButton = document.getElementById('templateNewButton');
                if (templateSearchToggle && templateSearchRow && copySearchRow) {
                    templateSearchToggle.addEventListener('click', () => {
                        const modal = document.getElementById('templateModal');
                        const isCopy = !!(modal && modal.dataset.mode === 'copy');
                        const targetRow = isCopy ? copySearchRow : templateSearchRow;
                        const targetInput = isCopy ? copySearch : templateSearch;
                        const visible = targetRow.style.display !== 'none';
                        
                        // 対象の検索行を表示/非表示
                        targetRow.style.display = visible ? 'none' : 'flex';
                        try { templateSearchToggle.style.display = visible ? '' : 'none'; } catch(_) {}
                        try { if (templateSearchClose) templateSearchClose.style.display = visible ? 'none' : ''; } catch(_) {}
                        try {
                            if (templateNewButton) {
                                const inCopyMode = !!(modal && modal.dataset.mode === 'copy');
                                templateNewButton.style.display = (!visible || inCopyMode) ? 'none' : '';
                            }
                        } catch(_) {}
                        try {
                            const title = document.getElementById('templateModalTitle');
                            if (title) title.style.display = visible ? '' : 'none';
                        } catch(_) {}
                        if (!visible && targetInput) {
                            raf(() => { try { targetInput.focus({ preventScroll: true }); } catch(_) {} });
                        }
                    });
                }
                if (templateSearchClose && templateSearchRow) {
                    templateSearchClose.addEventListener('click', () => {
                        const modal = document.getElementById('templateModal');
                        const isCopy = !!(modal && modal.dataset.mode === 'copy');
                        const targetRow = isCopy ? document.getElementById('copySearchRow') : templateSearchRow;
                        const targetInput = isCopy ? document.getElementById('copySearch') : templateSearch;
                        
                        try { if (targetRow) targetRow.style.display = 'none'; } catch(_) {}
                        try { templateSearchToggle.style.display = ''; } catch(_) {}
                        try { templateSearchClose.style.display = 'none'; } catch(_) {}
                        try {
                            if (targetInput) {
                                targetInput.value = '';
                                if (isCopy) {
                                    // コピー時: ローカル絞り込みを全件表示に戻す
                                    runSearchByTerm('');
                                } else {
                                    performTemplateSearch('');
                                }
                            }
                        } catch(_) {}
                        try {
                            if (templateNewButton) {
                                const inCopyMode = !!(modal && modal.dataset.mode === 'copy');
                                templateNewButton.style.display = inCopyMode ? 'none' : '';
                            }
                        } catch(_) {}
                        try {
                            const title = document.getElementById('templateModalTitle');
                            if (title) title.style.display = '';
                        } catch(_) {}
                    });
                }
            } catch(_) {}
        }

        // スクロール固定ユーティリティは廃止（getScrollableContainer/pinScrollWhile を削除）

        // テキストエリアを内容高さに合わせて自動調整（スクロール補正なし）
        // 注意: 自動リサイズ機能は無効化されています
        // ユーザーの要求により、テキストエリアの内容に応じた自動リサイズを行わないようにしています
        function fitTextareaToContent(el) {
            // 自動リサイズを完全に無効化
            return;
            // 以下のコードは無効化されています（将来の参考のため保持）
            /*
            try {
                if (!el) return;
                // iOSでは「備考」「内容」入力欄は自動リサイズを無効化
                try {
                    const isIOS = document.documentElement.classList.contains('is-ios');
                    if (isIOS) {
                        const id = el.id || '';
                        if (id === 'global-note-textarea' || id === 'original-textarea' || id === 'direct-input') return;
                    }
                } catch(_) {}
                // iOSで備考欄などオートリサイズ無効の指示がある場合は何もしない
                try { if (el.dataset && el.dataset.autogrowDisabled === 'true') return; } catch(_) {}
                // IME構成中は高さ更新を抑止（iOSでのジャンプ回避）
                if (el.__isComposing) return;
                let min = 0;
                let borderTop = 0, borderBottom = 0;
                try {
                    const cs = window.getComputedStyle(el);
                    min = parseFloat(cs.minHeight) || 0;
                    borderTop = parseFloat(cs.borderTopWidth) || 0;
                    borderBottom = parseFloat(cs.borderBottomWidth) || 0;
                } catch(_) {}
                el.style.height = 'auto';
                const h = Math.max(el.scrollHeight + borderTop + borderBottom, min);
                el.style.height = h + 'px';
            } catch(_) {}
            */
        }

        // Enterで改行が発生する直前の位置を保存し、次フレームで補正
        // stabilizeOnEnterForIOS は無効化（スクロール補正を行わない方針）

        // iOS/Safari向け: beforeinput(insertLineBreak)でも位置を保存してスクロールのジャンプを抑止
        // stabilizeOnBeforeInputForIOS は無効化（スクロール補正を行わない方針）

        // 貼り付け時のスクロールジャンプ抑止（1フレームだけ補正を抑止してからフィット）
        // suppressScrollOnPaste は無効化（スクロール補正を行わない方針）

        // 非表示→表示時の再フィットスケジューラは削除（scheduleFitWhenVisible 廃止）

        // 値がプログラムから設定された場合でも自動フィットするために value セッターをフック
        (function setupTextareaValueAutoFit() {
            try {
                const proto = HTMLTextAreaElement.prototype;
                const desc = Object.getOwnPropertyDescriptor(proto, 'value');
                if (!desc || typeof desc.set !== 'function' || typeof desc.get !== 'function') return;
                const originalSetter = desc.set;
                const originalGetter = desc.get;
                Object.defineProperty(proto, 'value', {
                    configurable: true,
                    enumerable: desc.enumerable,
                    get: function() { return originalGetter.call(this); },
                    set: function(v) {
                        originalSetter.call(this, v);
                        try {
                            // 対象は本文/直接入力のみ（編集モーダル内は除外しスクロール優先）
                            if (this && (this.id === 'original-textarea' || this.id === 'direct-input')) {
                                const self = this;
                                raf(() => { if (!self.__isComposing) fitTextareaToContent(self); });
                            }
                        } catch(_) {}
                    }
                });
            } catch(_) {}
        })();

        // Function to render the UI
        // 引数: addon_remarks_web_template_ok と同等のオブジェクト、もしくは data.json 互換/JSON文字列/テンプレート文字列
        function renderUI(payload) {
            // 以前の renderUI 呼び出しで受け取った値はすべて破棄し、最新のみ適用する
            try {
                persistedDefaults = { values: {}, persist: {}, types: {}, memos: {}, selects: {} };
                fieldHeights = {};
                fieldRows = {};
                lastSavedFieldName = null;
                selectedTemplateMeta = { key: null, title: '', is_blank: 1 };
                isSelectedTemplateKeyLocked = false;
                updateEditButtonVisibility();
                try { updateMemoToggleVisibility(); } catch(_) {}
            } catch(_) {}
            const originalTextarea = document.querySelector('#original-textarea');
            const titleElement = document.querySelector('.title-section h3');
            const renderMetaEl = document.getElementById('render-meta');

            let templateContent = '';
            let templateName = '';
            let template_key = null;
            let varsFromPayload = null;
            let instructionsFromPayload = null;

            // 文字列なら JSON パースを試み、失敗したらテンプレ文字列として扱う
            let input = payload;
            if (typeof input === 'string') {
                try {
                    input = JSON.parse(input);
                } catch (_) {
                    input = { __templateString: payload };
                }
            }

            if (input && typeof input === 'object' && !input.__templateString) {
                // addon_remarks_web_template_ok 相当かどうかを判定
                const looksLikeOkPayload = (
                    Object.prototype.hasOwnProperty.call(input, 'originalTemplate') ||
                    Object.prototype.hasOwnProperty.call(input, 'instructions') ||
                    Object.prototype.hasOwnProperty.call(input, 'variables') ||
                    Object.prototype.hasOwnProperty.call(input, 'template_key') ||
                    Object.prototype.hasOwnProperty.call(input, 'templateName')
                );

                if (looksLikeOkPayload) {
                    templateContent = input.originalTemplate ?? '';
                    templateName = input.templateName ?? '';
                    template_key = input.template_key ?? null;
                    varsFromPayload = input.variables ?? null;
                    instructionsFromPayload = typeof input.instructions === 'string' ? input.instructions : null;
                    // 明示的にメタへ反映（keyはロック中は上書きしない）
                    if (templateName) selectedTemplateMeta.title = templateName;
                    if (!isSelectedTemplateKeyLocked && template_key) selectedTemplateMeta.key = template_key;
                    // is_blank が含まれていれば反映（鉛筆表示制御に利用）
                    try {
                        if (Object.prototype.hasOwnProperty.call(input, 'is_blank')) {
                            selectedTemplateMeta.is_blank = Number(input.is_blank) || 0;
                        }
                    } catch(_) {}
                    // 変数に高さやメモが含まれていれば保持
                    if (input.variables && typeof input.variables === 'object') {
                        const newHeights = { ...fieldHeights };
                        const newMemos = { ...persistedDefaults.memos };
                        
                        // 新形式か旧形式かを判定
                        const isNewFormat = input.variables.checkbox || input.variables.fields;
                        
                        if (isNewFormat) {
                            // 新形式: fields のみ高さとメモ情報を持つ
                            if (input.variables.fields) {
                                Object.keys(input.variables.fields).forEach((key) => {
                                    const varEntry = input.variables.fields[key];
                                    if (varEntry && typeof varEntry === 'object') {
                                        const h = varEntry.height;
                                        if (typeof h === 'number' && h > 0) {
                                            // 高さは受信値をそのまま保持（丸めない）
                                            newHeights[key] = h;
                                        }
                                        // 変数内のメモも反映
                                        if (varEntry.memo !== undefined) {
                                            newMemos[key] = varEntry.memo;
                                        }
                                    }
                                });
                            }
                        } else {
                            // 旧形式: 従来通り処理（後方互換）
                            Object.keys(input.variables).forEach((key) => {
                                const varEntry = input.variables[key];
                                if (varEntry && typeof varEntry === 'object') {
                                    const h = varEntry.height;
                                    if (typeof h === 'number' && h > 0) {
                                        newHeights[key] = h;
                                    }
                                    // 変数内のメモも反映
                                    if (varEntry.memo !== undefined) {
                                        newMemos[key] = varEntry.memo;
                                    }
                                }
                            });
                        }
                        
                        fieldHeights = newHeights;
                        persistedDefaults.memos = newMemos;
                    }
                    // トップレベル variableMemos をUIに反映
                    if (input.variableMemos && typeof input.variableMemos === 'object') {
                        persistedDefaults.memos = { ...persistedDefaults.memos, ...input.variableMemos };
                    }
                    updateMemoToggleVisibility();
                    // トップレベル variableTypes をUIに反映
                    if (input.variableTypes && typeof input.variableTypes === 'object') {
                        persistedDefaults.types = { ...persistedDefaults.types, ...input.variableTypes };
                    }
            } else {
                    // data.json 等から抽出
                    const extracted = extractTemplateFromPayload(input);
                    templateContent = extracted.content;
                    templateName = extracted.name;
                    template_key = extracted.key ?? null;
                }
            } else {
                const text = input && input.__templateString ? input.__templateString : (typeof payload === 'string' ? payload : '');
                templateContent = text || '';
            }

            const isEmptyInit = !templateContent || String(templateContent).trim() === '';
            // 内容が空で instructions がある場合は、直接入力欄に下書きとして反映（template_key の有無は問わない）
            if (isEmptyInit && instructionsFromPayload && instructionsFromPayload.trim() !== '') {
                const directInputEl = document.querySelector('#direct-input');
                if (directInputEl) {
                    directInputEl.value = instructionsFromPayload;
                    raf(() => fitTextareaToContent(directInputEl));
                }
            }

            // 内容のテキストエリアに値を設定
            originalTextarea.value = templateContent;
            raf(() => fitTextareaToContent(originalTextarea));
            // 全体備考（任意）を反映
            // 備考（グローバルノート）の反映ポリシー
            // - 値(globalNote)は文字列として適用
            // - 高さ(globalNoteHeight)は数値(px)として適用。未指定/null/0以下は無視
            // - 受信元は addon_remarks_web_template_ok 相当（この画面からの送信データが戻るケースを想定）
            try {
                const globalNoteEl = document.getElementById('global-note-textarea');
                if (globalNoteEl && input && typeof input === 'object' && !input.__templateString) {
                    if (Object.prototype.hasOwnProperty.call(input, 'globalNote')) {
                        globalNoteEl.value = input.globalNote || '';
                    }
                    // 高さは外部値に依存せず内容優先
                }
            } catch (_) {}
            
            // 本文（instructionsHeight）が渡された場合でも優先せず、内容に合わせて自動調整
            try {
                if (input && typeof input === 'object' && !input.__templateString && Object.prototype.hasOwnProperty.call(input, 'instructionsHeight')) {
                    const el = (isEmptyInit ? document.getElementById('direct-input') : document.getElementById('original-textarea'));
                    if (el) raf(() => fitTextareaToContent(el));
                }
            } catch(_) {}
            
            // renderUI で受け取った元データを保存
            
            // テンプレート未選択（template_key 無し）も is_blank=1 として扱う
            try {
                const noTemplateSelected = (template_key === null || template_key === undefined || template_key === '');
                if (noTemplateSelected) {
                    selectedTemplateMeta.is_blank = 1;
                } else if (typeof selectedTemplateMeta.is_blank !== 'number') {
                    selectedTemplateMeta.is_blank = 0;
                }
            } catch(_) {}

            // タイトル/メモ表示トグルを設定（is_blank=1 の場合は鉛筆非表示と併せてメモ表示トグルも隠す）
            if (titleElement) {
                const isBlankHeader = Number(selectedTemplateMeta && selectedTemplateMeta.is_blank) === 1;
                // templateName が明示的に渡されている場合はそれを優先、なければ is_blank に応じて表示
                titleElement.textContent = templateName || (isBlankHeader ? '(テンプレート未選択)' : '備考');
            }
            try {
                updateMemoToggleVisibility();
            } catch(_) {}
            if (templateName) {
                selectedTemplateMeta.title = templateName;
            }
            if (!isSelectedTemplateKeyLocked && template_key !== null && template_key !== undefined && template_key !== '') {
                selectedTemplateMeta.key = template_key;
            }
            // テンプレートが未選択 または is_blank=1 なら鉛筆を隠す
            updateEditButtonVisibility();
            // 本文に変数がなければ変数トグルを非表示
            
            
            

            // Initialize form first
            synchronizeForm();
            
                // モーダルを初期化
                initializeModal();

            if (isEmptyInit) {
                const hasInstructions = !!(instructionsFromPayload && instructionsFromPayload.trim() !== '');
                const hasVars = !!(varsFromPayload && typeof varsFromPayload === 'object' && Object.keys(varsFromPayload).length > 0);
                // 入力データが無い場合でもテンプレート選択モーダルは開かない（直接入力へ）
                // 検索フォーカスを優先するため変数適用は少し遅延
                if (varsFromPayload) {
                    setTimeout(() => {
                        window.applyVariables({ variables: varsFromPayload });
                        // 変数適用後に初期値を保存
                        setTimeout(() => {
                            saveInitialValues();
                            updateInitialOkSnapshot();
                            // チェックボックスUI更新
                            document.querySelectorAll('.checkbox-container input[type="checkbox"]').forEach(checkbox => {
                                updateCheckboxUI(checkbox);
                            });
                        }, 50);
                    }, 250);
                } else {
                    // テンプレートが無い場合は直接入力欄を表示
                    const originalTextarea = document.querySelector('#original-textarea');
                    const directInput = document.querySelector('#direct-input');
                    const displayText = document.querySelector('#display-text');
                    if (originalTextarea && directInput && displayText) {
                        originalTextarea.style.display = 'none';
                        displayText.style.display = 'none';
                        directInput.style.display = 'block';
                        // 表示直後に内容高さへフィット
                        raf(() => fitTextareaToContent(directInput));
                    }
                    // 直接入力表示後に初期値を保存
                    setTimeout(() => {
                        saveInitialValues();
                        updateInitialOkSnapshot();
                        // チェックボックスUI更新
                        document.querySelectorAll('.checkbox-container input[type="checkbox"]').forEach(checkbox => {
                            updateCheckboxUI(checkbox);
                        });
                    }, 50);
                }
            } else {
                // 通常は即時に反映
                if (varsFromPayload) {
                    window.applyVariables({ variables: varsFromPayload });
                } else {
                    updateDisplayText();
                    // テンプレート本文を表示中は内容高さへフィット
                    raf(() => fitTextareaToContent(originalTextarea));
                }
                // 初期値を保存
                setTimeout(() => {
                    saveInitialValues();
                    updateInitialOkSnapshot();
                    // チェックボックスUI更新
                    document.querySelectorAll('.checkbox-container input[type="checkbox"]').forEach(checkbox => {
                        updateCheckboxUI(checkbox);
                    });
                }, 50);
            }

            // 受信した UTCMilliseconds / account_name を控えめに表示
            try {
                if (renderMetaEl) {
                    let ts = '';
                    let acct = '';
                    // payload 形態に応じて抽出
                    const obj = (typeof payload === 'string') ? (()=>{ try { return JSON.parse(payload); } catch(_) { return {}; } })() : (payload || {});
                    const rawMs = obj.UTCMilliseconds ?? (obj.meta && obj.meta.UTCMilliseconds);
                    const ms = normalizeUtcMilliseconds(rawMs);
                    const name = obj.account_name ?? (obj.meta && obj.meta.account_name);
                    if (typeof ms === 'number' && ms > 0) {
                        try {
                            const d = new Date(ms);
                            // ローカルタイムゾーン（自動判別）で表示
                            const tz = (typeof Intl !== 'undefined' && Intl.DateTimeFormat && Intl.DateTimeFormat().resolvedOptions().timeZone) ? Intl.DateTimeFormat().resolvedOptions().timeZone : '';
                            const offsetMin = -d.getTimezoneOffset(); // ローカルのUTCオフセット分（分）
                            const sign = offsetMin >= 0 ? '+' : '-';
                            const abs = Math.abs(offsetMin);
                            const oh = String(Math.floor(abs / 60)).padStart(2, '0');
                            const om = String(abs % 60).padStart(2, '0');
                            const offsetStr = `UTC${sign}${oh}${om}`;
                            const y = d.getFullYear();
                            const m2 = String(d.getMonth() + 1).padStart(2, '0');
                            const dd = String(d.getDate()).padStart(2, '0');
                            const hh = String(d.getHours()).padStart(2, '0');
                            const mm2 = String(d.getMinutes()).padStart(2, '0');
                            const label = tz ? `${tz}, ${offsetStr}` : offsetStr;
                            ts = `${label} ${y}/${m2}/${dd} ${hh}:${mm2}`;
                        } catch(_) {}
                    }
                    if (typeof name === 'string' && name.trim() !== '') {
                        acct = name.trim();
                    }
                    clearChildren(renderMetaEl);
                    if (ts) {
                        const tsEl = document.createElement('div');
                        tsEl.className = 'meta-line ts';
                        tsEl.textContent = ts;
                        renderMetaEl.appendChild(tsEl);
                    }
                    if (acct) {
                        const acctEl = document.createElement('div');
                        acctEl.className = 'meta-line acct';
                        acctEl.textContent = acct;
                        renderMetaEl.appendChild(acctEl);
                    }
                }
            } catch(_) {}
        }
        // 変更ログナビゲータ
        // 引数: primary_keys(string | Array<string>) - プライマリキー一覧（改行区切り文字列 または 配列）
        // オプション: { scriptName?: string } - Claris FileMaker スクリプト名（未指定なら呼ばない）
        window.setupChangeLogNavigator = function(primary_keys, options) {
            try {
                let prevBtn = document.getElementById('changeNavPrev');
                let nextBtn = document.getElementById('changeNavNext');
                if (!prevBtn || !nextBtn) return;
                const navContainer = document.querySelector('.change-nav');

                function normalizeKeys(input) {
                    let arr = [];
                    if (Array.isArray(input)) {
                        arr = input;
                    } else if (typeof input === 'string') {
                        // 改行コード: CRLF(\r\n) / LF(\n) / CR(\r: Claris FileMaker の段落区切り) すべて対応
                        arr = input.split(/\r\n?|\n|\r/);
                    }
                    return arr
                        .map(k => String(k ?? '').trim())
                        .filter(k => k !== '');
                }

                const keys = normalizeKeys(primary_keys);
                const scriptName = (options && typeof options.scriptName === 'string' && options.scriptName.trim() !== '') ? String(options.scriptName).trim() : null;

                let currentIndex = 0;
                // 直近クリック後は一定時間([<][>]無反応)にするロック
                let clickLockUntil = 0;
                function isClickLocked() {
                    try { return Date.now() < clickLockUntil; } catch(_) { return false; }
                }
                function triggerClickLock(ms) {
                    try {
                        const duration = Math.max(0, Number(ms) || 200);
                        clickLockUntil = Math.max(clickLockUntil, Date.now() + duration);
                        // ロック中は物理的に無効化して視覚的にも反応させない
                        setBtnDisabled(prevBtn, true);
                        setBtnDisabled(nextBtn, true);
                        setTimeout(() => {
                            // 最後のロック期限を過ぎていればボタン状態を元に戻す
                            if (Date.now() >= clickLockUntil) updateButtons();
                        }, duration + 10);
                    } catch(_) {}
                }

                function setBtnDisabled(btn, disabled) {
                    try {
                        btn.disabled = !!disabled;
                        if (disabled) btn.setAttribute('disabled', '');
                        else btn.removeAttribute('disabled');
                    } catch(_) {}
                }

                function updateButtons() {
                    const count = keys.length;
                    const isDisabledAll = count <= 1;
                    setBtnDisabled(prevBtn, isDisabledAll || currentIndex <= 0);
                    setBtnDisabled(nextBtn, isDisabledAll || currentIndex >= count - 1);
                    try {
                        const status = document.getElementById('changeNavStatus');
                        if (status) status.textContent = (count === 0) ? '0 / 0' : `${currentIndex + 1} / ${count}`;
                        if (navContainer) {
                            // 件数が 0 の場合はナビゲーション自体を非表示
                            navContainer.style.display = (count === 0) ? 'none' : 'inline-flex';
                        }
                    } catch(_) {}
                }

                function sendToFileMaker() {
                    if (window.FileMaker != null && keys.length > 0 && typeof scriptName === 'string' && scriptName) {
                        const currentKey = String(keys[currentIndex] ?? '');
                        try { (fmCall && fmCall(scriptName, currentKey, 5)); } catch (_) {}
                    }
                }
                function sendToFileMakerOnClick() {
                    if (window.FileMaker != null && keys.length > 0) {
                        const currentKey = String(keys[currentIndex] ?? '');
                        const nameToUse = (typeof scriptName === 'string' && scriptName) ? scriptName : 'addon_remarks_web_change_log_select';
                        try { (fmCall && fmCall(nameToUse, currentKey, 5)); } catch (_) {}
                    }
                }
                // 初期状態（末尾のキーを選択して送信）
                if (keys.length > 0) {
                    currentIndex = keys.length - 1;
                }
                // クローン差し替えより後に同期・送信（disabled属性の反映ズレ回避）
                // → 下のイベントバインド完了後に実行

                prevBtn = replaceWithClone(prevBtn);
                nextBtn = replaceWithClone(nextBtn);
                const freshPrev = document.getElementById('changeNavPrev');
                const freshNext = document.getElementById('changeNavNext');
                if (!freshPrev || !freshNext) return;
                prevBtn = freshPrev;
                nextBtn = freshNext;

                freshPrev.addEventListener('click', () => {
                    if (isClickLocked()) return;
                    triggerClickLock(400);
                    if (hasPendingChanges()) {
                        showConfirmModal(
                            '変更されています。破棄して移動しますか？',
                            () => {
                                if (currentIndex > 0) {
                                    currentIndex -= 1;
                                    updateButtons();
                                    sendToFileMakerOnClick();
                                    // 破棄扱いでスナップショットを現在値に更新
                                    updateInitialOkSnapshot();
                                }
                            },
                            () => {},
                            { cancelLabel: '編集に戻る', confirmLabel: '破棄して前へ' }
                        );
                        return;
                    }
                    if (currentIndex > 0) {
                        currentIndex -= 1;
                        updateButtons();
                        sendToFileMakerOnClick();
                    }
                });
                freshNext.addEventListener('click', () => {
                    if (isClickLocked()) return;
                    triggerClickLock(400);
                    if (hasPendingChanges()) {
                        showConfirmModal(
                            '変更されています。破棄して移動しますか？',
                            () => {
                                if (currentIndex < keys.length - 1) {
                                    currentIndex += 1;
                                    updateButtons();
                                    sendToFileMakerOnClick();
                                    updateInitialOkSnapshot();
                                }
                            },
                            () => {},
                            { cancelLabel: '編集に戻る', confirmLabel: '破棄して次へ' }
                        );
                        return;
                    }
                    if (currentIndex < keys.length - 1) {
                        currentIndex += 1;
                        updateButtons();
                        sendToFileMakerOnClick();
                    }
                });

                // クローン差し替え後の初期状態を再同期
                updateButtons();
                if (keys.length > 0) sendToFileMaker();

                // 公開API: 現在位置や配列を外部から更新したい場合
                window.changeLogNavigator = {
                    setIndex(idx) {
                        const i = Math.max(0, Math.min(keys.length - 1, Number(idx) || 0));
                        if (i !== currentIndex) {
                            currentIndex = i;
                            updateButtons();
                            sendToFileMaker();
                        }
                    },
                    getIndex() { return currentIndex; },
                    getLength() { return keys.length; },
                    getKey() { return keys[currentIndex] ?? null; },
                    setKeys(newKeys) {
                        const arr = normalizeKeys(newKeys);
                        keys.length = 0; arr.forEach(k => keys.push(k));
                        // 新配列適用時は末尾から開始
                        currentIndex = Math.max(0, keys.length - 1);
                        updateButtons();
                        if (keys.length > 0) sendToFileMaker();
                    }
                };
                try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('changeLogNavigator', window.changeLogNavigator); } catch(_) {}
            } catch (_) {}
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('setupChangeLogNavigator', window.setupChangeLogNavigator); } catch(_) {}
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('openVarSettingsModal', window.openVarSettingsModal); } catch(_) {}
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('applyVariables', window.applyVariables); } catch(_) {}
        // 直前に編集していたテンプレート行を強調しスクロールする（統一関数）
        window.markLastEditedInList = function() {
            try {
                const key = String(window.lastEditedTemplateKey || '');
                if (!key) return false;
                const listEl = document.getElementById('templateList');
                if (!listEl) return false;
                const target = listEl.querySelector(`.template-item[data-key="${CSS.escape(key)}"]`);
                if (!target) {
                    // ページに存在しない → 自動ページングで探す
                    try {
                        if (typeof totalPages === 'number' && totalPages > 1 && typeof performTemplateSearch === 'function') {
                            const current = Number(currentPage) || 1;
                            const state = window.__jumpToKeyState || { key: key, tried: {} };
                            state.key = key;
                            state.tried = state.tried || {};
                            state.tried[current] = true;
                            // 次に探すページ（現在の次ページから順に未試行を探す）
                            let nextPageToTry = null;
                            for (let i = 1; i <= totalPages; i++) {
                                const p = ((current - 1 + i) % totalPages) + 1;
                                if (!state.tried[p]) { nextPageToTry = p; break; }
                            }
                            if (!nextPageToTry) {
                                // 全ページ試行済み。あきらめて状態クリア
                                window.__jumpToKeyState = null;
                                window.lastEditedTemplateKey = null;
                                return false;
                            }
                            window.__jumpToKeyState = state;
                            setTimeout(() => { try { performTemplateSearch(currentSearchTerm, nextPageToTry, true); } catch(_) {} }, 0);
                            return 'pending';
                        }
                    } catch(_) {}
                    return false;
                }
                try { target.classList.add('was-edited'); } catch(_) {}
                try {
                    setTimeout(() => {
                        try {
                            target.classList.add('highlight-fade');
                            requestAnimationFrame(() => {
                                try { target.classList.remove('was-edited'); } catch(_) {}
                                setTimeout(() => { try { target.classList.remove('highlight-fade'); } catch(_) {} }, 400);
                            });
                        } catch(_) {}
                    }, 2000);
                } catch(_) {}
                
                // スクロール制御は行わない（ユーザー操作を尊重）
                try { window.lastEditedTemplateKey = null; } catch(_) {}
                try { window.__jumpToKeyState = null; } catch(_) {}
                return true;
            } catch(_) { return false; }
        };
        // テンプレート選択モーダルから編集モードへ入る処理（統合関数）
        window.enterTemplateEditMode = function(item) {
            try {
                // 編集対象キーを記録
                try { window.lastEditedTemplateKey = String(item.key ?? ''); } catch(_) {}

                const templateCreateSection = document.getElementById('templateCreateSection');
                const templateListEl = document.getElementById('templateList');
                const templatePaginationEl = document.getElementById('templatePagination');
                const templateSearchRow = document.getElementById('templateSearchRow');
                const templateActionsRow = document.getElementById('templateActionsRow');
                const templateModalTitle = document.getElementById('templateModalTitle');
                const templateCreateCancel = document.getElementById('templateCreateCancel');
                const templateCreateSubmit = document.getElementById('templateCreateSubmit');
                const templateCreateCancelBottom = document.getElementById('templateCreateCancelBottom');
                const templateCreateSubmitBottom = document.getElementById('templateCreateSubmitBottom');
                const templateCancelButton = document.getElementById('templateCancelButton');
                const nameEl = document.getElementById('createTemplateName');
                const contentEl = document.getElementById('createTemplateContent');
                if (!templateCreateSection || !nameEl || !contentEl) return;

                // 編集モードでのUI切替
                if (templateListEl) templateListEl.style.display = 'none';
                if (templatePaginationEl) templatePaginationEl.style.display = 'none';
                if (templateSearchRow) templateSearchRow.style.display = 'none';
                if (templateActionsRow) templateActionsRow.style.display = 'none';
                // モード同期ロジックだけではテンプレート一覧が再表示されるケースがあったため、明示的に非表示へ
                try {
                    const listEl = document.getElementById('templateList');
                    if (listEl) listEl.style.display = 'none';
                } catch(_) {}
                if (templateModalTitle) templateModalTitle.textContent = 'テンプレートを編集';
                if (templateNewButton) templateNewButton.style.display = 'none';
                // 編集モード: 検索アイコンを非表示、ヘルプ「?」を表示
                try {
                    const templateSearchToggleEl = document.getElementById('templateSearchToggle');
                    const templateSearchCloseEl = document.getElementById('templateSearchClose');
                    if (templateSearchToggleEl) templateSearchToggleEl.style.display = 'none';
                    if (templateSearchCloseEl) templateSearchCloseEl.style.display = 'none';
                } catch(_) {}
                try { const modalEl = document.getElementById('templateModal'); if (modalEl) { modalEl.dataset.mode = 'edit'; updateTemplateHeaderForMode(); } } catch(_) {}
                templateCreateSection.style.display = 'block';

                // 既存のボタン表示は一旦全て隠す
                // 擬似フッター要素は廃止のため無視
                if (templateCreateCancel) templateCreateCancel.style.display = 'none';
                if (templateCreateSubmit) templateCreateSubmit.style.display = 'none';
                if (templateCancelButton) templateCancelButton.style.display = 'none';

                // 擬似フッター構築は不要（既存フッターを使用）

                // 本文を確実にスクロール可能に（ヘッダー/フッターを除いた高さに制限）
                try {
                    const modal = document.getElementById('templateModal');
                    const mc = modal ? modal.querySelector('.modal-content') : null;
                    const headerEl = mc ? mc.querySelector('.modal-header') : null;
                    const bodyEl = mc ? mc.querySelector('.template-modal-body') : null;
                    const contentTextarea = document.getElementById('createTemplateContent');
                    // 擬似フッター廃止に伴い、実フッター高さを使用
                    const footerEl = document.querySelector('.footer');
                    const adjust = () => {
                        if (!mc || !bodyEl) return;
                        const totalH = mc.clientHeight;
                        const hh = headerEl ? headerEl.offsetHeight : 0;
                        const fh = footerEl ? footerEl.offsetHeight : 0;
                        const avail = Math.max(120, totalH - hh - fh - 24);
                        bodyEl.style.maxHeight = avail + 'px';
                        bodyEl.style.overflowY = 'auto';
                        bodyEl.style.webkitOverflowScrolling = 'touch';
                        // フッターに隠れないよう、ボディ下余白をフッター高ぶん確保
                        try { bodyEl.style.paddingBottom = Math.max(0, fh + 100) + 'px'; } catch(_) {}
                        if (contentTextarea) {
                            const nameRow = contentTextarea.closest('.modal-field-row')?.previousElementSibling;
                            const nameRowH = nameRow && nameRow.matches('.modal-field-row') ? nameRow.offsetHeight : 0;
                            const labelH = 24; // ラベル分の目安
                            const innerAvail = Math.max(80, avail - nameRowH - labelH - 32);
                            contentTextarea.style.height = innerAvail + 'px';
                            contentTextarea.style.minHeight = '120px';
                            contentTextarea.style.overflow = 'auto';
                        }
                    };
                    adjust();
                    // リサイズ/回転時も再計算
                    const onResize = () => { try { adjust(); } catch(_) {} };
                    window.addEventListener('resize', onResize);
                    window.addEventListener('orientationchange', onResize);
                    try { if (window.visualViewport) { window.visualViewport.addEventListener('resize', onResize); } } catch(_) {}
                    try { modal.__detachEditResize = () => { window.removeEventListener('resize', onResize); window.removeEventListener('orientationchange', onResize); try { if (window.visualViewport) { window.visualViewport.removeEventListener('resize', onResize); } } catch(_) {} }; } catch(_) {}
                } catch(_) {}

                // 初期値（編集対象の既存データ）をセット
                nameEl.value = item.title ?? '';
                contentEl.value = item.content ?? '';

                const restoreListUI = () => {
                    // フッターは選択モード表示（閉じるのみ）に切り替え
                    try { window.setFooterForTemplateMode && window.setFooterForTemplateMode('select'); } catch(_) {}
                    // 編集セクションを閉じて一覧へ戻す
                    templateCreateSection.style.display = 'none';
                    // 本文高さ調整の後始末
                    try {
                        const modal = document.getElementById('templateModal');
                        const mc = modal ? modal.querySelector('.modal-content') : null;
                        const bodyEl = mc ? mc.querySelector('.template-modal-body') : null;
                        if (bodyEl) { bodyEl.style.maxHeight = ''; bodyEl.style.overflowY = ''; bodyEl.style.webkitOverflowScrolling = ''; }
                        if (modal && modal.__detachEditResize) { modal.__detachEditResize(); modal.__detachEditResize = null; }
                    } catch(_) {}
                    if (templateListEl) templateListEl.style.display = '';
                    if (templatePaginationEl) templatePaginationEl.style.display = '';
                    if (templateSearchRow) templateSearchRow.style.display = (templateSearchRow.dataset && templateSearchRow.dataset.defaultDisplay) || 'flex';
                    if (templateActionsRow) templateActionsRow.style.display = (templateActionsRow.dataset && templateActionsRow.dataset.defaultDisplay) || 'flex';
                    if (templateNewButton) templateNewButton.style.display = '';
                    if (templateModalTitle) templateModalTitle.textContent = 'テンプレートを選択';
                    // 選択モード: 検索アイコン復帰、ヘルプ「?」は非表示
                    try {
                        const templateSearchToggleEl = document.getElementById('templateSearchToggle');
                        const templateSearchCloseEl = document.getElementById('templateSearchClose');
                        if (templateSearchToggleEl) templateSearchToggleEl.style.display = '';
                        if (templateSearchCloseEl) templateSearchCloseEl.style.display = 'none';
                    } catch(_) {}
                    try { const modalEl = document.getElementById('templateModal'); if (modalEl) { delete modalEl.dataset.mode; updateTemplateHeaderForMode(); } } catch(_) {}
                    // 擬似フッター復元は不要（既存フッターで閉じるを提供）
                    // 直前編集行の強調＆スクロール
                    try { window.markLastEditedInList(); } catch(_) {}
                };

                const submitHandler = () => {
                    const newName = String(nameEl.value || '').trim();
                    const newContent = String(contentEl.value || '').trim();
                    if (!newName) {
                        showMessageModal('テンプレート名を入力してください。', '入力が必要です');
                        return;
                    }
                    const key = item.key;
                    if (key != null) {
                        const payload = { id: key, title: newName, content: newContent };
                        const jsonData = JSON.stringify(payload);
                        fmCall && fmCall('addon_remarks_web_template_update', jsonData, 5);
                    }
                    // 楽観的にUI/キャッシュを更新
                    try {
                        item.title = newName; item.content = newContent;
                        const row = document.querySelector(`#templateList .template-item[data-key="${CSS.escape(String(item.key ?? ''))}"]`);
                        if (row) {
                            const tEl = row.querySelector('.template-title');
                            const bEl = row.querySelector('.template-body');
                            if (tEl) tEl.textContent = newName;
                            if (bEl) bEl.textContent = newContent;
                        }
                        allTemplatesCache = (allTemplatesCache || []).map(t => {
                            const tk = t.key; const ik = item.key;
                            return String(tk) === String(ik) ? { ...t, title: newName, content: newContent } : t;
                        });
                    } catch(_) {}
                    restoreListUI();
                };

                // ボタンにイベント付与
                const editCancelBtn = document.getElementById('templateEditCancel');
                const editSaveBtn = document.getElementById('templateEditSave');
                if (editCancelBtn) editCancelBtn.addEventListener('click', restoreListUI);
                if (editSaveBtn) editSaveBtn.addEventListener('click', submitHandler);

                // 既存フッターを編集モードに切替
                try { window.setFooterForTemplateMode && window.setFooterForTemplateMode('edit'); } catch(_) {}
                // フッター表示切替後に高さ再計算（フッター高さ反映）: リサイズ発火をスロットル
                ;(window.__throttledResize || (window.__throttledResize = throttle(() => { try { window.dispatchEvent(new Event('resize')); } catch(_) {} }, 120)))();

                // フッターから直接呼べるように編集モード用の関数を登録
                try {
                    const modal = document.getElementById('templateModal');
                    if (modal) {
                        modal.__editCancelFn = () => { try { restoreListUI(); } catch(_) {} };
                        modal.__editSaveFn = () => { try { submitHandler(); } catch(_) {} };
                        // 初期は更新ボタンを無効化
                        try { const saveBtn = document.getElementById('footerEditSave'); if (saveBtn) saveBtn.disabled = !(String(nameEl.value||'').trim() && String(contentEl.value||'').trim()); } catch(_) {}
                        // 入力監視で有効/無効を切り替え
                        const recomputeEditValidity = () => {
                            try {
                                const save = document.getElementById('footerEditSave');
                                if (save) save.disabled = !(String(nameEl.value||'').trim() && String(contentEl.value||'').trim());
                            } catch(_) {}
                        };
                        try {
                            nameEl.addEventListener('input', recomputeEditValidity);
                            contentEl.addEventListener('input', recomputeEditValidity);
                            recomputeEditValidity();
                        } catch(_) {}
                    }
                } catch(_) {}
            } catch(_) {}
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('enterTemplateEditMode', window.enterTemplateEditMode); } catch(_) {}

        // data.json のような構造からテンプレート情報を抽出するヘルパー関数
        function extractTemplateFromPayload(payload) {
            let content = '';
            let name = '';
            let key = null;
            
            if (payload && payload.response && Array.isArray(payload.response.data) && payload.response.data.length > 0) {
                // data.json 形式の場合、最初のレコードを使用
                const firstRecord = payload.response.data[0];
                const fd = firstRecord.fieldData || {};
                content = fd['template'] || fd['template_text'] || fd.content || '';
                name = fd['name'] || fd['template_title'] || fd.title || '';
                key = fd['primary_key'] ?? firstRecord.primary_key ?? fd.id ?? null;
            } else if (Array.isArray(payload) && payload.length > 0) {
                // 配列形式の場合、最初の要素を使用
                const firstItem = payload[0];
                content = firstItem.content || firstItem.template || '';
                name = firstItem.title || firstItem.name || '';
                key = firstItem.id ?? firstItem.primary_key ?? firstItem['primary_key'] ?? null;
            } else if (payload && Array.isArray(payload.templates) && payload.templates.length > 0) {
                // { templates: [...] } 形式の場合、最初の要素を使用
                const firstItem = payload.templates[0];
                content = firstItem.content || firstItem.template || '';
                name = firstItem.title || firstItem.name || '';
                key = firstItem.id ?? firstItem.primary_key ?? firstItem['primary_key'] ?? null;
            } else if (payload && (payload.content || payload.template)) {
                // 単一のテンプレートオブジェクトの場合
                content = payload.content || payload.template || '';
                name = payload.title || payload.name || '';
                key = payload.id ?? payload.primary_key ?? payload['primary_key'] ?? null;
            }
            
            return { content, name, key };
        }
//         renderUI(instructionText);



// addon_remarks_web_template_loaded の重複呼び出しを防ぐフラグ
        let __templateLoadedCalled = false;
        function exec() {
            // 既に呼び出し済みの場合は何もしない
            if (__templateLoadedCalled) return;
            
            if (!fmCall || !fmCall('addon_remarks_web_template_loaded', '', 5)) { 
                setTimeout(exec, 250); 
            } else {
                // 成功したらフラグを立てて以降の呼び出しを防ぐ
                __templateLoadedCalled = true;
            }
        }
        exec();

        document.getElementById('footerCancelButton').addEventListener('click', () => {
            // 変更がある場合のみ確認ダイアログを表示
            if (hasPendingChanges()) {
            showConfirmModal(
                '編集を終了してよろしいですか？',
                () => {
                    fmCall && fmCall('addon_remarks_web_template_cancel', '', 5);
                },
                    () => {},
                { cancelLabel: '編集に戻る', confirmLabel: '変更を破棄' }
            );
            } else {
                // 未変更ならダイアログ無しで破棄相当の動作
                fmCall && fmCall('addon_remarks_web_template_cancel', '', 5);
            }
        });

        document.getElementById('footerQueryButton').addEventListener('click', () => {
            // フレーズ選択のカードウインドウが開いていれば先に閉じる
            try { closePhraseModalIfOpen(true); } catch(_) {}
            const originalTextarea = document.getElementById('original-textarea');
            const directInput = document.getElementById('direct-input');
            const originalTemplate = originalTextarea.value; // 置換前の元テンプレート
            const globalNoteEl = document.getElementById('global-note-textarea');
            const globalNote = globalNoteEl ? globalNoteEl.value : '';
            
            // 内容が空かどうかで使用するテキストを切り替え
            let instructions = originalTextarea.value.trim() 
                ? originalTextarea.value 
                : directInput.value;

            // 入力DOMの収集は不要
            
            // {{...}} の範囲を収集（内部の [[...]] は置換対象から除外するため）
            const __braceRanges = collectBraceRanges(instructions);
            const __isInsideBrace = (start, len) => __braceRanges.some(([a, b]) => start >= a && start + len <= b);
            
            // 変数の置換を行う（テンプレートまたは直接入力がある場合）
            if (instructions.trim()) {
                // [[...]] 内は {{...}} を置換しないガードを事前に計算
                const __bracketRangesForQuery = collectBracketRanges(instructions);
                const __isInsideBracketForQuery = (start, len) => __bracketRangesForQuery.some(([a, b]) => start >= a && start + len <= b);

                // 0) {{名前:A|B|C}} を選択値で置換（variables.select優先）
                try {
                    instructions = instructions.replace(/\{\{\s*([^:}]+?)\s*:\s*([^}]+?)\s*\}\}/g, (m, nameRaw, optsRaw, offset) => {
                        if (__isInsideBracketForQuery(offset, m.length)) return m;
                        const name = String(nameRaw || '').trim();
                        let options = String(optsRaw || '').split('|').map(s => s.trim()).filter(s => s !== '');
                        if (!name) return m;
                        if (persistedDefaults.selects && persistedDefaults.selects[name] && Array.isArray(persistedDefaults.selects[name].options)) {
                            options = persistedDefaults.selects[name].options;
                        }
                        if (options.length === 0) return m;
                        const chosen = (persistedDefaults.selects && persistedDefaults.selects[name] && persistedDefaults.selects[name].value)
                            || (persistedDefaults.values && persistedDefaults.values[name]) || options[0];
                        return String(chosen ?? options[0]);
                    });
                } catch(_) {}

                // 1) {{field}} を persistedDefaults から置換
                try {
                    // まず全ての変数名を収集
                    const reFields = /\{\{\s*(.*?)\s*\}\}/g;
                    const fieldNames = new Set();
                    let m;
                    while ((m = reFields.exec(instructions)) !== null) {
                        const fieldName = String(m[1] || '').trim();
                        // 新セレクト記法（コロン含み）はスキップ済み
                        if (fieldName.includes(':')) continue;
                        if (isValidVarToken(fieldName)) {
                            fieldNames.add(fieldName);
                        }
                    }
                    
                    // 各変数を順次置換（値が空のときは置換せず、そのまま {{変数}} を残す）
                    fieldNames.forEach(fieldName => {
                        const type = (persistedDefaults.types && persistedDefaults.types[fieldName]) || 'multiline';
                        let raw = (persistedDefaults.values && persistedDefaults.values[fieldName]) || '';
                        try { raw = toHalfWidthForType(raw, type); } catch(_) {}
                        let value = '';
                        if (type === 'date') value = formatDateYMDWithWeekday(raw || '').trim();
                        else if (type === 'timestamp') value = formatDateTimeDisplay(raw || '').trim();
                        else if (type === 'time') { const mm = String(raw || '').match(/^(\d{1,2}):(\d{2})/); value = mm ? `${mm[1].padStart(2,'0')}:${mm[2]}` : ''; }
                        else value = String(raw || '').trim();
                        if (value !== '') {
                        const regex = new RegExp(`\\{\\{\\s*${escapeRegExp(fieldName)}\\s*\\}\\}`, 'g');
                        instructions = instructions.replace(regex, value);
                        }
                    });
                } catch(_) {}
                
                // 2) [[]] を置換（{{}} 内は置換しない）
                // 変数置換後の {{...}} 範囲を再計算
                const updatedBraceRanges = (() => {
                    const ranges = [];
                    try {
                        const re = /\{\{[\s\S]*?\}\}/g;
                        let m;
                        while ((m = re.exec(instructions)) !== null) {
                            ranges.push([m.index, m.index + m[0].length]);
                        }
                    } catch(_) {}
                    return ranges;
                })();
                const isInsideUpdatedBrace = (start, len) => updatedBraceRanges.some(([a, b]) => start >= a && start + len <= b);
                let __bracketSelectIndexOut2 = 0;
                instructions = instructions.replace(/\[\[\s*(.*?)\s*\]\]/g, (mtch, inner, offset) => {
                    const raw = String(inner || '').trim();
                    if (!raw) return mtch;
                    if (isInsideUpdatedBrace(offset, mtch.length)) return mtch;
                    // それ以外は従来どおりチェック記号
                    if (!isValidVarToken(raw)) return mtch;
                    const key = `checkbox_${raw}`;
                    const checked = !!(persistedDefaults.values && persistedDefaults.values[key]);
                    return `${checked ? '☑' : '☐'} ${raw}`;
                });
            }

            // 変数の値と「次回も使用」のチェック有無を収集
            // 方針（将来の拡張指針）
            // - テキストエリア等の高さは、値が未入力でも「ユーザーがレイアウトを調整した」事実として重要なので送信対象に含める
            // - チェックボックスは従来通り、チェック状態そのものが意味を持つため常に送信（高さは対象外）
            // - 送信形式は新形式 { checkbox: {...}, fields: {...} } を優先し、旧形式の互換は受信側で維持する
            // - 高さはピクセル値の整数（getBoundingClientRect().height を四捨五入）で送り、受信側は適用時にpx指定
            // - 将来的に他の見た目属性（例: 幅、行間、リードオンリー等）を追加する場合も、値未入力でもメタ情報として送る方針
            //   （項目単位の「ユーザー操作の痕跡」を正確に反映するため）
            // 注意
            // - variableTypes / variableMemos は最終的に「送信対象の fields/checkbox」のキーに合わせてフィルタする
            //   → 値無し・高さのみの項目でも、fields に含めるため type は欠かさず同梱する
            // 送信する変数は本文に出現する項目のみを persistedDefaults から構築
            const variables = { checkbox: {}, fields: {}, select: {} };
            const variableTypes = {};
            try {
                const reFields2 = /\{\{\s*(.*?)\s*\}\}/g;
                let m1; const seenFields = new Set();
                while ((m1 = reFields2.exec(originalTextarea.value || '')) !== null) {
                    const raw = String(m1[1] || '').trim();
                    if (!raw) continue;
                    // 新記法: {{名前:A|B|C}} は select へ（variables.select を優先）
                    if (raw.includes(':')) {
                        const idx = raw.indexOf(':');
                        const name = raw.slice(0, idx).trim();
                        const optionsPart = raw.slice(idx + 1);
                        let options = optionsPart.split('|').map(s => s.trim()).filter(s => s !== '');
                        if (!name) continue;
                        if (persistedDefaults.selects && persistedDefaults.selects[name] && Array.isArray(persistedDefaults.selects[name].options)) {
                            options = persistedDefaults.selects[name].options;
                        }
                        if (options.length === 0) continue;
                        const val = (persistedDefaults.selects && persistedDefaults.selects[name] && persistedDefaults.selects[name].value) || (persistedDefaults.values && persistedDefaults.values[name]) || options[0] || '';
                        variables.select[name] = { value: val, options: options };
                        continue;
                    }
                    const name = raw;
                    if (!isValidVarToken(name) || seenFields.has(name)) continue;
                    seenFields.add(name);
                    const val = (persistedDefaults.values && persistedDefaults.values[name]) || '';
                    const persist = !!(persistedDefaults.persist && persistedDefaults.persist[name]);
                    variables.fields[name] = { value: val, type: (persistedDefaults.types && persistedDefaults.types[name]) || 'multiline', persist: persist };
                    if (persistedDefaults.types && persistedDefaults.types[name]) variableTypes[name] = persistedDefaults.types[name];
                }
                const reBoxes2 = /\[\[\s*(.*?)\s*\]\]/g;
                let m2; const seenBoxes = new Set();
                let __bracketSelectIndexVars = 0;
                while ((m2 = reBoxes2.exec(originalTextarea.value || '')) !== null) {
                    const raw = String(m2[1] || '').trim();
                    if (!raw) continue;
                    // [[{{変数}}A|B|C]] のみ select として収集（変数名無しは無視）
                    if (raw.includes('|')) {
                        const mVar = raw.match(/^\{\{\s*([^}]+?)\s*\}\}/);
                        if (mVar) {
                            const varName = (mVar[1] || '').trim();
                            let optionsPart = raw.slice(mVar[0].length);
                            if (optionsPart.startsWith('|')) optionsPart = optionsPart.slice(1);
                            const options = optionsPart.split('|').map(s => s.trim()).filter(s => s !== '');
                            if (options.length === 0) continue;
                            const key = `select:${varName}`;
                            let value = options[0];
                            try { if (window.__bracketSelectState && window.__bracketSelectState[key]) value = window.__bracketSelectState[key]; } catch(_) {}
                            variables.select[key] = { value: value, options: options };
                            continue;
                        }
                    }
                    // それ以外はチェックボックス
                    const item = raw;
                    if (!isValidVarToken(item) || seenBoxes.has(item)) continue;
                    seenBoxes.add(item);
                    const key = `checkbox_${item}`;
                    const val = !!(persistedDefaults.values && persistedDefaults.values[key]);
                    const persist = !!(persistedDefaults.persist && persistedDefaults.persist[key]);
                    variables.checkbox[item] = { value: val, persist: persist };
                    if (persistedDefaults.types && persistedDefaults.types[key]) variableTypes[key] = persistedDefaults.types[key];
                }
            } catch(_) {}

            // variableTypesとvariableMemosも、存在する変数のみに絞る
            const filteredVariableTypes = {};
            const filteredVariableMemos = {};
            
            // fields カテゴリの変数を処理
            Object.keys(variables.fields).forEach(fieldName => {
                if (variableTypes[fieldName]) {
                    filteredVariableTypes[fieldName] = variableTypes[fieldName];
                }
                if (persistedDefaults.memos && persistedDefaults.memos[fieldName]) {
                    filteredVariableMemos[fieldName] = persistedDefaults.memos[fieldName];
                }
            });
            
            // checkbox カテゴリの変数を処理
            Object.keys(variables.checkbox).forEach(itemName => {
                const fieldName = `checkbox_${itemName}`;
                if (variableTypes[fieldName]) {
                    filteredVariableTypes[fieldName] = variableTypes[fieldName];
                }
                if (persistedDefaults.memos && persistedDefaults.memos[fieldName]) {
                    filteredVariableMemos[fieldName] = persistedDefaults.memos[fieldName];
                }
            });
            
            // 備考エリアの現在高さも送信（受け側で変更有無を判断できるように）
            let globalNoteHeight = null;
            try {
                const globalNoteEl = document.getElementById('global-note-textarea');
                if (globalNoteEl) {
                    const rect = globalNoteEl.getBoundingClientRect();
                    globalNoteHeight = Math.round(rect.height);
                }
            } catch (_) {}

            // 本文（instructions）の現在高さも送信
            let instructionsHeight = null;
            try {
                const instructionsSourceEl = (originalTextarea && (originalTextarea.value || '').trim()) ? originalTextarea : directInput;
                if (instructionsSourceEl) {
                    const rect = instructionsSourceEl.getBoundingClientRect();
                    instructionsHeight = Math.round(rect.height);
                }
            } catch (_) {}

            const data = { instructions: instructions, originalTemplate: originalTemplate, template_key: selectedTemplateMeta.key, templateName: selectedTemplateMeta.title, is_blank: Number(selectedTemplateMeta.is_blank) || 0, variables, variableTypes: filteredVariableTypes, variableMemos: filteredVariableMemos, globalNote: globalNote, globalNoteHeight: globalNoteHeight, instructionsHeight: instructionsHeight, timeToOkMs: getTimeToOkMs() };
            const jsonData = JSON.stringify(data);

            fmCall && fmCall('addon_remarks_web_template_ok', jsonData, 5);
        });
        // 検索状態管理
        let currentSearchTerm = '';
        let currentPage = 1;
        let totalPages = 1;
        let isSearching = false;
        let allTemplatesCache = []; // 全テンプレートのキャッシュ
        let copySelectionItems = []; // コピー候補のキャッシュ
        let totalTemplateCount = 0; // 全テンプレート数
        // ページング補助（ Claris FileMaker のfoundCountが不明な場合のフォールバック）
        let lastReturnedCount = 0; // 直近に表示した件数
        let lastPageLimit = 100;   // 1ページあたりの件数（ Claris FileMaker 制限）
        let isFoundTotalKnown = false; // dataInfoで総件数がわかるか
        let lastExecutedSearchTerm = null; // 直近に実行した検索語（同一検索の連打抑止）
        

        // JavaScript側でのテンプレート絞り込み関数
        function filterTemplatesLocally(searchTerm) {
            if (!searchTerm) return allTemplatesCache;
            
            const lowerSearchTerm = searchTerm.toLowerCase();
            return allTemplatesCache.filter(item => {
                const title = (item.title || '').toLowerCase();
                const content = (item.content || '').toLowerCase();
                return title.includes(lowerSearchTerm) || content.includes(lowerSearchTerm);
            });
        }

        // Claris FileMaker でテンプレート検索を実行する関数
        function performTemplateSearch(searchTerm, page = 1, forceRemote = false) {
            if (isSearching) return; // 重複実行防止
            
            currentSearchTerm = searchTerm;
            currentPage = page;
            
            // すぐにカードウインドウを開き、検索欄へフォーカス（A11yユーティリティがaria/フォーカスを補助）
            const templateModal = document.getElementById('templateModal');
            const templateSearchEl = document.getElementById('templateSearch');
                if (templateModal) {
                    // フレーズ選択カードが開いていれば先に閉じる
                    // フレーズモーダルとは独立運用するため、クロス閉鎖はしない
                    // ラッパで開く（選択モード）
                    try {
                        templateModalUI.openSelect();
                        if (templateModal && templateModal.dataset) {
                            const currentMode = templateModal.dataset.mode || '';
                            if (currentMode !== 'copy') {
                                templateModal.dataset.mode = 'select';
                            }
                            updateTemplateHeaderForMode();
                        }
                    } catch(_) {}
                    try { const mc = document.querySelector('#templateModal .modal-content'); if (mc) mc.classList.add('fullscreen'); } catch(_) {}
                    updateFooterHiddenState();
                    try {
                        // 枠外クリックで閉じない: 既存の標準クローズ（外側クリック含む）を解除
                        if (templateModal.__detachStandardClose) { templateModal.__detachStandardClose(); templateModal.__detachStandardClose = null; }
                        // ESCのみで閉じる
                        if (templateModal.__detachEsc) { templateModal.__detachEsc(); }
                        const detachEsc = attachEscToClose(() => {
                            try { closeModal(templateModal); } catch(_) {}
                            try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {}
                            try { updateFooterHiddenState(); } catch(_) {}
                        }, document);
                        try { templateModal.__detachEsc = detachEsc; } catch(_) {}
                    } catch(_) {}

                    // 戻り時の編集対象を強調（未発見なら markLastEditedInList 内で自動ページング）
                    try { if (typeof window.markLastEditedInList === 'function') window.markLastEditedInList(); } catch(_) {}

                    // 検索ボックスにフォーカス・UI更新
                    focusSearchAndUpdateUI();
                    // 初回表示時は未選択状態なので鉛筆ボタンを隠す
                    updateEditButtonVisibility();
                }

            // コピー用モードではキャッシュから絞り込み（FileMaker検索は実行しない）
            try {
                const modalForMode = document.getElementById('templateModal');
                if (modalForMode && modalForMode.dataset.mode === 'copy') {
                    const term = String(searchTerm || '').toLowerCase();
                    const filtered = term
                        ? copySelectionItems.filter(item => {
                            const t = String(item.title || '').toLowerCase();
                            const c = String(item.content || '').toLowerCase();
                            return t.includes(term) || c.includes(term);
                        })
                        : copySelectionItems.slice();
                    displayFilteredTemplates(filtered, searchTerm);
                    return;
                }
            } catch(_) {}
            
            // 全件が100件以内かつキャッシュがある場合は、JavaScript側で絞り込み
            if (!forceRemote && totalTemplateCount <= 100 && allTemplatesCache.length > 0) {
                const filteredTemplates = filterTemplatesLocally(searchTerm);
                
                // JavaScript絞り込み結果を表示
                displayFilteredTemplates(filteredTemplates, searchTerm);
                return;
            }
            
            // 100件を超える場合、または forceRemote 指定時は Claris FileMaker 側で検索
            isSearching = true;
            
            // ローディング表示
            const templateListEl = document.getElementById('templateList');
            const templatePaginationEl = document.getElementById('templatePagination');
            if (templateListEl) {
                clearChildren(templateListEl);
                const loading = document.createElement('div');
                loading.className = 'search-loading';
                loading.textContent = '検索中...';
                templateListEl.appendChild(loading);
            }
            if (templatePaginationEl) {
                templatePaginationEl.style.display = 'none';
            }
            // フォールバックのための直近件数を初期化
            lastReturnedCount = 0;
            
            // Claris FileMaker スクリプトを呼び出し（copyモードでは呼び出さない）
            if (window.FileMaker != null) {
                try {
                    const modalForMode = document.getElementById('templateModal');
                    if (modalForMode && modalForMode.dataset.mode === 'copy') {
                        // コピー時は検索スクリプトを呼ばず、空表示にして待機
                        displayFilteredTemplates([], searchTerm);
                        isSearching = false;
                        return;
                    }
                } catch(_) {}
                const searchParams = {
                    searchTerm: searchTerm,
                    page: page,
                    limit: 100
                };
                raf(() => { fmCall && fmCall('addon_remarks_web_template_search_paged', searchParams, 5); });
            } else {
                // Claris FileMaker がない環境ではローディングから復帰させる
                displayFilteredTemplates([], searchTerm);
                isSearching = false;
            }
        }
        
        function initializeCopyListDnD(listEl) {
            if (!listEl) return;
            if (listEl.__copyModeDnD) return;
            try { listEl.__copyModeDnD = true; } catch(_) {}

            let dragItem = null;
            let indicator = null;

            const isCopyModeActive = () => {
                try {
                    const modal = document.getElementById('templateModal');
                    return !!(modal && modal.dataset.mode === 'copy');
                } catch(_) { return false; }
            };

            const ensureIndicator = () => {
                if (!indicator) {
                    indicator = document.createElement('div');
                    indicator.className = 'drop-indicator active';
                }
                return indicator;
            };

            const clearIndicator = () => {
                try {
                    if (indicator && indicator.parentNode) {
                        indicator.parentNode.removeChild(indicator);
                    }
                    } catch(_) {}
                indicator = null;
            };

            const updateSortIndex = () => {
                try {
                    Array.from(listEl.querySelectorAll('.template-item')).forEach((node, idx) => {
                        node.dataset.sort = String(idx);
                    });
                } catch(_) {}
            };

            const syncCopyOrderFromDOM = () => {
                try {
                    if (!Array.isArray(copySelectionItems) || copySelectionItems.length === 0) return;
                    const nodes = Array.from(listEl.querySelectorAll('.template-item'));
                    if (nodes.length === 0) return;
                    const map = new Map();
                    copySelectionItems.forEach(item => {
                        const key = item && item.key != null ? String(item.key) : '';
                        if (!map.has(key)) map.set(key, item);
                    });
                    const ordered = [];
                    nodes.forEach(node => {
                        const key = node.dataset.key != null ? String(node.dataset.key) : '';
                        if (map.has(key)) {
                            ordered.push(map.get(key));
                            map.delete(key);
                        }
                    });
                    map.forEach(item => ordered.push(item));
                    copySelectionItems = ordered;
                } catch(_) {}
            };

            const runNextFrame = (cb) => {
                try { raf(cb); } catch(_) {
                    try { requestAnimationFrame(cb); } catch(__) { cb(); }
                }
            };

            const scheduleSendOrder = (() => {
                let timer = null;
                let pending = false;
                return (forceImmediate = false) => {
                    if (forceImmediate) {
                        if (timer != null) {
                            clearTimeout(timer);
                            timer = null;
                        }
                        pending = false;
                        sendOrderToFileMaker();
                        return;
                    }

                    pending = true;
                    if (timer != null) {
                        clearTimeout(timer);
                    }
                    timer = setTimeout(() => {
                        timer = null;
                        if (!pending) return;
                        pending = false;
                        sendOrderToFileMaker();
                    }, 150);
                };
            })();

            const cleanup = () => {
                try { listEl.classList.remove('drag-active'); } catch(_) {}
                if (dragItem) {
                    try { dragItem.classList.remove('dragging'); } catch(_) {}
                }
                dragItem = null;
                clearIndicator();
                // 念のため、当該スコープ内の残留インジケータを一掃
                try {
                    const scopeEl = (document.getElementById('templateModal')) || listEl;
                    scopeEl.querySelectorAll('.drop-indicator').forEach(el => { if (el && el.parentNode) el.parentNode.removeChild(el); });
                } catch(_) {}
            };

            const sendOrderToFileMaker = () => {
                try {
                    if (window.FileMaker == null) return;
                    const items = Array.from(listEl.querySelectorAll('.template-item'));
                    const orderPayload = items.map((item, idx) => ({ key: item.dataset.key || '', position: idx }));
                    const json = JSON.stringify({ order: orderPayload });
                    window.FileMaker.PerformScriptWithOption('addon_remarks_web_copy_reorder', json, 5);
                } catch(_) {}
            };

            listEl.addEventListener('dragstart', (e) => {
                if (!isCopyModeActive()) return;
                const target = e.target && e.target.closest('.template-item');
                if (!target) {
                    try { e.preventDefault(); } catch(_) {}
                    return;
                }
                dragItem = target;
                try { e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', ''); } catch(_) {}
                runNextFrame(() => {
                    try { target.classList.add('dragging'); } catch(_) {}
                    try { listEl.classList.add('drag-active'); } catch(_) {}
                    // 保険: 既存のインジケータを全て除去（他機能の残留や二重表示防止）
                    try {
                        const scopeEl = (document.getElementById('templateModal')) || listEl;
                        scopeEl.querySelectorAll('.drop-indicator').forEach(el => { if (el && el.parentNode) el.parentNode.removeChild(el); });
                    } catch(_) {}
                });

                const onGlobalEnd = () => {
                    cleanup();
                document.removeEventListener('drop', onGlobalEnd, false);
                document.removeEventListener('dragend', onGlobalEnd, false);
                    window.removeEventListener('blur', onGlobalEnd, true);
                    document.removeEventListener('pointerup', onGlobalEnd, true);
                    document.removeEventListener('mouseup', onGlobalEnd, true);
                    document.removeEventListener('keydown', onEsc, true);
                    scheduleSendOrder();
                };
                const onEsc = (ev) => {
                    if (ev.key === 'Escape') {
                        onGlobalEnd();
                    }
                };
                document.addEventListener('drop', onGlobalEnd, false);
                document.addEventListener('dragend', onGlobalEnd, false);
                window.addEventListener('blur', onGlobalEnd, true);
                document.addEventListener('pointerup', onGlobalEnd, true);
                document.addEventListener('mouseup', onGlobalEnd, true);
                document.addEventListener('keydown', onEsc, true);
            });

            listEl.addEventListener('dragend', () => {
                if (!isCopyModeActive()) return;
                cleanup();
                updateSortIndex();
                syncCopyOrderFromDOM();
                scheduleSendOrder(false);
            });

            listEl.addEventListener('dragover', (e) => {
                if (!isCopyModeActive()) return;
                if (!dragItem) return;
                e.preventDefault();
                const target = e.target && e.target.closest('.template-item');
                if (!target || target === dragItem) return;
                const rect = target.getBoundingClientRect();
                const after = (e.clientY - rect.top) > rect.height / 2;
                const ind = ensureIndicator();
                // 同一スコープ内の他インジケータを全除去（自分以外）
                try {
                    const scopeEl = (document.getElementById('templateModal')) || listEl;
                    scopeEl.querySelectorAll('.drop-indicator').forEach(el => { if (el !== ind && el.parentNode) el.parentNode.removeChild(el); });
                } catch(_) {}
                const parent = target.parentNode;
                if (!parent) return;
                try {
                    if (after) {
                        parent.insertBefore(ind, target.nextSibling);
                        } else {
                        parent.insertBefore(ind, target);
                    }
                } catch(_) {}
            });

            listEl.addEventListener('drop', (e) => {
                if (!isCopyModeActive()) return;
                if (!dragItem) return;
                e.preventDefault();
                try {
                    if (indicator && indicator.parentNode) {
                        indicator.parentNode.insertBefore(dragItem, indicator);
                    }
                } catch(_) {}
                updateSortIndex();
                syncCopyOrderFromDOM();
                cleanup();
                scheduleSendOrder(false);
            });

            listEl.addEventListener('mouseleave', () => {
                if (!isCopyModeActive()) return;
                if (!dragItem) return;
                clearIndicator();
            });
        }

        // コピー時に Claris FileMaker から渡されるレコードの値一覧から、値のみをコピーできる候補を生成する
        // payload には { key: value, key: value, ... } 形式のレコードオブジェクト、またはその JSON 文字列が渡される想定
        window.setCopySelectionOptions = function setCopySelectionOptions(payload, orderInfo) {
            try {
                // --- 入力正規化: 文字列の場合は JSON を解析し、オブジェクト以外は空扱い ---
                const parsePayload = (raw) => {
                    if (raw == null) return {};
                    if (typeof raw === 'string') {
                        const trimmed = raw.trim();
                        if (!trimmed) return {};
                        try { return JSON.parse(trimmed); } catch (_) { return {}; }
                    }
                    if (typeof raw === 'object' && !Array.isArray(raw)) return raw;
                    return {};
                };

                const source = parsePayload(payload);

                // --- 値をコピー対象とするアイテム配列へ変換 ---
                const stringifyValue = (value) => {
                    if (value == null) return '';
                    if (typeof value === 'string') return value;
                    if (typeof value === 'number' || typeof value === 'boolean') return String(value);
                    if (value instanceof Date) return value.toISOString();
                    // オブジェクト/配列などは JSON 文字列化（整形無しでコピーしやすさ優先）
                    try { return JSON.stringify(value); } catch (_) { return String(value); }
                };

                const entries = Object.keys(source);
                const items = entries.map((fieldKey, index) => {
                    const valueText = stringifyValue(source[fieldKey]);
                    const titleText = fieldKey || `field_${index + 1}`;
                    return {
                        key: titleText,
                        title: titleText,
                        content: valueText,
                        is_del: 0,
                        is_blank: 0,
                    };
                }).filter(item => typeof item.content === 'string');

                // --- モーダル表示を copy モードに切り替え ---
                const openCopyModal = () => {
                    try { templateModalUI && templateModalUI.openSelect && templateModalUI.openSelect(); } catch (_) {}
                    try {
                        const modal = document.getElementById('templateModal');
                        if (modal) {
                            modal.dataset.mode = 'copy';
                            try { updateTemplateHeaderForMode && updateTemplateHeaderForMode(); } catch (_) {}
                            try { modal.dataset.__wasOpenByCopy = '1'; } catch(_) {}
                        }
                        try {
                            document.body.classList.add('copy-mode');
                            document.body.classList.add('template-modal-open');
                        } catch(_) {}
                        const list = document.getElementById('copyList');
                        if (list) {
                            list.style.display = '';
                            initializeCopyListDnD(list);
                        }
                    } catch (_) {}
                };

                openCopyModal();
                copySelectionItems = Array.isArray(items) ? items : [];
                displayFilteredTemplates(copySelectionItems, '');
                try {
                    const modal = document.getElementById('templateModal');
                    const listEl = document.getElementById('copyList');
                    const isCopyMode = !!(modal && modal.dataset.mode === 'copy');
                    if (isCopyMode && listEl) {
                        initializeCopyListDnD(listEl);
                    }
                } catch(_) {}
                // 並び順情報が指定されている場合は、そのまま適用
                try {
                    if (orderInfo != null && typeof window.applyCopyOrder === 'function') {
                        window.applyCopyOrder(orderInfo);
                    }
                } catch(_) {}
            } catch (_) {
                // エラー時は copy モードを維持したまま空リスト表示にフォールバック
                try {
                    const modal = document.getElementById('templateModal');
                    if (modal) {
                        modal.dataset.mode = 'copy';
                        try { updateTemplateHeaderForMode && updateTemplateHeaderForMode(); } catch (_) {}
                    }
                } catch (_) {}
                try { templateModalUI && templateModalUI.openSelect && templateModalUI.openSelect(); } catch (_) {}
                try {
                    const list = document.getElementById('copyList');
                    if (list) list.style.display = '';
                } catch (_) {}
                try { displayFilteredTemplates([], ''); } catch (_) {}
            }
        };
        // コピー用リストの並び順を外部（Claris FileMaker 等）から適用する公開関数
        // 入力形式（いずれも可）:
        // - 配列: ["KeyA", "KeyB", ...]
        // - オブジェクト: { order: ["KeyA", "KeyB"] } または { order: [{ key: "KeyA" }, { key: "KeyB" }] }
        // - JSON文字列（上記いずれか）
        // 動作:
        // - copySelectionItems を指定キー順で並べ替え（未指定の要素は元の相対順で末尾へ）
        // - 表示中の #templateList の .template-item も同順に並べ替え、data-sort を再付与
        window.applyCopyOrder = function applyCopyOrder(input) {
            try {
                // --- 正規化: orderKeys を抽出（文字列配列） ---
                const normalize = (raw) => {
                    let src = raw;
                    if (typeof src === 'string') {
                        try { src = JSON.parse(src); } catch(_) { src = null; }
                    }
                    if (Array.isArray(src)) {
                        return src.map(x => (x && typeof x === 'object' && x.key != null) ? String(x.key) : String(x));
                    }
                    if (src && typeof src === 'object') {
                        const ord = src.order;
                        if (Array.isArray(ord)) return ord.map(x => (x && typeof x === 'object' && x.key != null) ? String(x.key) : String(x));
                        return Object.keys(src).map(k => String(k));
                    }
                    return [];
                };
                const orderKeys = normalize(input).filter(k => typeof k === 'string' && k.length > 0);
                if (orderKeys.length === 0) return false;

                // --- データ（copySelectionItems）を並べ替え ---
                if (Array.isArray(copySelectionItems) && copySelectionItems.length > 0) {
                    const keyToItem = new Map();
                    copySelectionItems.forEach(it => {
                        const key = (it && it.key != null) ? String(it.key) : '';
                        if (!keyToItem.has(key)) keyToItem.set(key, it);
                    });
                    const picked = new Set();
                    const ordered = [];
                    orderKeys.forEach(k => {
                        const key = String(k);
                        if (keyToItem.has(key) && !picked.has(key)) {
                            ordered.push(keyToItem.get(key));
                            picked.add(key);
                        }
                    });
                    copySelectionItems.forEach(it => {
                        const key = (it && it.key != null) ? String(it.key) : '';
                        if (!picked.has(key)) ordered.push(it);
                    });
                    copySelectionItems = ordered;
                }

                // --- DOM を並べ替え ---
                const listEl = document.getElementById('copyList') || document.getElementById('templateList');
                if (listEl) {
                    const nodes = Array.from(listEl.querySelectorAll('.template-item'));
                    if (nodes.length > 0) {
                        const keyToNode = new Map();
                        nodes.forEach(node => {
                            const key = node && node.dataset && node.dataset.key != null ? String(node.dataset.key) : '';
                            if (!keyToNode.has(key)) keyToNode.set(key, node);
                        });
                        const used = new Set();
                        const newOrderNodes = [];
                        orderKeys.forEach(k => {
                            const key = String(k);
                            const n = keyToNode.get(key);
                            if (n && !used.has(key)) { newOrderNodes.push(n); used.add(key); }
                        });
                        nodes.forEach(n => {
                            const key = n && n.dataset && n.dataset.key != null ? String(n.dataset.key) : '';
                            if (!used.has(key)) { newOrderNodes.push(n); used.add(key); }
                        });
                        const frag = document.createDocumentFragment();
                        newOrderNodes.forEach((n, idx) => { try { n.dataset.sort = String(idx); } catch(_) {} ; frag.appendChild(n); });
                        listEl.appendChild(frag);
                    }
                }

                return true;
            } catch(err) {
                try { reportError && reportError('applyCopyOrder', err, { level: 'warn' }); } catch(_) {}
                return false;
            }
        };

        // JavaScript絞り込み結果を表示する関数
        function displayFilteredTemplates(filteredTemplates, searchTerm) {
            const templateListEl = document.getElementById('templateList');
            const copyListEl = document.getElementById('copyList');
            const templateMetaEl = document.getElementById('templateMeta');
            const templatePaginationEl = document.getElementById('templatePagination');
            const originalTextarea = document.getElementById('original-textarea');
            const templateModal = document.getElementById('templateModal');
            const templateCreateSection = document.getElementById('templateCreateSection');
            const templateSearchEl = document.getElementById('templateSearch');
            // フッターボタン（一覧モードの標準状態へ復元）
            // 新規作成/編集モード中は変更しない
            try {
                const titleEl = document.getElementById('templateModalTitle');
                const createSectionEl = document.getElementById('templateCreateSection');
                const titleText = String(titleEl && titleEl.textContent || '').trim();
                const isCreateOrEdit = titleText === 'テンプレートを新規作成' || titleText === 'テンプレートを編集' || (createSectionEl && createSectionEl.style.display === 'block');
                if (!isCreateOrEdit) {
                    // 擬似フッター要素は廃止
                    const templateCancelButton = document.getElementById('templateCancelButton');
                    if (templateCancelButton) templateCancelButton.style.display = '';
                }
            } catch(_) {}

            if (!templateListEl) return;

            // モーダルを開く（ローカル絞り込み経路でも表示する）
            if (templateModal) {
                // フレーズ選択カードが開いていなければ何もしない（開いている場合のみ閉じる処理をスキップ）
                // try { closePhraseModalIfOpen(); } catch(_) {}
                try { templateModalUI.openSelect(); document.body.classList.add('template-modal-open'); } catch(_) {}
                updateFooterHiddenState();
                try {
                    // 枠外クリックで閉じない: 標準クローズを外し、ESCのみ付与
                    if (templateModal.__detachStandardClose) { templateModal.__detachStandardClose(); templateModal.__detachStandardClose = null; }
                    if (templateModal.__detachEsc) { templateModal.__detachEsc(); }
                    const detachEsc = attachEscToClose(() => {
                        try { closeModal(templateModal); } catch(_) {}
                        try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {}
                        try { updateFooterHiddenState(); } catch(_) {}
                    }, document);
                    try { templateModal.__detachEsc = detachEsc; } catch(_) {}
                } catch(_) {}
                focusSearchAndUpdateUI();
                // 常にテンプレートモーダルは全画面 + 既存フッター利用
                try {
                    const mc = templateModal.querySelector('.modal-content');
                    if (mc) mc.classList.add('fullscreen');
                } catch(_) {}
            }
            
            // ページング非表示（JavaScript絞り込みでは全件表示）
            if (templatePaginationEl) {
                templatePaginationEl.style.display = 'none';
            }

            // メタ情報更新
            if (templateMetaEl) {
                const searchInfo = searchTerm ? ` (検索: "${searchTerm}")` : '';
                templateMetaEl.textContent = `${filteredTemplates.length} 件${searchInfo}`;
            }
            // リスト表示（作成フォームを隠す）
            if (templateCreateSection) templateCreateSection.style.display = 'none';
            clearChildren(templateListEl);
            try {
                const isCopyModeRendering = !!(document.getElementById('templateModal') && document.getElementById('templateModal').dataset.mode === 'copy');
                if (isCopyModeRendering && copyListEl) {
                    clearChildren(copyListEl);
                    copyListEl.style.display = '';
                    templateListEl.style.display = 'none';
                } else {
                    if (copyListEl) copyListEl.style.display = 'none';
                    templateListEl.style.display = '';
                }
            } catch(_) {}
            if (filteredTemplates.length === 0) {
                const nores = document.createElement('div');
                nores.className = 'no-results';
                try {
                    const modalForMode = document.getElementById('templateModal');
                    const isCopyMode = !!(modalForMode && modalForMode.dataset.mode === 'copy');
                    nores.textContent = isCopyMode ? 'コピー元の候補がありません。' : '該当するテンプレートは見つかりませんでした';
                } catch(_) {
                    nores.textContent = '該当するテンプレートは見つかりませんでした';
                }
                try {
                    const isCopyModeRendering = !!(document.getElementById('templateModal') && document.getElementById('templateModal').dataset.mode === 'copy');
                    if (isCopyModeRendering && copyListEl) copyListEl.appendChild(nores);
                    else templateListEl.appendChild(nores);
                } catch(_) { templateListEl.appendChild(nores); }
            }
            const modalForMode = document.getElementById('templateModal');
            const isCopyModeRendering = !!(modalForMode && modalForMode.dataset.mode === 'copy');
            filteredTemplates.forEach((item, index) => {
                const title = item.title ?? '(無題)';
                const content = item.content ?? '';

                const div = document.createElement('div');
                div.className = 'template-item' + ((Number(item.is_del) === 1) ? ' is-deleted' : '');
                try { div.dataset.key = String(item.key ?? ''); } catch(_) {}
        if (isCopyModeRendering) {
            try { div.setAttribute('draggable', 'true'); } catch(_) {}
        }
                    const rowEl = document.createElement('div');
                    rowEl.className = 'template-item-row';
                let handleEl = null;
                if (isCopyModeRendering) {
                    handleEl = document.createElement('div');
                    handleEl.className = 'drag-handle';
                    try {
                        handleEl.style.flex = '0 0 auto';
                        handleEl.style.padding = '4px';
                        handleEl.style.color = '#6c757d';
                        handleEl.title = 'ドラッグして並び替え (↑↓キーでも操作可能)';
                        handleEl.setAttribute('aria-label', 'ドラッグして並び替え');
                        handleEl.tabIndex = 0;
                        handleEl.setAttribute('role', 'button');
                    } catch(_) {}
                    try { clearChildren(handleEl); handleEl.appendChild(createIcon('drag-handle')); } catch(_) {}
                    try {
                        handleEl.addEventListener('keydown', (e) => {
                            if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
                            e.preventDefault();
                            const current = handleEl.closest('.template-item');
                            if (!current) return;
                            const parent = current.parentNode;
                            if (!parent) return;
                            const siblings = Array.from(parent.querySelectorAll('.template-item'));
                            const currentIndex = siblings.indexOf(current);
                            const isUp = e.key === 'ArrowUp';
                            const target = isUp ? current.previousElementSibling : current.nextElementSibling;
                            if (!target) return;
                            if (isUp) {
                                parent.insertBefore(current, target);
                            } else {
                                parent.insertBefore(current, target.nextSibling);
                            }
                            try { if (typeof window.highlightAndScrollTemplateEl === 'function') window.highlightAndScrollTemplateEl(current); } catch(_) {}
                            raf(() => { try { handleEl.focus(); } catch(_) {} });
                            try { updateSortIndex(); scheduleSendOrder(); } catch(_) {}
                        });
                    } catch(_) {}
                }
                    const mainEl = document.createElement('div');
                    mainEl.className = 'template-item-main';
                    const titleEl = document.createElement('div');
                    titleEl.className = 'template-title';
                    titleEl.textContent = title;
                    // 長文タイトルでも全体を確認できるよう、ツールチップを付与
                    try {
                        titleEl.setAttribute('title', title);
                        titleEl.setAttribute('aria-label', title);
                    } catch(_) {}
                    try {
                        if (selectedTemplateMeta && selectedTemplateMeta.key != null && String(selectedTemplateMeta.key) === String(item.key)) {
                            div.classList.add('is-current');
                            const badge = document.createElement('span');
                            badge.className = 'current-badge';
                            badge.textContent = '現在選択中';
                            titleEl.appendChild(badge);
                        }
                    } catch(_) {}
                    const bodyEl = document.createElement('div');
                    bodyEl.className = 'template-body';
                    bodyEl.textContent = content;
                    // 候補本文にも同様にツールチップを付与し、長文の確認を容易にする
                    try {
                        bodyEl.setAttribute('title', content);
                        bodyEl.setAttribute('aria-label', content);
                    } catch(_) {}
                    mainEl.appendChild(titleEl);
                    mainEl.appendChild(bodyEl);
                    let actionsEl = null;
                    if (!isCopyModeRendering) {
                        actionsEl = createActionsMenu();
                        // is_del=1 の場合は削除ではなく復帰表示
                        try {
                            const delBtn = actionsEl.querySelector('.menu-delete');
                            if (delBtn) delBtn.textContent = (Number(item.is_del) === 1) ? '復帰' : '削除';
                        } catch(_) {}
                    }
                    if (handleEl) rowEl.appendChild(handleEl);
                    rowEl.appendChild(mainEl);
                    if (actionsEl) rowEl.appendChild(actionsEl);
                    div.appendChild(rowEl);

                    mainEl.addEventListener('click', async () => {
                    if (Number(item.is_del) === 1) return; // 削除フラグのものは選択不可

                    const templateModal = document.getElementById('templateModal');
                    const latestContent = item && typeof item.content !== 'undefined' ? (item.content ?? '') : content;

                    if (isCopyModeRendering) {
                        // コピー専用モード: 本文を変更せずクリップボードへ転送してモーダルを閉じる
                        let copySucceeded = false;
                        try {
                            copySucceeded = await copyTextToClipboard(latestContent);
                        } catch (err) {
                            copySucceeded = false;
                            try { reportError('copy_template_selection', err, { level: 'warn' }); } catch(_) {}
                        }

                        if (templateModal) {
                            try {
                    templateModalUI.close();
                    updateFooterHiddenState();
                            } catch(_) {}
                            try {
                                delete templateModal.dataset.mode;
                                updateTemplateHeaderForMode && updateTemplateHeaderForMode();
                            } catch(_) {}
                        }
                        
                        // コピーモード時は元のタイトルに戻す
                        try {
                            const titleElement = document.querySelector('.title-section h3');
                            if (titleElement && titleElement.dataset.originalTitle) {
                                titleElement.textContent = titleElement.dataset.originalTitle;
                                delete titleElement.dataset.originalTitle;
                            }
                        } catch(_) {}

                        try {
                            if (typeof showToast === 'function') {
                                showToast(copySucceeded ? 'クリップボードへコピーしました。' : 'コピーに失敗しました。', {
                                    role: copySucceeded ? 'status' : 'alert',
                                    duration: copySucceeded ? 2200 : 2600
                                });
                            }
                        } catch(_) {}
                        return;
                    }

                    manageUndoRedo(originalTextarea);
                    // クリック時点の最新データを使用する（編集後に反映されない問題の修正）
                    originalTextarea.value = latestContent;
                    raf(() => fitTextareaToContent(originalTextarea));
                    synchronizeForm();
                    updateDisplayText();
                    if (templateModal) {
                        templateModalUI.close();
                        updateFooterHiddenState();
                        templateModalUI.close();
                        updateFooterHiddenState();
                    }

                    // タイトルを更新（is_blank=1のときは(テンプレート未選択)）
                    const titleElement = document.querySelector('.title-section h3');
                    if (titleElement) {
                        // 一時保存されたタイトルをクリア（新しいタイトルに置き換えるため）
                        try { delete titleElement.dataset.originalTitle; } catch(_) {}
                        const isBlankHeader = Number(item.is_blank) === 1;
                        const currentTitle = item && typeof item.title !== 'undefined' ? (item.title ?? '(無題)') : title;
                        titleElement.textContent = isBlankHeader ? '(テンプレート未選択)' : `${currentTitle}`;
                    }

                    // 選択されたテンプレートのkey等を保持
                    const selectedTitleNow = item && typeof item.title !== 'undefined' ? (item.title ?? '(無題)') : title;
                    selectedTemplateMeta = { key: item.key ?? null, title: selectedTitleNow, is_blank: Number(item.is_blank) || 0 };
                    isSelectedTemplateKeyLocked = true; // 以降は renderUI で上書きしない

                        // 一覧側の強調表示と「現在選択中」バッジを更新
                        try {
                            document.querySelectorAll('#templateList .template-item').forEach(el => el.classList.remove('is-current'));
                            document.querySelectorAll('#templateList .template-item .current-badge').forEach(badge => {
                                try { badge.remove(); } catch(_) {}
                            });
                            div.classList.add('is-current');
                            const t = div.querySelector('.template-title');
                            if (t && !t.querySelector('.current-badge')) {
                                const badge = document.createElement('span');
                                badge.className = 'current-badge';
                                badge.textContent = '現在選択中';
                                t.appendChild(badge);
                            }
                        } catch(_) {}

                    if (!isCopyModeRendering && window.FileMaker != null && item.key != null) {
                        const key = item.key;
                        fmCall && fmCall('addon_remarks_web_template_selected', String(key), 5);
                    }
                });

                    if (!isCopyModeRendering && actionsEl) {
                        const moreBtn = actionsEl.querySelector('.more-btn');
                        const moreMenu = actionsEl.querySelector('.more-menu');
                        if (moreBtn && moreMenu) {
                            moreBtn.addEventListener('click', (e) => {
                                e.stopPropagation();
                                const isOpen = moreMenu.classList.contains('open');
                                document.querySelectorAll('#templateList .more-menu.open').forEach(mm => mm.classList.remove('open'));
                                if (!isOpen) {
                                    moreMenu.classList.add('open');
                                    try { window.positionTemplateMoreMenu(moreMenu); } catch(_) {}
                                }
                            });
                            document.addEventListener('click', (e) => {
                                if (!actionsEl.contains(e.target)) moreMenu.classList.remove('open');
                            });
                        }
                        const editBtn = actionsEl.querySelector('.menu-edit');
                        if (editBtn && moreMenu) {
                            editBtn.addEventListener('click', (e) => {
                                e.stopPropagation();
                                moreMenu.classList.remove('open');
                                window.enterTemplateEditMode(item);
                            });
                        }
                        const deleteBtn = actionsEl.querySelector('.menu-delete');
                        if (deleteBtn && moreMenu) {
                            deleteBtn.addEventListener('click', (e) => {
                                e.stopPropagation();
                                moreMenu.classList.remove('open');
                                const key = item.key;
                                if (!key) return;
                                const isDeleted = (Number(item.is_del) === 1);
                                if (isDeleted) {
                                    showConfirmModal(
                                        'このテンプレートを復帰します。よろしいですか？',
                                        () => {
                                            if (window.FileMaker != null) {
                                                raf(() => { try { fmCall && fmCall('addon_remarks_web_template_restore', String(key ?? '')); } catch(_) {} });
                                            }
                                            try {
                                                item.is_del = 0;
                                                const delBtn = actionsEl.querySelector('.menu-delete');
                                                if (delBtn) delBtn.textContent = '削除';
                                                try { div.classList.remove('is-deleted'); } catch(_) {}
                                            } catch(_) {}
                                        },
                                        () => { try { window.highlightAndScrollTemplateEl(div); } catch(_) {} },
                                        { confirmLabel: '復帰', cancelLabel: 'キャンセル' }
                                    );
                                } else {
                                    showConfirmModal(
                                        'このテンプレートを削除します。よろしいですか？',
                                        () => {
                                            if (window.FileMaker != null) {
                                                raf(() => { try { fmCall && fmCall('addon_remarks_web_template_delete', String(key ?? '')); } catch(_) {} });
                                            }
                                            try { div.remove(); } catch(_) {}
                                        },
                                        () => { try { window.highlightAndScrollTemplateEl(div); } catch(_) {} },
                                        { confirmLabel: '削除', cancelLabel: 'キャンセル', confirmClass: 'button-danger' }
                                    );
                                }
                            });
                        }
                    }

                    try {
                        const isCopyModeRendering = !!(document.getElementById('templateModal') && document.getElementById('templateModal').dataset.mode === 'copy');
                        if (isCopyModeRendering && copyListEl) copyListEl.appendChild(div);
                        else templateListEl.appendChild(div);
                    } catch(_) { templateListEl.appendChild(div); }
            });

            // 戻り時の編集対象を強調
            try { if (typeof window.markLastEditedInList === 'function') window.markLastEditedInList(); } catch(_) {}

            // フレーズ選択カードとテンプレートモーダルは独立して併存可能にする
            // try { closePhraseModalIfOpen(); } catch(_) {}
            try { templateModalUI.openSelect(); } catch(_) {}
            try { const mc = document.querySelector('#templateModal .modal-content'); if (mc) mc.classList.add('fullscreen'); } catch(_) {}
            updateFooterHiddenState();

            // 検索ボックスにフォーカス・UI更新
            focusSearchAndUpdateUI();
            // クリアボタンの表示更新
            const clearBtn2 = document.getElementById('templateSearchClear');
            if (clearBtn2 && templateSearchEl) {
                // 中央揃えのため inline-flex を使用
                clearBtn2.style.display = templateSearchEl.value.trim().length > 0 ? 'inline-flex' : 'none';
            }
        }
        // ページングボタンを生成する関数
        function renderPagination(currentPage, totalPages) {
            const paginationEl = document.getElementById('templatePagination');
            if (!paginationEl || totalPages <= 1) {
                if (paginationEl) paginationEl.style.display = 'none';
                return;
            }
            
            paginationEl.style.display = 'flex';
            clearChildren(paginationEl);
            
            // 前のページボタン
            const prevButton = document.createElement('button');
            prevButton.className = 'pagination-button';
            prevButton.textContent = '前へ';
            prevButton.disabled = currentPage <= 1;
            prevButton.addEventListener('click', () => {
                if (currentPage > 1) {
                    // ページ遷移は常にリモート検索を実行
                    performTemplateSearch(currentSearchTerm, currentPage - 1, true);
                }
            });
            paginationEl.appendChild(prevButton);
            
            // ページ情報
            const pageInfo = document.createElement('span');
            pageInfo.className = 'pagination-info';
            pageInfo.textContent = `${currentPage} / ${totalPages} ページ`;
            paginationEl.appendChild(pageInfo);
            
            // 次のページボタン
            const nextButton = document.createElement('button');
            nextButton.className = 'pagination-button';
            nextButton.textContent = '次へ';
            nextButton.disabled = currentPage >= totalPages;
            nextButton.addEventListener('click', () => {
                if (currentPage < totalPages) {
                    // ページ遷移は常にリモート検索を実行
                    performTemplateSearch(currentSearchTerm, currentPage + 1, true);
                }
            });
            paginationEl.appendChild(nextButton);
        }
        // テンプレート一覧の末尾に追加する関数
        // 入力形式:
        // - 単一アイテム: { key, title, content, is_del, is_blank }
        // - 配列: [{...}, {...}]
        // - Claris FileMaker 形式: { response: { data: [ { fieldData: {...} } ] } }
        window.appendTemplateToList = function(input) {
            try {
                // 正規化
                let items = [];
                let payload = input;
                if (typeof payload === 'string') {
                    try { payload = JSON.parse(payload); } catch(_) {}
                }
                if (payload && payload.response && Array.isArray(payload.response.data)) {
                    items = payload.response.data.map(rec => {
                        const fd = rec && rec.fieldData ? rec.fieldData : {};
                        return {
                            key: fd['primary_key'] ?? fd.id,
                            title: fd['name'] ?? fd['template_title'] ?? fd.title ?? '(無題)',
                            content: fd['template'] ?? fd['template_text'] ?? fd.content ?? '',
                            is_del: Number(fd['is_del'] ?? 0),
                            is_blank: Number(fd['is_blank'] ?? 0),
                            sort: (fd['sort'] != null ? Number(fd['sort']) : null),
                            recordId: (rec && rec.recordId != null ? String(rec.recordId) : null)
                        };
                    });
                } else if (Array.isArray(payload)) {
                    items = payload;
                } else if (payload && typeof payload === 'object') {
                    items = [payload];
                }

                if (!items || items.length === 0) return;

                const templateListEl = document.getElementById('templateList');
                if (!templateListEl) return;

                // 「該当なし」表示がある場合は除去
                try {
                    const emptyEl = templateListEl.querySelector('.no-results');
                    if (emptyEl) emptyEl.remove();
                } catch(_) {}
                const templateMetaEl = document.getElementById('templateMeta');
                let appended = 0;
                items.forEach((item) => {
                    try {
                        const originalTextarea = document.getElementById('original-textarea');
                        const templateModal = document.getElementById('templateModal');
                        const title = item.title ?? '(無題)';
                        const content = item.content ?? '';

                        const div = document.createElement('div');
                        div.className = 'template-item' + ((Number(item.is_del) === 1) ? ' is-deleted' : '');
                        try { div.dataset.key = String(item.key ?? ''); } catch(_) {}
                        try { if (item.sort != null) div.dataset.sort = String(item.sort); } catch(_) {}
                        try { if (item.recordId != null) div.dataset.recordId = String(item.recordId); } catch(_) {}
                        try { if (item.__noTemplate) div.dataset.noTemplate = '1'; } catch(_) {}

                        const rowEl = document.createElement('div');
                        rowEl.className = 'template-item-row';
                        const handleEl = document.createElement('div');
                        handleEl.className = 'drag-handle';
                        try {
                            handleEl.style.flex = '0 0 auto';
                            handleEl.style.padding = '4px';
                            handleEl.style.color = '#6c757d';
                            handleEl.title = '項目をドラッグして並び替え (↑↓キーでも操作可能)';
                            handleEl.tabIndex = 0;
                            handleEl.setAttribute('role', 'button');
                            handleEl.setAttribute('aria-label', 'ドラッグハンドル: 上下矢印キーで並び替え可能');
                            clearChildren(handleEl);
                            handleEl.appendChild(createIcon('drag-handle'));
                            if (item.__noTemplate) handleEl.style.visibility = 'hidden';
                        } catch(_) {}
                        try {
                            handleEl.addEventListener('keydown', (e) => {
                                if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
                                e.preventDefault();
                                const current = handleEl.closest('.template-item');
                                if (!current) return;
                                const parent = current.parentNode;
                                if (!parent) return;
                                const isUp = e.key === 'ArrowUp';
                                const items = Array.from(parent.querySelectorAll('.template-item'));
                                const noTemplateEl = parent.querySelector('.template-item[data-no-template="1"]');
                                const anchorIndex = noTemplateEl ? items.indexOf(noTemplateEl) : -1;
                                const currentIndex = items.indexOf(current);
                                let targetSibling = isUp ? current.previousElementSibling : current.nextElementSibling;
                                if (!targetSibling) return;
                                if (isUp) {
                                    const newIndex = currentIndex - 1;
                                    if (anchorIndex >= 0 && newIndex <= anchorIndex) {
                                        parent.insertBefore(current, noTemplateEl ? noTemplateEl.nextSibling : parent.firstChild);
                                    } else {
                                        parent.insertBefore(current, targetSibling);
                                    }
                                } else {
                                    parent.insertBefore(current, targetSibling.nextSibling);
                                }
                                try {
                                    const afterMoveItems = Array.from(parent.querySelectorAll('.template-item'));
                                    const idx = afterMoveItems.indexOf(current);
                                    const prev = afterMoveItems.slice(0, idx).reverse().find(el => el.dataset.noTemplate !== '1');
                                    const next = afterMoveItems.slice(idx + 1).find(el => el.dataset.noTemplate !== '1');
                                    const prevSort = prev && prev.dataset.sort != null ? Number(prev.dataset.sort) : (idx - 1);
                                    const nextSort = next && next.dataset.sort != null ? Number(next.dataset.sort) : (idx + 1);
                                    const newSort = (prevSort + nextSort) / 2;
                                    const recordId = current.dataset.recordId || current.getAttribute('data-recordid') || '';
                                    if (window.FileMaker != null && recordId) {
                                        const payload = { recordId: String(recordId), sort: newSort };
                                        try { fmCall && fmCall('addon_remarks_web_template_reorder', payload, 5); } catch(_) {}
                                    }
                                    current.dataset.sort = String(newSort);
                                    try { if (typeof window.highlightAndScrollTemplateEl === 'function') window.highlightAndScrollTemplateEl(current); } catch(_) {}
                                    raf(() => { try { handleEl.focus(); } catch(_) {} });
                                } catch(_) {}
                            });
                        } catch(_) {}
                        const mainEl = document.createElement('div');
                        mainEl.className = 'template-item-main';
                        const titleEl = document.createElement('div');
                        titleEl.className = 'template-title';
                        titleEl.textContent = title;
                        try {
                            titleEl.setAttribute('title', title);
                            titleEl.setAttribute('aria-label', title);
                        } catch(_) {}
                        const bodyEl = document.createElement('div');
                        bodyEl.className = 'template-body';
                        bodyEl.textContent = content;
                        try {
                            bodyEl.setAttribute('title', content);
                            bodyEl.setAttribute('aria-label', content);
                        } catch(_) {}
                        mainEl.appendChild(titleEl);
                        mainEl.appendChild(bodyEl);

                        const actionsEl = createActionsMenu();
                        try {
                            if (Number(item.is_blank) === 1) {
                                const mb = actionsEl.querySelector('.more-btn');
                                if (mb) mb.style.display = 'none';
                            }
                        } catch(_) {}
                        try {
                            const delBtn = actionsEl.querySelector('.menu-delete');
                            if (delBtn) delBtn.textContent = (Number(item.is_del) === 1) ? '復帰' : '削除';
                            if (Number(item.is_blank) === 1 && delBtn) delBtn.style.display = 'none';
                        } catch(_) {}

                        rowEl.appendChild(handleEl);
                        rowEl.appendChild(mainEl);
                        rowEl.appendChild(actionsEl);
                        div.appendChild(rowEl);
                        // ハンドル表示だけでなくHTML5 DnDが発火するよう item 自体をドラッグ可能に
                        try { if (!item.__noTemplate) div.setAttribute('draggable', 'true'); } catch(_) {}
                        // 本体クリックで選択適用
                        mainEl.addEventListener('click', () => {
                            if (Number(item.is_del) === 1) return; // 削除フラグのものは選択不可
                            manageUndoRedo(originalTextarea);
                            // クリック時点の最新データを使用
                            const currentContent = item && typeof item.content !== 'undefined' ? (item.content ?? '') : content;
                            originalTextarea.value = currentContent;
                            raf(() => fitTextareaToContent(originalTextarea));
                            synchronizeForm();
                            updateDisplayText();
                            if (templateModal) closeModal(templateModal);
                            updateFooterHiddenState();

                            // タイトル/メモ表示トグルを更新
                            const titleElement = document.querySelector('.title-section h3');
                            if (titleElement) {
                                // 一時保存されたタイトルをクリア（新しいタイトルに置き換えるため）
                                try { delete titleElement.dataset.originalTitle; } catch(_) {}
                                const isBlankHeader = Number(item.is_blank) === 1;
                                const currentTitle = item && typeof item.title !== 'undefined' ? (item.title ?? '(無題)') : title;
                                titleElement.textContent = isBlankHeader ? '(テンプレート未選択)' : `${currentTitle}`;
                            }
                            try {
                                const memoToggleWrapper = document.getElementById('toggleShowMemos')?.closest('div');
                                if (memoToggleWrapper) updateMemoToggleVisibility();
                            } catch(_) {}

                            // 選択されたテンプレートのkey等を保持（未選択行は is_blank=1）
                            try {
                                const selectedTitleNow = item && typeof item.title !== 'undefined' ? (item.title ?? '(無題)') : title;
                                selectedTemplateMeta = { key: item.__noTemplate ? null : (item.key ?? null), title: selectedTitleNow, is_blank: Number(item.is_blank) || 0 };
                                isSelectedTemplateKeyLocked = true;
                                updateEditButtonVisibility();
                            } catch(_) {}

                            // 一覧側の強調表示と「現在選択中」バッジを更新
                            try {
                                document.querySelectorAll('#templateList .template-item').forEach(el => el.classList.remove('is-current'));
                                document.querySelectorAll('#templateList .template-item .current-badge').forEach(badge => { try { badge.remove(); } catch(_) {} });
                                div.classList.add('is-current');
                                const t = div.querySelector('.template-title');
                                if (t && !t.querySelector('.current-badge')) {
                                    const badge = document.createElement('span');
                                    badge.className = 'current-badge';
                                    badge.textContent = '現在選択中';
                                    t.appendChild(badge);
                                }
                            } catch(_) {}

                            // 選択通知（テンプレートキーのみを引数として渡す）
                            if (window.FileMaker != null) {
                                const key = item.__noTemplate ? '' : (item.key ?? '');
                                window.FileMaker.PerformScriptWithOption('addon_remarks_web_template_selected', String(key), 5);
                            }
                        });

                        // 3点リーダーのメニュー動作
                        const moreBtn = actionsEl.querySelector('.more-btn');
                        const moreMenu = actionsEl.querySelector('.more-menu');
                        moreBtn.addEventListener('click', (e) => {
                            if (Number(item.is_blank) === 1) return; // 非表示扱い
                            e.stopPropagation();
                            const isOpen = moreMenu.classList.contains('open');
                            document.querySelectorAll('#templateList .more-menu.open').forEach(mm => mm.classList.remove('open'));
                            if (!isOpen) {
                                moreMenu.classList.add('open');
                                try { window.positionTemplateMoreMenu(moreMenu); } catch(_) {}
                            }
                        });
                        document.addEventListener('click', (e) => {
                            if (!actionsEl.contains(e.target)) moreMenu.classList.remove('open');
                        });
                        // 編集
                        actionsEl.querySelector('.menu-edit').addEventListener('click', (e) => {
                            e.stopPropagation();
                            moreMenu.classList.remove('open');
                            window.enterTemplateEditMode(item);
                        });
                        // 削除/復帰（is_del に応じて切替）
                        actionsEl.querySelector('.menu-delete').addEventListener('click', (e) => {
                            e.stopPropagation();
                            moreMenu.classList.remove('open');
                            const key = item.key;
                            if (!key) return;
                            const isDeleted = (Number(item.is_del) === 1);
                            if (isDeleted) {
                                // 復帰
                                showConfirmModal(
                                    'このテンプレートを復帰します。よろしいですか？',
                                    () => {
                                        if (window.FileMaker != null) {
                                            setTimeout(() => {
                                                try { window.FileMaker.PerformScriptWithOption('addon_remarks_web_template_restore', String(key ?? '')); } catch (_) {}
                                            }, 0);
                                        }
                                        // 楽観的更新: 復帰状態に切替
                                        try {
                                            item.is_del = 0;
                                            const delBtn = actionsEl.querySelector('.menu-delete');
                                            if (delBtn) delBtn.textContent = '削除';
                                            try { div.classList.remove('is-deleted'); } catch(_) {}
                                        } catch(_) {}
                                    },
                                    () => { try { window.highlightAndScrollTemplateEl(div); } catch(_) {} },
                                    { confirmLabel: '復帰', cancelLabel: 'キャンセル' }
                                );
                            } else {
                                // 削除
                                showConfirmModal(
                                    'このテンプレートを削除します。よろしいですか？',
                                    () => {
                                        if (window.FileMaker != null) {
                                            setTimeout(() => {
                                                try { window.FileMaker.PerformScriptWithOption('addon_remarks_web_template_delete', String(key ?? '')); } catch (_) {}
                                            }, 0);
                                        }
                                        try { div.remove(); } catch(_) {}
                                    },
                                    () => { try { window.highlightAndScrollTemplateEl(div); } catch(_) {} },
                                    { confirmLabel: '削除', cancelLabel: 'キャンセル', confirmClass: 'button-danger' }
                                );
                            }
                        });

                        // 末尾へ追加
                        templateListEl.appendChild(div);
                        appended += 1;
                        try { window.highlightAndScrollTemplateEl(div); } catch(_) {}
                    } catch(_) {}
                });

                // メタの件数を+appended（「xx 件(…)?」の xx を加算）
                try {
                    if (appended > 0 && templateMetaEl && typeof templateMetaEl.textContent === 'string') {
                        const m = templateMetaEl.textContent.trim().match(/^(\d+)\s*件(.*)$/);
                        if (m) {
                            const n = (parseInt(m[1], 10) || 0) + appended;
                            templateMetaEl.textContent = `${n} 件${m[2] || ''}`;
                        }
                    }
                } catch(_) {}
            } catch(_) {}
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('appendTemplateToList', window.appendTemplateToList); } catch(_) {}
        // Claris FileMaker から渡されるテンプレート一覧を表示する関数
        // data.json のような構造に対応
        // 例: { response: { dataInfo: {...}, data: [ { fieldData: { primary_key, template_title, template_text, ... }, recordId, modId }, ... ] }, messages: [ ... ] }
        // 後方互換として { templates: [...] } や配列も許容
        window.applyTemplateList = function(data) {
            try {
                let payload = data;
                if (typeof payload === 'string') {
                    payload = JSON.parse(payload);
                }
                // data.json 形式の正規化（0件や401でも安全に処理）
                let list = [];
                let meta = null;
                if (payload && payload.response) {
                    meta = payload.response.dataInfo || null;
                    const rows = Array.isArray(payload.response.data) ? payload.response.data : [];
                    if (rows.length > 0) {
                        list = rows.map(rec => {
                            const fd = rec.fieldData || {};
                            return {
                                key: fd['primary_key'] ?? fd.id,
                                title: fd['name'] ?? fd['template_title'] ?? fd.title ?? '(無題)',
                                content: fd['template'] ?? fd['template_text'] ?? fd.content ?? '',
                                is_del: Number(fd['is_del'] ?? 0),
                                is_blank: Number(fd['is_blank'] ?? 0),
                                sort: (fd['sort'] != null ? Number(fd['sort']) : null),
                                recordId: (rec && rec.recordId != null ? String(rec.recordId) : null)
                            };
                        });
                    }
                } else if (Array.isArray(payload)) {
                    list = payload;
                } else if (payload && Array.isArray(payload.templates)) {
                    list = payload.templates;
                }

                // 1ページ目の先頭に「(テンプレート未選択)」を常に表示
                try {
                    if (Number(currentPage) === 1) {
                        const noTemplateItem = {
                            key: null,
                            title: '(テンプレート未選択)',
                            content: '',
                            is_del: 0,
                            is_blank: 1,
                            __noTemplate: true
                        };
                        list = [noTemplateItem, ...list];
                    }
                } catch(_) {}

                const templateModal = document.getElementById('templateModal');
                const templateListEl = document.getElementById('templateList');
                const templateMetaEl = document.getElementById('templateMeta');
                const templateSearchEl = document.getElementById('templateSearch');
                const templateCreateSection = document.getElementById('templateCreateSection');
                
                const originalTextarea = document.getElementById('original-textarea');

                // 検索完了フラグをリセット
                isSearching = false;

                // キャッシュを更新（初回または全件取得時）
                if (currentSearchTerm === '' && currentPage === 1) {
                    allTemplatesCache = [...list];
                    totalTemplateCount = meta ? (meta.totalRecordCount ?? meta.foundCount ?? list.length) : list.length;
                }

                // 作成フォームが出ていたら隠す
                if (templateCreateSection) templateCreateSection.style.display = 'none';
                clearChildren(templateListEl);
                // 初回表示時のみ検索ボックスをクリア
                if (currentSearchTerm === '' && templateSearchEl) {
                    templateSearchEl.value = '';
                }
                // 0件時のメッセージ
                if (list.length === 0) {
                    const nores = document.createElement('div');
                    nores.className = 'no-results';
                    try {
                        const modalForMode = document.getElementById('templateModal');
                        const isCopyMode = !!(modalForMode && modalForMode.dataset.mode === 'copy');
                        nores.textContent = isCopyMode ? 'コピー元の候補がありません。' : '該当するテンプレートは見つかりませんでした';
                    } catch(_) {
                        nores.textContent = '該当するテンプレートは見つかりませんでした';
                    }
                    templateListEl.appendChild(nores);
                }
                
                // ページング情報を更新（foundが不明な場合のフォールバック含む）
                let found = null;
                if (meta) {
                    const total = meta.totalRecordCount ?? meta.returnedCount ?? list.length;
                    found = meta.foundCount ?? total;
                    isFoundTotalKnown = meta.foundCount != null || meta.totalRecordCount != null || meta.returnedCount != null;
                }
                lastReturnedCount = list.length;
                const limit = lastPageLimit;
                if (!isFoundTotalKnown) {
                    // 推定: 取得件数がlimit未満なら最終ページ、ちょうどlimitなら次ページありと仮定
                    if (lastReturnedCount < limit) {
                        totalPages = currentPage;
                    } else {
                        totalPages = currentPage + 1;
                    }
                } else {
                    const f = found ?? list.length;
                    totalPages = Math.max(1, Math.ceil(f / limit));
                }
                if (templateMetaEl) {
                    const searchInfo = currentSearchTerm ? ` (検索: "${currentSearchTerm}")` : '';
                    const displayCount = found ?? (currentPage - 1) * limit + lastReturnedCount;
                    templateMetaEl.textContent = `${displayCount} 件${searchInfo}`;
                }
                renderPagination(currentPage, totalPages);

                list.forEach(item => {
                    const title = item.title ?? '(無題)';
                    const content = item.content ?? '';

                const div = document.createElement('div');
                div.className = 'template-item' + ((Number(item.is_del) === 1) ? ' is-deleted' : '');
                div.dataset.key = item.key; // キーをdata-key属性に追加
                // 並べ替え用メタ（存在すれば付与）
                try { if (item.sort != null) div.dataset.sort = String(item.sort); } catch(_) {}
                try { if (item.recordId != null) div.dataset.recordId = String(item.recordId); } catch(_) {}
                try { if (item.__noTemplate) div.dataset.noTemplate = '1'; } catch(_) {}
                const rowEl = document.createElement('div');
                rowEl.className = 'template-item-row';
                // 左側にドラッグ用ハンドルを配置（フレーズ選択と同じUI）
                const handleEl = document.createElement('div');
                handleEl.className = 'drag-handle';
                try {
                    handleEl.style.flex = '0 0 auto';
                    handleEl.style.padding = '4px';
                    handleEl.style.color = '#6c757d';
                    handleEl.title = '項目をドラッグして並び替え (↑↓キーでも操作可能)';
                    handleEl.tabIndex = 0;
                    handleEl.setAttribute('role', 'button');
                    handleEl.setAttribute('aria-label', 'ドラッグハンドル: 上下矢印キーで並び替え可能');
                    clearChildren(handleEl);
                    handleEl.appendChild(createIcon('drag-handle'));
                    // (テンプレート未選択) にはハンドルを見せない（レイアウトは保持）
                    if (item.__noTemplate) handleEl.style.visibility = 'hidden';
                } catch(_) {}
                // キーボード操作で上下移動（テンプレ未選択より上へは移動不可）
                try {
                    handleEl.addEventListener('keydown', (e) => {
                        if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
                        e.preventDefault();
                        const current = handleEl.closest('.template-item');
                        if (!current) return;
                        const parent = current.parentNode;
                        if (!parent) return;
                        const isUp = e.key === 'ArrowUp';
                        const items = Array.from(parent.querySelectorAll('.template-item'));
                        const noTemplateEl = parent.querySelector('.template-item[data-no-template="1"]');
                        const anchorIndex = noTemplateEl ? items.indexOf(noTemplateEl) : -1;
                        const currentIndex = items.indexOf(current);
                        let targetSibling = isUp ? current.previousElementSibling : current.nextElementSibling;
                        if (!targetSibling) return;
                        // アンカーより上は禁止
                        if (isUp) {
                            const newIndex = currentIndex - 1;
                            if (anchorIndex >= 0 && newIndex <= anchorIndex) {
                                // ちょうどアンカー直下に固定
                                parent.insertBefore(current, noTemplateEl ? noTemplateEl.nextSibling : parent.firstChild);
                            } else {
                                parent.insertBefore(current, targetSibling);
                            }
                        } else {
                            parent.insertBefore(current, targetSibling.nextSibling);
                        }
                        // 並び順を Claris FileMaker へ通知（ドロップ時の計算と同等）
                        try {
                            const afterMoveItems = Array.from(parent.querySelectorAll('.template-item'));
                            const idx = afterMoveItems.indexOf(current);
                            const prev = afterMoveItems.slice(0, idx).reverse().find(el => el.dataset.noTemplate !== '1');
                            const next = afterMoveItems.slice(idx + 1).find(el => el.dataset.noTemplate !== '1');
                            const prevSort = prev && prev.dataset.sort != null ? Number(prev.dataset.sort) : (idx - 1);
                            const nextSort = next && next.dataset.sort != null ? Number(next.dataset.sort) : (idx + 1);
                            const newSort = (prevSort + nextSort) / 2;
                            const recordId = current.dataset.recordId || current.getAttribute('data-recordid') || '';
                            if (window.FileMaker != null && recordId) {
                                const payload = JSON.stringify({ recordId: String(recordId), sort: newSort });
                                try { window.FileMaker.PerformScriptWithOption('addon_remarks_web_template_reorder', payload, 5); } catch(_) {}
                            }
                            current.dataset.sort = String(newSort);
                            // 既存ハイライト機構で視認性を高める
                            try { if (typeof window.highlightAndScrollTemplateEl === 'function') window.highlightAndScrollTemplateEl(current); } catch(_) {}
                            // フォーカス維持
                            raf(() => { try { handleEl.focus(); } catch(_) {} });
                        } catch(_) {}
                    });
                } catch(_) {}
                rowEl.appendChild(handleEl);
                const mainEl = document.createElement('div');
                mainEl.className = 'template-item-main';
                const titleEl = document.createElement('div');
                titleEl.className = 'template-title';
                titleEl.textContent = title;
                try {
                    titleEl.setAttribute('title', title);
                    titleEl.setAttribute('aria-label', title);
                } catch(_) {}
                try {
                    const isCurrentlySelected = (
                        (selectedTemplateMeta && selectedTemplateMeta.key != null && String(selectedTemplateMeta.key) === String(item.key)) ||
                        (selectedTemplateMeta && (selectedTemplateMeta.key == null) && Number(selectedTemplateMeta.is_blank) === 1 && item.__noTemplate === true)
                    );
                    if (isCurrentlySelected) {
                        div.classList.add('is-current');
                        const badge = document.createElement('span');
                        badge.className = 'current-badge';
                        badge.textContent = '現在選択中';
                        titleEl.appendChild(badge);
                    }
                } catch(_) {}
                const bodyEl = document.createElement('div');
                bodyEl.className = 'template-body';
                bodyEl.textContent = content;
                try {
                    bodyEl.setAttribute('title', content);
                    bodyEl.setAttribute('aria-label', content);
                } catch(_) {}
                mainEl.appendChild(titleEl);
                mainEl.appendChild(bodyEl);
                const actionsEl = createActionsMenu();
                // is_blank=1 のテンプレートは3点リーダーを非表示
                try {
                    if (Number(item.is_blank) === 1) {
                        const mb = actionsEl.querySelector('.more-btn');
                        if (mb) mb.style.display = 'none';
                    }
                } catch(_) {}
                // is_del が 1 の場合は「復帰」表示、0 なら「削除」表示
                try {
                    const delBtn = actionsEl.querySelector('.menu-delete');
                    if (delBtn) delBtn.textContent = (Number(item.is_del) === 1) ? '復帰' : '削除';
                    if (Number(item.is_blank) === 1 && delBtn) delBtn.style.display = 'none';
                } catch(_) {}
                rowEl.appendChild(mainEl);
                rowEl.appendChild(actionsEl);
                div.appendChild(rowEl);
                // テンプレート一覧はカード全体をドラッグ可能（ただし (テンプレート未選択) は不可）
                try { if (!item.__noTemplate) div.setAttribute('draggable', 'true'); } catch(_) {}
                // 本体クリックで選択適用
                mainEl.addEventListener('click', () => {
                    if (Number(item.is_del) === 1) return; // 削除フラグのものは選択不可
                        manageUndoRedo(originalTextarea);
                        // クリック時点の最新データを使用
                        const currentContent = item && typeof item.content !== 'undefined' ? (item.content ?? '') : content;
                        originalTextarea.value = currentContent;
                        raf(() => fitTextareaToContent(originalTextarea));
                        synchronizeForm();
                        updateDisplayText();
                        closeModal(templateModal);
                        try { document.body.classList.remove('header-dimmed'); } catch (_) {}
                        updateFooterHiddenState();

                        // タイトル/メモ表示トグルを更新
                        const titleElement = document.querySelector('.title-section h3');
                        if (titleElement) {
                            // 一時保存されたタイトルをクリア（新しいタイトルに置き換えるため）
                            try { delete titleElement.dataset.originalTitle; } catch(_) {}
                            const isBlankHeader = Number(item.is_blank) === 1;
                            const currentTitle = item && typeof item.title !== 'undefined' ? (item.title ?? '(無題)') : title;
                            titleElement.textContent = isBlankHeader ? '(テンプレート未選択)' : `${currentTitle}`;
                        }
                        try {
                            const memoToggleWrapper = document.getElementById('toggleShowMemos')?.closest('div');
                            if (memoToggleWrapper) updateMemoToggleVisibility();
                        } catch(_) {}

                        // 選択されたテンプレートのkey等を保持（未選択行は is_blank=1）
                        const selectedTitleNow = item && typeof item.title !== 'undefined' ? (item.title ?? '(無題)') : title;
                        selectedTemplateMeta = { key: item.__noTemplate ? null : (item.key ?? null), title: selectedTitleNow, is_blank: Number(item.is_blank) || 0 };
                        isSelectedTemplateKeyLocked = true; // 以降は renderUI で上書きしない
                        updateEditButtonVisibility();

                        // 一覧側の強調表示と「現在選択中」バッジを更新
                        try {
                            document.querySelectorAll('#templateList .template-item').forEach(el => el.classList.remove('is-current'));
                            // 既存の「現在選択中」バッジを全て除去
                            document.querySelectorAll('#templateList .template-item .current-badge').forEach(badge => {
                                try { badge.remove(); } catch(_) {}
                            });
                            div.classList.add('is-current');
                            const t = div.querySelector('.template-title');
                            if (t && !t.querySelector('.current-badge')) {
                                const badge = document.createElement('span');
                                badge.className = 'current-badge';
                                badge.textContent = '現在選択中';
                                t.appendChild(badge);
                            }
                        } catch(_) {}

                        // 選択通知（テンプレートキーのみを引数として渡す）
                        if (window.FileMaker != null) {
                            const key = item.__noTemplate ? '' : (item.key ?? '');
                            fmCall && fmCall('addon_remarks_web_template_selected', String(key), 5);
                        }
                    });
                // 3点リーダーのメニュー動作
                const moreBtn = actionsEl.querySelector('.more-btn');
                const moreMenu = actionsEl.querySelector('.more-menu');
                moreBtn.addEventListener('click', (e) => {
                    if (Number(item.is_blank) === 1) return; // 非表示扱い
                    e.stopPropagation();
                    const isOpen = moreMenu.classList.contains('open');
                    document.querySelectorAll('#templateList .more-menu.open').forEach(mm => mm.classList.remove('open'));
                    if (!isOpen) {
                        moreMenu.classList.add('open');
                        try { window.positionTemplateMoreMenu(moreMenu); } catch(_) {}
                    }
                });
                document.addEventListener('click', (e) => {
                    if (!actionsEl.contains(e.target)) moreMenu.classList.remove('open');
                });
                // 編集
                actionsEl.querySelector('.menu-edit').addEventListener('click', (e) => {
                    if (Number(item.is_blank) === 1) return; // 非表示扱い
                    e.stopPropagation();
                    window.enterTemplateEditMode(item);
                });
                // 削除/復帰（is_del に応じて切替）
                actionsEl.querySelector('.menu-delete').addEventListener('click', (e) => {
                    if (Number(item.is_blank) === 1) return; // 非表示扱い
                    e.stopPropagation();
                    moreMenu.classList.remove('open');
                    const key = item.key;
                    if (!key) return;
                if (Number(item.is_del) === 1) {
                        // 復帰
                    showConfirmModal(
                        'このテンプレートを復帰します。よろしいですか？',
                        () => {
                                if (window.FileMaker != null) {
                                    setTimeout(() => {
                                        try { window.FileMaker.PerformScriptWithOption('addon_remarks_web_template_restore', String(key ?? '')); } catch (_) {}
                                    }, 0);
                                }
                                // 楽観的更新: 復帰状態に切替
                                try {
                                    item.is_del = 0;
                                    const delBtn = actionsEl.querySelector('.menu-delete');
                                    if (delBtn) delBtn.textContent = '削除';
                                    try { div.classList.remove('is-deleted'); } catch(_) {}
                                } catch(_) {}
                        },
                        () => { try { window.highlightAndScrollTemplateEl(div); } catch(_) {} },
                        { confirmLabel: '復帰', cancelLabel: 'キャンセル' }
                        );
                    } else {
                        // 削除
                    showConfirmModal(
                        'このテンプレートを削除します。よろしいですか？',
                        () => {
                                if (window.FileMaker != null) {
                                    setTimeout(() => {
                                        try { window.FileMaker.PerformScriptWithOption('addon_remarks_web_template_delete', String(key ?? '')); } catch (_) {}
                                    }, 0);
                                }
                                try { div.remove(); } catch(_) {}
                        },
                        () => { try { window.highlightAndScrollTemplateEl(div); } catch(_) {} },
                        { confirmLabel: '削除', cancelLabel: 'キャンセル', confirmClass: 'button-danger' }
                        );
                    }
                });

                templateListEl.appendChild(div);
                });

                try { templateModalUI.openSelect(); } catch(_) {}
                try { const mc = document.querySelector('#templateModal .modal-content'); if (mc) mc.classList.add('fullscreen'); } catch(_) {}
                updateFooterHiddenState();

                // ドラッグ&ドロップ（テンプレート一覧の順序入れ替え）
                (function enableTemplateListDnD(){
                    const listEl = templateListEl;
                    if (!listEl) return;
                    // 二重初期化防止（再描画時の多重リスナー起因でインジケータが複数出ないように）
                    if (listEl.__dndInitialized) return;
                    try { listEl.__dndInitialized = true; } catch(_) {}
                    let dragItem = null;
                    let dragOverRaf = null;
                    let indicator = null;
                    function ensureIndicator(){ if (!indicator){ indicator = document.createElement('div'); indicator.className = 'drop-indicator active'; } return indicator; }
                    function clearIndicator(){
                        try {
                            if (indicator && indicator.parentNode) {
                                indicator.parentNode.removeChild(indicator);
                            }
                        } catch(_) {}
                        try {
                            // カード内（templateModal）とリスト内の残存インジケータを全除去
                            const scopeEl = (typeof templateModal !== 'undefined' && templateModal) ? templateModal : listEl;
                            if (scopeEl && scopeEl.querySelectorAll) {
                                scopeEl.querySelectorAll('.drop-indicator').forEach(el => {
                                    if (el && el.parentNode) el.parentNode.removeChild(el);
                                });
                            }
                        } catch(_) {}
                        indicator = null;
                    }
                    function cancelOverRAF(){ try { if (dragOverRaf) cancelAnimationFrame(dragOverRaf); } catch(_) {} dragOverRaf = null; }
                    listEl.addEventListener('dragstart', (e) => {
                        const it = e.target && e.target.closest('.template-item');
                        if (!it || it.dataset.noTemplate === '1') { e.preventDefault(); return; }
                        dragItem = it;
                        // 開始時点でスコープ内の残存インジケータを全除去
                        try {
                            const scopeEl = (typeof templateModal !== 'undefined' && templateModal) ? templateModal : listEl;
                            scopeEl.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el));
                        } catch(_) {}
                        try { e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain',''); } catch(_) {}
                        raf(()=>{ try { dragItem.classList.add('dragging'); listEl.classList.add('drag-active'); } catch(_) {} });
                        // グローバル側のフェイルセーフ（外でドロップ/キャンセルされた場合でも確実にクリア）
                        try {
                            const onGlobalDropOrEnd = () => { 
                                cancelOverRAF(); 
                                clearIndicator(); 
                                // 他リストの残留インジケータも保険で全消去
                                try { document.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el)); } catch(_) {}
                                try { document.querySelectorAll('.template-list.drag-active').forEach(el => el.classList.remove('drag-active')); } catch(_) {}
                            };
                            document.addEventListener('drop', onGlobalDropOrEnd, { once: true });
                            document.addEventListener('dragend', onGlobalDropOrEnd, { once: true });
                            window.addEventListener('blur', onGlobalDropOrEnd, { once: true });
                            // 手を離した瞬間にも消す（即時応答）
                            document.addEventListener('pointerup', onGlobalDropOrEnd, { once: true, capture: true });
                            document.addEventListener('mouseup', onGlobalDropOrEnd, { once: true, capture: true });
                        } catch(_) {}
                    });
                    listEl.addEventListener('dragend', () => {
                        try { if (dragItem) dragItem.classList.remove('dragging'); listEl.classList.remove('drag-active'); } catch(_) {}
                        cancelOverRAF();
                        dragItem = null; clearIndicator();
                        // グローバルにも保険で除去
                        try { document.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el)); } catch(_) {}
                        try { document.querySelectorAll('.template-list.drag-active').forEach(el => el.classList.remove('drag-active')); } catch(_) {}
                    });
                    listEl.addEventListener('dragover', (e) => {
                        e.preventDefault(); if (!dragItem) return;
                        const evt = e;
                        if (dragOverRaf) cancelAnimationFrame(dragOverRaf);
                        dragOverRaf = requestAnimationFrame(()=>{
                            let target = evt.target && (evt.target.closest && evt.target.closest('.template-item'));
                            if (!target || target === dragItem) return;
                            const rect = target.getBoundingClientRect();
                            const after = (evt.clientY - rect.top) > rect.height/2;
                            const ind = ensureIndicator();
                            // 同一スコープ内の他インジケータを全除去（自分以外）
                            try {
                                const scopeEl = (typeof templateModal !== 'undefined' && templateModal) ? templateModal : listEl;
                                scopeEl.querySelectorAll('.drop-indicator').forEach(el => { if (el !== ind && el.parentNode) el.parentNode.removeChild(el); });
                            } catch(_) {}
                            const parent = target.parentNode;
                            const noTemplateEl = parent && parent.querySelector && parent.querySelector('.template-item[data-no-template="1"]');
                            if (noTemplateEl && parent === noTemplateEl.parentNode) {
                                const items = Array.from(parent.querySelectorAll('.template-item'));
                                const anchorIndex = items.indexOf(noTemplateEl);
                                const targetIndex = items.indexOf(target);
                                const insertIndex = after ? (targetIndex + 1) : targetIndex;
                                if (insertIndex <= anchorIndex) {
                                    parent.insertBefore(ind, noTemplateEl.nextSibling);
                                    return;
                                }
                            }
                            if (after) parent.insertBefore(ind, target.nextSibling); else parent.insertBefore(ind, target);
                        });
                    });
                    listEl.addEventListener('drop', (e) => {
                        e.preventDefault(); if (!dragItem || !indicator || !indicator.parentNode) return;
                        const parent = indicator.parentNode;
                        parent.insertBefore(dragItem, indicator);
                        // 並び順の反映（ Claris FileMaker へ送信）
                        try {
                            const items = Array.from(parent.querySelectorAll('.template-item')); 
                            const idx = items.indexOf(dragItem);
                            // (テンプレ未選択)はソート対象外
                            const prev = items.slice(0, idx).reverse().find(el=>el.dataset.noTemplate!=='1');
                            const next = items.slice(idx+1).find(el=>el.dataset.noTemplate!=='1');
                            const prevSort = prev && prev.dataset.sort != null ? Number(prev.dataset.sort) : (idx-1);
                            const nextSort = next && next.dataset.sort != null ? Number(next.dataset.sort) : (idx+1);
                            const newSort = (prevSort + nextSort) / 2;
                            const recordId = dragItem.dataset.recordId || dragItem.getAttribute('data-recordid') || '';
                    if (window.FileMaker != null && recordId) {
                                const payload = JSON.stringify({ recordId: String(recordId), sort: newSort });
                                // 失敗しないよう try-catch で囲む
                                try { window.FileMaker.PerformScriptWithOption('addon_remarks_web_template_reorder', payload, 5); } catch(_) {}
                            }
                            // DOMにも反映
                            dragItem.dataset.sort = String(newSort);
                            // 既存ハイライト機構を使用して視覚的に強調
                            try { if (typeof window.highlightAndScrollTemplateEl === 'function') window.highlightAndScrollTemplateEl(dragItem); } catch(_) {}
                        } catch(_) {}
                        cancelOverRAF();
                        clearIndicator();
                        // グローバルにも保険で除去
                        try { document.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el)); } catch(_) {}
                        try { document.querySelectorAll('.template-list.drag-active').forEach(el => el.classList.remove('drag-active')); } catch(_) {}
                    });
                    // リスト外にカーソルが出た場合も消しておく（保険）
                    try { listEl.addEventListener('mouseleave', () => { if (!dragItem) return; cancelOverRAF(); clearIndicator(); }); } catch(_) {}
                })();

                // 戻り時の編集対象を強調（未発見なら markLastEditedInList 内で自動ページング）
                try { if (typeof window.markLastEditedInList === 'function') window.markLastEditedInList(); } catch(_) {}

                // 検索ボックスにフォーカス・UI更新
                focusSearchAndUpdateUI();
                // 初回表示時は未選択状態なので鉛筆ボタンを隠す
                updateEditButtonVisibility();
            } catch (e) {
                // パース失敗時は何もしない
            }
        }
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('applyTemplateList', window.applyTemplateList); } catch(_) {}

        // Claris FileMaker からページング検索結果を受け取る関数
        // 既存の applyTemplateList と同じ形式だが、検索状態を保持
        window.applyTemplateSearchResult = function(data) {
            // applyTemplateList と同じ処理を実行
            window.applyTemplateList(data);
        }
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('applyTemplateSearchResult', window.applyTemplateSearchResult); } catch(_) {}
        // Claris FileMaker 側(addon_remarks_web_template_selected)から呼ばれる想定の関数
        // 引数は addon_remarks_web_template_ok と同じ形のオブジェクト or そのJSON文字列
        // 次回も使用(persist=true)の値のみを現在のフォームへ上書き適用する
        window.applySelectedTemplateDefaults = function(payload) {
            try {
                let data = payload;
                if (typeof data === 'string') {
                    data = JSON.parse(data);
                }
                if (!data || typeof data !== 'object' || Array.isArray(data)) return;

                // 不正データを検出して早期リターン
                const isPlainObject = (v) => v != null && typeof v === 'object' && !Array.isArray(v);
                const isValidPayload = (() => {
                    // originalTemplate / instructions は未指定か文字列
                    if (Object.prototype.hasOwnProperty.call(data, 'originalTemplate') && data.originalTemplate != null && typeof data.originalTemplate !== 'string') return false;
                    if (Object.prototype.hasOwnProperty.call(data, 'instructions') && data.instructions != null && typeof data.instructions !== 'string') return false;

                    // accountTemplateSettings はオブジェクト、values もオブジェクト
                    if (Object.prototype.hasOwnProperty.call(data, 'accountTemplateSettings')) {
                        if (!isPlainObject(data.accountTemplateSettings)) return false;
                        if (Object.prototype.hasOwnProperty.call(data.accountTemplateSettings, 'values') && data.accountTemplateSettings.values != null && !isPlainObject(data.accountTemplateSettings.values)) return false;
                    }

                    // variables の基本構造チェック（新旧形式対応）
                    if (Object.prototype.hasOwnProperty.call(data, 'variables')) {
                        if (!isPlainObject(data.variables)) return false;
                        const vars = data.variables;
                        const hasFields = Object.prototype.hasOwnProperty.call(vars, 'fields');
                        const hasCheckbox = Object.prototype.hasOwnProperty.call(vars, 'checkbox');

                        if (hasFields && vars.fields != null && !isPlainObject(vars.fields)) return false;
                        if (hasCheckbox && vars.checkbox != null && !isPlainObject(vars.checkbox)) return false;

                        if (hasFields && isPlainObject(vars.fields)) {
                            for (const fieldName of Object.keys(vars.fields)) {
                                const entry = vars.fields[fieldName];
                                if (!isPlainObject(entry)) return false;
                                if (Object.prototype.hasOwnProperty.call(entry, 'persist') && typeof entry.persist !== 'boolean') return false;
                                if (Object.prototype.hasOwnProperty.call(entry, 'height') && typeof entry.height !== 'number') return false;
                                if (Object.prototype.hasOwnProperty.call(entry, 'type') && typeof entry.type !== 'string') return false;
                            }
                        }
                        if (hasCheckbox && isPlainObject(vars.checkbox)) {
                            for (const itemName of Object.keys(vars.checkbox)) {
                                const entry = vars.checkbox[itemName];
                                if (!isPlainObject(entry)) return false;
                                if (Object.prototype.hasOwnProperty.call(entry, 'persist') && typeof entry.persist !== 'boolean') return false;
                            }
                        }

                        if (!hasFields && !hasCheckbox) {
                            // 旧形式: variables は field -> entry(plain object)
                            for (const fieldName of Object.keys(vars)) {
                                const entry = vars[fieldName];
                                if (!isPlainObject(entry)) return false;
                                if (Object.prototype.hasOwnProperty.call(entry, 'persist') && typeof entry.persist !== 'boolean') return false;
                                if (Object.prototype.hasOwnProperty.call(entry, 'height') && typeof entry.height !== 'number') return false;
                                if (Object.prototype.hasOwnProperty.call(entry, 'type') && typeof entry.type !== 'string') return false;
                            }
                        }
                    }
                    return true;
                })();

                if (!isValidPayload) return;

                // 本文（テンプレート）変更は行わない

                // 優先: accountTemplateSettings.values（persistのみ抽出済み）
                let valuesMap = (data.accountTemplateSettings && data.accountTemplateSettings.values && typeof data.accountTemplateSettings.values === 'object')
                    ? data.accountTemplateSettings.values
                    : null;

                // フォールバック: variables から persist=true のものだけ抽出
                if (!valuesMap && data.variables && typeof data.variables === 'object') {
                    const tmp = {};
                    
                    // 新形式か旧形式かを判定
                    const isNewFormat = data.variables.checkbox || data.variables.fields;
                    
                    if (isNewFormat) {
                        // 新形式: checkbox と fields の両方から処理
                        if (data.variables.fields) {
                            Object.keys(data.variables.fields).forEach((fieldName) => {
                                const entry = data.variables.fields[fieldName] || {};
                                if (entry.persist) tmp[fieldName] = entry.value;
                            });
                        }
                        if (data.variables.checkbox) {
                            Object.keys(data.variables.checkbox).forEach((itemName) => {
                                const fieldName = `checkbox_${itemName}`;
                                const entry = data.variables.checkbox[itemName] || {};
                                if (entry.persist) tmp[fieldName] = entry.value;
                            });
                        }
                    } else {
                        // 旧形式: 従来通り処理（後方互換）
                        Object.keys(data.variables).forEach((field) => {
                            const entry = data.variables[field] || {};
                            if (entry.persist) tmp[field] = entry.value;
                        });
                    }
                    valuesMap = tmp;
                }

                // 型情報の収集（variableTypes優先、無ければ variables[*].type）
                const typesMap = {};
                if (data.variableTypes && typeof data.variableTypes === 'object') {
                    Object.keys(data.variableTypes).forEach((k) => {
                        const norm = normalizeFieldType(data.variableTypes[k]);
                        if (norm) typesMap[k] = norm;
                    });
                }
                if (data.variables && typeof data.variables === 'object') {
                    const isNewFormat = data.variables.checkbox || data.variables.fields;
                    
                    if (isNewFormat) {
                        // 新形式: checkbox と fields の両方から処理
                        if (data.variables.fields) {
                            Object.keys(data.variables.fields).forEach((fieldName) => {
                                const t = data.variables.fields[fieldName] && data.variables.fields[fieldName].type;
                                const norm = normalizeFieldType(t);
                                if (norm && !typesMap[fieldName]) typesMap[fieldName] = norm;
                            });
                        }
                        if (data.variables.checkbox) {
                            Object.keys(data.variables.checkbox).forEach((itemName) => {
                                const fieldName = `checkbox_${itemName}`;
                                // チェックボックスは型を設定しない
                                if (!typesMap[fieldName]) delete typesMap[fieldName];
                            });
                        }
                    } else {
                        // 旧形式: 従来通り処理（後方互換）
                        Object.keys(data.variables).forEach((field) => {
                            const t = data.variables[field] && data.variables[field].type;
                            const norm = normalizeFieldType(t);
                            if (norm && !typesMap[field]) typesMap[field] = norm;
                        });
                    }
                }

                // 高さの反映（variables[*].height があれば保持）
                let changedHeights = false;
                if (data.variables && typeof data.variables === 'object') {
                    const newHeights = { ...fieldHeights };
                    const isNewFormat = data.variables.checkbox || data.variables.fields;
                    
                    if (isNewFormat) {
                        // 新形式: fields のみ高さ情報を持つ
                        if (data.variables.fields) {
                            Object.keys(data.variables.fields).forEach((fieldName) => {
                                const h = data.variables.fields[fieldName] && data.variables.fields[fieldName].height;
                                if (typeof h === 'number' && h > 0) {
                                    const rounded = Math.round(h);
                                    if (newHeights[fieldName] !== rounded) changedHeights = true;
                                    newHeights[fieldName] = rounded;
                                }
                            });
                        }
                    } else {
                        // 旧形式: 従来通り処理（後方互換）
                        Object.keys(data.variables).forEach((field) => {
                            const h = data.variables[field] && data.variables[field].height;
                            if (typeof h === 'number' && h > 0) {
                                const rounded = Math.round(h);
                                if (newHeights[field] !== rounded) changedHeights = true;
                                newHeights[field] = rounded;
                            }
                        });
                    }
                    fieldHeights = newHeights;
                }

                // 永続フラグ情報の収集と反映
                let persistMap = {};
                if (data.variables && typeof data.variables === 'object') {
                    // 新形式か旧形式かを判定
                    const isNewFormat = data.variables.checkbox || data.variables.fields;
                    
                    if (isNewFormat) {
                        // 新形式: checkbox と fields の両方から処理
                        if (data.variables.fields) {
                            Object.keys(data.variables.fields).forEach((fieldName) => {
                                const entry = data.variables.fields[fieldName] || {};
                                if (entry.persist !== undefined) persistMap[fieldName] = !!entry.persist;
                            });
                        }
                        if (data.variables.checkbox) {
                            Object.keys(data.variables.checkbox).forEach((itemName) => {
                                const fieldName = `checkbox_${itemName}`;
                                const entry = data.variables.checkbox[itemName] || {};
                                if (entry.persist !== undefined) persistMap[fieldName] = !!entry.persist;
                            });
                        }
                    } else {
                        // 旧形式: 従来通り処理（後方互換）
                        Object.keys(data.variables).forEach((field) => {
                            const entry = data.variables[field] || {};
                            if (entry.persist !== undefined) persistMap[field] = !!entry.persist;
                        });
                    }
                }
                // persistedDefaultsに反映
                const prevPersist = { ...persistedDefaults.persist };
                const persistKeys = Object.keys(persistMap);
                const changedPersist = persistKeys.some((k) => prevPersist[k] !== persistMap[k]);
                if (persistKeys.length > 0 && changedPersist) {
                    persistedDefaults.persist = { ...prevPersist, ...persistMap };
                }

                // 型の反映（UIへ即時反映したいので永続側へ取り込む）
                const prevTypes = { ...(persistedDefaults.types || {}) };
                const typeKeys = Object.keys(typesMap);
                if (typeKeys.length > 0) {
                    // 正規化済み typesMap をマージ
                    const merged = { ...prevTypes };
                    typeKeys.forEach((k) => { merged[k] = typesMap[k]; });
                    persistedDefaults.types = merged;
                }

                const hasValues = !!(valuesMap && Object.keys(valuesMap).length > 0);
                if (hasValues) {
                    const varsForApply = {};
                    Object.keys(valuesMap).forEach((field) => {
                        const v = valuesMap[field];
                        const entry = { value: v, persist: true };
                        if (typesMap[field]) entry.type = typesMap[field];
                        varsForApply[field] = entry;
                    });
                    // 既存の仕組みに乗せて適用（UI/表示も更新）
                    window.applyVariables({ variables: varsForApply });
                } else {
                    // 値が無い場合は、型/高さ/永続フラグに変更があるときのみ再描画
                    if (changedHeights || changedTypes || changedPersist) {
                        synchronizeForm();
                        updateDisplayText();
                    }
                }
            } catch (_) {
                // 何もしない（安全に無視）
            }
        }
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('applySelectedTemplateDefaults', window.applySelectedTemplateDefaults); } catch(_) {}
        // アンドゥ・リドゥの履歴を管理（要素単位）
        const elementHistoryMap = new WeakMap();
        let isApplyingHistory = false;
        const MAX_HISTORY_ENTRIES = 300;
        // アンドゥ・リドゥを管理する関数
        function getHistoryFor(element) {
            let history = elementHistoryMap.get(element);
            if (!history) {
                // 初期値を現値として設定（空文字を強制投入しない）
                const initial = element.value ?? '';
                const undoStack = [initial];
                history = { undoStack, redoStack: [] };
                elementHistoryMap.set(element, history);
            }
            return history;
        }
        function manageUndoRedo(element) {
            if (isApplyingHistory) return;
            const { undoStack, redoStack } = getHistoryFor(element);
            const newValue = element.value ?? '';
            const last = undoStack.length > 0 ? undoStack[undoStack.length - 1] : undefined;
            if (last !== newValue) {
                undoStack.push(newValue);
                redoStack.length = 0; // 新しい変更があった場合、リドゥスタックをクリア
                if (undoStack.length > MAX_HISTORY_ENTRIES) {
                    undoStack.splice(0, undoStack.length - MAX_HISTORY_ENTRIES);
                }
            }
        }
        // change-nav の表示を安全に復元（件数 0 の場合は非表示）
        window.restoreChangeNavDisplay = function() {
            try {
                const changeNav = document.querySelector('.footer .change-nav');
                if (!changeNav) return;
                const len = (window.changeLogNavigator && typeof window.changeLogNavigator.getLength === 'function')
                    ? Number(window.changeLogNavigator.getLength())
                    : null;
                if (len === 0) {
                    changeNav.style.display = 'none';
                    return;
                }
                // 件数が 0 でない場合は、過去の保存値に関わらず確実に表示する
                changeNav.style.display = 'inline-flex';
            } catch(_) {}
        };

        // テキストエリアのイベントリスナーを追加
        const originalTextarea = document.getElementById('original-textarea');
        const directInput = document.getElementById('direct-input');
        // 初期状態をヒストリに投入（明示呼び出し不要だが念のため確保）
        if (originalTextarea) { getHistoryFor(originalTextarea); }

        // 内容のテキストエリアにアンドゥ・リドゥ機能を追加 + 入力時に自動フィット
        originalTextarea.addEventListener('input', (e) => {
            // 履歴積みはドキュメント側のデバウンス処理に委譲
            synchronizeForm();
            updateDisplayText();
            try { fitTextareaToContent(originalTextarea); } catch(_) {}
        });

        // 直接入力欄にも入力時の自動フィットを付与
        if (directInput) {
            directInput.addEventListener('input', () => {
                try { fitTextareaToContent(directInput); } catch(_) {}
            });
        }

        // iOS/iPad 判定: iPhone/iPad もしくは iPadOS(デスクトップUA) を含む
        (function markIOSClass(){
            try {
                const ua = navigator.userAgent || '';
                const isIOSUA = /iPad|iPhone|iPod/i.test(ua);
                const isIPadOSDesktopUA = (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
                const isIPad = /iPad/i.test(ua) || isIPadOSDesktopUA;
                if (isIOSUA || isIPadOSDesktopUA) {
                    try { document.documentElement.classList.add('is-ios'); } catch(_) {}
                }
                if (isIPad) {
                    try { document.documentElement.classList.add('is-ipad'); } catch(_) {}
                }
            } catch(_) {}
        })();

        // テキストエリアのキャレット位置ユーティリティ（重複排除）
        // caretUtilsInit: 全体備考の中央化は不要になったため削除

        // 備考欄: グローバル備考は自動リサイズを無効化（スクロール変動を抑止）
        (function setupGlobalNoteSizing(){
            const el = document.getElementById('global-note-textarea');
            if (!el) return;
            try {
                el.dataset.autogrowDisabled = 'true';
                // iOS では内部スクロールを維持
                if (document.documentElement.classList.contains('is-ios')) {
                    el.style.overflow = 'auto';
                    el.style.webkitOverflowScrolling = 'touch';
                }
            } catch(_) {}
        })();

        // 備考の自動高さ調整（初回のみ）は不要のため削除

        // 内容入力欄（original/direct）: iOSでは自動リサイズ無効化＋内部スクロール
        (function setupIOSBehaviorForContentAreas(){
            try {
                const isIOS = document.documentElement.classList.contains('is-ios');
                if (!isIOS) return;
                const original = document.getElementById('original-textarea');
                const direct = document.getElementById('direct-input');
                [original, direct].forEach((el) => {
                    if (!el) return;
                    try {
                        el.style.overflow = 'auto';
                        el.style.webkitOverflowScrolling = 'touch';
                        el.dataset.autogrowDisabled = 'true';
                        // 既に高さがオートリサイズで大きくなっている場合に備えて一旦リセット
                        el.style.height = '';
                    } catch(_) {}
                });
            } catch(_) {}
        })();

        // チェックボックスの相互作用を設定する関数
        function setupCheckboxInteractions() {
            // 全てのチェックボックスコンテナに対してクリックイベントを設定
            document.querySelectorAll('.checkbox-container').forEach(container => {
                const checkbox = container.querySelector('input[type="checkbox"]');
                const label = container.querySelector('.checkbox-label');
                
                if (!checkbox) return;
                
                // 初期状態のUIを設定
                updateCheckboxUI(checkbox);
                
                // ラベルクリック時にチェックボックスを切り替え
                if (label) {
                    label.addEventListener('click', (e) => {
                        e.preventDefault();
                        checkbox.checked = !checkbox.checked;
                        updateCheckboxUI(checkbox);
                        
                        // change イベントを発火（他のリスナーのため）
                        checkbox.dispatchEvent(new Event('change', { bubbles: true }));
                    });
                }
                
                // コンテナクリック時にチェックボックスを切り替え（チェックボックス自体以外）
                container.addEventListener('click', (e) => {
                    if (e.target === checkbox || e.target === label) return; // チェックボックスとラベル以外
                    checkbox.checked = !checkbox.checked;
                    updateCheckboxUI(checkbox);
                    
                    // change イベントを発火（他のリスナーのため）
                    checkbox.dispatchEvent(new Event('change', { bubbles: true }));
                });
            });
        }

        

        // アンドゥ・リドゥの実装
        function applyProgrammaticValue(target, value) {
            // 値適用時も履歴に1ステップとして積む（OSライク）
            const history = getHistoryFor(target);
            const prev = history.undoStack.length ? history.undoStack[history.undoStack.length - 1] : '';
            const next = value ?? '';
            isApplyingHistory = true;
            target.value = next;
            isApplyingHistory = false;
            if (prev !== next) {
                history.undoStack.push(next);
                history.redoStack.length = 0;
                if (history.undoStack.length > MAX_HISTORY_ENTRIES) {
                    history.undoStack.splice(0, history.undoStack.length - MAX_HISTORY_ENTRIES);
                }
            }
            if (target.id === 'original-textarea') {
                synchronizeForm();
                updateDisplayText();
            } else {
                updateDisplayText();
            }
        }

        function undoForElement(element) {
            const { undoStack, redoStack } = getHistoryFor(element);
            if (undoStack.length > 1) {
                const lastValue = undoStack.pop();
                redoStack.push(lastValue);
                const currentValue = undoStack[undoStack.length - 1] ?? '';
                applyProgrammaticValue(element, currentValue);
            }
        }
        function redoForElement(element) {
            const { undoStack, redoStack } = getHistoryFor(element);
            if (redoStack.length > 0) {
                const lastRedoValue = redoStack.pop();
                undoStack.push(lastRedoValue);
                applyProgrammaticValue(element, lastRedoValue);
            }
        }

        // UIは変更しない要件のため、ボタン連携は行わない
        // テンプレートや変数の状態をすべて初期化し、引数の文字列を備考に設定する
        // 期待動作:
        // - テンプレート本文（#original-textarea）を空にする
        // - 変数の永続値や型・メモ等（persistedDefaults）をクリア
        // - 項目の高さ情報（fieldHeights）をクリア
        // - 選択テンプレートメタ（selectedTemplateMeta）を初期化
        // - 備考（#global-note-textarea）に引数の文字列をそのまま設定
        window.applyRemarkOnly = function(remarkText) {
            try {
                // 状態リセット
                persistedDefaults = { values: {}, persist: {}, types: {}, memos: {} };
                fieldHeights = {};
                lastSavedFieldName = null;
                // 未選択状態は is_blank=1 相当として扱う
                selectedTemplateMeta = { key: null, title: '', is_blank: 1 };
                isSelectedTemplateKeyLocked = false;
                updateEditButtonVisibility();

                // テンプレート本文を空にしてフォームを同期
                const originalTextareaEl = document.getElementById('original-textarea');
                if (originalTextareaEl) {
                    applyProgrammaticValue(originalTextareaEl, '');
                } else {
                    // 念のため（DOM未取得時のフォールバック）
                    synchronizeForm();
                    updateDisplayText();
                }

                // タイトル/メモ表示トグルを未選択表示に更新
                try {
                    const titleElement = document.querySelector('.title-section h3');
                    if (titleElement) titleElement.textContent = '(テンプレート未選択)';
                    const memoToggleWrapper = document.getElementById('toggleShowMemos')?.closest('div');
                    if (memoToggleWrapper) memoToggleWrapper.style.display = 'none';
                } catch (_) {}
                updateMemoToggleVisibility();

                // 備考を設定
                const globalNoteEl = document.getElementById('global-note-textarea');
                if (globalNoteEl) {
                    const value = (remarkText == null) ? '' : String(remarkText);
                    globalNoteEl.value = value;
                }

                // 左右カラムの表示が崩れないように念のため同期
                try { updateDisplayText(); } catch (_) {}
            } catch (_) {
                // 何もしない（安全に無視）
            }
        }
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('applyRemarkOnly', window.applyRemarkOnly); } catch(_) {}
        // =============================
        // デバッグ支援ユーティリティ
        // =============================
        const debugLog = (level, message, payload) => {
            try {
                const loggerObj = (typeof logger !== 'undefined') ? logger : null;
                const fn = loggerObj && typeof loggerObj[level] === 'function' ? loggerObj[level] : null;
                if (fn) {
                    fn.call(loggerObj, message, payload);
                } else {
                    // Fallback: コンソールへ直接出力
                    if (typeof console !== 'undefined' && typeof console[level] === 'function') {
                        console[level](message, payload);
                    } else if (typeof console !== 'undefined' && typeof console.log === 'function') {
                        console.log(`[${level}] ${message}`, payload);
                    }
                }
            } catch (_) {
                try { console.log(`[${level}] ${message}`, payload); } catch(__) {}
            }
        };

        // =============================
        // フレーズピッカー（備考用）
        // =============================
        ;(function setupPhrasePicker(){
            try {
                const phraseButton = document.getElementById('phrasePickerButton');
                if (phraseButton) {
                    phraseButton.addEventListener('click', () => {
                        try {
                            if (window.FileMaker != null) {
                                // Claris FileMaker 側でフレーズ収集スクリプトを実行
                                window.FileMaker.PerformScriptWithOption('addon_remarks_web_phrase_request', '', 5);
                            } else {
                                // デバッグ用ダミーデータ
                                const dummy = [
                                    { id: 'p1', phrase: 'お世話になっております。', title: '挨拶' },
                                    { id: 'p2', phrase: '何卒よろしくお願い申し上げます。', title: '依頼' },
                                    { id: 'p3', phrase: 'ご確認のほどお願いいたします。', title: '確認依頼' },
                                ];
                                raf(() => { try { window.showPhrasePicker(dummy); } catch(_) {} });
                            }
                        } catch(_) {}
                    });
                }
            } catch(_) {}

            // Claris FileMaker から呼ばれる想定のコールバック
            // 受領形式のばらつきに強くする（後方互換）
            // 許容: 配列/文字列(JSON)/{phrases}/Data API 風/単体オブジェクト/空/null
            window.showPhrasePicker = function(payload) {
                try {
                    // デバッグ: 呼び出しを記録
                    logger.info('showPhrasePicker 呼び出し開始', { payloadType: typeof payload, payloadSample: JSON.stringify(payload).slice(0, 200) });
                    const raw = (payload && typeof payload === 'string') ? JSON.parse(payload) : payload;
                    let phrases = [];
                    let receivedRecordId = '';
                    const coerceItem = (item, index) => {
                        const id = (item && (item.id != null ? String(item.id) : String(index + 1)));
                        const text = (item && (item.text != null ? String(item.text) : (item.phrase != null ? String(item.phrase) : '')));
                        const title = (item && (item.title != null ? String(item.title) : ''));
                        return { id, text, title };
                    };
                    const coerceArray = (arr) => {
                        if (!Array.isArray(arr)) return [];
                        return arr.map((it, i) => coerceItem(it, i)).filter(x => x.text);
                    };
                    // fieldData.json に格納されたフレーズ群を安全に抽出する（Claris FileMaker からの形式ブレを吸収）
                    const extractJsonEntries = (jsonValue, debugContext) => {
                        try {
                            if (jsonValue == null) return [];
                            let parsed = jsonValue;
                            if (typeof parsed === 'string') {
                                const trimmed = parsed.trim();
                                if (!trimmed) return [];
                                try {
                                    parsed = JSON.parse(trimmed);
                                } catch (err) {
                                    try {
                                        logger.warn('fieldData.json の解析に失敗', { error: err, context: debugContext, sample: trimmed.slice(0, 200) });
                                    } catch(_) {}
                                    return [];
                                }
                            }
                            if (Array.isArray(parsed)) return parsed;
                            if (parsed && typeof parsed === 'object') return [parsed];
                            return [];
                        } catch(_) {
                            return [];
                        }
                    };
                    
                    // 配列: 直接
                    if (Array.isArray(raw)) {
                        // 直接配列形式: [{"phrase":"テスト1","title":"フレーズテスト1"}, ...]
                        logger.debug('フレーズデータ受信:', raw);
                        phrases = coerceArray(raw);
                        logger.debug('変換後のフレーズ:', phrases);
                    // Data API 風
                    } else if (raw && raw.response && raw.response.data && Array.isArray(raw.response.data)) {
                        // Claris FileMaker Data API レスポンス形式（fieldData.json を優先的に利用）
                        phrases = [];
                        raw.response.data.forEach((record, recordIndex) => {
                            const fieldData = record.fieldData || {};
                            const primaryKey = fieldData.primary_key || fieldData.record_id || record.recordId || '';
                            if (!receivedRecordId) {
                                if (record && record.recordId != null) {
                                    receivedRecordId = String(record.recordId);
                                } else if (fieldData && fieldData.record_id != null) {
                                    receivedRecordId = String(fieldData.record_id);
                                }
                            }
                            let jsonEntries = extractJsonEntries(fieldData.json, { recordIndex, primaryKey });
                            if (jsonEntries.length === 0) {
                                // 後方互換: json フィールドが空でも fieldData に直接 phrase/title が入っているケースを救済
                                const fallback = coerceItem(fieldData, 0);
                                jsonEntries = fallback.text ? [fallback] : [];
                            }
                            if (jsonEntries.length === 0) return;
                            jsonEntries.forEach((item, index) => {
                                const normalized = coerceItem(item, index);
                                if (!normalized.text) return;
                                const fallbackId = primaryKey ? `${primaryKey}_${index}` : normalized.id;
                                const finalId = (normalized.id && normalized.id !== String(index + 1)) ? normalized.id : fallbackId;
                                phrases.push({
                                    id: finalId || `${recordIndex + 1}_${index + 1}`,
                                    text: normalized.text,
                                    title: normalized.title
                                });
                            });
                        });
                        // 空要素を除外
                        phrases = phrases.filter(item => item && item.text);
                    // { phrases: [...] }（従来形式）
                    } else if (Array.isArray(raw && raw.phrases)) {
                        // 成功時の Data API が message(s) を併記する場合があるため、先に phrases を優先評価
                        phrases = coerceArray(raw.phrases);
                        // 可能なら recordId の同梱も取り出す
                        try { if (!receivedRecordId && raw.recordId != null) receivedRecordId = String(raw.recordId); } catch(_) {}
                    // エラーメッセージ配列
                    } else if (raw && raw.messages && Array.isArray(raw.messages)) {
                        // Claris FileMaker のレスポンスは成功時にも messages を含むため、
                        // 非0のみ「エラー」とみなし、それ以外は何もしない（空にしない）
                        const errorMsg = raw.messages.find(msg => String(msg && msg.code) !== "0");
                        if (errorMsg) {
                            logger.warn('フレーズ取得エラー:', errorMsg && errorMsg.message);
                            phrases = [];
                        }
                    // 単体オブジェクト（phrase/titleのみ or 文字列）
                    } else if (raw && (raw.phrase != null || raw.text != null)) {
                        const solo = coerceItem(raw, 0);
                        phrases = solo.text ? [solo] : [];
                        try { if (!receivedRecordId && raw.recordId != null) receivedRecordId = String(raw.recordId); } catch(_) {}
                    } else if (typeof raw === 'string') {
                        // JSON以外の生文字列をそのまま1件として扱う（後方互換の救済）
                        const solo = { id: '1', text: raw, title: '' };
                        phrases = solo.text ? [solo] : [];
                    }
                    
                debugLog('info', 'showPhrasePicker: openPhrasePickerModal 呼び出し前', { phrasesCount: phrases.length, receivedRecordId, rawPayload: raw });
                    openPhrasePickerModal(phrases, receivedRecordId);
                debugLog('info', 'showPhrasePicker: openPhrasePickerModal 呼び出し完了', { phrasesCount: phrases.length });
                    // デバッグ: 少し後にもモーダルが存在するか確認
                    setTimeout(() => {
                        const pm = document.getElementById('phraseModal');
                    debugLog('info', 'showPhrasePicker: 100ms後のモーダル存在確認', { 
                            exists: !!pm,
                            display: pm ? pm.style.display : 'N/A',
                            hasParent: pm ? !!pm.parentNode : false,
                            bodyClass: document.body ? document.body.className : ''
                        });
                        if (!pm) {
                            logger.warn('showPhrasePicker: 100ms後に phraseModal が存在しません。', {
                                copyModeActive: document.body ? document.body.classList.contains('copy-mode') : undefined,
                                templateModalDataset: (document.getElementById('templateModal') || {}).dataset || {}
                            });
                        }
                    }, 100);
                } catch(e) {
                    logger.error('フレーズデータの解析に失敗:', e);
                    openPhrasePickerModal([], '');
                }
            };
            try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('showPhrasePicker', window.showPhrasePicker); } catch(_) {}
            try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('openInlineVariablePopover', window.openInlineVariablePopover); } catch(_) {}

            // フレーズ送信用の共通ヘルパー
            function getPhraseRecordId() {
                try { return String(window.__currentRecordIdForPhrase || ''); } catch(_) { return ''; }
            }
            function buildPhrasePayloadFromItems(recordIdValue, items) {
                const rid = String(recordIdValue || '');
                const normalized = (items || []).map(p => ({
                    title: String(p && (p.title || '') ),
                    phrase: String(p && ((p.text != null) ? p.text : (p.phrase || '')) )
                }));
                return JSON.stringify({ recordId: rid, phrases: normalized });
            }

            // グローバル: フレーズのドラッグ&ドロップで入力欄へ差し込みを有効化
            (function ensureGlobalPhraseDndHandlers(){
                try {
                    if (window.__phraseDndInstalled) return;
                    window.__phraseDndInstalled = true;

                    // ドラッグ中のカスタムゴースト管理（通常/薄い）
                    try {
                        window.__phraseDragCtx = null;
                        window.__createPhraseGhost = function(text, opacity) {
                            try {
                                const el = document.createElement('div');
                                el.textContent = String(text || '');
                                el.style.position = 'fixed';
                                el.style.left = '-9999px';
                                el.style.top = '-9999px';
                                el.style.pointerEvents = 'none';
                                el.style.zIndex = 2147483647;
                                el.style.maxWidth = '320px';
                                el.style.whiteSpace = 'pre-wrap';
                                el.style.wordBreak = 'break-word';
                                el.style.background = '#fff';
                                el.style.border = '1px solid rgba(0,0,0,0.15)';
                                el.style.borderRadius = '6px';
                                el.style.padding = '8px 10px';
                                el.style.boxShadow = '0 4px 12px rgba(0,0,0,0.12)';
                                el.style.fontSize = '13px';
                                el.style.lineHeight = '1.5';
                                el.style.color = '#333';
                                el.style.opacity = String(opacity != null ? opacity : 0.6);
                                document.body.appendChild(el);
                                return el;
                            } catch(_) { return null; }
                        };
                        window.__disposePhraseGhost = function(el){ try { if (el && el.parentNode) el.parentNode.removeChild(el); } catch(_) {} };
                    } catch(_) {}

                    function findEditableTarget(startEl) {
                        try {
                            if (!startEl) return null;
                            if (startEl.closest && startEl.closest('#phraseModal')) return null; // モーダル内は除外（並び替え用）
                            const editable = (startEl.closest && startEl.closest('textarea, input, [contenteditable=""], [contenteditable="true"]')) || null;
                            return editable;
                        } catch(_) { return null; }
                    }

                    function insertIntoInputOrTextarea(el, text) {
                        try {
                            const value = String(el.value || '');
                            const start = (typeof el.selectionStart === 'number') ? el.selectionStart : value.length;
                            const end = (typeof el.selectionEnd === 'number') ? el.selectionEnd : value.length;
                            const before = value.slice(0, start);
                            const after = value.slice(end);
                            const insert = String(text || '');
                            const nextValue = before + insert + after;
                            // 履歴に積みつつ値を適用
                            try { applyProgrammaticValue(el, nextValue); } catch(_) { try { el.value = nextValue; } catch(__) {} }
                            const caret = before.length + insert.length;
                            // グローバル備考ではキャレット/スクロール補正を行わない
                            const isGlobalNote = (el && el.id === 'global-note-textarea');
                            try { el.focus({ preventScroll: true }); } catch(_) { try { el.focus(); } catch(__) {} }
                            if (!isGlobalNote) {
                            try { if (el.setSelectionRange) el.setSelectionRange(caret, caret); } catch(_) {}
                            try { if (typeof fitTextareaToContent === 'function' && el.tagName === 'TEXTAREA') fitTextareaToContent(el); } catch(_) {}
                            }
                        } catch(_) {}
                    }

                    function insertIntoContentEditable(el, text) {
                        try {
                            const insert = String(text || '');
                            el.focus();
                            const sel = (window.getSelection && window.getSelection()) || null;
                            if (sel && sel.getRangeAt && sel.rangeCount) {
                                const range = sel.getRangeAt(0);
                                range.deleteContents();
                                range.insertNode(document.createTextNode(insert));
                                range.collapse(false);
                                sel.removeAllRanges();
                                sel.addRange(range);
                            } else {
                                el.innerText = String(el.innerText || '') + insert;
                            }
                        } catch(_) {}
                    }

                    let __phraseOverlay = null;
                    let __phraseOverlayOriginal = null;

                    function removeOverlay() {
                        try {
                            if (__phraseOverlay && __phraseOverlay.parentNode) __phraseOverlay.parentNode.removeChild(__phraseOverlay);
                        } catch(_) {}
                        __phraseOverlay = null;
                        __phraseOverlayOriginal = null;
                    }

                    function positionOverlay(overlay, original) {
                        try {
                            const rect = original.getBoundingClientRect();
                            overlay.style.position = 'fixed';
                            overlay.style.left = rect.left + 'px';
                            overlay.style.top = rect.top + 'px';
                            overlay.style.width = rect.width + 'px';
                            overlay.style.height = rect.height + 'px';
                            // モーダル(=12000)より下に配置して、カードを覆わない
                            overlay.style.zIndex = 11990;
                        } catch(_) {}
                    }

                    function ensureOverlayFor(original) {
                        try {
                            if (__phraseOverlay && __phraseOverlayOriginal === original) return __phraseOverlay;
                            removeOverlay();
                            const overlay = document.createElement('div');
                            overlay.setAttribute('contenteditable', 'true');
                            overlay.dataset.phraseOverlay = '1';
                            // ベーススタイル
                            const cs = window.getComputedStyle(original);
                            overlay.style.whiteSpace = 'pre-wrap';
                            overlay.style.wordWrap = 'break-word';
                            overlay.style.overflow = 'hidden';
                            overlay.style.boxSizing = 'border-box';
                            overlay.style.padding = cs.padding;
                            overlay.style.border = cs.border;
                            overlay.style.borderRadius = cs.borderRadius;
                            overlay.style.fontSize = cs.fontSize;
                            overlay.style.fontFamily = cs.fontFamily;
                            overlay.style.lineHeight = cs.lineHeight;
                            overlay.style.letterSpacing = cs.letterSpacing;
                            overlay.style.background = 'transparent';
                            overlay.style.color = cs.color;
                            overlay.style.outline = 'none';
                            overlay.style.cursor = 'text';
                            overlay.style.userSelect = 'text';
                            positionOverlay(overlay, original);
                            overlay.textContent = String(original.value || '');
                            document.body.appendChild(overlay);
                            __phraseOverlay = overlay;
                            __phraseOverlayOriginal = original;
                            try { overlay.focus(); } catch(_) {}
                            return overlay;
                        } catch(_) { return null; }
                    }

                    function getOverlayCaretIndex(overlay) {
                        try {
                            const sel = window.getSelection && window.getSelection();
                            if (!sel || sel.rangeCount === 0) return null;
                            const range = sel.getRangeAt(0);
                            const preRange = document.createRange();
                            preRange.selectNodeContents(overlay);
                            preRange.setEnd(range.endContainer, range.endOffset);
                            const idx = preRange.toString().length;
                            return idx;
                        } catch(_) { return null; }
                    }

                    function focusAndShowCaret(target, clientX, clientY) {
                        try {
                            if (!target) return;
                            if (target.tagName === 'TEXTAREA' || target.tagName === 'INPUT') {
                                const overlay = ensureOverlayFor(target);
                                if (overlay) {
                                    positionOverlay(overlay, target);
                                    // ポインタ位置にキャレットを移動
                                    try {
                                        let caretRange = null;
                                        if (document.caretRangeFromPoint) {
                                            caretRange = document.caretRangeFromPoint(clientX, clientY);
                                        } else if (document.caretPositionFromPoint) {
                                            const pos = document.caretPositionFromPoint(clientX, clientY);
                                            if (pos) {
                                                caretRange = document.createRange();
                                                caretRange.setStart(pos.offsetNode, pos.offset);
                                                caretRange.collapse(true);
                                            }
                                        }
                                        if (caretRange) {
                                            const sel = window.getSelection && window.getSelection();
                                            if (sel) {
                                                sel.removeAllRanges();
                                                sel.addRange(caretRange);
                                            }
                                        }
                                    } catch(_) {}
                                }
                            } else if (target.isContentEditable) {
                                try { target.focus(); } catch(_) {}
                                const x = clientX, y = clientY;
                                let range = null;
                                try {
                                    if (document.caretRangeFromPoint) {
                                        range = document.caretRangeFromPoint(x, y);
                                    } else if (document.caretPositionFromPoint) {
                                        const pos = document.caretPositionFromPoint(x, y);
                                        if (pos) {
                                            range = document.createRange();
                                            range.setStart(pos.offsetNode, pos.offset);
                                            range.collapse(true);
                                        }
                                    }
                                } catch(_) {}
                                try {
                                    const sel = window.getSelection && window.getSelection();
                                    if (sel) {
                                        sel.removeAllRanges();
                                        if (range) sel.addRange(range);
                                    }
                                } catch(_) {}
                            }
                        } catch(_) {}
                    }

                    // モード切替（sort/insert）表示と状態管理
                    function setPhraseDragMode(mode) {
                        try {
                            if (window.__phraseDragMode === mode) return;
                            window.__phraseDragMode = mode;
                            const pm = document.getElementById('phraseModal');
                            if (pm) {
                                pm.classList.remove('mode-insert');
                                const badge = pm.querySelector('.mode-badge');
                                // 挿入モードの表示は行わない（常に非表示のまま）
                                if (badge) badge.textContent = '';
                            }
                        } catch(_) {}
                    }
                    function clearPhraseDragMode() {
                        try {
                            window.__phraseDragMode = '';
                            const pm = document.getElementById('phraseModal');
                            if (pm) {
                                pm.classList.remove('mode-insert');
                                const badge = pm.querySelector('.mode-badge');
                                if (badge) badge.textContent = '';
                            }
                        } catch(_) {}
                    }

                    document.addEventListener('dragover', function(e){
                        try {
                            const dt = e.dataTransfer;
                            if (!dt) return;
                            const hasPhraseType = (dt.types && (Array.prototype.indexOf.call(dt.types, 'application/x-phrase') !== -1)) ||
                                                  (dt.types && (Array.prototype.indexOf.call(dt.types, 'text/plain') !== -1));
                            if (!hasPhraseType) return;
                            const target = findEditableTarget(e.target);
                            if (!target) {
                                // モーダル上や非入力領域 → 並び替えモード（オーバーレイ除去）
                                removeOverlay();
                                try { dt.dropEffect = 'move'; } catch(_) {}
                                // 並び替え時はブラウザ標準のゴーストを使用（カスタム指定しない）
                                setPhraseDragMode('sort');
                                return;
                            }
                            e.preventDefault();
                            try { dt.dropEffect = 'copy'; } catch(_) {}
                            setPhraseDragMode('insert');
                            // 入力欄上では薄いゴーストに切替（挿入モード時のみカスタム）
                            try {
                                if (window.__phraseDragCtx && window.__phraseDragCtx.lightGhost) {
                                    dt.setDragImage(window.__phraseDragCtx.lightGhost, 6, 6);
                                }
                            } catch(_) {}
                            // キャレットを表示（挿入位置の視覚フィードバック）
                            focusAndShowCaret(target, e.clientX, e.clientY);
                        } catch(_) {}
                    }, false);

                    document.addEventListener('dragenter', function(e){
                        try {
                            const dt = e.dataTransfer;
                            if (!dt) return;
                            const hasPhraseType = (dt.types && (Array.prototype.indexOf.call(dt.types, 'application/x-phrase') !== -1)) ||
                                                  (dt.types && (Array.prototype.indexOf.call(dt.types, 'text/plain') !== -1));
                            if (!hasPhraseType) return;
                            const target = findEditableTarget(e.target);
                            if (!target) { removeOverlay(); setPhraseDragMode('sort'); return; }
                            try { dt.dropEffect = 'copy'; } catch(_) {}
                            setPhraseDragMode('insert');
                            focusAndShowCaret(target, e.clientX, e.clientY);
                        } catch(_) {}
                    }, false);

                    document.addEventListener('drop', function(e){
                        try {
                            const dt = e.dataTransfer;
                            if (!dt) return;
                            // モーダル内のドロップは（並び替え等のため）スキップ
                            if (e.target && e.target.closest && e.target.closest('#phraseModal')) return;

                            let payloadText = '';
                            try {
                                const custom = dt.getData('application/x-phrase');
                                if (custom) {
                                    const parsed = JSON.parse(custom);
                                    payloadText = String((parsed && parsed.text) ? parsed.text : '');
                                }
                            } catch(_) {}
                            if (!payloadText) {
                                try { payloadText = dt.getData('text/plain') || ''; } catch(_) {}
                            }
                            if (!payloadText) return;

                            let target = findEditableTarget(e.target);
                            // オーバーレイ上のドロップは実体へリダイレクト
                            if ((!target || (target && target.dataset && target.dataset.phraseOverlay === '1')) && __phraseOverlay && __phraseOverlayOriginal) {
                                target = __phraseOverlay; // 一旦オーバーレイを対象として扱い
                            }
                            if (!target && __phraseOverlay && __phraseOverlayOriginal) {
                                target = __phraseOverlay; // フォールバック
                            }
                            if (!target) return;
                            e.preventDefault();

                            // オーバーレイ → 実体へ反映
                            if (target.dataset && target.dataset.phraseOverlay === '1' && __phraseOverlayOriginal) {
                                const overlay = target;
                                const original = __phraseOverlayOriginal;
                                // caret index を取得して元要素に適用
                                const idx = getOverlayCaretIndex(overlay);
                                try { if (typeof idx === 'number') original.setSelectionRange(idx, idx); } catch(_) {}
                                insertIntoInputOrTextarea(original, payloadText);
                                removeOverlay();
                                return;
                            }

                            if (target.tagName === 'TEXTAREA' || target.tagName === 'INPUT') {
                                // 念のため既存オーバーレイを除去
                                if (__phraseOverlay) removeOverlay();
                                insertIntoInputOrTextarea(target, payloadText);
                            } else if (target.isContentEditable) {
                                if (__phraseOverlay) removeOverlay();
                                insertIntoContentEditable(target, payloadText);
                            }
                            // ゴースト掃除
                            try {
                                if (window.__phraseDragCtx) {
                                    window.__disposePhraseGhost(window.__phraseDragCtx.defaultGhost);
                                    window.__disposePhraseGhost(window.__phraseDragCtx.lightGhost);
                                    window.__phraseDragCtx = null;
                                }
                            } catch(_) {}
                            // 並び替え用インジケータも保険で全消去
                            try {
                                document.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el));
                            } catch(_) {}
                            clearPhraseDragMode();
                        } catch(_) {}
                    }, false);

                    // ドラッグ終了時にオーバーレイを掃除
                    document.addEventListener('dragend', function(){
                        removeOverlay();
                        try {
                            if (window.__phraseDragCtx) {
                                window.__disposePhraseGhost(window.__phraseDragCtx.defaultGhost);
                                window.__disposePhraseGhost(window.__phraseDragCtx.lightGhost);
                                window.__phraseDragCtx = null;
                            }
                        } catch(_) {}
                        // 残留インジケータを確実に除去
                        try { document.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el)); } catch(_) {}
                        try { document.querySelectorAll('.template-list.drag-active').forEach(el => el.classList.remove('drag-active')); } catch(_) {}
                        clearPhraseDragMode();
                    }, false);
                } catch(_) {}
            })();

            function openPhrasePickerModal(phrases, recordId) {
                logger.info('openPhrasePickerModal呼び出し', { count: phrases.length, recordId, phrases });
            try {
                window.__currentRecordIdForPhrase = String(recordId || '');
                // フレーズモーダルが開いている間は、他モーダルのクロージャが勝手にリセットしないようフラグを立てる
                window.__phraseModalOpenedAt = Date.now();
            } catch(_) {}
            const popoverId = 'phraseModal';
            let popover = document.getElementById(popoverId);
            if (popover && popover.parentNode) popover.parentNode.removeChild(popover);

                popover = document.createElement('div');
                popover.id = popoverId;
                popover.className = 'modal';
                const mc = document.createElement('div');
                mc.className = 'modal-content';
                mc.setAttribute('role', 'dialog');
                mc.setAttribute('aria-modal', 'true');
                mc.setAttribute('aria-label', 'フレーズ一覧');
                popover.style.display = 'block';

                // ヘッダー追加（ドラッグハンドル）
                const header = document.createElement('div');
                header.className = 'modal-header is-draggable';
                const headerLeft = document.createElement('div');
                headerLeft.className = 'header-left';
                const headerTitle = document.createElement('h3');
                headerTitle.id = 'phraseModalTitle';
                headerTitle.textContent = 'フレーズを選択';
                headerLeft.appendChild(headerTitle);
                const headerRight = document.createElement('div');
                headerRight.className = 'header-right';
                // 検索トグルと検索入力
                const phraseSearchToggle = document.createElement('button');
                phraseSearchToggle.type = 'button';
                phraseSearchToggle.id = 'phraseSearchToggle';
                phraseSearchToggle.className = 'search-button';
                phraseSearchToggle.title = '検索';
                phraseSearchToggle.setAttribute('aria-label', '検索');
                clearChildren(phraseSearchToggle);
                phraseSearchToggle.appendChild(createIcon('search'));
                headerRight.appendChild(phraseSearchToggle);

                const phraseSearchWrap = document.createElement('div');
                phraseSearchWrap.id = 'phraseSearchWrap';
                phraseSearchWrap.style.display = 'none';
                phraseSearchWrap.style.alignItems = 'center';
                phraseSearchWrap.style.gap = '6px';
                const phraseSearchInput = document.createElement('input');
                phraseSearchInput.type = 'search';
                phraseSearchInput.id = 'phraseSearchInput';
                phraseSearchInput.className = 'template-search';
                phraseSearchInput.placeholder = 'フレーズを検索...';
                phraseSearchInput.style.width = '220px';
                phraseSearchInput.style.height = '32px';
                const phraseSearchExec = document.createElement('button');
                phraseSearchExec.type = 'button';
                phraseSearchExec.id = 'phraseSearchExec';
                phraseSearchExec.className = 'search-button';
                phraseSearchExec.title = '検索実行';
                phraseSearchExec.setAttribute('aria-label', '検索実行');
                clearChildren(phraseSearchExec);
                phraseSearchExec.appendChild(createIcon('search'));
                phraseSearchWrap.appendChild(phraseSearchInput);
                phraseSearchWrap.appendChild(phraseSearchExec);
                headerRight.appendChild(phraseSearchWrap);
                // 検索閉じる[X]
                const phraseSearchClose = document.createElement('button');
                phraseSearchClose.type = 'button';
                phraseSearchClose.id = 'phraseSearchClose';
                phraseSearchClose.className = 'clear-button';
                phraseSearchClose.title = '検索を閉じる';
                phraseSearchClose.setAttribute('aria-label', '検索を閉じる');
                phraseSearchClose.style.display = 'none';
                clearChildren(phraseSearchClose);
                phraseSearchClose.appendChild(createIcon('close'));
                headerRight.appendChild(phraseSearchClose);
                // [+] ボタン
                const phraseNewButton = document.createElement('button');
                phraseNewButton.type = 'button';
                phraseNewButton.id = 'phraseNewButton';
                phraseNewButton.className = 'new-button';
                phraseNewButton.title = '新規作成';
                phraseNewButton.setAttribute('aria-label', '新規フレーズを作成');
                clearChildren(phraseNewButton);
                phraseNewButton.appendChild(createIcon('plus'));
                headerRight.appendChild(phraseNewButton);
                // [?] ヘルプアイコン（他UIと同様の仕組みに合わせる）
                const phraseHelpIcon = document.createElement('span');
                phraseHelpIcon.id = 'syntaxHelpTriggerPhrase';
                phraseHelpIcon.className = 'help-icon';
                phraseHelpIcon.title = 'ヘルプ';
                phraseHelpIcon.textContent = '?';
                phraseHelpIcon.style.display = 'none';
                headerRight.appendChild(phraseHelpIcon);
                // モード表示バッジは不要
                header.appendChild(headerLeft);
                header.appendChild(headerRight);

                // フレーズモーダル用 UI コントローラ（表示状態の一元管理）
                const headerUI = (function createPhraseHeaderUIController(){
                    let lastSearchDisplay = 'none';
                    function applySearchVisibility(show){
                        try {
                            if (!phraseSearchWrap) return;
                            if (show) {
                                try { lastSearchDisplay = phraseSearchWrap.style.display || 'none'; } catch(_) {}
                                phraseSearchWrap.style.display = 'flex';
                            } else {
                                phraseSearchWrap.style.display = 'none';
                            }
                            if (phraseSearchToggle) phraseSearchToggle.style.display = show ? 'none' : '';
                            if (phraseSearchClose) phraseSearchClose.style.display = show ? '' : 'none';
                            // 編集/新規作成モード中は常に [+] を隠す
                            if (phraseNewButton) {
                                const isEditing = !!(header && header.classList && header.classList.contains('is-editing'));
                                phraseNewButton.style.display = (show || isEditing) ? 'none' : '';
                            }
                            try { headerTitle.style.display = show ? 'none' : ''; } catch(_) {}
                        } catch(_) {}
                    }
                    return {
                        setMode(mode){
                            try {
                                if (mode === 'list') {
                                    try { header.classList.remove('is-editing'); } catch(_) {}
                                    try { headerTitle.textContent = 'フレーズを選択'; } catch(_) {}
                                    if (phraseNewButton) phraseNewButton.style.display = '';
                                    if (phraseHelpIcon) phraseHelpIcon.style.display = 'none';
                                    // 直前の検索表示状態へ復帰（無ければ非表示）
                                    const prev = (phraseSearchWrap && phraseSearchWrap.dataset && phraseSearchWrap.dataset.lastDisplay) || lastSearchDisplay;
                                    applySearchVisibility(prev != null && prev !== 'none');
                                } else if (mode === 'edit') {
                                    try { header.classList.add('is-editing'); } catch(_) {}
                                    try { headerTitle.textContent = 'フレーズを編集'; } catch(_) {}
                                    // 先に検索UI表示状態を確定させ、その後に [+] を隠す
                                    applySearchVisibility(false);
                                    if (phraseNewButton) phraseNewButton.style.display = 'none';
                                    if (phraseHelpIcon) phraseHelpIcon.style.display = '';
                                    if (phraseSearchToggle) phraseSearchToggle.style.display = 'none';
                                } else if (mode === 'create') {
                                    try { header.classList.add('is-editing'); } catch(_) {}
                                    try { headerTitle.textContent = 'フレーズを新規作成'; } catch(_) {}
                                    // 先に検索UI表示状態を確定させ、その後に [+] を隠す
                                    applySearchVisibility(false);
                                    if (phraseNewButton) phraseNewButton.style.display = 'none';
                                    if (phraseHelpIcon) phraseHelpIcon.style.display = '';
                                    if (phraseSearchToggle) phraseSearchToggle.style.display = 'none';
                                }
                            } catch(_) {}
                        },
                        toggleSearch(show){
                            applySearchVisibility(!!show);
                        }
                    };
                })();

                const body = document.createElement('div');
                body.className = 'modal-scroll-body template-modal-body';

                // 検索UIは非表示（要望により削除）

                // "+"ボタンは非表示（要望により削除）

                const list = document.createElement('div');
                list.className = 'template-list';
                list.id = 'phrasePickerList';
                body.appendChild(list);

                // ページング（テンプレートと同様の外観を流用）
                const phrasePaginationEl = document.createElement('div');
                phrasePaginationEl.id = 'phrasePagination';
                phrasePaginationEl.className = 'template-pagination';
                phrasePaginationEl.style.display = 'none';
                body.appendChild(phrasePaginationEl);

                const footer = document.createElement('div');
                footer.className = 'modal-buttons';
                const footerBar = document.createElement('div');
                footerBar.className = 'bar';
                const footerCloseBtn = document.createElement('button');
                footerCloseBtn.type = 'button';
                footerCloseBtn.className = 'button button-secondary';
                footerCloseBtn.textContent = '戻る';
                footerBar.appendChild(footerCloseBtn);
                // 一覧ビューへ復帰（テンプレートと同様にフッターのキャンセル等から呼ぶ）
                function restorePhraseListUI() {
                    try {
                        // 直前に編集していた要素 または 新規作成された要素を特定（ID優先、なければインデックス）
                        let highlightId = null;
                        let highlightIndex = (typeof editingIndex === 'number') ? editingIndex : null;
                        
                        // 新規作成されたアイテムを優先的にハイライト
                        if (newlyCreatedId) {
                            highlightId = String(newlyCreatedId);
                            newlyCreatedId = null; // 使用後はリセット
                        } else {
                            // 編集時のハイライト処理（既存）
                            try {
                                if (editingIndex != null && phrases && phrases[editingIndex]) {
                                    const pid = phrases[editingIndex].id;
                                    if (pid != null) highlightId = String(pid);
                                }
                            } catch(_) {}
                        }
                        isRegisterMode = false;
                        registerWrap.style.display = 'none';
                        editingIndex = null;
                        registerWrap.dataset.mode = '';
                        list.style.display = '';
                        // ページングも復帰
                        const phrasePagination = document.getElementById('phrasePagination');
                        if (phrasePagination) phrasePagination.style.display = (totalPhrasePages > 1) ? '' : 'none';
                        // 新規作成アイテムが現在ページに存在しない場合は、そのページへ自動移動
                        try {
                            if (highlightId) {
                                // 現在DOMに無ければページを算出
                                let targetInDom = list.querySelector(`.template-item[data-id="${CSS.escape(String(highlightId))}"]`);
                                if (!targetInDom) {
                                    const idx = (phrases || []).findIndex(x => String(x && x.id != null ? x.id : '') === String(highlightId));
                                    if (idx >= 0) {
                                        const desiredPage = Math.floor(idx / PHRASE_PAGE_LIMIT) + 1;
                                        if (desiredPage !== currentPhrasePage) {
                                            currentPhrasePage = desiredPage;
                                            filterAndRender();
                                        }
                                    }
                                }
                            }
                        } catch(_) {}
                        // フッター切替
                        footerCloseBtn.style.display = '';
                        editFooterCancelBtn.style.display = 'none';
                        editFooterSaveBtn.style.display = 'none';
                        // 全画面時は固定フッターを「閉じる」に切替
                        try {
                            if (mc.classList && mc.classList.contains('fullscreen')) {
                                // 内部フッターは使わない
                                try { footer && (footer.style.display = 'none'); } catch(_) {}
                                const footerCancel = document.getElementById('footerCancelButton');
                                const footerOk = document.getElementById('footerQueryButton');
                                const footerEditCancel = document.getElementById('footerEditCancel');
                                const footerEditSave = document.getElementById('footerEditSave');
                                const changeNav = document.querySelector('.footer .change-nav');
                                if (footerCancel) footerCancel.style.display = 'none';
                                if (footerOk) footerOk.style.display = 'none';
                                if (changeNav) { changeNav.dataset.prevDisplay = changeNav.style.display || ''; changeNav.style.display = 'none'; }
                                if (footerEditSave) footerEditSave.style.display = 'none';
                                if (footerEditCancel) {
                                    try { popover.__footerCancelHandler && footerEditCancel.removeEventListener('click', popover.__footerCancelHandler); } catch(_) {}
                                    try { popover.__footerSaveHandler && footerEditCancel.removeEventListener('click', popover.__footerSaveHandler); } catch(_) {}
                                    try { footerEditCancel.dataset.prevLabel = footerEditCancel.textContent || ''; } catch(_) {}
                                    footerEditCancel.textContent = '戻る';
                                    footerEditCancel.style.display = '';
                                    // 閉じるを割り当て（単一登録）
                                    try { unbindSingleEvent(footerEditCancel, 'click', 'phrasePopover:close'); } catch(_) {}
                                    popover.__footerCloseHandler = () => { try { closePopover(); } catch(_) { try { popover.parentNode.removeChild(popover); } catch(__) {} } };
                                    bindSingleEvent(footerEditCancel, 'click', 'phrasePopover:close', popover.__footerCloseHandler, false);
                                }
                                updateFooterHiddenState();
                            }
                        } catch(_) {}
                        // ヘッダーを一覧用表示へ戻す
                        try { headerUI.setMode('list'); } catch(_) {}
                        // 一覧へ戻った直後に、編集/新規作成していた行へスクロールしつつ一時的に強調表示
                        try {
                            setTimeout(() => {
                                try {
                                    let target = null;
                                    if (highlightId) {
                                        target = list.querySelector(`.template-item[data-id="${CSS.escape(highlightId)}"]`);
                                    }
                                    if (!target && highlightIndex != null) {
                                        const items = list.querySelectorAll('.template-item');
                                        target = items && items[highlightIndex] ? items[highlightIndex] : null;
                                    }
                                    if (target) {
                                        // 可能なら中央へスクロール
                                        try { target.scrollIntoView({ behavior: 'smooth', block: 'center' }); } catch(_) { try { target.scrollIntoView(true); } catch(__) {} }
                                        // スクロール制御は行わない（ユーザー操作を尊重）
                                        target.classList.add('was-edited');
                                        setTimeout(() => {
                                            try {
                                                target.classList.add('highlight-fade');
                                                requestAnimationFrame(() => {
                                                    try { target.classList.remove('was-edited'); } catch(_) {}
                                                    setTimeout(() => { try { target.classList.remove('highlight-fade'); } catch(_) {} }, 400);
                                                });
                                            } catch(_) {}
                                        }, 2000);
                                    }
                                } catch(_) {}
                            }, 0);
                        } catch(_) {}
                    } catch(_) {}
                }

                // 編集/追加モード用のフッターボタン（通常は非表示）
                const editFooterCancelBtn = document.createElement('button');
                editFooterCancelBtn.type = 'button';
                editFooterCancelBtn.className = 'button button-secondary';
                editFooterCancelBtn.textContent = 'キャンセル';
                editFooterCancelBtn.style.display = 'none';
                footerBar.appendChild(editFooterCancelBtn);

                const editFooterSaveBtn = document.createElement('button');
                editFooterSaveBtn.type = 'button';
                editFooterSaveBtn.className = 'button';
                editFooterSaveBtn.textContent = '保存';
                editFooterSaveBtn.style.display = 'none';
                footerBar.appendChild(editFooterSaveBtn);

                footer.appendChild(footerBar);

                mc.appendChild(header);
                mc.appendChild(body);
                mc.appendChild(footer);
                // 初期状態でもフッターが確実に表示されるよう、空でもボタンバーを表示
                footer.style.display = '';
                popover.appendChild(mc);
                document.body.appendChild(popover);
                try { document.body.classList.add('phrase-modal-open'); } catch(_) {}
                // デバッグ: モーダルが DOM に追加されたことを確認
                logger.info('phraseModal を DOM に追加完了', { 
                    modalId: popover.id, 
                    isInDOM: !!document.getElementById('phraseModal'),
                    display: popover.style.display
                });
                // グローバル固定フッターの保存ボタン横にもヒントを追加（存在する場合）
                try {
                    const globalFooter = document.querySelector('.footer .container');
                    const globalFooterSave = document.getElementById('footerEditSave');
                    if (globalFooter && globalFooterSave && !document.getElementById('globalFooterPhraseSaveHint')) {
                        const hint = document.createElement('span');
                        hint.id = 'globalFooterPhraseSaveHint';
                        hint.textContent = 'タイトルとフレーズを入力してください';
                        hint.style.marginLeft = '8px';
                        hint.style.color = '#6c757d';
                        hint.style.fontSize = '12px';
                        hint.style.alignSelf = 'center';
                        hint.style.display = 'none';
                        try { globalFooterSave.insertAdjacentElement('afterend', hint); } catch(_) { try { globalFooter.appendChild(hint); } catch(__) {} }
                    }
                } catch(_) {}

                // 画面が狭い場合は全画面表示、それ以外は右上に配置
                try {
                    const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                    const vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
                    const rect = mc.getBoundingClientRect();
                    const pad = 8;
                    const NARROW_BP = 600; // 狭幅の判定閾値(px)
                    if (vw <= NARROW_BP) {
                        mc.classList.add('fullscreen');
                        try { footerCloseBtn.textContent = '戻る'; } catch(_) {}
                        mc.style.left = '0px';
                        mc.style.top = '0px';
                        try { header.classList.remove('is-draggable'); } catch(_) {}
                        // 全画面時（一覧表示）は固定フッターを「閉じる」だけにする（[キャンセル][転記]は非表示）
                        try {
                            // 一覧ビューへ強制復帰（編集中/新規作成中でも戻す）
                            try { restorePhraseListUI(); } catch(_) {}
                            // モーダル内部フッターは使わない
                            try { footer && (footer.style.display = 'none'); } catch(_) {}
                            // 既存の固定フッターを使用: [キャンセル][転記]を隠し、[閉じる]を表示
                            const footerCancel = document.getElementById('footerCancelButton');
                            const footerOk = document.getElementById('footerQueryButton');
                            const footerEditCancel = document.getElementById('footerEditCancel');
                            const footerEditSave = document.getElementById('footerEditSave');
                            const changeNav = document.querySelector('.footer .change-nav');
                            if (footerCancel) footerCancel.style.display = 'none';
                            if (footerOk) footerOk.style.display = 'none';
                            if (changeNav) { changeNav.dataset.prevDisplay = changeNav.style.display || ''; changeNav.style.display = 'none'; }
                            if (footerEditSave) footerEditSave.style.display = 'none';
                            if (footerEditCancel) {
                                try { footerEditCancel.dataset.prevLabel = footerEditCancel.textContent || ''; } catch(_) {}
                                footerEditCancel.textContent = '戻る';
                                footerEditCancel.style.display = '';
                                // 重複防止: 単一登録
                                try { unbindSingleEvent(footerEditCancel, 'click', 'phrasePopover:close'); } catch(_) {}
                                popover.__footerCloseHandler = () => { try { closePopover(); } catch(_) {} };
                                bindSingleEvent(footerEditCancel, 'click', 'phrasePopover:close', popover.__footerCloseHandler, false);
                            }
                            // フッターを必ず表示
                            updateFooterHiddenState();
                        } catch(_) {}
                    } else {
                        mc.style.left = Math.max(pad, vw - rect.width - pad) + 'px';
                        mc.style.top  = pad + 'px';
                        try { footerCloseBtn.textContent = '閉じる'; } catch(_) {}
                        // 非全画面時は固定フッターを通常状態（[キャンセル][転記]）にする
                        try {
                            const footerCancel = document.getElementById('footerCancelButton');
                            const footerOk = document.getElementById('footerQueryButton');
                            const footerEditCancel = document.getElementById('footerEditCancel');
                            const footerEditSave = document.getElementById('footerEditSave');
                            const changeNav = document.querySelector('.footer .change-nav');
                            if (footerCancel) footerCancel.style.display = '';
                            if (footerOk) footerOk.style.display = '';
                            if (footerEditCancel) {
                                footerEditCancel.style.display = 'none';
                                try { popover.__footerCloseHandler && footerEditCancel.removeEventListener('click', popover.__footerCloseHandler); } catch(_) {}
                                try { popover.__footerCancelHandler && footerEditCancel.removeEventListener('click', popover.__footerCancelHandler); } catch(_) {}
                            }
                            if (footerEditSave) {
                                footerEditSave.style.display = 'none';
                                try { popover.__footerSaveHandler && footerEditSave.removeEventListener('click', popover.__footerSaveHandler); } catch(_) {}
                            }
                            try { window.restoreChangeNavDisplay && window.restoreChangeNavDisplay(); } catch(_) {}
                            updateFooterHiddenState();
                        } catch(_) {}
                    }
                } catch(_) {}

                // ドラッグ可能化（ヘッダーがハンドル）
                try {
                    if (typeof attachDragMove === 'function') {
                        attachDragMove(mc, {
                            handleEl: header,
                            clampToViewport: true,
                            padding: 8,
                            disallowSelection: true,
                            disabledPredicate: () => mc.classList.contains('fullscreen'),
                            blockSelector: 'button, input, textarea, select, .template-search, #phraseSearchWrap, .template-item, .template-item-actions, .more-menu',
                            onStart: () => {
                                try {
                                    document.body.classList.add('dragging');
                                    header.classList.add('grabbing');
                                } catch(_) {}
                            },
                            onEnd: () => {
                                try {
                                    document.body.classList.remove('dragging');
                                    header.classList.remove('grabbing');
                                } catch(_) {}
                            },
                        });
                    }
                } catch(_) {}

                // 一覧レンダリング
                function render(items) {
                    logger.debug('render関数呼び出し', { count: items.length, items });
                    clearChildren(list);
                    items.forEach((p, index) => {
                        const item = document.createElement('div');
                        item.className = 'template-item';
                        const sortingDisabled = isPhraseFiltered();
                        // 絞り込み中も外部挿入用のドラッグは許可（内部並び替えは dragover/drop 側で無効化）
                        item.setAttribute('draggable', 'true');
                        item.dataset.id = String(p.id ?? index);

                        const row = document.createElement('div');
                        row.className = 'template-item-row';

                        const handle = document.createElement('div');
                        handle.className = 'drag-handle';
                        handle.style.flex = '0 0 auto';
                        handle.style.padding = '4px';
                        handle.style.color = '#6c757d';
                        if (sortingDisabled) {
                            handle.title = '検索中でもドラッグで挿入できます（並び替え不可）';
                            handle.tabIndex = 0;
                            try { handle.style.cursor = 'grab'; handle.style.opacity = ''; } catch(_) {}
                            try { handle.removeAttribute('aria-disabled'); } catch(_) {}
                            handle.classList.remove('is-disabled');
                        } else {
                            handle.title = '項目をドラッグして並び替え (↑↓キーでも操作可能)';
                            handle.tabIndex = 0;
                            handle.removeAttribute('aria-disabled');
                            handle.classList.remove('is-disabled');
                            try { handle.style.opacity = ''; handle.style.cursor = ''; } catch(_) {}
                        }
                        // 並び替え無効時でも挿入ドラッグは許可するため、押下抑止は行わない
                        handle.addEventListener('mousedown', () => {}, true);
                        handle.addEventListener('touchstart', () => {}, { passive: true, capture: true });
                        handle.setAttribute('role', 'button');
                        handle.setAttribute('aria-label', 'ドラッグハンドル: 上下矢印キーで並び替え可能');
                        clearChildren(handle);
                        handle.appendChild(createIcon('drag-handle'));
                        
                        // キーボード操作サポート
                        handle.addEventListener('keydown', (e) => {
                            if (isPhraseFiltered()) { e.preventDefault(); return; }
                            if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
                                e.preventDefault();
                                const currentItem = handle.closest('.template-item');
                                if (!currentItem) return;
                                
                                const isUp = e.key === 'ArrowUp';
                                const sibling = isUp ? currentItem.previousElementSibling : currentItem.nextElementSibling;
                                
                                if (sibling && sibling.classList.contains('template-item')) {
                                    // アニメーション効果を追加
                                    currentItem.style.transition = 'transform 0.2s ease';
                                    sibling.style.transition = 'transform 0.2s ease';
                                    
                                    // 移動実行
                                    if (isUp) {
                                        sibling.parentNode.insertBefore(currentItem, sibling);
                                    } else {
                                        sibling.parentNode.insertBefore(currentItem, sibling.nextSibling);
                                    }
                                    // 既存ハイライト機構で視覚的に強調
                                    try { if (typeof window.highlightAndScrollTemplateEl === 'function') window.highlightAndScrollTemplateEl(currentItem); } catch(_) {}
                                    
                                    // フォーカスを維持
                                    raf(() => {
                                        handle.focus();
                                        // アニメーションをリセット
                                        currentItem.style.transition = '';
                                        sibling.style.transition = '';
                                    });
                                    
                                    // 変更を通知
                                    setTimeout(() => {
                                        try { syncPhrasesOrderFromDOM(); } catch(_) {}
                                        try { sendAllPhrasesToFM(); } catch(_) {}
                                    }, 250);
                                }
                            }
                        });
                        row.appendChild(handle);

                        const main = document.createElement('div');
                        main.className = 'template-item-main';

                        const body = document.createElement('div');
                        body.className = 'template-body';
                        const rawBody = String(p.text ?? '');
                        body.textContent = rawBody || '<<情報なし>>';

                        // ツールチップにタイトルと本文を表示
                        const rawTitle = String(p.title ?? '').trim();
                        const tooltip = rawTitle ? (rawBody ? `${rawTitle}\n${rawBody}` : rawTitle) : rawBody;
                        try {
                            item.setAttribute('title', tooltip || '');
                            item.setAttribute('aria-label', tooltip || '');
                        } catch(_) {}

                        main.appendChild(body);
                        row.appendChild(main);

                        // 3点リーダーメニューを追加
                        const actionsEl = document.createElement('div');
                        actionsEl.className = 'template-item-actions';
                        const built = createActionsMenu();
                        actionsEl.appendChild(built.querySelector('.more-btn'));
                        actionsEl.appendChild(built.querySelector('.more-menu'));
                        row.appendChild(actionsEl);

                        // 3点リーダーのメニュー動作
                        const moreBtn = actionsEl.querySelector('.more-btn');
                        const moreMenu = actionsEl.querySelector('.more-menu');
                        moreBtn.addEventListener('click', (e) => {
                            e.stopPropagation();
                            const isOpen = moreMenu.classList.contains('open');
                            document.querySelectorAll('#phrasePickerList .more-menu.open').forEach(mm => mm.classList.remove('open'));
                            if (!isOpen) {
                                moreMenu.classList.add('open');
                            }
                        });
                        
                        // 外側クリックでメニューを閉じる（テンプレート選択と同様）
                        document.addEventListener('click', (e) => {
                            if (!actionsEl.contains(e.target)) {
                                moreMenu.classList.remove('open');
                            }
                        });

                        // メニュー項目のイベント
                        const editBtn = actionsEl.querySelector('.menu-edit');
                        const deleteBtn = actionsEl.querySelector('.menu-delete');
                        
                        editBtn.addEventListener('click', (e) => {
                            e.stopPropagation();
                            moreMenu.classList.remove('open');
                            // IDベースで正しい対象を特定（絞り込み表示中でもズレない）
                            const targetId = String((p && p.id != null) ? p.id : (item && item.dataset ? item.dataset.id : ''));
                            const idx = (phrases || []).findIndex(x => String(x && x.id != null ? x.id : '') === targetId);
                            if (idx >= 0 && phrases[idx]) {
                                // 一覧ビュー → 編集ビューへ
                                (function switchToEdit() {
                                    try {
                                        isRegisterMode = true;
                                        registerWrap.dataset.mode = 'edit';
                                        editingIndex = idx;
                                        editingId = targetId || null;
                                        newlyCreatedId = null; // 編集モードに入る時はリセット
                                        saveBtn.textContent = '更新';
                                        titleInput.value = String(phrases[idx].title || '');
                                        ta.value = String(phrases[idx].text || '');
                                        registerWrap.style.display = 'block';
                                        list.style.display = 'none';
                                        phrasePaginationEl.style.display = 'none';
                                        // フッター切替（閉じる→キャンセル/更新）
                                        footerCloseBtn.style.display = 'none';
                                        editFooterCancelBtn.style.display = '';
                                        editFooterSaveBtn.style.display = '';
                                        editFooterSaveBtn.textContent = '更新';
                                        // フルスクリーン時は固定フッターを[キャンセル][更新]に切替
                                        try {
                                            if (mc.classList && mc.classList.contains('fullscreen')) {
                                                try { footer && (footer.style.display = 'none'); } catch(_) {}
                                                const footerCancel = document.getElementById('footerCancelButton');
                                                const footerOk = document.getElementById('footerQueryButton');
                                                const footerEditCancel = document.getElementById('footerEditCancel');
                                                const footerEditSave = document.getElementById('footerEditSave');
                                                const changeNav = document.querySelector('.footer .change-nav');
                                                if (footerCancel) footerCancel.style.display = 'none';
                                                if (footerOk) footerOk.style.display = 'none';
                                                if (changeNav) { changeNav.dataset.prevDisplay = changeNav.style.display || ''; changeNav.style.display = 'none'; }
                                                if (footerEditCancel) {
                                                    try { unbindSingleEvent(footerEditCancel, 'click', 'phraseEdit:cancel'); } catch(_) {}
                                                    // フルスクリーン一覧時に割り当てていた「戻る(閉じる)」用の一時ハンドラを解除
                                                    try { unbindSingleEvent(footerEditCancel, 'click', 'phrasePopover:close'); } catch(_) {}
                                                    try { popover.__footerCloseHandler && footerEditCancel.removeEventListener('click', popover.__footerCloseHandler); } catch(_) {}
                                                    footerEditCancel.textContent = 'キャンセル';
                                                    footerEditCancel.style.display = '';
                                                    popover.__footerCancelHandler = () => { try { restorePhraseListUI(); } catch(_) {} };
                                                    bindSingleEvent(footerEditCancel, 'click', 'phraseEdit:cancel', popover.__footerCancelHandler, false);
                                                }
                                                if (footerEditSave) {
                                                    try { unbindSingleEvent(footerEditSave, 'click', 'phraseEdit:save'); } catch(_) {}
                                                    footerEditSave.textContent = '更新';
                                                    footerEditSave.style.display = '';
                                                    popover.__footerSaveHandler = () => { try { saveBtn.click(); } catch(_) {} };
                                                    bindSingleEvent(footerEditSave, 'click', 'phraseEdit:save', popover.__footerSaveHandler, false);
                                                }
                                                updateFooterHiddenState();
                                            }
                                        } catch(_) {}
                                        // ヘッダーを編集モード表示に変更
                                        try { headerUI.setMode('edit'); } catch(_) {}
                                        try { updatePhraseSaveEnabled(); } catch(_) {}
                                        raf(() => { try { ta.focus(); } catch(_) {} });
                                    } catch(_) {}
                                })();
                            }
                        });

                        deleteBtn.addEventListener('click', (e) => {
                            e.stopPropagation();
                            moreMenu.classList.remove('open');
                            // 既存のカード型確認ダイアログを使用
                            try {
                                showConfirmModal(
                                    'このフレーズを削除しますか？',
                                    () => {
                                        // 絞り込み中は DOM のインデックスと元配列のインデックスが一致しないため、IDベースで削除する
                                        const targetId = String((p && p.id != null) ? p.id : (item && item.dataset ? item.dataset.id : ''));
                                        let removeIndex = -1;
                                        if (targetId) {
                                            try { removeIndex = phrases.findIndex(x => String(x && x.id != null ? x.id : '') === targetId); } catch(_) { removeIndex = -1; }
                                        }
                                        if (removeIndex < 0) {
                                            // 予備: 参照一致
                                            try { removeIndex = phrases.indexOf(p); } catch(_) { removeIndex = -1; }
                                        }
                                        if (removeIndex < 0) {
                                            // 予備: タイトル＋本文一致
                                            try {
                                                const pt = String(p && p.title != null ? p.title : '');
                                                const px = String(p && p.text != null ? p.text : '');
                                                removeIndex = phrases.findIndex(x => String(x && x.title != null ? x.title : '') === pt && String(x && x.text != null ? x.text : '') === px);
                                            } catch(_) { removeIndex = -1; }
                                        }
                                        if (removeIndex >= 0) {
                                            const removed = phrases.splice(removeIndex, 1)[0];
                                            try {
                                                if (window.FileMaker != null && removed) {
                                                    const payload = buildPhrasePayloadFromItems(window.__currentRecordIdForPhrase, [removed]);
                                                    window.FileMaker.PerformScriptWithOption('addon_remarks_web_phrase_delete', payload, 5);
                                                }
                                            } catch(_) {}
                                            filterAndRender();
                                            // 削除後も Claris FileMaker 側に全件の最新状態を通知（編集時と同様）
                                            try { sendAllPhrasesToFM(); } catch(_) {}
                                        }
                                    },
                                    null,
                                    { confirmLabel: '削除', cancelLabel: 'キャンセル', confirmClass: 'button-danger' }
                                );
                            } catch(_) {}
                        });

                        // 行全体をクリック可能にする（ハンドルと3点メニューは除外）
                        item.style.cursor = 'pointer';
                        item.addEventListener('click', (e) => {
                            // ドラッグハンドル（左の3点）または3点メニュー（右の3点）がクリックされた場合は無視
                            if (e.target === handle || handle.contains(e.target) || 
                                e.target.closest('.template-item-actions')) {
                                return;
                            }
                            
                            const selectedText = String(p.text ?? '');
                            // 選択見た目の更新（薄い黄色で明示）→ 一定時間後に自動解除
                            try {
                                list.querySelectorAll('.template-item.is-selected').forEach(n => {
                                    try { if (n.__selectTimer) { clearTimeout(n.__selectTimer); n.__selectTimer = null; } } catch(_) {}
                                    n.classList.remove('is-selected');
                                });
                                item.classList.add('is-selected');
                                // ゆっくり消えるようにフェード用クラスを併用
                                item.classList.add('highlight-fade');
                                try { if (item.__selectTimer) { clearTimeout(item.__selectTimer); } } catch(_) {}
                                item.__selectTimer = setTimeout(() => {
                                    try { item.classList.remove('is-selected'); } catch(_) {}
                                }, 1800);
                            } catch(_) {}
                            // Shift+クリック: 備考に転記。それ以外: クリップボードにコピー
                            if (e.shiftKey) {
                                try {
                                    insertPhraseToGlobalNote(selectedText);
                                    try { showToast('備考に転記しました'); } catch(_) {}
                                    // 転記時も行を軽くハイライト
                                    try {
                                        item.classList.add('was-edited');
                                        setTimeout(() => {
                                            try {
                                                item.classList.add('highlight-fade');
                                                requestAnimationFrame(() => {
                                                    try { item.classList.remove('was-edited'); } catch(_) {}
                                                    setTimeout(() => { try { item.classList.remove('highlight-fade'); } catch(_) {} }, 400);
                                                });
                                            } catch(_) {}
                                        }, 0);
                                    } catch(_) {}
                                    // Claris FileMaker へも（可能なら）選択通知
                                    try {
                                        if (window.FileMaker != null) {
                                            const payload = buildPhrasePayloadFromItems(window.__currentRecordIdForPhrase, [{ title: String(p.title || ''), text: selectedText }]);
                                            window.FileMaker.PerformScriptWithOption('addon_remarks_web_phrase_select', payload, 5);
                                        }
                                    } catch(_) {}
                                } catch(_) {}
                                // 全画面時は自動で閉じる
                                if (mc.classList && mc.classList.contains('fullscreen')) {
                                    try { closePopover(); } catch(_) { try { popover.parentNode.removeChild(popover); } catch(__) {} }
                                }
                                return;
                            }
                            // クリップボードへコピー（共通ユーティリティを再利用）
                            try {
                                Promise.resolve(copyTextToClipboard(selectedText))
                                    .then((ok) => {
                                        try { showToast(ok ? 'クリップボードにコピーしました' : 'コピーに失敗しました'); } catch(_) {}
                                        if (ok) {
                                            try {
                                                item.classList.add('was-edited');
                                                setTimeout(() => {
                                                    try {
                                                        item.classList.add('highlight-fade');
                                                        requestAnimationFrame(() => {
                                                            try { item.classList.remove('was-edited'); } catch(_) {}
                                                            setTimeout(() => { try { item.classList.remove('highlight-fade'); } catch(_) {} }, 400);
                                                        });
                                                    } catch(_) {}
                                                }, 0);
                                            } catch(_) {}
                                        }
                                    })
                                    .catch(() => { try { showToast('コピーに失敗しました'); } catch(_) {} })
                                    .finally(() => {
                                        // 全画面時は自動で閉じる
                                        if (mc.classList && mc.classList.contains('fullscreen')) {
                                            try { closePopover(); } catch(_) { try { popover.parentNode.removeChild(popover); } catch(__) {} }
                                        }
                                    });
                            } catch(_) {
                                try { showToast('コピーに失敗しました'); } catch(__) {}
                            }
                        });

                        item.appendChild(row);
                        list.appendChild(item);
                    });
                }

                // ページング状態
                let currentPhrasePage = 1;
                let totalPhrasePages = 1;
                const PHRASE_PAGE_LIMIT = 20;
                // 検索状態
                let phraseSearchTerm = '';
                function isPhraseFiltered() {
                    try { return String(phraseSearchTerm || '').trim().length > 0; } catch(_) { return false; }
                }
                function renderPhrasePagination(page, total) {
                    if (!phrasePaginationEl) return;
                    if (total <= 1) { phrasePaginationEl.style.display = 'none'; clearChildren(phrasePaginationEl); return; }
                    phrasePaginationEl.style.display = '';
                    clearChildren(phrasePaginationEl);
                    const prevButton = document.createElement('button');
                    prevButton.className = 'button button-secondary';
                    prevButton.textContent = '前へ';
                    prevButton.disabled = page <= 1;
                    prevButton.addEventListener('click', () => {
                        if (currentPhrasePage > 1) {
                            currentPhrasePage -= 1;
                            filterAndRender();
                        }
                    });
                    const pageInfo = document.createElement('span');
                    pageInfo.textContent = `${page} / ${total} ページ`;
                    pageInfo.style.margin = '0 8px';
                    const nextButton = document.createElement('button');
                    nextButton.className = 'button button-secondary';
                    nextButton.textContent = '次へ';
                    nextButton.disabled = page >= total;
                    nextButton.addEventListener('click', () => {
                        if (currentPhrasePage < totalPhrasePages) {
                            currentPhrasePage += 1;
                            filterAndRender();
                        }
                    });
                    phrasePaginationEl.appendChild(prevButton);
                    phrasePaginationEl.appendChild(pageInfo);
                    phrasePaginationEl.appendChild(nextButton);
                }

                function filterAndRender() {
                    const base = Array.isArray(phrases) ? phrases : [];
                    const term = String(phraseSearchTerm || '').trim().toLowerCase();
                    const filtered = term ? base.filter(p => {
                        const t = String(p.text || '').toLowerCase();
                        const ti = String(p.title || '').toLowerCase();
                        return t.includes(term) || ti.includes(term);
                    }) : base;
                    totalPhrasePages = Math.max(1, Math.ceil(filtered.length / PHRASE_PAGE_LIMIT));
                    if (currentPhrasePage > totalPhrasePages) currentPhrasePage = totalPhrasePages;
                    const start = (currentPhrasePage - 1) * PHRASE_PAGE_LIMIT;
                    const pageItems = filtered.slice(start, start + PHRASE_PAGE_LIMIT);
                    render(pageItems);
                    renderPhrasePagination(currentPhrasePage, totalPhrasePages);
                }

                // すべてのフレーズを Claris FileMaker に送信（現在順）
                function sendAllPhrasesToFM() {
                    try {
                        if (window.FileMaker == null) return;
                        const json = buildPhrasePayloadFromItems(recordId, phrases || []);
                        // 一括保存用スクリプト（要 Claris FileMaker 側実装）
                        window.FileMaker.PerformScriptWithOption('addon_remarks_web_phrase_save_all', json, 5);
                    } catch(_) {}
                }

                // DOM順序から phrases 配列の順序を同期
                function syncPhrasesOrderFromDOM() {
                    try {
                        const listEl = document.getElementById('phrasePickerList');
                        if (!listEl) return;
                        const items = Array.from(listEl.querySelectorAll('.template-item'));
                        if (items.length === 0) return;
                        const idToPhrase = new Map();
                        (phrases || []).forEach(p => { idToPhrase.set(String(p.id ?? ''), p); });
                        const newOrder = [];
                        items.forEach((el) => {
                            const id = String(el.dataset.id ?? '');
                            const hit = idToPhrase.get(id);
                            if (hit) {
                                newOrder.push(hit);
                                idToPhrase.delete(id);
                            }
                        });
                        // 既に newOrder に入らなかった要素は末尾に（保険）
                        idToPhrase.forEach((val) => newOrder.push(val));
                        if (newOrder.length === (phrases || []).length) {
                            phrases = newOrder;
                        }
                    } catch(_) {}
                }

                // 改善されたドラッグ&ドロップ（モダンUX）
                let dragEl = null;
                let currentDropIndicator = null;
                let dragStartY = 0;
                let lastDropTarget = null;
                let lastInsertAfter = null;
                
                // プレースホルダーを作成
                function createDropIndicator() {
                    const indicator = document.createElement('div');
                    indicator.className = 'drop-indicator';
                    return indicator;
                }
                
                // 全てのドロップインジケーターをクリア
                function clearDropIndicators() {
                    const indicators = list.querySelectorAll('.drop-indicator');
                    indicators.forEach(indicator => indicator.remove());
                    currentDropIndicator = null;
                }
                // ドキュメント全体からインジケータを強制除去（保険）
                function hardClearDropIndicatorsGlobal() {
                    try { document.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el)); } catch(_) {}
                    currentDropIndicator = null;
                }
                
                // ドラッグ開始
                list.addEventListener('dragstart', (e) => {
                    const target = e.target && e.target.closest('.template-item');
                    if (!target) return;
                    
                    dragEl = target;
                    dragStartY = e.clientY;
                    
                    try { 
                        // 外部入力欄へのドロップに備えてテキストも転送
                        e.dataTransfer.effectAllowed = isPhraseFiltered() ? 'copy' : 'copyMove';
                        e.dataTransfer.setData('text/html', ''); // IE対応
                        // ドラッグ元のフレーズ本文を抽出
                        let textForDnD = '';
                        try {
                            const id = String(target.dataset.id || '');
                            const hit = (phrases || []).find(it => String(it.id ?? '') === id);
                            textForDnD = String((hit && (hit.text != null ? hit.text : hit.phrase)) || '');
                        } catch(_) {}
                        if (textForDnD) {
                            try { e.dataTransfer.setData('text/plain', textForDnD); } catch(_) {}
                            try { e.dataTransfer.setData('application/x-phrase', JSON.stringify({ id: String(target.dataset.id || ''), text: textForDnD })); } catch(_) {}
                            // カスタムゴースト（通常 / 薄い）を生成して初期設定
                            try {
                                if (window.__createPhraseGhost) {
                                    // 文字数が極端に多い場合は見やすさの為に短縮
                                    const previewText = String(textForDnD).slice(0, 180) + (textForDnD.length > 180 ? '…' : '');
                                    const defaultGhost = window.__createPhraseGhost(previewText, 0.55); // 並び替え時は使わず、ブラウザ標準に任せる
                                    const lightGhost = window.__createPhraseGhost(previewText, 0.12);   // 挿入モード時（かなり薄い）
                                    window.__phraseDragCtx = { defaultGhost, lightGhost };
                                }
                            } catch(_) {}
                        }
                    } catch(_) {}
                    
                    // ドラッグ要素にクラス追加（遅延実行でブラウザのスナップショット後に適用）
                    setTimeout(() => {
                        if (dragEl) {
                            dragEl.classList.add('dragging');
                            // 他の要素にhover効果を追加
                            list.classList.add('drag-active');
                        }
                    }, 0);

                    // グローバル側のフェイルセーフ（外でドロップ/キャンセル/フォーカス喪失でも確実にクリア）
                    try {
                        const onGlobalEnd = () => {
                            try { hardClearDropIndicatorsGlobal(); } catch(_) {}
                            try { list.classList.remove('drag-active'); } catch(_) {}
                            try { list.querySelectorAll('.template-item.drag-over').forEach(n => n.classList.remove('drag-over')); } catch(_) {}
                            lastDropTarget = null;
                            lastInsertAfter = null;
                        };
                        document.addEventListener('drop', onGlobalEnd, { once: true });
                        document.addEventListener('dragend', onGlobalEnd, { once: true });
                        window.addEventListener('blur', onGlobalEnd, { once: true });
                        // ウィンドウ外へ出たと判断できる dragleave も保険で監視
                        const onDocDragLeave = (ev) => {
                            const x = ev.clientX, y = ev.clientY;
                            if (x <= 0 || y <= 0 || x >= window.innerWidth || y >= window.innerHeight) {
                                onGlobalEnd();
                                document.removeEventListener('dragleave', onDocDragLeave, true);
                            }
                        };
                        document.addEventListener('dragleave', onDocDragLeave, true);
                        // Esc キーでキャンセルした場合もクリア
                        const onEsc = (ev) => { if (ev.key === 'Escape') { onGlobalEnd(); document.removeEventListener('keydown', onEsc, true); } };
                        document.addEventListener('keydown', onEsc, true);
                    } catch(_) {}
                });
                
                // ドラッグ終了
                list.addEventListener('dragend', (e) => {
                    const target = e.target && e.target.closest('.template-item');
                    if (target) {
                        target.classList.remove('dragging');
                    }
                    
                    // 全ての状態をクリア
                    list.classList.remove('drag-active');
                    clearDropIndicators();
                    hardClearDropIndicatorsGlobal();
                    lastDropTarget = null;
                    lastInsertAfter = null;
                    
                    // 他の要素のhover状態もクリア
                    list.querySelectorAll('.template-item.drag-over').forEach(item => {
                        item.classList.remove('drag-over');
                    });
                    
                    dragEl = null;
                    
                    // 変更を通知（少し遅延して DOM が安定してから）
                    setTimeout(() => {
                        try { syncPhrasesOrderFromDOM(); } catch(_) {}
                        try { sendAllPhrasesToFM(); } catch(_) {}
                        
                        // 成功フィードバック（控えめに）
                        if (dragEl && dragEl.classList.contains('dragging')) {
                            // 一時的な成功表示
                            const items = list.querySelectorAll('.template-item');
                            items.forEach((item, index) => {
                                setTimeout(() => {
                                    item.style.transform = 'scale(1.02)';
                                    setTimeout(() => {
                                        item.style.transform = '';
                                    }, 150);
                                }, index * 20);
                            });
                        }
                    }, 100);
                    // グローバルにも保険で除去（テンプレートD&Dの残骸も含む）
                    try { document.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el)); } catch(_) {}
                    try { document.querySelectorAll('.template-list.drag-active').forEach(el => el.classList.remove('drag-active')); } catch(_) {}
                });
                
                // ドラッグオーバー（さらに安定化: rAF + ヒステリシス + 再利用）
                let overRafId = null;
                list.addEventListener('dragover', (e) => {
                    if (isPhraseFiltered()) { 
                        // 内部並び替えは許可しないが、外部挿入用途のドラッグは維持
                        try { e.preventDefault(); } catch(_) {}
                        return; 
                    }
                    e.preventDefault();
                    if (!dragEl) return;
                    try { if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'; } catch(_) {}

                    if (overRafId) cancelAnimationFrame(overRafId);
                    const evt = e; // キャプチャ
                    overRafId = requestAnimationFrame(() => {
                        let target = evt.target && (evt.target.closest && evt.target.closest('.template-item'));
                        if (!target) {
                            // インジケーター上の場合は最後のターゲットを使う
                            if (currentDropIndicator && (evt.target === currentDropIndicator || (evt.target.closest && evt.target.closest('.drop-indicator')))) {
                                target = lastDropTarget;
                            }
                        }
                        if (!target || dragEl === target) return;

                        const rect = target.getBoundingClientRect();
                        const mouseY = evt.clientY;
                        const mid = rect.top + rect.height / 2;
                        const diff = mouseY - mid;
                        const HYST = 4;
                        let insertAfter;
                        if (diff > HYST) insertAfter = true; else if (diff < -HYST) insertAfter = false; else insertAfter = (lastInsertAfter ?? diff > 0);

                        if (lastDropTarget === target && lastInsertAfter === insertAfter) return;
                        lastDropTarget = target;
                        lastInsertAfter = insertAfter;

                        const indicator = currentDropIndicator || createDropIndicator();
                        currentDropIndicator = indicator;
                        const parent = target.parentNode;
                        if (insertAfter) {
                            if (indicator.previousSibling !== target) parent.insertBefore(indicator, target.nextSibling);
                        } else {
                            if (indicator !== target) parent.insertBefore(indicator, target);
                        }
                        indicator.classList.add('active');

                        if (!target.classList.contains('drag-over')) {
                            list.querySelectorAll('.template-item.drag-over').forEach(n => n.classList.remove('drag-over'));
                            target.classList.add('drag-over');
                        }
                    });
                });
                
                // ドロップ処理
                list.addEventListener('drop', (e) => {
                    if (isPhraseFiltered()) { try { e.preventDefault(); } catch(_) {} return; }
                    e.preventDefault();
                    if (!dragEl || !currentDropIndicator) return;
                    
                    // インジケーターの位置にドラッグ要素を移動
                    currentDropIndicator.parentNode.insertBefore(dragEl, currentDropIndicator);
                    // 既存ハイライト機構を使用して視覚的に強調
                    try { if (typeof window.highlightAndScrollTemplateEl === 'function') window.highlightAndScrollTemplateEl(dragEl); } catch(_) {}
                    // インジケータを即時クリア（手を離した瞬間に消えるように）
                    try { clearDropIndicators(); } catch(_) {}
                    try { hardClearDropIndicatorsGlobal(); } catch(_) {}
                    // さらに次フレームでもう一度保険でクリア（ブラウザ差異吸収）
                    try { requestAnimationFrame(() => { try { clearDropIndicators(); hardClearDropIndicatorsGlobal(); } catch(_) {} }); } catch(_) {}
                    // リストの drag-active も解除
                    try { list.classList.remove('drag-active'); } catch(_) {}
                });

                // リスト外にカーソルが出た場合もインジケータを消す（保険）
                try {
                    list.addEventListener('mouseleave', () => {
                        if (isPhraseFiltered()) return;
                        if (!dragEl) return;
                        try { clearDropIndicators(); } catch(_) {}
                    });
                } catch(_) {}

                // 追加/編集/削除
                let isRegisterMode = false;
                let editingIndex = null;
                let editingId = null; // 絞り込み時でも正しく対象を特定するためのID
                let newlyCreatedId = null; // 新規作成されたアイテムのIDを保持
                const registerWrap = document.createElement('div');
                registerWrap.style.display = 'none';
                registerWrap.style.marginTop = '8px';
                registerWrap.style.display = 'none';
                registerWrap.style.borderTop = '1px solid #eee';
                registerWrap.style.paddingTop = '8px';

                const titleInput = document.createElement('input');
                titleInput.type = 'text';
                titleInput.placeholder = 'タイトル...';
                titleInput.style.width = '100%';
                titleInput.style.boxSizing = 'border-box';
                titleInput.style.marginBottom = '6px';
                const ta = document.createElement('textarea');
                ta.rows = 3;
                ta.placeholder = '新規フレーズを入力...';
                // 入力欄へのフォーカスを阻害しないよう、ドラッグ用ハンドラ等へのバブリングを抑止
                try {
                    titleInput.addEventListener('mousedown', (e) => { e.stopPropagation(); }, true);
                    titleInput.addEventListener('touchstart', (e) => { e.stopPropagation(); }, { passive: true, capture: true });
                    ta.addEventListener('mousedown', (e) => { e.stopPropagation(); }, true);
                    ta.addEventListener('touchstart', (e) => { e.stopPropagation(); }, { passive: true, capture: true });
                } catch(_) {}
                const saveBtn = document.createElement('button');
                saveBtn.type = 'button';
                saveBtn.className = 'button';
                saveBtn.textContent = '保存';
                // 初期は無効（タイトル・本文未入力のため）
                try { saveBtn.disabled = true; saveBtn.setAttribute('disabled', ''); } catch(_) {}
                // 内部の保存/キャンセルは表示しない（フッターに配置するため）
                saveBtn.style.display = 'none';
                const cancelEditBtn = document.createElement('button');
                cancelEditBtn.type = 'button';
                cancelEditBtn.className = 'button button-secondary';
                cancelEditBtn.textContent = 'キャンセル';
                cancelEditBtn.style.display = 'none';
                const buttonsRow = document.createElement('div');
                buttonsRow.style.display = 'none';
                buttonsRow.style.gap = '8px';
                buttonsRow.style.justifyContent = 'flex-end';

                registerWrap.appendChild(titleInput);
                registerWrap.appendChild(ta);
                // 保存不可時のガイダンス表示（新規作成時に表示）
                const saveHint = document.createElement('span');
                saveHint.className = 'input-hint';
                saveHint.textContent = 'タイトルとフレーズを入力してください';
                saveHint.style.marginRight = 'auto';
                saveHint.style.color = '#6c757d';
                saveHint.style.fontSize = '12px';
                saveHint.style.alignSelf = 'center';
                saveHint.style.display = 'none';
                buttonsRow.appendChild(saveHint);
                buttonsRow.appendChild(cancelEditBtn);
                buttonsRow.appendChild(saveBtn);
                registerWrap.appendChild(buttonsRow);
                body.appendChild(registerWrap);

                // 保存ボタンの活性/非活性制御（タイトルと本文の両方必須）
                function updatePhraseSaveEnabled() {
                    try {
                        const titleVal = String(titleInput.value || '').trim();
                        const textVal = String(ta.value || '').trim();
                        const canSave = !!(titleVal && textVal);
                        try { setBtnDisabled(saveBtn, !canSave); } catch(_) { try { saveBtn.disabled = !canSave; if (!canSave) saveBtn.setAttribute('disabled', ''); else saveBtn.removeAttribute('disabled'); } catch(_) {} }
                        try { setBtnDisabled(editFooterSaveBtn, !canSave); } catch(_) { try { editFooterSaveBtn.disabled = !canSave; if (!canSave) editFooterSaveBtn.setAttribute('disabled', ''); else editFooterSaveBtn.removeAttribute('disabled'); } catch(_) {} }
                        // 無効時の明示: ARIA とタイトル、ヒント表示
                        try { saveBtn.setAttribute('aria-disabled', String(!canSave)); } catch(_) {}
                        try { if (editFooterSaveBtn) editFooterSaveBtn.setAttribute('aria-disabled', String(!canSave)); } catch(_) {}
                        try { saveBtn.title = canSave ? '' : 'タイトルとフレーズを入力してください'; } catch(_) {}
                        try { if (editFooterSaveBtn) editFooterSaveBtn.title = canSave ? '' : 'タイトルとフレーズを入力してください'; } catch(_) {}
                        // グローバル固定フッター側の保存ボタン（全画面時に使用）も同期
                        try {
                            const globalFooterSave = document.getElementById('footerEditSave');
                            if (globalFooterSave) {
                                try { setBtnDisabled(globalFooterSave, !canSave); } catch(_) {}
                                try { globalFooterSave.disabled = !canSave; if (!canSave) globalFooterSave.setAttribute('disabled', ''); else globalFooterSave.removeAttribute('disabled'); } catch(_) {}
                                try { globalFooterSave.setAttribute('aria-disabled', String(!canSave)); } catch(_) {}
                                try { globalFooterSave.title = canSave ? '' : 'タイトルとフレーズを入力してください'; } catch(_) {}
                            }
                        } catch(_) {}
                        // グローバルフッターのヒント表示
                        try {
                            const globalHint = document.getElementById('globalFooterPhraseSaveHint');
                            if (globalHint) {
                                if (registerWrap && registerWrap.dataset && registerWrap.dataset.mode === 'create') globalHint.style.display = canSave ? 'none' : '';
                                else globalHint.style.display = 'none';
                            }
                        } catch(_) {}
                        try {
                            if (registerWrap && registerWrap.dataset && registerWrap.dataset.mode === 'create') {
                                saveHint.style.display = canSave ? 'none' : '';
                            } else {
                                saveHint.style.display = 'none';
                            }
                        } catch(_) {}
                    } catch(_) {}
                }
                try { titleInput.addEventListener('input', updatePhraseSaveEnabled); } catch(_) {}
                try { ta.addEventListener('input', updatePhraseSaveEnabled); } catch(_) {}
                try { updatePhraseSaveEnabled(); } catch(_) {}

                // [+] クリックで新規作成フォーム表示
                try {
                    phraseNewButton.addEventListener('click', () => {
                        try {
                            // 既存のモード制御に一元化（setMode('create') が [+] を隠す）
                            isRegisterMode = true;
                            registerWrap.dataset.mode = 'create';
                            editingIndex = null;
                            newlyCreatedId = null; // 新規作成モードに入る時はリセット
                            try { headerUI.setMode('create'); } catch(_) {}
                            titleInput.value = '';
                            ta.value = '';
                            registerWrap.style.display = 'block';
                            list.style.display = 'none';
                            phrasePaginationEl.style.display = 'none';
                            // フッター切替（閉じる→キャンセル/保存）
                            footerCloseBtn.style.display = 'none';
                            editFooterCancelBtn.style.display = '';
                            editFooterSaveBtn.style.display = '';
                            editFooterSaveBtn.textContent = '保存';
                            try { editFooterSaveBtn.disabled = true; editFooterSaveBtn.setAttribute('disabled', ''); editFooterSaveBtn.setAttribute('aria-disabled', 'true'); editFooterSaveBtn.title = 'タイトルとフレーズを入力してください'; } catch(_) {}
                            // フルスクリーン時は固定フッターを[キャンセル][保存]に切替
                            try {
                                if (mc.classList && mc.classList.contains('fullscreen')) {
                                    try { footer && (footer.style.display = 'none'); } catch(_) {}
                                    const footerCancel = document.getElementById('footerCancelButton');
                                    const footerOk = document.getElementById('footerQueryButton');
                                    const footerEditCancel = document.getElementById('footerEditCancel');
                                    const footerEditSave = document.getElementById('footerEditSave');
                                    const changeNav = document.querySelector('.footer .change-nav');
                                    if (footerCancel) footerCancel.style.display = 'none';
                                    if (footerOk) footerOk.style.display = 'none';
                                    if (changeNav) { changeNav.dataset.prevDisplay = changeNav.style.display || ''; changeNav.style.display = 'none'; }
                                    if (footerEditCancel) {
                                        try { unbindSingleEvent(footerEditCancel, 'click', 'phraseCreate:cancel'); } catch(_) {}
                                        // フルスクリーン一覧時に割り当てていた「戻る(閉じる)」用の一時ハンドラを解除
                                        try { unbindSingleEvent(footerEditCancel, 'click', 'phrasePopover:close'); } catch(_) {}
                                        try { popover.__footerCloseHandler && footerEditCancel.removeEventListener('click', popover.__footerCloseHandler); } catch(_) {}
                                        footerEditCancel.textContent = 'キャンセル';
                                        footerEditCancel.style.display = '';
                                        popover.__footerCancelHandler = () => { try { restorePhraseListUI(); } catch(_) {} };
                                        bindSingleEvent(footerEditCancel, 'click', 'phraseCreate:cancel', popover.__footerCancelHandler, false);
                                    }
                                    if (footerEditSave) {
                                        try { unbindSingleEvent(footerEditSave, 'click', 'phraseCreate:save'); } catch(_) {}
                                        footerEditSave.textContent = '保存';
                                        footerEditSave.style.display = '';
                                        popover.__footerSaveHandler = () => { try { saveBtn.click(); } catch(_) {} };
                                        bindSingleEvent(footerEditSave, 'click', 'phraseCreate:save', popover.__footerSaveHandler, false);
                                    }
                                    updateFooterHiddenState();
                                }
                            } catch(_) {}
                            updatePhraseSaveEnabled();
                            // 念のためドラッグ中フラグを解除（ユーザー選択を有効化）
                            try { document.body.classList.remove('dragging'); } catch(_) {}
                            raf(() => { try { titleInput.focus(); } catch(_) {} });
                        } catch(_) {}
                    });
                } catch(_) {}

                // ヘッダーの3点ボタンは使わない

                cancelEditBtn.addEventListener('click', () => { restorePhraseListUI(); });

                saveBtn.addEventListener('click', () => {
                    const value = ta.value || '';
                    const titleVal = (titleInput.value || '').trim();
                    // タイトルまたは本文が空なら保存不可（本文は trim() で判定、保存時は元の値を使用）
                    if (!value.trim() || !titleVal) return;
                    const mode = registerWrap.dataset.mode || 'create';
                    if (mode === 'edit') {
                        // IDベースで編集対象を再特定（絞り込みや並び替え後も正しく更新）
                        let idxToUpdate = -1;
                        try {
                            if (editingId != null) {
                                idxToUpdate = (phrases || []).findIndex(x => String(x && x.id != null ? x.id : '') === String(editingId));
                            } else if (editingIndex != null) {
                                idxToUpdate = editingIndex;
                            }
                        } catch(_) { idxToUpdate = editingIndex != null ? editingIndex : -1; }
                        if (idxToUpdate >= 0 && phrases[idxToUpdate]) {
                        // 更新
                        const target = phrases[idxToUpdate];
                        target.text = value;
                        target.title = titleVal;
                        try {
                            if (window.FileMaker != null) {
                                const payload = buildPhrasePayloadFromItems(recordId, [target]);
                                window.FileMaker.PerformScriptWithOption('addon_remarks_web_phrase_update', payload, 5);
                            }
                        } catch(_) {}
                        }
                    } else {
                        // 新規作成
                        const item = { id: String(Date.now()), text: value, title: titleVal };
                        newlyCreatedId = item.id; // 新規作成されたアイテムのIDを保持
                        phrases.push(item);
                        try {
                            if (window.FileMaker != null) {
                                const payload = buildPhrasePayloadFromItems(recordId, [{ title: titleVal, text: value }]);
                                window.FileMaker.PerformScriptWithOption('addon_remarks_web_phrase_create', payload, 5);
                            }
                        } catch(_) {}
                    }
                    // クリア・閉じる
                    titleInput.value = '';
                    ta.value = '';
                    filterAndRender();
                    // 一括保存も通知
                    try { sendAllPhrasesToFM(); } catch(_) {}
                        // 一覧ビューへ戻る（共通処理）
                        restorePhraseListUI();
                        // 編集コンテキストをクリア
                        try { editingIndex = null; editingId = null; } catch(_) {}
                });

                // 検索UIの動作
                try {
                    phraseSearchToggle.addEventListener('click', () => {
                        const visible = phraseSearchWrap.style.display !== 'none';
                        try { headerUI.toggleSearch(!visible); } catch(_) {}
                            if (!visible) {
                            raf(() => {
                                try {
                                    phraseSearchInput.focus({ preventScroll: true });
                                    const len = phraseSearchInput.value.length;
                                    phraseSearchInput.setSelectionRange(len, len);
                                } catch(_) {}
                            });
                        }
                    });
                    // 入力時はドラッグ開始させず、確実にフォーカスできるようにする
                    try {
                        phraseSearchInput.addEventListener('mousedown', (e) => { e.stopPropagation(); }, true);
                        phraseSearchInput.addEventListener('touchstart', (e) => { e.stopPropagation(); }, { passive: true, capture: true });
                        phraseSearchWrap.addEventListener('mousedown', (e) => { e.stopPropagation(); }, true);
                        phraseSearchWrap.addEventListener('touchstart', (e) => { e.stopPropagation(); }, { passive: true, capture: true });
                    } catch(_) {}
                    const execSearch = () => {
                        phraseSearchTerm = String(phraseSearchInput.value || '').trim();
                        currentPhrasePage = 1;
                        filterAndRender();
                    };
                    phraseSearchExec.addEventListener('click', execSearch);
                    phraseSearchInput.addEventListener('keydown', (e) => {
                        if (e.key === 'Enter') { e.preventDefault(); execSearch(); }
                    });
                    // [x] で検索解除＆非表示
                    phraseSearchClose.addEventListener('click', () => {
                        try { phraseSearchInput.value = ''; } catch(_) {}
                        phraseSearchTerm = '';
                        currentPhrasePage = 1;
                        filterAndRender();
                        try { headerUI.toggleSearch(false); } catch(_) {}
                    });
                } catch(_) {}
                
                const closePopover = () => {
                    try { popover.parentNode.removeChild(popover); } catch(_) {}
                    // 全画面用に一時変更した固定フッターを元に戻す
                    try {
                        const footerCancel = document.getElementById('footerCancelButton');
                        const footerOk = document.getElementById('footerQueryButton');
                        const footerEditCancel = document.getElementById('footerEditCancel');
                        const footerEditSave = document.getElementById('footerEditSave');
                        const changeNav = document.querySelector('.footer .change-nav');
                        if (footerEditCancel) {
                            try { footerEditCancel.textContent = footerEditCancel.dataset && footerEditCancel.dataset.prevLabel ? footerEditCancel.dataset.prevLabel : 'キャンセル'; } catch(_) {}
                            footerEditCancel.style.display = 'none';
                            try { popover.__footerCloseHandler && footerEditCancel.removeEventListener('click', popover.__footerCloseHandler); } catch(_) {}
                            try { popover.__footerCancelHandler && footerEditCancel.removeEventListener('click', popover.__footerCancelHandler); } catch(_) {}
                        }
                        if (footerEditSave) footerEditSave.style.display = 'none';
                        if (footerCancel) footerCancel.style.display = '';
                        if (footerOk) footerOk.style.display = '';
                        try { window.restoreChangeNavDisplay && window.restoreChangeNavDisplay(); } catch(_) {}
                        updateFooterHiddenState();
                        // タイミングずれ対策：次フレームでもう一度復帰
                        setTimeout(() => { try { window.resetFooterForTemplate && window.resetFooterForTemplate(); } catch(_) {} try { updateFooterHiddenState(); } catch(_) {} }, 0);
                    } catch(_) {}
                };
                // スコープ管理（フレーズピッカー用）
                try { popover.__scope && popover.__scope.cleanup && popover.__scope.cleanup(); } catch(_) {}
                try { popover.__scope = createScope(); } catch(_) {}
                
                popover.__scope && popover.__scope.addEvent && popover.__scope.addEvent(footerCloseBtn, 'click', closePopover, false);
                // ESCのみで閉じる（外側クリックでは閉じない）
                try { popover.__scope && popover.__scope.addDetach && popover.__scope.addDetach(attachEscToClose(closePopover, document)); } catch(_) {}
                // 編集フッターボタンの動作
                popover.__scope && popover.__scope.addEvent && popover.__scope.addEvent(editFooterCancelBtn, 'click', restorePhraseListUI, false);
                popover.__scope && popover.__scope.addEvent && popover.__scope.addEvent(editFooterSaveBtn, 'click', () => { try { saveBtn.click(); } catch(_) {} }, false);

                // フレーズピッカー外クリックでメニューを閉じる
                popover.__scope && popover.__scope.addEvent && popover.__scope.addEvent(document, 'click', (e) => {
                    if (!e.target.closest('#phraseModal')) {
                        document.querySelectorAll('#phrasePickerList .more-menu.open').forEach(mm => mm.classList.remove('open'));
                    }
                    // どこかをクリックしたら残留するドロップインジケータ（青い線）を確実に除去
                    try { document.querySelectorAll('.drop-indicator').forEach(el => el && el.parentNode && el.parentNode.removeChild(el)); } catch(_) {}
                    try { document.querySelectorAll('.template-list.drag-active').forEach(el => el.classList.remove('drag-active')); } catch(_) {}
                }, false);

                // フレーズピッカー内のクリックでもメニューを閉じる（3点ボタン以外）
                popover.__scope && popover.__scope.addEvent && popover.__scope.addEvent(popover, 'click', (e) => {
                    // 3点ボタンやメニュー自体のクリックは除外
                    if (!e.target.closest('.template-item-actions')) {
                        document.querySelectorAll('#phrasePickerList .more-menu.open').forEach(mm => mm.classList.remove('open'));
                    }
                    // 内側クリック時も残留インジケータ（青い線）は除去するが、入力要素クリックではフォーカスを保つ
                    try { clearDropIndicators(); } catch(_) {}
                    try { hardClearDropIndicatorsGlobal(); } catch(_) {}
                    try { list.classList.remove('drag-active'); list.querySelectorAll('.template-item.drag-over').forEach(n => n.classList.remove('drag-over')); } catch(_) {}
                    try {
                        const isInputClick = !!(e.target && (e.target.closest && e.target.closest('input, textarea, select, [contenteditable=""], [contenteditable="true"], #phraseSearchWrap')));
                        const isRegisterAreaClick = !!(e.target && e.target.closest && e.target.closest('[data-mode="create"], [data-mode="edit"], #phrasePickerList + div'));
                        if (isInputClick || isRegisterAreaClick) {
                            return; // 入力関連のクリックではフォーカスを外さない
                        }
                        const active = document.activeElement;
                        if (active && (list.contains(active) || (active.closest && active.closest('#phraseModal')))) {
                            if (typeof active.blur === 'function') active.blur();
                        }
                    } catch(_) {}
                }, false);

                // 検索なしで全件表示
                (function renderAll(){
                    const arr = Array.isArray(phrases) ? phrases : [];
                    if (arr.length === 0) {
                        // 空状態の案内を表示
                        clearChildren(list);
                        const empty = document.createElement('div');
                        empty.className = 'template-item';
                        const row = document.createElement('div');
                        row.className = 'template-item-row';
                        const main = document.createElement('div');
                        main.className = 'template-item-main';
                        const bodyEl = document.createElement('div');
                        bodyEl.className = 'template-body';
                        bodyEl.textContent = 'フレーズがありません';
                        main.appendChild(bodyEl);
                        row.appendChild(main);
                        empty.appendChild(row);
                        list.appendChild(empty);
                        phrasePaginationEl.style.display = 'none';
                        return;
                    }
                    const pageItems = arr.slice(0, PHRASE_PAGE_LIMIT);
                    render(pageItems);
                    totalPhrasePages = Math.max(1, Math.ceil(arr.length / PHRASE_PAGE_LIMIT));
                    renderPhrasePagination(1, totalPhrasePages);
                })();
                // デバッグ: モーダル作成完了を確認
                logger.info('openPhrasePickerModal 完了', { 
                    modalStillInDOM: !!document.getElementById('phraseModal'),
                    phrasesRendered: phrases.length
                });
            }

            function insertPhraseToGlobalNote(text) {
                try {
                    const el = document.getElementById('global-note-textarea');
                    if (!el) return;
                    const start = (typeof el.selectionStart === 'number') ? el.selectionStart : (el.value ? el.value.length : 0);
                    const end = (typeof el.selectionEnd === 'number') ? el.selectionEnd : start;
                    const value = String(el.value || '');
                    const before = value.slice(0, start);
                    const after = value.slice(end);
                    const insert = String(text || '');
                    const nextValue = before + insert + after;
                    const caret = before.length + insert.length;
                    // 履歴対応のプログラム挿入（失敗時フォールバック）
                    try { if (typeof applyProgrammaticValue === 'function') { applyProgrammaticValue(el, nextValue); } else { el.value = nextValue; } } catch(_) { try { el.value = nextValue; } catch(__) {} }
                    // グローバル備考ではキャレット/スクロール補正を行わない
                    try { el.focus({ preventScroll: true }); } catch(_) { try { el.focus(); } catch(__) {} }
                } catch(_) {}
            }
            // フレーズ編集モーダル機能
            window.enterPhraseEditMode = function(phraseItem, phraseIndex, refreshCallback) {
                try {
                    const modal = document.getElementById('phraseEditModal');
                    const textarea = document.getElementById('phraseEditTextarea');
                    const cancelBtn = document.getElementById('phraseEditCancel');
                    const saveBtn = document.getElementById('phraseEditSave');
                    
                    if (!modal || !textarea || !cancelBtn || !saveBtn) return;
                    
                    // モーダルを表示
                    openModal(modal);
                    try { updateFooterHiddenState(); } catch(_) {}
                    
                    // 現在のフレーズテキストを設定
                    textarea.value = String(phraseItem.text ?? '');
                    
                    // ドラッグ可能にする
                    try {
                        const content = modal.querySelector('.modal-content');
                        if (content && typeof makeModalDraggable === 'function') {
                            makeModalDraggable(modal, content);
                        }
                    } catch(_) {}
                    
                    // フォーカス設定
                    raf(() => { 
                        try { 
                            textarea.focus(); 
                            textarea.setSelectionRange(textarea.value.length, textarea.value.length);
                        } catch(_) {} 
                    });
                    
                    // キャンセル処理
                    const handleCancel = () => {
                        closeModal(modal);
                        try { updateFooterHiddenState(); } catch(_) {}
                        cleanup();
                    };
                    
                    // 保存処理
                    const handleSave = () => {
                        const newText = (textarea.value || '').trim();
                        if (!newText) {
                            try {
                                showMessageModal('フレーズが入力されていません。', '入力エラー');
                            } catch(_) {}
                            return;
                        }
                        
                        if (newText !== String(phraseItem.text ?? '')) {
                            // フレーズを更新
                            phraseItem.text = newText;
                            
                            // Claris FileMaker に通知
                            try {
                                if (window.FileMaker != null) {
                                    const payload = buildPhrasePayloadFromItems(window.__currentRecordIdForPhrase, [{ title: phraseItem.title, text: newText }]);
                                    window.FileMaker.PerformScriptWithOption('addon_remarks_web_phrase_update', payload, 5);
                                }
                            } catch(_) {}
                            
                            // リストを更新
                            if (typeof refreshCallback === 'function') {
                                refreshCallback();
                            }
                        }
                        
                        closeModal(modal);
                        try { updateFooterHiddenState(); } catch(_) {}
                        cleanup();
                    };
                    
                    // イベントリスナーのクリーンアップ
                    const cleanup = () => {
                        cancelBtn.removeEventListener('click', handleCancel);
                        saveBtn.removeEventListener('click', handleSave);
                        try { if (modal.__detachOutside) { modal.__detachOutside(); modal.__detachOutside = null; } } catch(_) {}
                        try { if (modal.__detachEsc) { modal.__detachEsc(); modal.__detachEsc = null; } } catch(_) {}
                    };
                    
                    // 外側クリックで閉じる（共通関数）
                    try { if (modal.__detachOutside) { modal.__detachOutside(); } } catch(_) {}
                    const detachOutsidePhrase = attachOutsideClickClose(modal, handleCancel);
                    try { modal.__detachOutside = detachOutsidePhrase; } catch(_) {}
                    
                    // ESCキーで閉じる（共通関数）
                    try { if (modal.__detachEsc) { modal.__detachEsc(); } } catch(_) {}
                    const detachEscKey = attachEscToClose(handleCancel, document);
                    try { modal.__detachEsc = detachEscKey; } catch(_) {}
                    
                    // イベントリスナーを設定
                    cancelBtn.addEventListener('click', handleCancel);
                    saveBtn.addEventListener('click', handleSave);
                    // 外側クリック/ESCは共通関数で付与済み
                    
                } catch(_) {}
            };

            // フレーズピッカー用ドラッグ機能
            function enablePhrasePickerDrag(popover) {
                if (!popover) return;
                const handle = popover.querySelector('.popover-header');
                if (!handle) return;
                const detach = attachDragMove(popover, {
                    handleEl: handle,
                    clampToViewport: true,
                    padding: 8,
                    blockSelector: 'button, .var-gear-btn, .popover-close-x',
                    disallowSelection: true,
                    smoothing: 0.25,
                    disabledPredicate: () => !!(popover && popover.classList && popover.classList.contains('fullscreen')),
                    onStart: () => { try { document.body.classList.add('dragging'); popover.classList.remove('drag-animate'); } catch(_) {} },
                    onEnd: () => { try { document.body.classList.remove('dragging'); requestAnimationFrame(() => popover.classList.add('drag-animate')); } catch(_) {} },
                });
                popover.__dragCleanup = () => { try { detach(); } catch(_) {} };
            }
        })();
        /*
         * FileMaker Web ビューア特有のカーソル移動問題に対する回避策
         * - 一部環境（Claris FileMaker Web ビューア）では textarea/input の行頭・行末で左右矢印キーを押すと
         *   フォーカスがコンポーネント外へ移動したり、予期しないナビゲーションが発生する既知の挙動があります。
         * - 本ガードは「入力中の要素で、選択範囲がなく、キャレットが先頭/末尾にあるとき」に限って
         *   ArrowLeft/ArrowRight をキャンセルしてフォーカス逸脱を防止します。
         * - IME 変換中は抑止しません（composition イベントを考慮）。
         * - 本挙動はアクセシビリティ上の配慮として最小限の範囲に限定しており、一般的な環境でも入力体験を損ねない設計です。
         * - もし他環境で不要な場合は、当該リスナーの登録箇所（document.addEventListener('keydown', ... , true)）を無効化してください。
         */
        // 矢印キーガード（テキストエリアの端で左右矢印を無効化）
        function guardArrowKeysOnEdges(event) {
            if (event.isComposing) return; // IME入力中は無視
            const target = event.target;
            const isTextarea = target instanceof HTMLTextAreaElement;
            const isSingleLineInput = (target instanceof HTMLInputElement) && (
                target.type === 'text' || target.type === 'search' || target.type === 'url' || target.type === 'email' || target.type === 'password'
            );
            if (!isTextarea && !isSingleLineInput) return;

            const selectionStart = target.selectionStart;
            const selectionEnd = target.selectionEnd;
            if (selectionStart == null || selectionEnd == null) return;

            // 選択範囲がある場合は対象外（"カーソル"ではないため）
            if (selectionStart !== selectionEnd) return;

            if (isTextarea) {
                // テキストエリア全体の先頭/末尾のみ無効化
                const value = target.value ?? '';
                if (event.key === 'ArrowLeft' && selectionStart === 0) {
                    event.preventDefault();
                    event.stopPropagation();
                    return;
                }
                // Fn+← が Home として報告される環境向け
                if (event.key === 'Home' && selectionStart === 0) {
                    event.preventDefault();
                    event.stopPropagation();
                    return;
                }
                if (event.key === 'ArrowRight' && selectionStart === value.length) {
                    event.preventDefault();
                    event.stopPropagation();
                    return;
                }
                // Fn+→ が End として報告される環境向け
                if (event.key === 'End' && selectionStart === value.length) {
                    event.preventDefault();
                    event.stopPropagation();
                    return;
                }
            } else if (isSingleLineInput) {
                if (event.key === 'ArrowLeft' && selectionStart === 0) {
                    event.preventDefault();
                    event.stopPropagation();
                    return;
                }
                if (event.key === 'Home' && selectionStart === 0) {
                    event.preventDefault();
                    event.stopPropagation();
                    return;
                }
                if (event.key === 'ArrowRight' && selectionStart === target.value.length) {
                    event.preventDefault();
                    event.stopPropagation();
                    return;
                }
                if (event.key === 'End' && selectionStart === target.value.length) {
                    event.preventDefault();
                    event.stopPropagation();
                    return;
                }
            }
        }
        // 動的生成のテキストエリアやモーダル内も含め、全テキストエリアで効くようにドキュメントに登録
        document.addEventListener('keydown', guardArrowKeysOnEdges, true);

        // IME入力（日本語変換など）は1回のUndoで戻せるようにグルーピング
        document.addEventListener('compositionstart', (e) => {
            const t = e.target;
            const isTextarea = t instanceof HTMLTextAreaElement;
            const isSingleLineInput = (t instanceof HTMLInputElement) && (
                t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
            );
            if (!isTextarea && !isSingleLineInput) return;
            t.__isComposing = true;
            t.__valueBeforeComposition = t.value ?? '';
            // 初回の編集でも必ず基点ができるよう、ここで履歴を初期化
            try { getHistoryFor(t); } catch(_) {}
        }, true);

        // 最初のキー入力前に履歴の基点を確実に作る（英数字入力の初回対策）
        document.addEventListener('focusin', (e) => {
            const t = e.target;
            const isTextarea = t instanceof HTMLTextAreaElement;
            const isSingleLineInput = (t instanceof HTMLInputElement) && (
                t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
            );
            if (!isTextarea && !isSingleLineInput) return;
            try { getHistoryFor(t); } catch(_) {}
        }, true);

        document.addEventListener('compositionend', (e) => {
            const t = e.target;
            const isTextarea = t instanceof HTMLTextAreaElement;
            const isSingleLineInput = (t instanceof HTMLInputElement) && (
                t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
            );
            if (!isTextarea && !isSingleLineInput) return;
            t.__isComposing = false;
            // 変換確定後の値を1ステップとして履歴に積む
            manageUndoRedo(t);
        }, true);

        // 入力時に各要素の履歴を更新（動的要素対応）
        document.addEventListener('input', (e) => {
            const t = e.target;
            const isTextarea = t instanceof HTMLTextAreaElement;
            const isSingleLineInput = (t instanceof HTMLInputElement) && (
                t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
            );
            if (!isTextarea && !isSingleLineInput) return;
            if (e.isComposing || t.__isComposing) return; // 変換中は履歴に積まない
            // IME確定や貼り付けの度に履歴へ積む（短時間の大量input連打をバッチ化）
            if (t.__undoDebounceTimer) clearTimeout(t.__undoDebounceTimer);
            t.__undoDebounceTimer = setTimeout(() => {
                manageUndoRedo(t);
                t.__undoDebounceTimer = null;
            }, 150);
        }, true);
        // チェックボックスの状態変更に応じてUI更新
        function updateCheckboxUI(checkbox) {
            if (!(checkbox instanceof HTMLInputElement) || checkbox.type !== 'checkbox') return;
            if (!checkbox.classList.contains('variable-input')) return;
            
            const container = checkbox.closest('.checkbox-container');
            if (!container) return;
            
            const statusElement = container.querySelector('.checkbox-status');
            if (statusElement) {
                statusElement.textContent = checkbox.checked ? 'チェック済み' : 'チェックなし';
            }
            
            if (checkbox.checked) {
                container.classList.add('checked');
            } else {
                container.classList.remove('checked');
            }
            
            // 未入力スタイルの更新
            container.classList.toggle('is-empty', !checkbox.checked);
            checkbox.classList.toggle('is-empty', !checkbox.checked);
        }

        // 日付/時刻ピッカーで選択後に自動で閉じる（blur）
        function blurIfDateOrTimeInput(target) {
            if (!(target instanceof HTMLInputElement)) return;
            const typ = (target.type || '').toLowerCase();
            if (typ !== 'date' && typ !== 'time' && typ !== 'datetime-local') return;
            raf(() => {
                try { target.blur(); } catch (_) {}
            });
        }
        
        document.addEventListener('change', (e) => {
            blurIfDateOrTimeInput(e.target);
            // チェックボックスの状態更新
            if (e.target instanceof HTMLInputElement && e.target.type === 'checkbox') {
                updateCheckboxUI(e.target);
            }
        }, true);
        // inputイベント側のblurは不要（changeで十分）

        // Cmd/Ctrl + Z で独自のアンドゥ、Shift+Cmd/Ctrl+Z または Cmd/Ctrl+Y でリドゥ
        document.addEventListener('keydown', (event) => {
            if (event.isComposing) return;
            const isModifier = event.metaKey || event.ctrlKey;
            if (!isModifier) return;
            const key = String(event.key || '').toLowerCase();
            const active = document.activeElement;
            const isTextarea = active instanceof HTMLTextAreaElement;
            const isSingleLineInput = (active instanceof HTMLInputElement) && (
                active.type === 'text' || active.type === 'search' || active.type === 'url' || active.type === 'email' || active.type === 'password'
            );
            if (!isTextarea && !isSingleLineInput) return;

            if (key === 'z') {
                event.preventDefault();
                event.stopPropagation();
                if (event.shiftKey) {
                    redoForElement(active);
                } else {
                    undoForElement(active);
                }
            } else if (key === 'y') {
                // Ctrl/Cmd+Y もリドゥとして扱う
                event.preventDefault();
                event.stopPropagation();
                redoForElement(active);
            }
        }, true);


        // フッター実高さに合わせて余白を動的に調整（下に本文が覗かないように）
        (function setupFooterHeightVar() {
            function updateFooterHeightVar() {
                const footer = document.querySelector('.footer');
                if (!footer) return;
                const h = Math.ceil(footer.getBoundingClientRect().height || 0);
                if (h > 0) {
                    document.documentElement.style.setProperty('--footer-h', h + 'px');
                }
            }
            const ro = new ResizeObserver(() => updateFooterHeightVar());
            const footerEl = document.querySelector('.footer');
            if (footerEl) ro.observe(footerEl);
            updateFooterHeightVar();
            window.addEventListener('resize', updateFooterHeightVar);
            window.addEventListener('orientationchange', updateFooterHeightVar);
            window.addEventListener('load', updateFooterHeightVar);
            document.addEventListener('DOMContentLoaded', updateFooterHeightVar);
        })();

        /**
         * ========================================
         * 文章作成支援アドオン - グローバル汚染の解消（名前空間導入）
         * ========================================
         * 目的:
         *   - 既存の公開関数を単一名前空間 `window.App` に集約し、保守性を高める
         *   - 後方互換のため、既存のグローバル名は非列挙プロパティとしてエイリアスを残す
         * 方針:
         *   - 公開APIは `App.api` を唯一の実体とする（window直下はゲッター/セッターで委譲）
         *   - 今後新規の公開は `App.exportPublic(name, fn)` を使用
         */
        (function setupAppNamespace(){
            const root = window;
            const App = root.App || (root.App = {});
            App.api = App.api || {};
            App.state = App.state || {};
            App.utils = App.utils || {};
            App.fm = App.fm || {};

            /**
             * 公開 API を登録し、window 直下に非列挙のエイリアスを張る
             * @param {string} name
             * @param {any} value
             */
            App.exportPublic = function exportPublic(name, value) {
                App.api[name] = value;
                try {
                    Object.defineProperty(root, name, {
                        configurable: true,
                        enumerable: false,
                        get() { return App.api[name]; },
                        set(v) { App.api[name] = v; },
                    });
                } catch(_) {
                    // Fallback（環境依存で defineProperty 不可な場合）
                    try { root[name] = value; } catch(_) {}
                }
                return value;
            };

            // 既存の FileMaker 呼び出しラッパを集約
            try { if (typeof root.fmCall === 'function') App.fm.call = root.fmCall; } catch(_) {}

            // 既存の公開関数・オブジェクトを App に集約（必要最小限の代表群）
            const PUBLIC_EXPORTS = [
                'setFooterForTemplateMode',
                'resetFooterForTemplate',
                'positionTemplateMoreMenu',
                'highlightAndScrollTemplateEl',
                'scrollAndHighlightTemplateByKey',
                'applyVariables',
                'openInlineVariablePopover',
                'openVarSettingsModal',
                'setupChangeLogNavigator',
                'changeLogNavigator',
                'markLastEditedInList',
                'enterTemplateEditMode',
                'applyTemplateList',
                'applyTemplateSearchResult',
                'applySelectedTemplateDefaults',
                'applyRemarkOnly',
                'showPhrasePicker',
                'appendTemplateToList',
                'fmCall'
            ];

            PUBLIC_EXPORTS.forEach((name) => {
                try {
                    if (typeof root[name] !== 'undefined' && typeof App.api[name] === 'undefined') {
                        App.exportPublic(name, root[name]);
                    } else if (typeof App.api[name] !== 'undefined') {
                        // 既に App にある場合も、window 側へ非列挙リンクを張る
                        try {
                            Object.defineProperty(root, name, {
                                configurable: true,
                                enumerable: false,
                                get() { return App.api[name]; },
                                set(v) { App.api[name] = v; },
                            });
                        } catch(_) {}
                    }
                } catch(_) {}
            });
        })();

        /**
         * ========================================
         * インフラ: 設定・ロガー・安全ユーティリティ
         * ========================================
         * - 設定オブジェクト、レベル付きロガー、安全なイベント登録、DOM 準備後の安全初期化を提供
         * - 既存実装を壊さないため、グローバルは追加のみ（置換はしない）
         */
        (function setupInfrastructure(){
            const root = window;
            const App = root.App || (root.App = {});

            // 設定（必要に応じて上書き可能）
            App.config = App.config || {
                logging: { enabled: true, level: 'info' } // 'debug' | 'info' | 'warn' | 'error'
            };

            // ロガー
            (function setupLogger(){
                const LEVELS = ['debug','info','warn','error'];
                function normalizeLevel(level){
                    const idx = LEVELS.indexOf(String(level || '').toLowerCase());
                    return idx >= 0 ? LEVELS[idx] : 'info';
                }
                function shouldLog(level){
                    if (!App.config || !App.config.logging || App.config.logging.enabled !== true) return false;
                    const current = normalizeLevel(App.config.logging.level);
                    return LEVELS.indexOf(level) >= LEVELS.indexOf(current);
                }
                const base = App.log || {};
                App.log = {
                    setEnabled(flag){ try { App.config.logging.enabled = !!flag; } catch(_) {} },
                    setLevel(level){ try { App.config.logging.level = normalizeLevel(level); } catch(_) {} },
                    debug(){ if (shouldLog('debug')) { try { console.debug('[DEBUG]', ...arguments); } catch(_) {} } },
                    info(){ if (shouldLog('info')) { try { console.info('[INFO ]', ...arguments); } catch(_) {} } },
                    warn(){ if (shouldLog('warn')) { try { console.warn('[WARN ]', ...arguments); } catch(_) {} } },
                    error(){ if (shouldLog('error')) { try { console.error('[ERROR]', ...arguments); } catch(_) {} } },
                    // 後方互換（既存があれば維持）
                    ...base
                };
            })();

            // ユーティリティ領域の初期化
            App.utils = App.utils || {};

            // 安全タイマー/RAF: ID追跡と一括停止、例外境界
            (function setupSafeTimers(){
                const active = { timeouts: new Set(), intervals: new Set(), rafs: new Set() };
                function track(set, id) { try { set.add(id); } catch(_) {} return id; }
                function untrack(set, id) { try { set.delete(id); } catch(_) {} }
                App.utils.safeTimeout = function safeTimeout(fn, ms){
                    try {
                        const id = setTimeout(() => {
                            untrack(active.timeouts, id);
                            try { fn && fn(); } catch(err) { try { App.log.error('safeTimeout', err); } catch(_) {} }
                        }, Number(ms || 0));
                        return track(active.timeouts, id);
                    } catch(err){ try { App.log.error('safeTimeout_setup', err); } catch(_) {} return -1; }
                };
                App.utils.clearSafeTimeout = function clearSafeTimeout(id){ try { clearTimeout(id); untrack(active.timeouts, id); } catch(_) {} };
                App.utils.safeInterval = function safeInterval(fn, ms){
                    try {
                        const id = setInterval(() => {
                            try { fn && fn(); } catch(err) { try { App.log.error('safeInterval', err); } catch(_) {} }
                        }, Number(ms || 0));
                        return track(active.intervals, id);
                    } catch(err){ try { App.log.error('safeInterval_setup', err); } catch(_) {} return -1; }
                };
                App.utils.clearSafeInterval = function clearSafeInterval(id){ try { clearInterval(id); untrack(active.intervals, id); } catch(_) {} };
                App.utils.safeRAF = function safeRAF(fn){
                    try {
                        const id = requestAnimationFrame(() => {
                            untrack(active.rafs, id);
                            try { fn && fn(); } catch(err) { try { App.log.error('safeRAF', err); } catch(_) {} }
                        });
                        return track(active.rafs, id);
                    } catch(err){ try { App.log.error('safeRAF_setup', err); } catch(_) {} return -1; }
                };
                App.utils.cancelSafeRAF = function cancelSafeRAF(id){ try { cancelAnimationFrame(id); untrack(active.rafs, id); } catch(_) {} };
                // 一括停止（pagehide/unload用）
                App.utils.cancelAllTimers = function cancelAllTimers(){
                    try { active.timeouts.forEach((id)=>clearTimeout(id)); active.timeouts.clear(); } catch(_) {}
                    try { active.intervals.forEach((id)=>clearInterval(id)); active.intervals.clear(); } catch(_) {}
                    try { active.rafs.forEach((id)=>cancelAnimationFrame(id)); active.rafs.clear(); } catch(_) {}
                };
            })();

            // Promise タイムアウト/Abort 合成ユーティリティ
            (function setupPromiseGuards(){
                App.utils.raceWithTimeout = function raceWithTimeout(promise, ms, { abortSignal, onTimeout } = {}){
                    return new Promise((resolve, reject) => {
                        let settled = false;
                        const timeoutId = App.utils.safeTimeout(() => {
                            if (settled) return; settled = true;
                            try { onTimeout && onTimeout(); } catch(_) {}
                            reject(new Error('Operation timed out'));
                        }, ms);
                        const onAbort = () => { if (settled) return; settled = true; App.utils.clearSafeTimeout(timeoutId); reject(new DOMException('Aborted','AbortError')); };
                        try { if (abortSignal) abortSignal.addEventListener('abort', onAbort, { once: true }); } catch(_) {}
                        promise.then((v)=>{ if (settled) return; settled = true; App.utils.clearSafeTimeout(timeoutId); try { abortSignal && abortSignal.removeEventListener('abort', onAbort); } catch(_) {} resolve(v); })
                               .catch((e)=>{ if (settled) return; settled = true; App.utils.clearSafeTimeout(timeoutId); try { abortSignal && abortSignal.removeEventListener('abort', onAbort); } catch(_) {} reject(e); });
                    });
                };
                App.utils.CompositeAbort = function CompositeAbort(signals = []){
                    const controller = new AbortController();
                    const detach = [];
                    try {
                        signals.forEach((sig) => {
                            if (!sig) return;
                            const onAbort = () => { try { controller.abort(); } catch(_) {} };
                            try { sig.addEventListener('abort', onAbort); detach.push(()=>{ try { sig.removeEventListener('abort', onAbort); } catch(_) {} }); } catch(_) {}
                        });
                    } catch(_) {}
                    controller.cleanup = () => { try { detach.forEach((d)=>d()); detach.length = 0; } catch(_) {} };
                    return controller;
                };
            })();

            // 可視状態やページ遷移時のクリーンアップ
            (function setupLifecycleCleanup(){
                try { window.addEventListener('pagehide', () => { try { App.utils.cancelAllTimers && App.utils.cancelAllTimers(); } catch(_) {} }, { passive: true }); } catch(_) {}
                try { window.addEventListener('beforeunload', () => { try { App.utils.cancelAllTimers && App.utils.cancelAllTimers(); } catch(_) {} }); } catch(_) {}
                try {
                    document.addEventListener('visibilitychange', () => {
                        if (document.visibilityState === 'hidden') {
                            try { /* 必要に応じて処理の間引きや停止をここで */ } catch(_) {}
                        }
                    });
                } catch(_) {}
            })();

            /**
             * 安全なイベント登録：ハンドラを try/catch で囲い、デタッチ関数を返す
             * @template TEl extends EventTarget
             * @param {TEl} target
             * @param {string} type
             * @param {(ev: Event) => void} handler
             * @param {AddEventListenerOptions|boolean} [options]
             * @returns {() => void} detach function
             */
            App.utils.addSafeEventListener = function addSafeEventListener(target, type, handler, options){
                if (!target || typeof target.addEventListener !== 'function') return function noop(){};
                const wrapped = function(ev){
                    try { return handler.call(this, ev); }
                    catch(err) { try { App.log.error('Event handler error:', { type, target }, err); } catch(_) {} }
                };
                try { target.addEventListener(type, wrapped, options); } catch(_) {}
                return function detach(){
                    try { target.removeEventListener(type, wrapped, options); } catch(_) {}
                };
            };

            /**
             * 関数を例外境界でラップ
             * @template TArgs
             * @param {(…args: any[]) => any} fn
             * @param {string} [label]
             * @returns {(…args: any[]) => any}
             */
            App.utils.withErrorBoundary = function withErrorBoundary(fn, label){
                if (typeof fn !== 'function') return function noop(){};
                return function(){
                    try { return fn.apply(this, arguments); }
                    catch(err) { try { App.log.error(label || 'withErrorBoundary', err); } catch(_) {} }
                };
            };

            /**
             * DOM 準備後にキューを一度だけ実行する安全初期化
             * - 多重起動防止、例外隔離
             * @param {() => void} callback
             */
            (function setupSafeInit(){
                let initialized = false;
                const queue = [];
                function runAll(){
                    if (initialized) return;
                    initialized = true;
                    while (queue.length > 0) {
                        const fn = queue.shift();
                        try { fn && fn(); }
                        catch(err) { try { App.log.error('safeInit callback failed', err); } catch(_) {} }
                    }
                }
                App.safeInit = function safeInit(callback){
                    if (typeof callback !== 'function') return;
                    const ready = document.readyState === 'interactive' || document.readyState === 'complete';
                    if (initialized || ready) {
                        try { callback(); } catch(err) { try { App.log.error('safeInit immediate callback failed', err); } catch(_) {} }
                        return;
                    }
                    queue.push(callback);
                };
                if (document.readyState === 'complete' || document.readyState === 'interactive') {
                    try { setTimeout(runAll, 0); } catch(_) {}
                } else {
                    try { document.addEventListener('DOMContentLoaded', runAll, { once: true }); } catch(_) {}
                    try { window.addEventListener('load', runAll, { once: true }); } catch(_) {}
                }
            })();
        })();

    </script>
    <div id="toastContainer" class="toast-container is-hidden" aria-live="polite" aria-atomic="true"></div>
</body>
</html>]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_setting::B22117BEC0EF91B95C93F302DF4F9EC3" repetition="1" UUID="95AEA9A1-B76E-49D6-B8B7-73F9A4AF5C42">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
    <title>レイアウト一覧</title>
    <style>
        :root { --footer-h: 56px; }
        html, body { height: 100%; margin: 0; padding: 0; }
        body { font-family: system-ui, -apple-system, "Hiragino Kaku Gothic ProN", Meiryo, Arial, sans-serif; background: #fff; color: #333; display: flex; flex-direction: column; min-height: 100dvh; }
        .header { position: sticky; top: 0; background: #fff; border-bottom: 1px solid #e5e5e5; z-index: 9998; padding: 12px 16px; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
        .page-title { font-weight: 700; font-size: 20px; margin: 0; }
        .page-body { position: relative; flex: 1 1 auto; min-height: 0; overflow: auto; padding: 16px; padding-bottom: calc(16px + var(--footer-h) + env(safe-area-inset-bottom)); box-sizing: border-box; }
        .footer { position: fixed; left: 0; right: 0; bottom: 0; height: var(--footer-h); background: #f7f7f7; border-top: 1px solid #e5e5e5; display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 8px 12px; box-sizing: border-box; z-index: 9999; transition: transform .24s ease, opacity .24s ease; }
        .btn { appearance: none; border: 1px solid #d0d0d0; background: #fff; color: #333; border-radius: 8px; padding: 10px 14px; font-size: 14px; cursor: pointer; }
        .btn.primary { background: #007bff; color: #fff; border-color: #007bff; }

        /* list */
        .list { max-width: 800px; margin: 0 auto; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
        .list-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; border: 1px solid #e5e5e5; border-radius: 8px; background: #fafafa; cursor: pointer; }
        .list-item-title { font-weight: 700; font-size: 14px; color: #111; }
        .list-item-meta { font-size: 12px; color: #666; }
        .empty { max-width: 800px; margin: 12px auto; font-size: 14px; color: #666; }

        .search { max-width: 800px; margin: 0 auto 12px; display: flex; gap: 8px; }
        .input { flex: 1 1 auto; display: block; box-sizing: border-box; padding: .375rem .75rem; font-size: 14px; border: 1px solid #ced4da; border-radius: .375rem; }

        .toast-container { position: fixed; left: 50%; bottom: calc(var(--footer-h) + 16px + env(safe-area-inset-bottom)); transform: translateX(-50%); z-index: 13000; display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none; }
        .toast-message { background: rgba(0, 0, 0, 0.85); color: #fff; border-radius: 8px; padding: 10px 14px; font-size: 14px; line-height: 1.4; box-shadow: 0 4px 12px rgba(0,0,0,0.25); opacity: 0; transform: translateY(6px); transition: opacity 160ms ease, transform 160ms ease; max-width: min(92vw, 520px); pointer-events: auto; }
        .toast-message.show { opacity: 1; transform: translateY(0); }
    </style>
    </head>
<body>
    <div class="header">
        <h1 class="page-title">レイアウト一覧</h1>
        <div class="header-actions" style="display:flex; align-items:center; gap:8px;"></div>
    </div>

    <div class="page-body">
        <div class="search">
            <input id="search-input" class="input" type="search" placeholder="レイアウト名で絞り込み" />
        </div>
        <ul id="layout-list" class="list"></ul>
        <div id="empty-view" class="empty" style="display:none;">レイアウトがありません。</div>
    </div>

    <div class="footer">
        <button id="btn-cancel" class="btn">キャンセル</button>
    </div>

    <script>
    // ---------- Toast ----------
    function ensureToastContainer() {
        let c = document.getElementById('toast-container');
        if (!c) {
            c = document.createElement('div');
            c.id = 'toast-container';
            c.className = 'toast-container';
            document.body.appendChild(c);
        }
        return c;
    }
    function showToast(message, durationMs = 2400) {
        try {
            const container = ensureToastContainer();
            const el = document.createElement('div');
            el.className = 'toast-message';
            el.textContent = String(message ?? '');
            container.appendChild(el);
            requestAnimationFrame(() => el.classList.add('show'));
            const remove = () => { el.classList.remove('show'); setTimeout(() => el.remove(), 200); };
            setTimeout(remove, Math.max(1200, durationMs|0));
            return remove;
        } catch (_) { return () => {}; }
    }

    // ---------- Claris FileMaker Web Viewerカーソル移動問題回避策 ----------
    /**
     * Claris FileMaker Web Viewア特有のカーソル移動問題に対する回避策
     * - 一部環境（Claris FileMaker Web ビューア）では textarea/input の行頭・行末で左右矢印キーを押すと
     *   フォーカスがコンポーネント外へ移動したり、予期しないナビゲーションが発生する既知の挙動があります。
     * - 本ガードは「入力中の要素で、選択範囲がなく、キャレットが先頭/末尾にあるとき」に限って
     *   ArrowLeft/ArrowRight をキャンセルしてフォーカス逸脱を防止します。
     * - IME 変換中は抑止しません（composition イベントを考慮）。
     * - 本挙動はアクセシビリティ上の配慮として最小限の範囲に限定しており、一般的な環境でも入力体験を損ねない設計です。
     * - もし他環境で不要な場合は、当該リスナーの登録箇所（document.addEventListener('keydown', ... , true)）を無効化してください。
     */
    (function initArrowKeyGuard() {
        let isComposing = false;

        // IME変換状態の追跡
        document.addEventListener('compositionstart', () => { isComposing = true; });
        document.addEventListener('compositionend', () => { isComposing = false; });

        // 矢印キーガード（テキストエリアの端で左右矢印を無効化）
        document.addEventListener('keydown', function(e) {
            // IME変換中は抑止しない
            if (isComposing) return;

            // ArrowLeft/ArrowRightのみが対象
            if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return;

            // 対象の要素: input[type="text"], input[type="search"], textarea
            const target = e.target;
            if (!target || !target.matches || !target.matches('input[type="text"], input[type="search"], textarea')) return;

            // 選択範囲がある場合は通常動作させる
            const selStart = target.selectionStart;
            const selEnd = target.selectionEnd;
            if (selStart !== selEnd) return;

            const value = target.value || '';
            const pos = selStart;

            // ArrowLeftで先頭位置にいる場合、またはArrowRightで末尾位置にいる場合
            if ((e.key === 'ArrowLeft' && pos === 0) || (e.key === 'ArrowRight' && pos === value.length)) {
                e.preventDefault();
                e.stopPropagation();
            }
        }, true); // キャプチャフェーズで処理
    })();

    // ---------- Undo/Redo システム ----------
    /**
     * index.html を参考にしたundo/redo機能の実装
     * - 各要素ごとに独立した履歴管理（WeakMap使用）
     * - undoStack（操作履歴）とredoStack（やり直し履歴）を保持
     * - 最大300件の履歴を保持し、それを超えた分は古いものから削除
     * - キーボードショートカット: Ctrl/Cmd+Z (undo), Ctrl/Cmd+Y or Ctrl/Cmd+Shift+Z (redo)
     */
    const elementHistoryMap = new WeakMap();
    let isApplyingHistory = false;
    const MAX_HISTORY_ENTRIES = 300;

    function getHistoryFor(element) {
        let history = elementHistoryMap.get(element);
        if (!history) {
            // 初期値を現在の値として設定
            const initial = element.value ?? '';
            const undoStack = [initial];
            history = { undoStack, redoStack: [] };
            elementHistoryMap.set(element, history);
        }
        return history;
    }

    function manageUndoRedo(element) {
        if (isApplyingHistory) return;
        const { undoStack, redoStack } = getHistoryFor(element);
        const newValue = element.value ?? '';
        const last = undoStack.length > 0 ? undoStack[undoStack.length - 1] : undefined;
        if (last !== newValue) {
            undoStack.push(newValue);
            redoStack.length = 0; // 新しい変更があった場合、redoStackをクリア
            if (undoStack.length > MAX_HISTORY_ENTRIES) {
                undoStack.splice(0, undoStack.length - MAX_HISTORY_ENTRIES);
            }
        }
    }

    function applyProgrammaticValue(element, value) {
        const history = getHistoryFor(element);
        const prev = history.undoStack.length ? history.undoStack[history.undoStack.length - 1] : '';
        const next = value ?? '';
        isApplyingHistory = true;
        element.value = next;
        isApplyingHistory = false;
        if (prev !== next) {
            history.undoStack.push(next);
            history.redoStack.length = 0;
            if (history.undoStack.length > MAX_HISTORY_ENTRIES) {
                history.undoStack.splice(0, history.undoStack.length - MAX_HISTORY_ENTRIES);
            }
        }
        // 検索入力欄の場合、フィルターを再適用
        if (element.id === 'search-input') {
            applyFilter(next);
        }
    }

    function undoForElement(element) {
        const { undoStack, redoStack } = getHistoryFor(element);
        if (undoStack.length > 1) {
            const lastValue = undoStack.pop();
            redoStack.push(lastValue);
            const currentValue = undoStack[undoStack.length - 1] ?? '';
            applyProgrammaticValue(element, currentValue);
        }
    }

    function redoForElement(element) {
        const { undoStack, redoStack } = getHistoryFor(element);
        if (redoStack.length > 0) {
            const lastRedoValue = redoStack.pop();
            undoStack.push(lastRedoValue);
            applyProgrammaticValue(element, lastRedoValue);
        }
    }

    // キーボードショートカットの実装
    document.addEventListener('keydown', (event) => {
        if (event.isComposing) return;
        const isModifier = event.metaKey || event.ctrlKey;
        if (!isModifier) return;
        const key = String(event.key || '').toLowerCase();
        const active = document.activeElement;
        const isInput = (active instanceof HTMLInputElement) && (
            active.type === 'text' || active.type === 'search' || active.type === 'url' || active.type === 'email' || active.type === 'password'
        );
        const isTextarea = active instanceof HTMLTextAreaElement;
        if (!isInput && !isTextarea) return;

        if (key === 'z') {
            event.preventDefault();
            event.stopPropagation();
            if (event.shiftKey) {
                redoForElement(active);
            } else {
                undoForElement(active);
            }
        } else if (key === 'y') {
            // Ctrl/Cmd+Y もredoとして扱う
            event.preventDefault();
            event.stopPropagation();
            redoForElement(active);
        }
    }, true);

    // ---------- FileMaker Hook ----------
    function callFileMaker(scriptName, param) {
        try {
            if (window.FileMaker && typeof window.FileMaker.PerformScriptWithOption === 'function') {
                window.FileMaker.PerformScriptWithOption(String(scriptName), String(param ?? ''), 5);
                return true;
            }
        } catch (_) {}
        return false;
    }

    const FM_SCRIPTS = {
        loaded: 'addon_remarks_web_layout_list_loaded',
        request: 'addon_remarks_web_layout_list_request',
        cancel: 'addon_remarks_web_layout_list_cancel',
        select: 'addon_remarks_web_layout_list_select'
    };

    // ---------- Data & Rendering ----------
    const state = {
        rawLayouts: [],
        filteredLayouts: []
    };

    function normalizeLayouts(raw) {
        // Accepts: string (JSON array or newline-separated), array of strings/objects
        try {
            if (typeof raw === 'string') {
                const s = raw.trim();
                if (s.startsWith('[') && s.endsWith(']')) {
                    const arr = JSON.parse(s);
                    return normalizeLayouts(arr);
                }
                const lines = s.length === 0 ? [] : s.split(/\r\n?|\n|\r/);
                return lines.map(v => ({ label: v, name: v, table: '', baseTable: '' }));
            }
            if (Array.isArray(raw)) {
                return raw.map(item => {
                    if (item == null) return { label: '', name: '', table: '', baseTable: '' };
                    if (typeof item === 'string' || typeof item === 'number') {
                        const v = String(item);
                        return { label: v, name: v, table: '', baseTable: '' };
                    }
                    if (typeof item === 'object') {
                        const label = (item.name ?? item.table ?? item.baseTable ?? item.value ?? '');
                        return {
                            label: String(label),
                            name: String(item.name ?? label ?? ''),
                            table: String(item.table ?? ''),
                            baseTable: String(item.baseTable ?? '')
                        };
                    }
                    return { label: '', name: '', table: '', baseTable: '' };
                });
            }
        } catch (e) { /* noop: fallthrough to empty */ }
        return [];
    }

    function renderList(list) {
        const ul = document.getElementById('layout-list');
        const empty = document.getElementById('empty-view');
        if (!ul || !empty) return;
        ul.innerHTML = '';
        while (ul.firstChild) ul.removeChild(ul.firstChild);
        if (!Array.isArray(list) || list.length === 0) {
            empty.style.display = 'block';
            return;
        }
        empty.style.display = 'none';
        for (const it of list) {
            const li = document.createElement('li');
            li.className = 'list-item';
            const left = document.createElement('div');
            const title = document.createElement('div');
            title.className = 'list-item-title';
            title.textContent = String(it.label ?? '');
            const meta = document.createElement('div');
            meta.className = 'list-item-meta';
            const metaBits = [];
            if (it.table) metaBits.push('table: ' + it.table);
            if (it.baseTable) metaBits.push('base: ' + it.baseTable);
            meta.textContent = metaBits.join('  ');
            left.appendChild(title);
            if (metaBits.length) left.appendChild(meta);
            li.appendChild(left);
            li.addEventListener('click', () => {
                const payload = { name: String(it.name || it.label || ''), table: String(it.table || ''), baseTable: String(it.baseTable || ''), label: String(it.label || '') };
                const ok = callFileMaker(FM_SCRIPTS.select, JSON.stringify(payload));
                if (!ok) showToast('選択の送信に失敗しました。FileMaker 接続を確認してください。');
            });
            ul.appendChild(li);
        }
    }

    function applyFilter(term) {
        const q = String(term || '').toLowerCase();
        if (!q) {
            state.filteredLayouts = state.rawLayouts.slice();
        } else {
            state.filteredLayouts = state.rawLayouts.filter(it =>
                (it.label || '').toLowerCase().includes(q) ||
                (it.table || '').toLowerCase().includes(q) ||
                (it.baseTable || '').toLowerCase().includes(q)
            );
        }
        renderList(state.filteredLayouts);
    }

    // ---------- External API (FileMaker → JS) ----------
    // FileMaker 側からレイアウト一覧を渡してください。受理形式は以下のいずれか：
    // - 文字列: JSON配列 or 改行区切り
    // - 配列: ['LayoutA', 'LayoutB'] または [{ name, table, baseTable }]
    window.applyLayoutList = function(payload) {
        try {
            const data = (typeof payload === 'string') ? (payload.trim().startsWith('{') || payload.trim().startsWith('[') ? JSON.parse(payload) : payload) : payload;
            const list = normalizeLayouts(data);
            state.rawLayouts = list;
            applyFilter(document.getElementById('search-input')?.value || '');
            if (!Array.isArray(list) || list.length === 0) {
                showToast('レイアウト情報がありません。設定から利用するレイアウトを指定してください。');
            } else {
                showToast('レイアウト一覧を読み込みました');
            }
            return true;
        } catch (e) {
            console.error(e);
            showToast('レイアウト一覧の読み込みに失敗しました');
            return false;
        }
    };

    // ---------- Startup: notify + request ----------
    (function startup() {
        const MAX_TRY = 20; // 約5秒
        let tried = 0;
        function exec() {
            const okLoaded = callFileMaker(FM_SCRIPTS.loaded, '');
            const okRequest = callFileMaker(FM_SCRIPTS.request, '');
            if (!okLoaded || !okRequest) {
                tried++;
                if (tried < MAX_TRY) {
                    setTimeout(exec, 250);
                } else if (!okLoaded && !okRequest) {
                    showToast('FileMaker への接続を確認してください。初期化に失敗しました。');
                }
            }
        }
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', exec, { once: true });
        } else {
            setTimeout(exec, 0);
        }
    })();

    // ---------- IME変換対応（グローバル処理） ----------
    // IME入力（日本語変換など）は1回のundoで戻せるようにグルーピング
    document.addEventListener('compositionstart', (e) => {
        const t = e.target;
        const isInput = (t instanceof HTMLInputElement) && (
            t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
        );
        const isTextarea = t instanceof HTMLTextAreaElement;
        if (!isInput && !isTextarea) return;
        t.__isComposing = true;
        // 変換開始時に現在の状態を履歴として保存（変換前の状態を確保）
        try { getHistoryFor(t); } catch(_) {}
    }, true);

    document.addEventListener('compositionend', (e) => {
        const t = e.target;
        const isInput = (t instanceof HTMLInputElement) && (
            t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
        );
        const isTextarea = t instanceof HTMLTextAreaElement;
        if (!isInput && !isTextarea) return;
        t.__isComposing = false;
        // 変換確定後の値を1ステップとして履歴に積む
        manageUndoRedo(t);
    }, true);

    // 入力時に各要素の履歴を更新（英数字入力対応）
    document.addEventListener('input', (e) => {
        const t = e.target;
        const isInput = (t instanceof HTMLInputElement) && (
            t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
        );
        const isTextarea = t instanceof HTMLTextAreaElement;
        if (!isInput && !isTextarea) return;
        if (e.isComposing || t.__isComposing) return; // 変換中は履歴に積まない
        
        // 検索入力欄の場合はフィルターも適用
        if (t.id === 'search-input') {
            applyFilter(t.value);
        }
        
        // IME確定や英数字入力の度に履歴へ積む（短時間の大量input連打をバッチ化）
        if (t.__undoDebounceTimer) clearTimeout(t.__undoDebounceTimer);
        t.__undoDebounceTimer = setTimeout(() => {
            manageUndoRedo(t);
            t.__undoDebounceTimer = null;
        }, 150); // 150ms後に履歴を記録（index.htmlと同じ）
    }, true);

    // ---------- Events ----------
    // 検索入力欄の初期履歴設定
    const searchInput = document.getElementById('search-input');
    if (searchInput) {
        // 初期状態の履歴を設定
        getHistoryFor(searchInput);
        
        // 即座の履歴記録が必要なケース（フォーカス移動時など）
        searchInput.addEventListener('blur', () => {
            if (searchInput.__undoDebounceTimer) {
                clearTimeout(searchInput.__undoDebounceTimer);
                manageUndoRedo(searchInput);
                searchInput.__undoDebounceTimer = null;
            }
        });
    }
    
    document.getElementById('btn-cancel').addEventListener('click', () => {
        const ok = callFileMaker(FM_SCRIPTS.cancel, '');
        if (!ok) showToast('キャンセル通知に失敗しました。FileMaker 接続を確認してください。');
    });
    </script>
</body>
</html>


]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_setting::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="5E29EA12-EACD-42FF-8482-27E2F2A194AD">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="16" name="com.fmi.basetable.field.Remarks_setting::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="1EFCCD4E-3C48-4EB9-9DE0-7B3C2C96F991">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="17" name="com.fmi.basetable.field.Remarks_setting::9EDF303B672B089544FAB146E30EE430" repetition="1" UUID="BF15B1FB-0A7B-47AD-84B4-11839DD125CB">
								<BaseTableReference id="130" name="com.fmi.basetable.ADFFE20FB61514CDA07F2B4DD41260D7" UUID="C722DC74-E029-464D-B6CD-D1C5ACE83E9D"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>フレーズを選択</title>
    <style>
        :root {
            --space-6: 6px; --space-8: 8px; --space-10: 10px; --space-12: 12px; --space-16: 16px;
            --footer-h: 64px;
            --color-bg: #ffffff; --color-bg-subtle: #f8f9fa; --color-border: #e0e0e0; --color-text: #222;
            --shadow-card: 0 10px 30px rgba(0,0,0,.24), 0 16px 56px rgba(0,0,0,.20);
            --font-size-base: 14px; --font-size-heading: 16px; --line-height-base: 1.6;
        }
        html, body { height: 100%; }
        body { margin:0; background:#fff; color:#222; font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; font-size: var(--font-size-base); line-height: var(--line-height-base); }
        /* index.html のモーダル構造に合わせ、全画面にフィットさせる */
        .modal-content { position: fixed; right: 8px; top: 8px; display:grid; grid-template-rows:auto 1fr auto; max-width: 360px; width: min(92vw, 360px); height: auto; margin: 0; box-shadow: var(--shadow-card); background:#fff; border-radius: 10px; }
        .modal-content.fullscreen { inset: 0; max-width: none; width: 100vw; height: 100dvh; border-radius: 0; right: auto; top: auto; }
        body.phrase-edit-desktop .modal-content { position: static; inset: auto; right:auto; top:auto; left:auto; transform:none; max-width:100vw; width:100vw; margin: 0; min-height:100vh; height:auto; overflow:visible; border-radius:0; }
        body.phrase-edit-desktop .template-modal-body { overflow:auto; padding-bottom: calc(var(--footer-h) + var(--space-16)); }
        body.phrase-edit-desktop #phraseRegisterWrap { overflow:visible; margin-bottom: var(--space-16); }
        .modal-header { display:flex; align-items:center; justify-content:space-between; background: var(--color-bg-subtle); padding: var(--space-10) var(--space-10); }
        .modal-header .header-left h3 { margin:0; font-size: var(--font-size-heading); }
        .modal-header .header-right { display:flex; align-items:center; gap:8px; }
        .template-search { height:32px; padding:6px 10px; border:1px solid var(--color-border); border-radius:6px; font-size: var(--font-size-base); }
        .search-button, .clear-button, .new-button { border:1px solid var(--color-border); background:#fff; padding:6px 10px; border-radius:6px; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; width:36px; height:36px; }
        .search-button svg, .clear-button svg { width:20px; height:20px; }
        .new-button svg { width:20px; height:20px; }
        /* 新規作成ボタンの微調整（index.html に準拠） */
        .new-button { padding:0; background-color: transparent; }
        /* 共通ボタン（index.html と一致） */
        .button { display:inline-flex; align-items:center; justify-content:center; gap: var(--space-6); padding: var(--space-8) var(--space-16); min-height:36px; border:1px solid transparent; border-radius:6px; font-weight:600; font-size: var(--font-size-base); color:#fff; background-color:#0d6efd; cursor:pointer; text-decoration:none; user-select:none; transition: background-color 0.2s ease, filter 0.2s ease, opacity 0.2s ease; white-space:nowrap; word-break: keep-all; }
        /* 入力系のフォントを本文に統一 */
        input, select, button, textarea { font-family: inherit; font-size: var(--font-size-base); }
        .button:hover { background-color:#0056b3; }
        .button-secondary { background-color:#6c757d; color:#fff; border-color:#6c757d; }
        .button-secondary:hover { background-color:#5a6268; }
        @media (max-width:480px){ .button { padding: var(--space-8) var(--space-12); } }
        .button:disabled, .button[disabled] { opacity:.5; cursor: default; pointer-events:none; }
        .modal-scroll-body.template-modal-body { overflow:auto; -webkit-overflow-scrolling:touch; padding: var(--space-16) var(--space-16) calc(var(--footer-h) + 80px); overscroll-behavior: contain; will-change: scroll-position; contain: layout style paint; }
        @supports (padding: env(safe-area-inset-bottom)) {
            .modal-scroll-body.template-modal-body { padding-bottom: calc(var(--footer-h) + env(safe-area-inset-bottom) + 80px); }
        }
        .modal-scroll-body.template-modal-body { scrollbar-gutter: stable both-edges; }
        /* iOS Safari の anchor スクロールによるジャンプ抑止（index.html と同様の対策） */
        #phraseTextarea { overflow-anchor: none; }
        .template-list { display:flex; flex-direction:column; gap: var(--space-10); }
        .template-item { border:1px solid var(--color-border); border-radius:6px; background:#fff; }
        /* 並べ替えの視覚効果 */
        .template-item.dragging { opacity: 0.3; transform: rotate(2deg); z-index: 100; }
        .template-item.drag-over { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
        .template-list.drag-active .template-item:not(.dragging) { transition: all 0.2s ease; }
        /* ドラッグハンドルと三点メニュー（index.html 準拠） */
        .drag-handle { opacity: 0.6; cursor: grab; padding: 4px; color: #6c757d; border-radius:4px; }
        .drag-handle:active { cursor: grabbing; }
        .template-item-actions { position: relative; }
        .template-item-actions .more-btn { border:none; background: transparent; padding:4px; cursor: pointer; border-radius:4px; }
        .template-item-actions .more-btn:hover { background: #f0f4ff; }
        .template-item-actions .more-menu { position: absolute; top: 0; right: 0; min-width: 120px; background: #fff; border: 1px solid #e5e5e5; border-radius: 6px; box-shadow: 0 6px 18px rgba(0,0,0,0.12); padding: 6px; display: none; z-index: 20; }
        .template-item-actions .more-menu.open { display:block; }
        .template-item-actions .more-menu button { width:100%; text-align:left; border:none; background:transparent; padding:8px 10px; border-radius:4px; cursor: pointer; font-size: var(--font-size-base); }
        .template-item-actions .more-menu button:hover { background:#f5f5f5; }
        /* フレーズ一覧では本文（template-body）のみ見せ、タイトルは非表示 */
        .template-item-row { display:flex; align-items:center; gap: var(--space-8); padding: var(--space-10); }
        .template-item-main { flex:1 1 auto; min-width:0; }
        .template-title { display:none !important; }
        /* 長文は3行分のみ表示し、全文はtitle属性で確認できるようにする */
        .template-body { margin:0; color:#444; font-size:0.92em; line-height:1.5; white-space:pre-line; word-break:break-word; overflow:hidden; text-overflow:ellipsis; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; line-clamp:3; }
        .template-item:hover { background:#f8fbff; border-color:#cfe2ff; }
        /* 並び替え時のドロップインジケータ（index.html 準拠の見た目に寄せる） */
        .drop-indicator { height: 3px; background: #007bff; border-radius: 2px; margin: 2px 0; opacity: .85; transform-origin: left; transform: scaleX(1); transition: opacity .12s ease, transform .12s ease; }
        .drop-indicator.active { opacity: 1; transform: scaleX(1); box-shadow: 0 0 8px rgba(0,123,255,.3); }
        /* ドラッグ中の選択抑止 */
        body.dragging, body.dragging * { user-select: none !important; }
        /* 並べ替え後の一時ハイライト（index.html のトーンに近づける） */
        .template-item.was-edited { background: #fff8e1; border-color: #ffd699; }
        .template-item.highlight-fade { transition: background-color 0.35s ease, border-color 0.35s ease; }
        /* フッター（index.html と一致） */
        .modal-buttons { display:flex; align-items:center; justify-content:flex-end; gap: var(--space-10); padding: var(--space-10); margin:0; border-top:1px solid #eee; background:#f8f9fa; position:sticky; bottom:0; z-index:210; }
        .modal-buttons .bar { display:flex; gap: var(--space-10); justify-content:flex-end; width:100%; }
        .template-pagination { display:none; }
        /* 編集フォームの左右バランスを統一 */
        #phraseRegisterWrap { padding: var(--space-12); box-sizing: border-box; }
        #phraseRegisterWrap input,
        #phraseRegisterWrap textarea { width:100%; max-width:100%; box-sizing:border-box; }
        #phraseRegisterWrap textarea { resize: none; }
        /* 入力欄のフォントサイズを index.html に合わせる */
        #phraseTitleInput, #phraseTextarea { font-size: var(--font-size-base); }
        /* iPhone等の小画面でフォーカス時の自動ズームを防ぐ（入力だけ16px以上） */
        @media (max-width:480px){
            input, select, textarea,
            .modal-content input[type="text"],
            .modal-content textarea { font-size:16px !important; }
        }

        /* トースト通知（index.html に準拠した簡易版） */
        .toast-container { position: fixed; left: 50%; bottom: calc(var(--footer-h) + var(--space-16)); transform: translateX(-50%); z-index: 13000; display: flex; flex-direction: column; align-items: center; gap: var(--space-8); pointer-events: none; }
        .toast-message { background: rgba(0,0,0,.85); color: #fff; border-radius: 8px; padding: 12px 20px; font-size: var(--font-size-base); opacity: 0; transform: translateY(8px); transition: opacity .2s ease, transform .2s ease; min-width: 200px; max-width: min(92vw, 640px); text-align: center; pointer-events: auto; }
        .toast-message.show { opacity: 1; transform: translateY(0); }

        /* 簡易確認モーダル（index.html風、中央表示） */
        #confirmModal { position: fixed; inset: 0; background: rgba(0,0,0,.5); display: none; z-index: 13000; display: none; align-items: center; justify-content: center; padding: var(--space-16); }
        #confirmModal .modal-content { position: static; right: auto; top: auto; inset: auto; display: block; width: min(92vw, 500px); max-width: 500px; height: auto; margin: 0; box-shadow: var(--shadow-card); border-radius: 14px; overflow: hidden; background: #fff; }
        #confirmModal .modal-scroll-body { padding: 24px; text-align: center; }
        #confirmModal .modal-buttons { justify-content: center; gap: var(--space-10); }
        #confirmModalTitle { margin: 0; color: #dc3545; }
    </style>
</head>
<body>
    <div class="modal-content fullscreen" role="dialog" aria-modal="true" aria-label="フレーズ一覧">
        <div class="modal-header">
            <div class="header-left"><h3 id="phraseModalTitle">フレーズを選択</h3></div>
            <div class="header-right">
                <button id="phraseSearchToggle" type="button" class="search-button" title="検索" aria-label="検索">
                    <svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79L20 21.5 21.5 20l-6-6zM9.5 14C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
                </button>
                <div id="phraseSearchWrap" style="display:none; align-items:center; gap:6px;">
                    <input id="phraseSearchInput" class="template-search" type="search" placeholder="フレーズを検索..." aria-label="検索">
                </div>
                <button type="button" id="phraseNewButton" class="new-button" title="新規作成" aria-label="新規フレーズを作成">
                    <svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M19 11h-6V5a1 1 0 10-2 0v6H5a1 1 0 100 2h6v6a1 1 0 102 0v-6h6a1 1 0 100-2z"/></svg>
                </button>
            </div>
        </div>
        <div class="modal-scroll-body template-modal-body">
            <div id="phrasePickerList" class="template-list"></div>
            <div id="phrasePagination" class="template-pagination"></div>
            <!-- 新規/編集フォーム（一覧と切り替え） -->
            <div id="phraseRegisterWrap" style="display:none; margin-top:8px;">
                <div class="modal-field-row" style="margin-bottom: 10px;">
                    <label for="phraseTitleInput" class="modal-field-label" style="display:block; font-weight:bold; margin-bottom:6px;">タイトル:</label>
                    <input id="phraseTitleInput" type="text" placeholder="タイトルを入力" style="width:100%; height:36px; padding:6px 10px; border:1px solid var(--color-border); border-radius:6px;">
                </div>
                <textarea id="phraseTextarea" rows="8" placeholder="フレーズを入力してください..." style="width:100%; padding:10px; border:1px solid var(--color-border); border-radius:6px;"></textarea>
            </div>
        </div>
        <div class="modal-buttons"><div class="bar">
            <button id="closeBtn" type="button" class="button button-secondary">閉じる</button>
            <button id="editFooterCancel" type="button" class="button button-secondary" style="display:none;">キャンセル</button>
            <button id="editFooterSave" type="button" class="button" style="display:none;">保存</button>
        </div></div>
    </div>

    <!-- トーストと確認モーダル（index.html簡易互換） -->
    <div id="toastContainer" class="toast-container" aria-live="polite" aria-atomic="true"></div>
    <div id="confirmModal" class="modal" aria-hidden="true">
        <div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="confirmModalTitle" aria-describedby="confirmModalMessage">
            <div class="modal-header"><h3 id="confirmModalTitle">確認</h3></div>
            <div class="modal-scroll-body"><p id="confirmModalMessage">よろしいですか？</p></div>
            <div class="modal-buttons"><div class="bar" style="justify-content:center;">
                <button id="confirmModalCancel" class="button button-secondary">キャンセル</button>
                <button id="confirmModalConfirm" class="button">OK</button>
            </div></div>
        </div>
    </div>

    <script>
    (function(){
        'use strict';
        /**
         * 本ファイルは単体で開いて使用できる簡易フレーズ選択UIです。
         * 親側へは window.postMessage({ type:'phrase:selected', phrase, title }) で返却します。
         * データの受け取り方法（優先順）:
         * 1) URLSearchParams の payload または data に JSON を渡す
         *    - 形式A: [{ id, phrase, title }]
         *    - 形式B: { response: { data: [ { fieldData: { template(json文字列 or 配列 or 単一) } } ] } }
         *    - 形式C: { phrases: [ { id, text, title } ] }
         * 2) window.postMessage で { type:'phrase:data', ... } を送信
         */
        const DOM = {
            list: document.getElementById('phrasePickerList'),
            headerTitle: document.getElementById('phraseModalTitle'),
            searchWrap: document.getElementById('phraseSearchWrap'),
            searchToggle: document.getElementById('phraseSearchToggle'),
            searchInput: document.getElementById('phraseSearchInput'),
            newButton: document.getElementById('phraseNewButton'),
            registerWrap: document.getElementById('phraseRegisterWrap'),
            titleInput: document.getElementById('phraseTitleInput'),
            textarea: document.getElementById('phraseTextarea'),
            editCancel: document.getElementById('editFooterCancel'),
            editSave: document.getElementById('editFooterSave'),
            close: document.getElementById('closeBtn'),
            pagination: document.getElementById('phrasePagination'),
            toastContainer: document.getElementById('toastContainer'),
            confirmModal: document.getElementById('confirmModal'),
            confirmMessage: document.getElementById('confirmModalMessage'),
            confirmOk: document.getElementById('confirmModalConfirm'),
            confirmCancel: document.getElementById('confirmModalCancel')
        };

        const state = (function createStateStore(){
            const data = {
                phrases: [],
                editingIndex: null,
                newlyCreatedId: null,
                highlightId: null,
                pagination: {
                    currentPage: 1,
                    totalPages: 1,
                    searchTerm: ''
                }
            };
            return {
                getData: function(){ return data; },
                getPhrases: function(){ if (!Array.isArray(data.phrases)) { data.phrases = []; } return data.phrases; },
                setPhrases: function(list){ data.phrases = Array.isArray(list) ? list : []; return data.phrases; },
                findIndexById: function(id){
                    const targetId = toStringValue(id);
                    return this.getPhrases().findIndex(function(item){
                        return toStringValue(item && item.id) === targetId;
                    });
                },
                paging: {
                    get: function(key){ return data.pagination[key]; },
                    set: function(key, value){ data.pagination[key] = value; return data.pagination[key]; },
                    reset: function(){ data.pagination.currentPage = 1; data.pagination.totalPages = 1; data.pagination.searchTerm = ''; }
                },
                setFlag: function(key, value){ data[key] = value; return data[key]; },
                getFlag: function(key){ return data[key]; }
            };
        })();

        const listEl = DOM.list;
        const headerTitleEl = DOM.headerTitle;
        const searchWrapEl = DOM.searchWrap;
        const searchToggleEl = DOM.searchToggle;
        const searchEl = DOM.searchInput;
        const newBtn = DOM.newButton;
        const registerWrap = DOM.registerWrap;
        const titleInput = DOM.titleInput;
        const ta = DOM.textarea;
        const editCancelBtn = DOM.editCancel;
        const editSaveBtn = DOM.editSave;
        const closeBtn = DOM.close;
        const paginationEl = DOM.pagination;
        const toastContainer = DOM.toastContainer;
        const confirmModal = DOM.confirmModal;
        const confirmMessageEl = DOM.confirmMessage;
        const confirmOkBtn = DOM.confirmOk;
        const confirmCancelBtn = DOM.confirmCancel;
        const IS_IOS_DEVICE = (function detectIOS(){
            try {
                const ua = toStringValue(navigator.userAgent);
                const platform = toStringValue(navigator.platform);
                const maxTouch = Number(navigator.maxTouchPoints || 0);
                const isIOSUA = /iPad|iPhone|iPod/.test(ua);
                const isIPadOS = platform === 'MacIntel' && maxTouch > 1;
                return isIOSUA || isIPadOS;
            } catch(_) { return false; }
        })();
        const TEXT_INPUT_TYPES = ['text', 'search', 'url', 'email', 'password'];

        /**
         * テキスト入力系要素かどうかを判定する共通ユーティリティ
         * - インプットタイプの判定を一元化し、判定ロジックの重複を解消
         */
        function isEditableTextField(element){
            try {
                if (!element) return false;
                if (element instanceof HTMLTextAreaElement) return true;
                if (element instanceof HTMLInputElement) {
                const type = toLowerCaseString(element && element.type);
                    return TEXT_INPUT_TYPES.indexOf(type) !== -1;
                }
            } catch(_) {}
            return false;
        }

        /**
         * null/undefined 安全な文字列変換
         * - 将来的に変換ルールを拡張する際もこの関数を更新すればよいよう集約
         */
        function toStringValue(value){
            return value == null ? '' : String(value);
        }

        /**
         * 前後の空白を除去した文字列を取得するヘルパー
         * - trim ロジックを共通化し、UI 入力値処理の統一を図る
         */
        function toTrimmedString(value){
            return toStringValue(value).trim();
        }

        /**
         * 小文字に正規化した文字列を返却するヘルパー
         * - toLowerCase の呼び出しを集約し、毎回の String 変換を不要にする
         */
        function toLowerCaseString(value){
            return toStringValue(value).toLowerCase();
        }

        /**
         * 安全な JSON.parse ラッパー
         * - try/catch を繰り返し書かないように統一する
         */
        function tryParseJSON(value){
            try {
                return JSON.parse(value);
            } catch(_) {
                return null;
            }
        }

        /**
         * 関数かどうかを判定するユーティリティ
         * - 外部連携オブジェクトの存在チェックを簡潔に記述する目的で導入
         */
        function isCallable(fn){
            return typeof fn === 'function';
        }

        /**
         * dataset から安全に値を取得する
         * - undefined を空文字に正規化し、呼び出し側の条件分岐を簡略化
         */
        function getDatasetValue(element, key){
            try {
                return toStringValue(element && element.dataset && element.dataset[key]);
            } catch(_) {
                return '';
            }
        }

        function isEditingUIOpen(){
            try { return registerWrap && registerWrap.style.display !== 'none'; } catch(_) { return false; }
        }

        function bindFormMouseGuards(){
            if (bindFormMouseGuards.__done) return;
            bindFormMouseGuards.__done = true;
            try {
                [titleInput, ta].forEach(function(el){
                    if (!el) return;
                    el.addEventListener('mousedown', function(e){ e.stopPropagation(); }, true);
                });
            } catch(_) {}
        }

        /**
         * スクロール固定ユーティリティ: 近傍のスクロールコンテナを検出
         * index.html の備考欄と同じ仕組みを使用
         */
        function getScrollableContainer(startEl) {
            try {
                let el = startEl && startEl.parentElement;
                while (el) {
                    const cs = window.getComputedStyle(el);
                    const oy = (cs && cs.overflowY) || '';
                    if ((oy === 'auto' || oy === 'scroll') && el.scrollHeight > el.clientHeight) return el;
                    el = el.parentElement;
                }
            } catch(_) {}
            return document.scrollingElement || document.documentElement || document.body;
        }

        /**
         * 要素位置が変わらないように一時的にスクロールをピン留め
         * index.html の備考欄と同じ仕組みを使用してジャンプを完全に抑止
         */
        function pinScrollWhile(el, callback) {
            try {
                const scroller = getScrollableContainer(el);
                const isWin = (scroller === document.scrollingElement || scroller === document.documentElement || scroller === document.body);
                const beforeTop = (typeof el.__savedTop === 'number') ? el.__savedTop : el.getBoundingClientRect().top;
                const beforeScrollTop = isWin ? (window.pageYOffset || document.documentElement.scrollTop || 0) : scroller.scrollTop;
                callback();
                requestAnimationFrame(function() {
                    try {
                        const afterTop = el.getBoundingClientRect().top;
                        const dy = afterTop - beforeTop;
                        if (el.__forcePinAbsolute === true) {
                            // iOSの改行時などは絶対スクロール位置を戻す
                            if (isWin) { window.scrollTo(0, beforeScrollTop); }
                            else { scroller.scrollTop = beforeScrollTop; }
                        } else {
                            if (Math.abs(dy) > 0.5) {
                                if (isWin) { window.scrollBy(0, dy); }
                                else { scroller.scrollTop += dy; }
                            }
                        }
                        try { el.__savedTop = null; } catch(_) {}
                        try { el.__forcePinAbsolute = false; } catch(_) {}
                    } catch(_) {}
                });
            } catch(_) { try { callback(); } catch(_) {} }
        }

        /**
         * テキストエリアを内容高さに合わせて自動調整（スクロール不要に）
         * index.html の備考欄と同じ仕組みを使用してジャンプを完全に抑止
         */
        function fitTextareaToContent(el) {
            try {
                if (!el) return;
                // IME構成中は高さ更新を抑止（iOSでのジャンプ回避）
                if (el.__isComposing) return;
                pinScrollWhile(el, function() {
                    let min = 0;
                    let borderTop = 0, borderBottom = 0;
                    try {
                        const cs = window.getComputedStyle(el);
                        min = parseFloat(cs.minHeight) || 0;
                        borderTop = parseFloat(cs.borderTopWidth) || 0;
                        borderBottom = parseFloat(cs.borderBottomWidth) || 0;
                    } catch(_) {}
                    el.style.height = 'auto';
                    const h = Math.max(el.scrollHeight + borderTop + borderBottom, min);
                    el.style.height = h + 'px';
                });
            } catch(_) {}
        }

        // ---------- 定数/スクリプト名（集中管理） ----------
        /** 設定値（マジックナンバーの一元管理） */
        const CONFIG = {
            HISTORY_MAX: 300,
            PAGE_LIMIT: 2000
        };
        /** Claris FileMaker スクリプト名（再利用しやすく一元管理） */
        const SCRIPT = {
            SAVE_ALL: 'addon_remarks_web_phrase_save_all',
            SELECT:   'addon_remarks_web_phrase_select',
            CANCEL:   'addon_remarks_web_template_cancel',
            REQUEST:  'addon_remarks_web_phrase_request'
        };

        /**
         * フレーズデータの正規化ロジックを一元管理するモジュール
         * - Data API / 直接配列 / 従来形式を共通APIで扱う
         * - recordId も併せて抽出し、呼び出し元で再利用できるようにする
         */
        const PhraseParser = (function(){
            function toPhraseObject(item, fallbackId){
                if (!item || typeof item !== 'object') return null;
                const text = (item.text != null) ? item.text : item.phrase;
                if (!text) return null;
                let id = item.id != null ? toStringValue(item.id) : toStringValue(fallbackId);
                if (!id) {
                    id = toStringValue(fallbackId || Date.now());
                }
                return {
                    id: id,
                    text: toStringValue(text),
                    title: toStringValue(item && item.title)
                };
            }

            function normalizeArray(list, idPrefix){
                const out = [];
                if (!Array.isArray(list)) return out;
                list.forEach(function(item, index){
                    const fallback = idPrefix ? (idPrefix + '_' + (index + 1)) : toStringValue(index + 1);
                    const normalized = toPhraseObject(item, fallback);
                    if (normalized) out.push(normalized);
                });
                return out;
            }

            function normalizeValue(value, idPrefix){
                if (!value) return [];
                if (Array.isArray(value)) return normalizeArray(value, idPrefix);
                if (typeof value === 'object') {
                    const single = toPhraseObject(value, idPrefix ? (idPrefix + '_1') : '1');
                    return single ? [single] : [];
                }
                return [];
            }

            function parseDataApiPayload(data){
                const result = { phrases: [], recordId: '' };
                try {
                const records = (data && data.response && Array.isArray(data.response.data)) ? data.response.data : [];
                    records.forEach(function(record, recIdx){
                        const fieldData = record && record.fieldData || {};
                const recordId = (record && record.recordId != null) ? toStringValue(record.recordId) : '';
                        if (!result.recordId && recordId) {
                            result.recordId = recordId;
                        }

                let jsonPayload = fieldData.json;
                if (typeof jsonPayload === 'string') {
                    jsonPayload = tryParseJSON(jsonPayload);
                }
                        result.phrases = result.phrases.concat(normalizeValue(jsonPayload, recordId || ('record' + (recIdx + 1))));

                let templatePayload = fieldData.template;
                        if (typeof templatePayload === 'string') {
                            templatePayload = tryParseJSON(templatePayload);
                        }
                        result.phrases = result.phrases.concat(normalizeValue(templatePayload, recordId || ('record' + (recIdx + 1))));
                    });
                } catch(_) {}
                return result;
            }

            function parseObject(data){
                if (!data) return { phrases: [], recordId: '' };
                if (Array.isArray(data)) {
                    return { phrases: normalizeArray(data, ''), recordId: '' };
                }
                if (Array.isArray(data.phrases)) {
                    return { phrases: normalizeArray(data.phrases, ''), recordId: '' };
                }
                if (data.response && Array.isArray(data.response.data)) {
                    return parseDataApiPayload(data);
                }
                return { phrases: [], recordId: '' };
            }

            function extract(payload){
                if (payload == null) return { phrases: [], recordId: '' };
                let source = payload;
                if (typeof payload === 'string') {
                    source = tryParseJSON(payload);
                }
                return parseObject(source);
            }

            return {
                extract: extract,
                normalize: function(payload){
                    return extract(payload).phrases;
                }
            };
        })();

        // ---------- Claris FileMaker 連携ヘルパー ----------
        /**
         * Claris FileMaker へのスクリプト実行（fmCall ラッパー）
         */
        function fmPerform(scriptName, payload, option){
            try {
                return isCallable(fmCall) ? fmCall(toStringValue(scriptName), (payload==null?'':payload), Number(option||5)) : false;
            } catch(_) {
                return false;
            }
        }
        /** すべてのフレーズを一括保存 */
        function fmSaveAll(){
            try {
                const recId = isCallable(getPhraseRecordId) ? getPhraseRecordId() : '';
                const payload = isCallable(buildPhrasePayloadFromItems) ? buildPhrasePayloadFromItems(recId, state.getPhrases() || []) : '';
                if (payload) fmPerform(SCRIPT.SAVE_ALL, payload, 5);
            } catch(_) {}
        }
        /** 選択した1件を通知 */
        function fmNotifySelect(item){
            try {
                const recId = isCallable(getPhraseRecordId) ? getPhraseRecordId() : '';
                const payload = isCallable(buildPhrasePayloadFromItems) ? buildPhrasePayloadFromItems(recId, [{ title: toStringValue(item && item.title), text: toStringValue(item && item.text) }]) : '';
                if (payload) fmPerform(SCRIPT.SELECT, payload, 5);
            } catch(_) {}
        }
        /** 編集キャンセルを通知 */
        function fmNotifyCancel(){
            try { fmPerform(SCRIPT.CANCEL, '', 5); } catch(_) {}
        }
        /** フレーズ取得要求を送信 */
        function fmRequestPhrases(){
            try { fmPerform(SCRIPT.REQUEST, '', 5); } catch(_) {}
        }

        // ---------- Undo/Redo システム ----------
        /**
         * layout_list.html を参考にしたundo/redo機能の実装
         * - 各要素ごとに独立した履歴管理（WeakMap使用）
         * - undoStack（操作履歴）とredoStack（やり直し履歴）を保持
         * - 最大300件の履歴を保持し、それを超えた分は古いものから削除
         * - キーボードショートカット: Ctrl/Cmd+Z (undo), Ctrl/Cmd+Y or Ctrl/Cmd+Shift+Z (redo)
         */
        const elementHistoryMap = new WeakMap();
        let isApplyingHistory = false;
        const MAX_HISTORY_ENTRIES = CONFIG.HISTORY_MAX;

        function getHistoryFor(element) {
            let history = elementHistoryMap.get(element);
            if (!history) {
                // 初期値を現在の値として設定
                const initial = toStringValue(element && element.value);
                const undoStack = [initial];
                history = { undoStack: undoStack, redoStack: [] };
                elementHistoryMap.set(element, history);
            }
            return history;
        }

        // 初期値として値を設定し、履歴を「その値だけ」にリセットする
        function resetHistoryTo(element, value) {
            const next = toStringValue(value);
            const history = getHistoryFor(element);
            isApplyingHistory = true;
            try {
            element.value = next;
            } finally {
                isApplyingHistory = false;
            }
            history.undoStack = [next];
            history.redoStack = [];
        }

        function manageUndoRedo(element) {
            if (isApplyingHistory) return;
            const history = getHistoryFor(element);
            const undoStack = history.undoStack;
            const redoStack = history.redoStack;
            const newValue = toStringValue(element && element.value);
            const last = undoStack.length > 0 ? undoStack[undoStack.length - 1] : undefined;
            if (last !== newValue) {
                undoStack.push(newValue);
                redoStack.length = 0; // 新しい変更があった場合、redoStackをクリア
                if (undoStack.length > MAX_HISTORY_ENTRIES) {
                    undoStack.splice(0, undoStack.length - MAX_HISTORY_ENTRIES);
                }
            }
        }

        function applyProgrammaticValue(element, value) {
            const history = getHistoryFor(element);
            const prev = history.undoStack.length ? history.undoStack[history.undoStack.length - 1] : '';
            const next = toStringValue(value);
            isApplyingHistory = true;
            try {
                element.value = next;
            } finally {
                isApplyingHistory = false;
            }
            if (prev !== next) {
                history.undoStack.push(next);
                history.redoStack.length = 0;
                if (history.undoStack.length > MAX_HISTORY_ENTRIES) {
                    history.undoStack.splice(0, history.undoStack.length - MAX_HISTORY_ENTRIES);
                }
            }
            // 検索入力欄の場合、フィルターを再適用
            if (element.id === 'phraseSearchInput') {
                try { applyFilter(); } catch(_) {}
            }
        }

        function undoForElement(element) {
            const history = getHistoryFor(element);
            const undoStack = history.undoStack;
            const redoStack = history.redoStack;
            if (undoStack.length > 1) {
                const lastValue = undoStack.pop();
                redoStack.push(lastValue);
                const currentValue = undoStack[undoStack.length - 1] || '';
                applyProgrammaticValue(element, currentValue);
            }
        }

        function redoForElement(element) {
            const history = getHistoryFor(element);
            const undoStack = history.undoStack;
            const redoStack = history.redoStack;
            if (redoStack.length > 0) {
                const lastRedoValue = redoStack.pop();
                undoStack.push(lastRedoValue);
                applyProgrammaticValue(element, lastRedoValue);
            }
        }

        // キーボードショートカットの実装
        document.addEventListener('keydown', function(event) {
            if (event.isComposing) return;
            const isModifier = event.metaKey || event.ctrlKey;
            if (!isModifier) return;
            const key = toStringValue(event.key).toLowerCase();
            const active = document.activeElement;
            // 編集中の入力欄ではショートカットを無効化（干渉防止）
            try {
                if (isEditingUIOpen() && (active === titleInput || active === ta)) return;
            } catch(_) {}
            if (!isEditableTextField(active)) return;

            if (key === 'z') {
                event.preventDefault();
                event.stopPropagation();
                if (event.shiftKey) {
                    redoForElement(active);
                } else {
                    undoForElement(active);
                }
            } else if (key === 'y') {
                // Ctrl/Cmd+Y もredoとして扱う
                event.preventDefault();
                event.stopPropagation();
                redoForElement(active);
            }
        }, true);

        // ---- FileMaker ブリッジ（利用箇所より前に定義しておく） ----
        (function setupFileMakerBridge(){
            const queue = [];
            function isReady(){ try { return !!(window.FileMaker && typeof window.FileMaker.PerformScriptWithOption === 'function'); } catch(_) { return false; } }
            function drain(){ if (!isReady()) return; let item; while ((item = queue.shift())) { const name = item[0], param = item[1], opt = item[2]; try { window.FileMaker.PerformScriptWithOption(String(name||''), String(param||''), Number(opt||5)); } catch(_) {} } }
            function ensureTimer(){ if (setupFileMakerBridge.__timer) return; try { setupFileMakerBridge.__timer = setInterval(function(){ if (isReady()) { try { clearInterval(setupFileMakerBridge.__timer); } catch(_) {} setupFileMakerBridge.__timer = null; } drain(); }, 250); } catch(_) {} }
            window.fmCall = function(name, param, option){ try { const payload = (param == null) ? '' : (typeof param === 'string' ? param : JSON.stringify(param)); if (isReady()) { try { window.FileMaker.PerformScriptWithOption(String(name||''), String(payload), Number(option||5)); } catch(_) {} return true; } queue.push([name, payload, option]); ensureTimer(); return false; } catch(_) { return false; } };
        })();

        // フレーズ用のペイロード構築（index.html と同等の形式）
        // 注意: JSON.stringify は改行を \n としてエスケープして保持します
        function getPhraseRecordId(){
            try {
                return toStringValue(window.__currentRecordIdForPhrase);
            } catch(_) {
                return '';
            }
        }
        function buildPhrasePayloadFromItems(recordIdValue, items){
            try {
                const rid = toStringValue(recordIdValue);
                // 改行を含むテキストをそのまま保持（toStringValue は改行を削除しません）
                const normalized = (items||[]).map(function(p){
                    return { title: toStringValue(p && p.title), phrase: toStringValue(p && p.text) };
                });
                // JSON.stringify は改行を \n としてエスケープして保持します
                return JSON.stringify({ recordId: rid, phrases: normalized });
            } catch(_) {
                return JSON.stringify({ recordId: '', phrases: [] });
            }
        }

        /**
         * 汎用パーサー: 受け取ったデータをフレーズ配列に正規化する
         * 対応フォーマット:
         * 1) 直接配列: [{ phrase, title }] または [{ text, title }]
         * 2) 従来形式: { phrases: [{ text, title }] }
         * 3) Claris FileMaker Data API 形式: { response: { data: [{ fieldData: { json: "[...]" | "{...}", template?: "[...]" } }] } }
         *    - fieldData.json は JSON 文字列（配列/単一オブジェクト）を想定
         *    - json が優先、なければ後方互換の template を読む
         * @param {*} data - パース対象（JSONオブジェクト想定。文字列は上位でJSON.parseしてから渡す）
         * @returns {Array<{id:string,text:string,title:string}>}
         */
        function normalizeFromAny(data){
            const parsed = PhraseParser.extract(data);
            return parsed.phrases;
        }

        /**
         * Data API レスポンス（response.data[].fieldData.json）からフレーズ配列を抽出
         * - json は JSON 文字列を想定（配列/単一オブジェクトのどちらにも対応）
         * - id は recordId をベースに生成
         * @param {Object} data - { response: { data: [...] } } 形式
         * @returns {Array<{id:string,text:string,title:string}>}
         */
        function parseDataApiResponse(data){
            try {
                const extracted = PhraseParser.extract(data);
                return extracted.phrases;
            } catch(_) { return []; }
        }

        /**
         * phrases をセットして描画するヘルパー
         * - 既定では検索語とページをリセットして 1 ページ目から表示
         * @param {Array} list - 表示するフレーズ配列
         * @param {boolean} [resetSearch=true] - false で検索語/ページを維持
         */
        function setPhrasesAndRender(list, resetSearch){
            state.setPhrases(list);
            if (resetSearch !== false) {
                state.paging.reset();
            }
            filterAndRender();
        }

        /**
         * フレーズ一覧をDOMに描画する
         * - 各行にドラッグハンドル、三点メニュー（編集/削除）、ツールチップを付与
         * - 行の本文クリックで selectAndReturn を呼び出して挿入
         * @param {Array<{id:string,text:string,title:string}>} list
         */
        function render(list){
            state.getPhrases();
            listEl.innerHTML = '';
            list.forEach(function(p){
                const item = document.createElement('div');
                item.className = 'template-item';
                item.dataset.id = toStringValue(p && p.id);
                item.dataset.text = toStringValue(p && p.text);
                item.setAttribute('draggable', 'true');
                const row = document.createElement('div');
                row.className = 'template-item-row';
                // ドラッグハンドル
                const handle = document.createElement('div');
                handle.className = 'drag-handle';
                handle.setAttribute('role', 'button');
                handle.setAttribute('aria-label', 'ドラッグハンドル: 上下矢印キーで並び替え可能');
                /* 6ドットのドラッグアイコン（index.html の drag-handle 相当） */
                (function(){
                    const svgNS = 'http://www.w3.org/2000/svg';
                    const svg = document.createElementNS(svgNS, 'svg');
                    svg.setAttribute('viewBox', '0 0 24 24');
                    svg.setAttribute('aria-hidden', 'true');
                    svg.setAttribute('style', 'width:20px;height:20px;');
                    const coords = [[8,6],[8,12],[8,18],[14,6],[14,12],[14,18]];
                    coords.forEach(function(c){
                        const circle = document.createElementNS(svgNS, 'circle');
                        circle.setAttribute('cx', String(c[0]));
                        circle.setAttribute('cy', String(c[1]));
                        circle.setAttribute('r', '1.6');
                        circle.setAttribute('fill', 'currentColor');
                        svg.appendChild(circle);
                    });
                    while (handle.firstChild) handle.removeChild(handle.firstChild);
                    handle.appendChild(svg);
                })();
                row.appendChild(handle);
                const main = document.createElement('div');
                main.className = 'template-item-main';
                const body = document.createElement('div');
                body.className = 'template-body';
                body.textContent = p.text;
                main.appendChild(body);
                row.appendChild(main);
                // 三点メニュー
                const actionsEl = document.createElement('div');
                actionsEl.className = 'template-item-actions';
                const moreBtn = document.createElement('button');
                moreBtn.type = 'button';
                moreBtn.className = 'more-btn';
                (function(){
                    const svgNS = 'http://www.w3.org/2000/svg';
                    const svg = document.createElementNS(svgNS, 'svg');
                    svg.setAttribute('viewBox', '0 0 24 24');
                    svg.setAttribute('aria-hidden', 'true');
                    svg.setAttribute('style', 'width:20px;height:20px;');
                    const path = document.createElementNS(svgNS, 'path');
                    path.setAttribute('fill', 'currentColor');
                    path.setAttribute('d', 'M12 8a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4z');
                    svg.appendChild(path);
                    while (moreBtn.firstChild) moreBtn.removeChild(moreBtn.firstChild);
                    moreBtn.appendChild(svg);
                })();
                const moreMenu = document.createElement('div');
                moreMenu.className = 'more-menu';
                const editBtn = document.createElement('button'); editBtn.type = 'button'; editBtn.className = 'menu-edit'; editBtn.textContent = '編集';
                const deleteBtn = document.createElement('button'); deleteBtn.type = 'button'; deleteBtn.className = 'menu-delete'; deleteBtn.textContent = '削除';
                moreMenu.appendChild(editBtn); moreMenu.appendChild(deleteBtn);
                actionsEl.appendChild(moreBtn); actionsEl.appendChild(moreMenu);
                row.appendChild(actionsEl);
                item.appendChild(row);
                item.tabIndex = 0;
                // ツールチップ（タイトル+本文）
                try {
                    const rawBody = toStringValue(p && p.text);
                    const rawTitle = toTrimmedString(p && p.title);
                    const tooltip = rawTitle ? (rawBody ? (rawTitle + '\n' + rawBody) : rawTitle) : rawBody;
                    item.setAttribute('title', tooltip || '');
                    item.setAttribute('aria-label', tooltip || '');
                } catch(_) {}
                // 行クリックで選択（ハンドル/メニュー上は除外）
                item.addEventListener('click', function(e){
                    if (e.target === handle || (handle.contains && handle.contains(e.target))) return;
                    const insideMenu = e.target.closest && e.target.closest('.template-item-actions');
                    if (insideMenu) return;
                    selectAndReturn(p, item);
                });
                // 右クリック（または Ctrl+クリック）で行の三点メニューを開く
                item.addEventListener('contextmenu', function(e){
                    try {
                        e.preventDefault(); e.stopPropagation();
                        // 既存メニューを閉じる
                        try { listEl.querySelectorAll('.more-menu.open').forEach(function(mm){ mm.classList.remove('open'); }); } catch(_) {}
                        moreMenu.classList.add('open');
                    } catch(_) {}
                });
                // マウス押下で右ボタンを判定（Mac の Ctrl+左 も対応）
                item.addEventListener('mousedown', function(e){
                    try {
                        const isRight = (e.button === 2) || (!!e.ctrlKey && e.button === 0);
                        if (isRight) {
                            e.preventDefault(); e.stopPropagation();
                            try { listEl.querySelectorAll('.more-menu.open').forEach(function(mm){ mm.classList.remove('open'); }); } catch(_) {}
                            moreMenu.classList.add('open');
                        }
                    } catch(_) {}
                }, true);
                // 入力欄のフォーカスを阻害しない（index.html と同じ配慮）
                try {
                    titleInput.addEventListener('mousedown', function(e){ e.stopPropagation(); }, true);
                    ta.addEventListener('mousedown', function(e){ e.stopPropagation(); }, true);
                } catch(_) {}
                // 外部D&D用に dataTransfer へ設定できない場合のフォールバック（例えば一部のWebビューア）
                item.addEventListener('dragstart', function(e){
                    try {
                    const dragText = toStringValue(p && p.text);
                        if (e && e.dataTransfer && !e.dataTransfer.getData('text/plain')) {
                            e.dataTransfer.setData('text/plain', dragText);
                        }
                    } catch(_) {}
                });
                item.addEventListener('keydown', function(e){ if (e.key === 'Enter') selectAndReturn(p, item); });
                listEl.appendChild(item);

                // 3点メニューの動作
                moreBtn.addEventListener('click', function(e){
                    e.stopPropagation();
                    const isOpen = moreMenu.classList.contains('open');
                    listEl.querySelectorAll('.more-menu.open').forEach(function(mm){ mm.classList.remove('open'); });
                    if (!isOpen) moreMenu.classList.add('open');
                });
                // クローズはドキュメント委譲側で一括対応するため、ここでは未設定
                editBtn.addEventListener('click', function(e){
                    e.stopPropagation();
                    moreMenu.classList.remove('open');
                    // data-id からフレーズ配列内の正しいインデックスを解決
                    const id = getDatasetValue(item, 'id');
                    const idx = state.findIndexById(id);
                    if (idx >= 0) { switchToEditMode('edit', idx); }
                });
                deleteBtn.addEventListener('click', function(e){
                    e.stopPropagation();
                    moreMenu.classList.remove('open');
                    // data-id からフレーズ配列内の正しいインデックスを解決
                    const id = getDatasetValue(item, 'id');
                    const idx = state.findIndexById(id);
                    if (idx >= 0) {
                        showConfirm('このフレーズを削除しますか？', function(){
                            let removed = null;
                            try { removed = state.getPhrases().splice(idx, 1)[0]; } catch(_) { removed = null; }
                            filterAndRender(function(){
                                try { showToast('削除しました'); } catch(_) {}
                                fmSaveAll();
                            });
                        }, function(){ /* no-op cancel */ }, { confirmLabel: '削除', confirmClass: 'button-danger' });
                    }
                });

                // ハンドルでのキーボード並べ替え
                handle.tabIndex = 0;
                handle.addEventListener('keydown', function(e){
                    if (isFiltered()) { e.preventDefault(); return; }
                    if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
                        e.preventDefault();
                        var cur = handle.closest('.template-item'); if (!cur) return;
                        var isUp = e.key === 'ArrowUp';
                        var sibling = isUp ? cur.previousElementSibling : cur.nextElementSibling;
                        if (sibling && sibling.classList.contains('template-item')) {
                            if (isUp) { sibling.parentNode.insertBefore(cur, sibling); } else { sibling.parentNode.insertBefore(cur, sibling.nextSibling); }
                            // 配列へ反映
                            syncPhrasesOrderFromDOM();
                            // 再描画してフォーカス維持
                            filterAndRender(function(){
                                try { handle.focus(); } catch(_) {}
                                try { showToast('並び替えを保存しました'); } catch(_) {}
                                fmSaveAll();
                            });
                        }
                    }
                });
            });
            bindDnD();
        }

        // ページングと検索状態
        let PHRASE_PAGE_LIMIT = CONFIG.PAGE_LIMIT;
        function isFiltered(){ return toTrimmedString(state.paging.get('searchTerm')).length > 0; }

        function renderPhrasePagination(page, total){
            const el = document.getElementById('phrasePagination');
            if (!el) return;
            if (total <= 1) { el.style.display = 'none'; el.innerHTML=''; return; }
            el.style.display = '';
            el.innerHTML = '';
            const prev = document.createElement('button'); prev.className = 'button button-secondary'; prev.textContent = '前へ'; prev.disabled = page <= 1;
            const info = document.createElement('span'); info.textContent = page + ' / ' + total + ' ページ'; info.style.margin = '0 8px';
            const next = document.createElement('button'); next.className = 'button button-secondary'; next.textContent = '次へ'; next.disabled = page >= total;
            prev.addEventListener('click', function(){ if (state.paging.get('currentPage') > 1) { state.paging.set('currentPage', state.paging.get('currentPage') - 1); filterAndRender(); } });
            next.addEventListener('click', function(){ if (state.paging.get('currentPage') < state.paging.get('totalPages')) { state.paging.set('currentPage', state.paging.get('currentPage') + 1); filterAndRender(); } });
            el.appendChild(prev); el.appendChild(info); el.appendChild(next);
        }

        /**
         * 検索フィルタとページネーションを適用してレンダリング
         * - 検索語は state.paging.get('searchTerm')、ページは state.paging.get('currentPage') から決定
         * - 1ページの件数は PHRASE_PAGE_LIMIT（既定: 20）
         * @param {Function} [after] - レンダリング後のコールバック（任意）
         */
        function filterAndRender(after){
            const base = state.getPhrases();
            const term = toLowerCaseString(toTrimmedString(state.paging.get('searchTerm')));
            const filtered = term ? base.filter(function(p){ 
                const t = toLowerCaseString(p && p.text);
                const ti = toLowerCaseString(p && p.title);
                return t.indexOf(term) !== -1 || ti.indexOf(term) !== -1; 
            }) : base;
            state.paging.set('totalPages', Math.max(1, Math.ceil(filtered.length / PHRASE_PAGE_LIMIT)));
            if (state.paging.get('currentPage') > state.paging.get('totalPages')) {
                state.paging.set('currentPage', state.paging.get('totalPages'));
            }
            const start = (state.paging.get('currentPage') - 1) * PHRASE_PAGE_LIMIT;
            const pageItems = filtered.slice(start, start + PHRASE_PAGE_LIMIT);
            render(pageItems);
            renderPhrasePagination(state.paging.get('currentPage'), state.paging.get('totalPages'));
            if (typeof after === 'function') { try { after(); } catch(_) {} }
        }

        /**
         * ドラッグ&ドロップ後のDOM順序を phrases 配列に反映
         * - id 属性から元のオブジェクトを引き当てて順序を再構築
         */
        function syncPhrasesOrderFromDOM(){
            try {
                const items = Array.prototype.slice.call(listEl.querySelectorAll('.template-item'));
                if (!items.length) return;
                const idToPhrase = new Map();
                const currentList = state.getPhrases();
                currentList.forEach(function(p){ idToPhrase.set(toStringValue(p && p.id), p); });
                const newOrder = [];
                items.forEach(function(el){ const id = getDatasetValue(el, 'id'); const hit = idToPhrase.get(id); if (hit) { newOrder.push(hit); idToPhrase.delete(id); } });
                idToPhrase.forEach(function(v){ newOrder.push(v); });
                if (newOrder.length === currentList.length) { state.setPhrases(newOrder); }
            } catch(_) {}
        }

        /**
         * ドラッグ&ドロップ機能を各フレーズ項目に設定
         * - 検索中は内部並べ替えを無効化（誤操作防止）。外部挿入用途のD&Dは維持
         * - ドロップインジケータ（青線）で挿入位置を可視化
         */
        function bindDnD(){
            const items = Array.prototype.slice.call(listEl.querySelectorAll('.template-item'));
            if (!items.length) return;
            let dragEl = null; let currentDropIndicator = null; let lastDropTarget = null; let lastInsertAfter = null;
            function createDropIndicator(){ const di = document.createElement('div'); di.className = 'drop-indicator'; return di; }
            function clearDropIndicators(){ try { listEl.querySelectorAll('.drop-indicator').forEach(function(n){ n.parentNode && n.parentNode.removeChild(n); }); } catch(_) {} currentDropIndicator = null; }
            items.forEach(function(el){
                el.addEventListener('dragstart', function(e){
                    // 検索中は内部並べ替え不可（外部挿入目的のドラッグは維持）
                    try {
                        e.dataTransfer.effectAllowed = isFiltered() ? 'copy' : 'copyMove';
                        var dragText = getDatasetValue(el, 'text');
                        e.dataTransfer.setData('text/plain', dragText);
                        // 互換用（環境によっては 'text' を見る場合がある）
                        try { e.dataTransfer.setData('text', dragText); } catch(_) {}
                    } catch(_) {}
                    dragEl = el;
                    document.body.classList.add('dragging');
                    el.classList.add('dragging');
                    listEl.classList.add('drag-active');
                });
                el.addEventListener('dragend', function(){
                    document.body.classList.remove('dragging');
                    if (dragEl) dragEl.classList.remove('dragging');
                    listEl.classList.remove('drag-active');
                    listEl.querySelectorAll('.drag-over').forEach(function(t){ t.classList.remove('drag-over'); });
                    clearDropIndicators(); dragEl = null; lastDropTarget = null; lastInsertAfter = null;
                });
            });
            listEl.addEventListener('dragover', function(e){
                if (isFiltered()) { try { e.preventDefault(); } catch(_) {} return; }
                e.preventDefault(); if (!dragEl) return;
                const target = e.target && (e.target.closest && e.target.closest('.template-item'));
                if (!target || target === dragEl) return;
                const rect = target.getBoundingClientRect(); const mouseY = e.clientY; const mid = rect.top + rect.height/2; const diff = mouseY - mid; const HYST = 4; const insertAfter = diff > HYST ? true : (diff < -HYST ? false : (lastInsertAfter != null ? lastInsertAfter : diff > 0));
                if (lastDropTarget === target && lastInsertAfter === insertAfter) return;
                lastDropTarget = target; lastInsertAfter = insertAfter;
                const indicator = currentDropIndicator || createDropIndicator(); currentDropIndicator = indicator;
                const parent = target.parentNode;
                if (insertAfter) { if (indicator.previousSibling !== target) parent.insertBefore(indicator, target.nextSibling); }
                else { if (indicator !== target) parent.insertBefore(indicator, target); }
                indicator.classList.add('active');
                listEl.querySelectorAll('.template-item.drag-over').forEach(function(n){ n.classList.remove('drag-over'); });
                target.classList.add('drag-over');
            });
            listEl.addEventListener('drop', function(e){
                if (isFiltered()) { try { e.preventDefault(); } catch(_) {} return; }
                e.preventDefault(); if (!dragEl || !currentDropIndicator) return;
                currentDropIndicator.parentNode.insertBefore(dragEl, currentDropIndicator);
                clearDropIndicators(); listEl.classList.remove('drag-active');
                // 配列に反映
                syncPhrasesOrderFromDOM();
                // 再描画して視覚効果を付与
                filterAndRender(function(){
                    try {
                        var id = getDatasetValue(dragEl, 'id');
                        var target = listEl.querySelector('.template-item[data-id="' + id.replace(/"/g,'\\"') + '"]');
                        if (target) {
                            target.classList.add('was-edited');
                            setTimeout(function(){ try { target.classList.add('highlight-fade'); requestAnimationFrame(function(){ try { target.classList.remove('was-edited'); setTimeout(function(){ try { target.classList.remove('highlight-fade'); } catch(_) {} }, 400); } catch(_) {} }); } catch(_) {} }, 200);
                        }
                    } catch(_) {}
                    try { showToast('並び替えを保存しました'); } catch(_) {}
                    fmSaveAll();
                });
            });
            listEl.addEventListener('mouseleave', function(){ if (!dragEl) return; clearDropIndicators(); });
        }

        /**
         * 検索入力欄の値で state.paging.get('searchTerm') を更新し、ページを先頭に戻して再描画する。
         * - 入力イベントや Enter 実行から呼び出される。
         */
        function applyFilter(){
            state.paging.set('searchTerm', toTrimmedString(searchEl && searchEl.value));
            state.paging.set('currentPage', 1);
            filterAndRender();
        }

        /**
         * フレーズを選択し、クリップボードにコピーする
         * - クリップボードへのコピー機能: navigator.clipboard.writeText を使用
         * - 成功時にトースト通知を表示（将来のAI・開発者のための明示的な動作確認）
         * - コピー時に行をうっすら黄色くハイライト表示（視覚的フィードバック）
         * - 備考への転記は行わず、クリップボードコピーのみを実行
         * @param {Object} p - フレーズオブジェクト { text, title }
         * @param {HTMLElement} itemElement - クリックされたフレーズ行の要素（ハイライト表示用）
         */
        function selectAndReturn(p, itemElement){
            try {
                var textToCopy = String(p.text || '');
                // 先に視覚的なフィードバックを即時表示
                try { highlightCopiedItem(itemElement); } catch(_) {}
                
                // クリップボードにコピー（非同期処理）
                if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
                    navigator.clipboard.writeText(textToCopy).then(function() {
                        // コピー成功時にトースト通知を表示
                        try { showToast('クリップボードにコピーしました'); } catch(_) {}
                    }).catch(function(err) {
                        // コピー失敗時にも通知（開発者向けデバッグ情報）
                        try { showToast('コピーに失敗しました'); } catch(_) {}
                        console.warn('Clipboard copy failed:', err);
                    });
                } else {
                    // フォールバック: 古いブラウザ向けに document.execCommand を使用
                    try {
                        var textarea = document.createElement('textarea');
                        textarea.value = textToCopy;
                        textarea.style.position = 'fixed';
                        textarea.style.opacity = '0';
                        document.body.appendChild(textarea);
                        textarea.select();
                        var success = document.execCommand('copy');
                        document.body.removeChild(textarea);
                        if (success) {
                            try { showToast('クリップボードにコピーしました'); } catch(_) {}
                        } else {
                            try { showToast('コピーに失敗しました'); } catch(_) {}
                        }
                    } catch(_) {
                        try { showToast('コピーに失敗しました'); } catch(_) {}
                    }
                }
                
                // 備考への転記を行わないため、Claris FileMaker および親ウィンドウへの通知はコメントアウト
                // var payload = { type: 'phrase:selected', phrase: textToCopy, title: String(p.title || '') };
                // fmNotifySelect(p);
                // if (window.opener && typeof window.opener.postMessage === 'function') {
                //     window.opener.postMessage(payload, '*');
                // }
                // if (window.parent && window.parent !== window && typeof window.parent.postMessage === 'function') {
                //     window.parent.postMessage(payload, '*');
                // }
            } catch(_) {}
            // ハイライトが確実に見えるよう、クローズを十分遅延
            setTimeout(function(){ try { window.close(); } catch(_) {} }, 900);
        }

        /**
         * コピーされた行を一時的に黄色くハイライト表示する
         * - 既存の .was-edited クラスを再利用（黄色のハイライト効果）
         * - フェードアウトアニメーションで自然に元に戻す
         * @param {HTMLElement} itemElement - ハイライトする要素
         */
        function highlightCopiedItem(itemElement){
            try {
                if (!itemElement) return;
                itemElement.classList.add('was-edited');
                // ほんの少し遅らせてフェード用クラスを付与（トランジション適用のため）
                setTimeout(function(){ try { itemElement.classList.add('highlight-fade'); } catch(_) {} }, 50);
                // 薄い黄色の表示時間を長めに確保してから解除
                setTimeout(function(){
                    try {
                        itemElement.classList.remove('was-edited');
                        setTimeout(function(){
                            try { itemElement.classList.remove('highlight-fade'); } catch(_) {}
                        }, 400);
                    } catch(_) {}
                }, 800);
            } catch(_) {}
        }

        function toggleSearchUI(show){
            try {
                var visible = !!show;
                searchWrapEl.style.display = visible ? 'flex' : 'none';
                if (searchToggleEl) searchToggleEl.style.display = visible ? 'none' : '';
                // 検索ラップに内蔵のクリア(X)ボタンを右端に表示（トグルの代わり）
                if (visible) {
                    try {
                        if (!document.getElementById('phraseSearchCloseInline')) {
                            var clearBtn = document.createElement('button');
                            clearBtn.id = 'phraseSearchCloseInline';
                            clearBtn.type = 'button';
                            clearBtn.className = 'clear-button';
                            clearBtn.title = '検索を閉じる';
                            clearBtn.setAttribute('aria-label', '検索を閉じる');
                            (function(){
                                const svgNS = 'http://www.w3.org/2000/svg';
                                const svg = document.createElementNS(svgNS, 'svg');
                                svg.setAttribute('viewBox', '0 0 24 24');
                                svg.setAttribute('aria-hidden', 'true');
                                const path = document.createElementNS(svgNS, 'path');
                                path.setAttribute('fill', 'currentColor');
                                path.setAttribute('d', 'M18.3 5.71L12 12.01 5.7 5.71 4.29 7.12l6.3 6.29-6.3 6.3 1.41 1.41 6.3-6.3 6.29 6.3 1.41-1.41-6.3-6.3 6.3-6.29z');
                                svg.appendChild(path);
                                clearBtn.appendChild(svg);
                            })();
                            clearBtn.addEventListener('click', function(){ toggleSearchUI(false); try { applyProgrammaticValue(searchEl, ''); } catch(_) {}; });
                            searchWrapEl.appendChild(clearBtn);
                        }
                    } catch(_) {}
                } else {
                    state.paging.reset();
                }
                if (headerTitleEl) headerTitleEl.style.display = visible ? 'none' : '';
                if (newBtn) newBtn.style.display = visible ? 'none' : '';
                if (visible) { setTimeout(function(){ try { searchEl && searchEl.focus(); } catch(_) {} }, 0); }
            } catch(_) {}
        }

        /**
         * FileMaker Web ビューア特有のカーソル移動問題に対する回避策
         * - 一部環境（Claris FileMaker Web ビューア）では textarea/input の行頭・行末で左右矢印キーを押すと
         *   フォーカスがコンポーネント外へ移動したり、予期しないナビゲーションが発生する既知の挙動があります。
         * - 本ガードは「入力中の要素で、選択範囲がなく、キャレットが先頭/末尾にあるとき」に限って
         *   ArrowLeft/ArrowRight をキャンセルしてフォーカス逸脱を防止します。
         * - IME 変換中は抑止しません（composition イベントを考慮）。
         * - 本挙動はアクセシビリティ上の配慮として最小限の範囲に限定しており、一般的な環境でも入力体験を損ねない設計です。
         * - もし他環境で不要な場合は、当該リスナーの登録箇所（document.addEventListener('keydown', ... , true)）を無効化してください。
         */
        function guardArrowKeysOnEdges(event){
            try {
                if (event.isComposing) return;
                var t = event.target;
                var isTextarea = t instanceof HTMLTextAreaElement;
                var isSingleLineInput = (t instanceof HTMLInputElement) && (t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password');
                if (!isTextarea && !isSingleLineInput) return;
                var selectionStart = t.selectionStart; var selectionEnd = t.selectionEnd;
                if (selectionStart == null || selectionEnd == null) return;
                if (selectionStart !== selectionEnd) return; // 範囲選択は対象外
                if (isTextarea) {
                    var value = String(t.value || '');
                    if (event.key === 'ArrowLeft' && selectionStart === 0) { event.preventDefault(); event.stopPropagation(); return; }
                    if (event.key === 'Home' && selectionStart === 0) { event.preventDefault(); event.stopPropagation(); return; }
                    if (event.key === 'ArrowRight' && selectionStart === value.length) { event.preventDefault(); event.stopPropagation(); return; }
                    if (event.key === 'End' && selectionStart === value.length) { event.preventDefault(); event.stopPropagation(); return; }
                } else if (isSingleLineInput) {
                    if (event.key === 'ArrowLeft' && selectionStart === 0) { event.preventDefault(); event.stopPropagation(); return; }
                    if (event.key === 'Home' && selectionStart === 0) { event.preventDefault(); event.stopPropagation(); return; }
                    if (event.key === 'ArrowRight' && selectionStart === String(t.value || '').length) { event.preventDefault(); event.stopPropagation(); return; }
                    if (event.key === 'End' && selectionStart === String(t.value || '').length) { event.preventDefault(); event.stopPropagation(); return; }
                }
            } catch(_) {}
        }

        function switchToEditMode(mode, index){
            // mode: 'create' | 'edit'
            try {
                const listRef = state.getPhrases();
                // スクロール位置は保存・復元しない（ジャンプ原因のため）
                let focusTarget = titleInput;

                if (mode === 'create') {
                    state.setFlag('editingIndex', null);
                    if (headerTitleEl) headerTitleEl.textContent = 'フレーズを新規作成';
                    resetHistoryTo(titleInput, '');
                    resetHistoryTo(ta, '');
                } else {
                    state.setFlag('editingIndex', (typeof index === 'number') ? index : null);
                    if (headerTitleEl) headerTitleEl.textContent = 'フレーズを編集';
                    const currentIndex = state.getFlag('editingIndex');
                    if (currentIndex != null && listRef[currentIndex]) {
                        resetHistoryTo(titleInput, String(listRef[currentIndex].title || ''));
                        resetHistoryTo(ta, String(listRef[currentIndex].text || ''));
                        focusTarget = ta;
                    }
                }

                toggleSearchUI(false);
                if (newBtn) newBtn.style.display = 'none';
                if (searchToggleEl) searchToggleEl.style.display = 'none';
                document.getElementById('phrasePickerList').style.display = 'none';
                document.getElementById('phrasePagination').style.display = 'none';
                registerWrap.style.display = 'block';
                closeBtn.style.display = 'none';
                editCancelBtn.style.display = '';
                editSaveBtn.style.display = '';

                // 編集フォーム表示直後に自動リサイズを実行
                requestAnimationFrame(function(){
                    try { fitTextareaToContent(ta); } catch(_) {}
                    if (focusTarget) {
                        try {
                            focusTarget.focus({ preventScroll: true });
                        } catch(_) {
                            try { focusTarget.focus(); } catch(__) {}
                        }
                    }
                });

                bindFormMouseGuards();
            } catch(_) {}
        }

        function restoreListUI(){
            try {
                // タイトル戻す
                if (headerTitleEl) headerTitleEl.textContent = 'フレーズを選択';
                // 入力欄を閉じて一覧へ
                registerWrap.style.display = 'none';
                document.getElementById('phrasePickerList').style.display = '';
                // ページングは現状未使用のため非表示のまま
                document.getElementById('phrasePagination').style.display = 'none';
                // [+] を戻す
                if (newBtn) newBtn.style.display = '';
                if (searchToggleEl) searchToggleEl.style.display = '';
                // 編集状態解除
                state.setFlag('editingIndex', null);
                // フッター切替
                closeBtn.style.display = '';
                editCancelBtn.style.display = 'none';
                editSaveBtn.style.display = 'none';
                // 保存/作成後のハイライト
                try {
                    var target = null;
                    var idToUse = state.getFlag('highlightId');
                    if (!idToUse) { idToUse = state.getFlag('newlyCreatedId') || ''; }
                    if (idToUse) {
                        target = document.querySelector('.template-item[data-id="' + idToUse.replace(/\"/g,'\\"') + '"]');
                    }
                    if (target) {
                        try { target.scrollIntoView({ block: 'center', behavior: 'smooth' }); } catch(_) { try { target.scrollIntoView(); } catch(__) {} }
                        target.classList.add('was-edited');
                        setTimeout(function(){
                            try {
                                target.classList.add('highlight-fade');
                                requestAnimationFrame(function(){
                                    try { target.classList.remove('was-edited'); setTimeout(function(){ try { target.classList.remove('highlight-fade'); } catch(_) {} }, 400); } catch(_) {}
                                });
                            } catch(_) {}
                        }, 200);
                    }
                } catch(_) {}
                state.setFlag('highlightId', null);
                state.setFlag('newlyCreatedId', null);
                // スタイルをリセット
                try {
                    document.body.classList.remove('phrase-edit-desktop');
                    ta.style.removeProperty('height');
                    ta.style.removeProperty('overflow-y');
                    ta.style.removeProperty('min-height');
                    registerWrap.style.removeProperty('max-height');
                    registerWrap.style.removeProperty('max-width');
                    registerWrap.style.removeProperty('overflow');
                    registerWrap.style.removeProperty('overflow-y');
                } catch(_) {}
            } catch(_) {}
        }

        function setupFromURL(){
            try {
                var sp = new URLSearchParams(location.search);
                var raw = sp.get('payload') || sp.get('data') || '';
                if (raw) {
                    try { setPhrasesAndRender(normalizeFromAny(JSON.parse(raw))); } catch(_) { setPhrasesAndRender([]); }
                }
            } catch(_) { setPhrasesAndRender([]); }
        }

        function handleIncomingMessage(ev){
            try {
                var data = ev && ev.data;
                if (!data) return;
                if (data.type === 'phrase:data') {
                    setPhrasesAndRender(normalizeFromAny(data.payload));
                }
            } catch(_) {}
        }

        // ---------- IME変換対応（グローバル処理） ----------
        // IME入力（日本語変換など）は1回のundoで戻せるようにグルーピング
        document.addEventListener('compositionstart', function(e) {
            var t = e.target;
            var isInput = (t instanceof HTMLInputElement) && (
                t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
            );
            var isTextarea = t instanceof HTMLTextAreaElement;
            if (!isInput && !isTextarea) return;
            t.__isComposing = true;
            // 変換開始時に現在の状態を履歴として保存（変換前の状態を確保）
            try { getHistoryFor(t); } catch(_) {}
        }, true);

        document.addEventListener('compositionend', function(e) {
            var t = e.target;
            var isInput = (t instanceof HTMLInputElement) && (
                t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
            );
            var isTextarea = t instanceof HTMLTextAreaElement;
            if (!isInput && !isTextarea) return;
            t.__isComposing = false;
            // 変換確定後の値を1ステップとして履歴に積む
            manageUndoRedo(t);
        }, true);

        // 入力時に各要素の履歴を更新（英数字入力対応）
        document.addEventListener('input', function(e) {
            var t = e.target;
            var isInput = (t instanceof HTMLInputElement) && (
                t.type === 'text' || t.type === 'search' || t.type === 'url' || t.type === 'email' || t.type === 'password'
            );
            var isTextarea = t instanceof HTMLTextAreaElement;
            if (!isInput && !isTextarea) return;
            if (e.isComposing || t.__isComposing) return; // 変換中は履歴に積まない
            
            // 検索入力欄の場合はフィルターも適用
            if (t.id === 'phraseSearchInput') {
                try { applyFilter(); } catch(_) {}
            }
            
            // IME確定や英数字入力の度に履歴へ積む（短時間の大量input連打をバッチ化）
            if (t.__undoDebounceTimer) clearTimeout(t.__undoDebounceTimer);
            t.__undoDebounceTimer = setTimeout(function() {
                manageUndoRedo(t);
                t.__undoDebounceTimer = null;
            }, 150); // 150ms後に履歴を記録（layout_list.htmlと同じ）
        }, true);

        // 入力欄の初期履歴設定とblurイベント
        function setupInputHistoryAndEvents(element) {
            if (!element) return;
            // 初期状態の履歴を設定
            getHistoryFor(element);
            
            // 即座の履歴記録が必要なケース（フォーカス移動時など）
            element.addEventListener('blur', function() {
                if (element.__undoDebounceTimer) {
                    clearTimeout(element.__undoDebounceTimer);
                    manageUndoRedo(element);
                    element.__undoDebounceTimer = null;
                }
            });
        }

        // 各入力欄に履歴機能を設定
        setupInputHistoryAndEvents(searchEl);
        setupInputHistoryAndEvents(titleInput);
        setupInputHistoryAndEvents(ta);
        
        // テキストエリアの入力時に自動リサイズ（index.html の備考欄と同じ仕組み）
        ta.addEventListener('input', function(){
            if (registerWrap && registerWrap.style.display !== 'none') {
                try { fitTextareaToContent(ta); } catch(_) {}
            }
        });

        /**
         * Enterで改行が発生する直前の位置を保存し、次フレームで補正
         * index.html の備考欄と同じ仕組みでジャンプを抑止
         */
        (function stabilizeOnEnterForIOS(){
            try {
                document.addEventListener('keydown', function(e) {
                    try {
                        if (e.isComposing) return;
                        if (e.key !== 'Enter') return;
                        var t = e.target;
                        if (!(t instanceof HTMLTextAreaElement)) return;
                        // 編集フォーム内のテキストエリアのみ対象
                        if (t !== ta) return;
                        // 直前の位置を保存（fitTextareaToContent内のpinScrollWhileで利用）
                        try { t.__savedTop = t.getBoundingClientRect().top; } catch(_) { t.__savedTop = null; }
                        try { t.__forcePinAbsolute = true; } catch(_) {}
                        // 入力後のレイアウト確定に合わせて再フィット（念のため）
                        requestAnimationFrame(function() { try { if (!t.__isComposing) fitTextareaToContent(t); } catch(_) {} });
                    } catch(_) {}
                }, true);
            } catch(_) {}
        })();

        /**
         * iOS/Safari向け: beforeinput(insertLineBreak)でも位置を保存してスクロールのジャンプを抑止
         * index.html の備考欄と同じ仕組みを使用
         */
        (function stabilizeOnBeforeInputForIOS(){
            try {
                document.addEventListener('beforeinput', function(e) {
                    try {
                        // 改行の直前イベントのみ対象
                        if (!e || e.inputType !== 'insertLineBreak') return;
                        var t = e.target;
                        if (!(t instanceof HTMLTextAreaElement)) return;
                        // 編集フォーム内のテキストエリアのみ対象
                        if (t !== ta) return;
                        // 直前の位置を保存し、絶対位置復元モードに
                        try { t.__savedTop = t.getBoundingClientRect().top; } catch(_) { t.__savedTop = null; }
                        try { t.__forcePinAbsolute = true; } catch(_) {}
                        // 次フレームで高さを再計算（オートリサイズと整合）
                        requestAnimationFrame(function() { try { if (!t.__isComposing) fitTextareaToContent(t); } catch(_) {} });
                    } catch(_) {}
                }, true);
            } catch(_) {}
        })();

        /**
         * IME変換中かどうかをイベントから判定するユーティリティ。
         * @param {Event} e - キーボード/入力関連イベント
         * @returns {boolean} 変換中なら true
         */
        function isComposingEvent(e){
            try {
                var t = e && e.target;
                return !!(e && e.isComposing) || !!(t && t.__isComposing);
            } catch(_) { return false; }
        }

        // 初期化
        setupFromURL();
        filterAndRender();
        window.addEventListener('message', handleIncomingMessage);
        // 検索入力は既にグローバル入力イベントで処理されるため、重複を避ける
        // searchEl.addEventListener('input', applyFilter);
        // トグル: 検索表示/非表示を切替、×はトグルへ戻す
        searchToggleEl.addEventListener('click', function(){ toggleSearchUI(true); });
        // 検索ラップ内の Enter/Escape を一元処理（IME変換中は無視）
        searchEl.addEventListener('keydown', function(e){
            if (isComposingEvent(e)) return;
            if (e.key === 'Escape') { toggleSearchUI(false); applyFilter(); return; }
            if (e.key === 'Enter') { e.preventDefault(); applyFilter(); return; }
        });
        // 入力欄の左右矢印キー境界制御（ドキュメント委譲、index.html と同じ）
        // すべてのテキスト入力系要素に適用（編集フォーム含む）
        document.addEventListener('keydown', guardArrowKeysOnEdges, true);
        
        // 三点メニュー（編集/削除カード）の外側クリック/ESCでのクローズ（ドキュメント委譲）
        (function bindGlobalMoreMenuDismiss(){
            try {
                if (document.__phraseMoreMenuDismissBound) return;
                document.__phraseMoreMenuDismissBound = true;
                function closeAllMoreMenus(){
                    try { listEl && listEl.querySelectorAll('.more-menu.open').forEach(function(mm){ mm.classList.remove('open'); }); } catch(_) {}
                }
                // 外側クリックでクローズ
                document.addEventListener('click', function(e){
                    try {
                        var t = e && e.target;
                        if (!t || !(t.closest instanceof Function)) { closeAllMoreMenus(); return; }
                        // メニュー内部や三点ボタン上のクリックは無視
                        if (t.closest('.more-menu') || t.closest('.more-btn') || t.closest('.template-item-actions')) return;
                        closeAllMoreMenus();
                    } catch(_) { closeAllMoreMenus(); }
                });
                // 右クリックメニュー表示時も、対象外ならクローズ
                document.addEventListener('contextmenu', function(e){
                    try {
                        var t = e && e.target;
                        if (t && t.closest && (t.closest('.more-menu') || t.closest('.template-item-actions'))) return;
                        closeAllMoreMenus();
                    } catch(_) { closeAllMoreMenus(); }
                });
                // Escape キーでクローズ（キャプチャで先に処理）
                document.addEventListener('keydown', function(e){
                    try { if (e && e.key === 'Escape') closeAllMoreMenus(); } catch(_) {}
                }, true);
            } catch(_) {}
        })();
        document.getElementById('closeBtn').addEventListener('click', function(){
            try {
                // 編集を破棄して閉じる通知
                fmNotifyCancel();
            } catch(_) {}
            try { window.close(); } catch(_) {}
        });

        // FileMaker 連携: ページ読込時にフレーズ取得スクリプトを要求（index.html と同様）
        // Web ビューアでは FileMaker オブジェクトの注入が遅れることがあるため、リトライで安定化
        (function requestPhrasesFromFileMaker(){
            var sent = false;
            function callOnce(){
                if (sent) return true;
                try {
                    // fmCall は準備前でもキューされるため、存在確認のみで投入
                    if (typeof fmCall === 'function') { try { fmRequestPhrases(); } catch(_) {} sent = true; return true; }
                } catch(_) {}
                return false;
            }
            // 即時試行
            if (callOnce()) return;
            // DOMContentLoaded / load でも試行
            try { document.addEventListener('DOMContentLoaded', callOnce, { once: true }); } catch(_) {}
            try { window.addEventListener('load', callOnce, { once: true }); } catch(_) {}
            // ポーリング（最大5秒、100ms間隔）
            var tries = 0; var maxTries = 50; var timer = setInterval(function(){
                tries += 1;
                if (callOnce() || tries >= maxTries) {
                    try { clearInterval(timer); } catch(_) {}
                }
            }, 100);
            // タブがアクティブになった際にも再試行
            try { document.addEventListener('visibilitychange', function(){ if (!sent && !document.hidden) callOnce(); }); } catch(_) {}
        })();

        /**
         * FileMaker からのコールバック関数
         * - Claris FileMaker の PerformScriptWithOption から呼び出す想定
         * - 受領形式は index.html と同じ分岐に対応
         * @param {string|Object} payload - フレーズデータ（JSON文字列またはオブジェクト）
         * @param {string} [recordId] - 関連するレコードID（任意）
         */
        window.showPhrasePicker = function(payload, recordId) {
            try {
                var extracted = PhraseParser.extract(payload);
                var resolvedRecordId = String(recordId != null ? recordId : extracted.recordId || '');
                try { window.__currentRecordIdForPhrase = resolvedRecordId; } catch(_) {}
                setPhrasesAndRender(extracted.phrases);
            } catch(e) {
                setPhrasesAndRender([]);
            }
        };
        try { if (window.App && typeof window.App.exportPublic === 'function') window.App.exportPublic('showPhrasePicker', window.showPhrasePicker); } catch(_) {}

        // [+] 新規作成
        newBtn.addEventListener('click', function(){ switchToEditMode('create'); });
        editCancelBtn.addEventListener('click', function(){
            try {
                var currentIndex = state.getFlag('editingIndex');
                var currentList = state.getPhrases();
                if (currentIndex != null && currentList[currentIndex]) {
                    state.setFlag('highlightId', String(currentList[currentIndex].id || ''));
                }
            } catch(_) { state.setFlag('highlightId', null); }
            restoreListUI();
        });
        editSaveBtn.addEventListener('click', function(){
            try {
                // タイトルは前後の空白を削除
                var titleVal = String((titleInput.value || '').trim());
                // テキスト本文は改行を保持するため、trim()を使わずに生の値を取得
                var textVal = String(ta.value || '');
                // 空文字チェック：タイトルはtrim後、テキストはtrim後の値が空でないかをチェック
                if (!titleVal || !textVal.trim()) return;
                var listRef = state.getPhrases();
                var currentIndex = state.getFlag('editingIndex');
                if (currentIndex != null && listRef[currentIndex]) {
                    listRef[currentIndex].title = titleVal;
                    listRef[currentIndex].text = textVal;
                    try { state.setFlag('highlightId', String(listRef[currentIndex].id || '')); } catch(_) { state.setFlag('highlightId', null); }
                    try {
                        var recIdUp = getPhraseRecordId && getPhraseRecordId();
                        var allUpPayload = buildPhrasePayloadFromItems && buildPhrasePayloadFromItems(recIdUp, listRef);
                        if (allUpPayload) { try { fmCall && fmCall('addon_remarks_web_phrase_save_all', allUpPayload, 5); } catch(_) {} }
                    } catch(_) {}
                } else {
                    var item = { id: String(Date.now()), title: titleVal, text: textVal };
                    state.setFlag('newlyCreatedId', item.id);
                    listRef.push(item);
                    try {
                        var recIdCr = getPhraseRecordId && getPhraseRecordId();
                        var allCrPayload = buildPhrasePayloadFromItems && buildPhrasePayloadFromItems(recIdCr, listRef);
                        if (allCrPayload) { try { fmCall && fmCall('addon_remarks_web_phrase_save_all', allCrPayload, 5); } catch(_) {} }
                    } catch(_) {}
                }
                filterAndRender(function(){
                    try { showToast(state.getFlag('editingIndex') != null ? '保存しました' : '追加しました'); } catch(_) {}
                });
                restoreListUI();
            } catch(_) {}
        });

        // ---- 簡易トースト/確認モーダル（index.html 互換API風） ----
        function showToast(message, options){
            try {
                var container = document.getElementById('toastContainer');
                if (!container) return;
                var div = document.createElement('div');
                div.className = 'toast-message';
                div.setAttribute('role', (options && options.role) || 'status');
                div.textContent = String(message || '');
                container.appendChild(div);
                requestAnimationFrame(function(){ div.classList.add('show'); });
                var duration = (options && options.duration) || 1800;
                setTimeout(function(){ try { div.classList.remove('show'); setTimeout(function(){ try { div.parentNode && div.parentNode.removeChild(div); } catch(_) {} }, 220); } catch(_) {} }, duration);
            } catch(_) {}
        }

        function showConfirm(message, onConfirm, onCancel, opts){
            try {
                var modal = document.getElementById('confirmModal');
                var msgEl = document.getElementById('confirmModalMessage');
                var okEl = document.getElementById('confirmModalConfirm');
                var cancelEl = document.getElementById('confirmModalCancel');
                if (!modal || !msgEl || !okEl || !cancelEl) { if (confirm(String(message||'よろしいですか？'))) { if (onConfirm) onConfirm(); } else { if (onCancel) onCancel(); } return; }
                msgEl.textContent = String(message || 'よろしいですか？');
                try {
                    okEl.textContent = (opts && opts.confirmLabel) ? String(opts.confirmLabel) : 'OK';
                    cancelEl.textContent = (opts && opts.cancelLabel) ? String(opts.cancelLabel) : 'キャンセル';
                    okEl.classList.remove('button-danger');
                    if (opts && opts.confirmClass === 'button-danger') okEl.classList.add('button-danger');
                } catch(_) {}
                var cleanup = function(){ try { modal.style.display = 'none'; modal.style.removeProperty('display'); } catch(_) {} };
                var handleConfirm = function(){ cleanup(); try { onConfirm && onConfirm(); } catch(_) {} };
                var handleCancel = function(){ cleanup(); try { onCancel && onCancel(); } catch(_) {} };
                // 一旦既存リスナを外すため、cloneで置換
                var okClone = okEl.cloneNode(true); okEl.parentNode.replaceChild(okClone, okEl); okEl = okClone;
                var cancelClone = cancelEl.cloneNode(true); cancelEl.parentNode.replaceChild(cancelClone, cancelEl); cancelEl = cancelClone;
                okEl.addEventListener('click', handleConfirm);
                cancelEl.addEventListener('click', handleCancel);
                modal.style.display = 'flex';
            } catch(_) {
                if (confirm(String(message||'よろしいですか？'))) { try { onConfirm && onConfirm(); } catch(__) {} } else { try { onCancel && onCancel(); } catch(__) {} }
            }
        }
    })();
    </script>
</body>
</html>


]]></Data>
							</StyledText>
						</Cell>
					</Row>
				</RowList>
			</Records>
			<Records>
				<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
				<RowList membercount="3">
					<Row membercount="11" id="1">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_template::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="2C375672-251E-4258-9804-3D243A89D9C3">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[D9E9D4AC-0AC5-4A2A-A4B6-D5B2CA168220]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_template::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="16301BD5-599F-461A-8DC9-E42FDBBC913E">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/13/2025 11:39:06 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_template::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="7B299D7A-B176-4FEE-9139-56445C3FDA70">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_template::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="A4A97B21-FE0B-4C2D-A2C3-6633B116C033">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:59:25 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_template::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="01C38F92-97FE-4344-8C7F-8ADA1052494C">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_template::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="6BDCA83E-EE01-4FF4-BE0B-378E1F5092F4">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_template::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="0F455DE2-C599-4B97-9F2C-900B3A923F4D">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_template::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="73739EC3-9576-40C3-8E0E-E66E6579C7EF">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_template::033877BC7B32E89448812C2F43235F97" repetition="1" UUID="BDFAA5C1-C4E6-45ED-BC32-968433DCB464">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[日付：{{日付}}
作成者：{{作成者}}
カテゴリ：{{カテゴリ:業務|アイデア|タスク|その他}}

――――――――――――――――――――
■ 内容
{{内容}}

■ 優先度
{{優先度:低|中|高}}

■ 状態
[[未着手]]
[[進行中]]
[[完了]]

■ 関連者
{{関連者}}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_template::2E66778AEEED872528333545F46EF9E7" repetition="1" UUID="0479A8D9-2CC3-4671-8752-45A803665F99">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[メモ1]]></Data>
								<CharacterStyleList membercount="1">
									<CharacterStyle Start="0" End="4294967295">
										<Text><![CDATA[メモ1]]></Text>
										<Style>
											<FontReference id="7" name="Hiragino Kaku Gothic ProN" postscriptName="HiraKakuProN-W3" kind="4" type="ShiftJIS"></FontReference>
											<FontStyle mask="1"></FontStyle>
										</Style>
									</CharacterStyle>
								</CharacterStyleList>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="16" name="com.fmi.basetable.field.Remarks_default_template::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="3CB2CEA1-4E7A-4347-87D0-42C90BEE52CC">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="11" id="2">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_template::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="2C375672-251E-4258-9804-3D243A89D9C3">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[E6DC4AF9-98DC-46C0-8343-24956B2D4914]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_template::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="16301BD5-599F-461A-8DC9-E42FDBBC913E">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/14/2025 04:14:23 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_template::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="7B299D7A-B176-4FEE-9139-56445C3FDA70">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_template::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="A4A97B21-FE0B-4C2D-A2C3-6633B116C033">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:59:25 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_template::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="01C38F92-97FE-4344-8C7F-8ADA1052494C">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_template::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="6BDCA83E-EE01-4FF4-BE0B-378E1F5092F4">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_template::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="0F455DE2-C599-4B97-9F2C-900B3A923F4D">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_template::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="73739EC3-9576-40C3-8E0E-E66E6579C7EF">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[2]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_template::033877BC7B32E89448812C2F43235F97" repetition="1" UUID="BDFAA5C1-C4E6-45ED-BC32-968433DCB464">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[【個人メモ】

日付：{{日付}}
タイトル：{{タイトル}}

――――――――――――――――――――
■ メモ内容
{{内容}}

■ 気分
{{気分:😀|😐|😓}}

■ 優先度
{{優先度:低|中|高}}

■ タスクチェック
[[やること1]] {{やること1内容}}
[[やること2]] {{やること2内容}}

■ 次の一歩
{{次の一歩}}

――――――――――――――――――――
]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_template::2E66778AEEED872528333545F46EF9E7" repetition="1" UUID="0479A8D9-2CC3-4671-8752-45A803665F99">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[メモ2]]></Data>
								<CharacterStyleList membercount="1">
									<CharacterStyle Start="0" End="4294967295">
										<Text><![CDATA[メモ2]]></Text>
										<Style>
											<FontReference id="7" name="Hiragino Kaku Gothic ProN" postscriptName="HiraKakuProN-W3" kind="4" type="ShiftJIS"></FontReference>
											<FontStyle mask="1"></FontStyle>
										</Style>
									</CharacterStyle>
								</CharacterStyleList>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="16" name="com.fmi.basetable.field.Remarks_default_template::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="3CB2CEA1-4E7A-4347-87D0-42C90BEE52CC">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[2]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="11" id="3">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_template::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="2C375672-251E-4258-9804-3D243A89D9C3">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[3823ECA2-D0D4-411F-91EC-A9E043F2F86C]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_template::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="16301BD5-599F-461A-8DC9-E42FDBBC913E">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/14/2025 04:51:34 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_template::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="7B299D7A-B176-4FEE-9139-56445C3FDA70">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_template::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="A4A97B21-FE0B-4C2D-A2C3-6633B116C033">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:59:25 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_template::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="01C38F92-97FE-4344-8C7F-8ADA1052494C">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_template::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="6BDCA83E-EE01-4FF4-BE0B-378E1F5092F4">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_template::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="0F455DE2-C599-4B97-9F2C-900B3A923F4D">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_template::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="73739EC3-9576-40C3-8E0E-E66E6579C7EF">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[3]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_template::033877BC7B32E89448812C2F43235F97" repetition="1" UUID="BDFAA5C1-C4E6-45ED-BC32-968433DCB464">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[【顧客対応メモ】

日付：{{日付}}
対応者：{{対応者}}

――――――――――――――――――――
本日、{{顧客}} 様よりお問い合わせをいただきました。  
{{顧客}} 様のご要望は以下の通りです。  

■ ご要望
{{要望内容}}

■ 状況
{{顧客}} 様は現在、{{状況:検討中|導入済み|保留}} の状態です。  
次回のご連絡は {{次回予定日}} に {{顧客}} 様へ行う予定です。  

■ 備考
{{顧客}} 様の担当者は {{担当者}} です。  
重要度：{{重要度:低|中|高}}

――――――――――――――――――――
]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_template::2E66778AEEED872528333545F46EF9E7" repetition="1" UUID="0479A8D9-2CC3-4671-8752-45A803665F99">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[メモ3]]></Data>
								<CharacterStyleList membercount="1">
									<CharacterStyle Start="0" End="4294967295">
										<Text><![CDATA[メモ3]]></Text>
										<Style>
											<FontReference id="7" name="Hiragino Kaku Gothic ProN" postscriptName="HiraKakuProN-W3" kind="4" type="ShiftJIS"></FontReference>
											<FontStyle mask="1"></FontStyle>
										</Style>
									</CharacterStyle>
								</CharacterStyleList>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="16" name="com.fmi.basetable.field.Remarks_default_template::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="3CB2CEA1-4E7A-4347-87D0-42C90BEE52CC">
								<BaseTableReference id="144" name="com.fmi.basetable.0B8C7CC8737A7685DE0FD67A7427A76A" UUID="5BCD2E8D-BF91-4E3B-A969-CCF3BAA9FEB1"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[3]]></Data>
							</StyledText>
						</Cell>
					</Row>
				</RowList>
			</Records>
			<Records>
				<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
				<RowList membercount="12">
					<Row membercount="12" id="1">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[9DC173D4-23F9-44D7-9F4E-5F7774081699]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 01:02:03 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[お世話になっております。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[挨拶]]></Data>
								<CharacterStyleList membercount="1">
									<CharacterStyle Start="0" End="4294967295">
										<Text><![CDATA[挨拶]]></Text>
										<Style>
											<FontReference id="7" name="Hiragino Kaku Gothic ProN" postscriptName="HiraKakuProN-W3" kind="4" type="ShiftJIS"></FontReference>
											<FontStyle mask="1"></FontStyle>
										</Style>
									</CharacterStyle>
								</CharacterStyleList>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"お世話になっております。","title":"挨拶"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="2">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[BDC8F92F-5BEE-41BB-B7DF-6D36E33F8333]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:22:08 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[2]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[恐れ入りますが、ご対応をお願いいたします。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[依頼]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"恐れ入りますが、ご対応をお願いいたします。","title":"依頼"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[2]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="3">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[9B6B703F-843E-446D-8064-8ADF53421B20]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:22:21 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[3]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[念のためご確認いただけますでしょうか。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[確認]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"念のためご確認いただけますでしょうか。","title":"確認"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[3]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="4">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[07E78FD0-6515-4774-BBFB-3C59F4363EB1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:22:32 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[4]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[迅速なご対応ありがとうございます。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[感謝]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"迅速なご対応ありがとうございます。","title":"感謝"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[4]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="5">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[4E417763-1B81-473B-96E3-CFC60057D994]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:22:45 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[5]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[ご迷惑をおかけし申し訳ございません。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[お詫び]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"ご迷惑をおかけし申し訳ございません。","title":"お詫び"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[5]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="6">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[95A35706-CABC-4839-B373-064C21F78F14]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:22:57 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[6]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[詳細は添付の資料をご参照ください。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[案内]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"詳細は添付の資料をご参照ください。","title":"案内"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[6]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="7">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0E252033-1F16-43CF-AC97-F3EA0150E05E]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:24:51 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[7]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[ちょっと相談したいことがあるのですが、今お時間大丈夫でしょうか？]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[相談（軽め）]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"ちょっと相談したいことがあるのですが、今お時間大丈夫でしょうか？","title":"相談（軽め）"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[7]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="8">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[D4C5007A-B717-4ED7-9265-9B73148612BD]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:25:06 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[8]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[こちらの内容で進めても問題ないか、ご確認いただけますか？]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[確認依頼]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"こちらの内容で進めても問題ないか、ご確認いただけますか？","title":"確認依頼"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[8]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="9">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[7764E926-34E0-4C7F-94CF-6C95AF0F04D8]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:25:17 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[9]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[さっそくご対応いただきありがとうございます！とても助かりました。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[感謝（サポート後）]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"さっそくご対応いただきありがとうございます！とても助かりました。","title":"感謝（サポート後）"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[9]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="10">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[4A25FE2C-E56B-44E8-9A31-2EEC685A7539]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:25:32 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[10]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[すみません、対応が少し遅れてしまいました。すぐに取り掛かります。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[謝罪（遅れ）]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"すみません、対応が少し遅れてしまいました。すぐに取り掛かります。","title":"謝罪（遅れ）"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[10]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="11">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[EC329867-566D-4152-A127-B34A296BA084]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:25:44 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[11]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[恐縮ですが、この部分を少しお手伝いいただけると助かります。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[依頼]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"恐縮ですが、この部分を少しお手伝いいただけると助かります。","title":"依頼"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[11]]></Data>
							</StyledText>
						</Cell>
					</Row>
					<Row membercount="12" id="12">
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="6" name="com.fmi.basetable.field.Remarks_default_phrase::ADF8AF2CA222D67D4EB53071483F55AF" repetition="1" UUID="FF6838A8-5F13-4311-AC2F-B12D350605A4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[4DCD5E12-C1BF-4038-B457-1ED8EFFC5901]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="7" name="com.fmi.basetable.field.Remarks_default_phrase::18E021218E41B88FF9C3CF47B35EF40F" repetition="1" UUID="0A8CC510-9360-42B5-9ED5-DFDA7B83B3B4">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/15/2025 06:26:03 AM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="8" name="com.fmi.basetable.field.Remarks_default_phrase::A79537EA3FAFF4A4E9D1198926E639BD" repetition="1" UUID="2FFF3D76-86DC-4FBC-9B03-3AD0D4ECFD27">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Timestamp" id="9" name="com.fmi.basetable.field.Remarks_default_phrase::3A93644F4F51283D086937D2A24D3FEA" repetition="1" UUID="35382A3D-F17A-4111-903D-13BB62C58D53">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[09/21/2025 01:58:50 PM]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="10" name="com.fmi.basetable.field.Remarks_default_phrase::28FD1A7D87D0407F117F6E08E0A5E346" repetition="1" UUID="18125FE4-6B7C-4C5B-8774-203704EBB8C6">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[Admin]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="11" name="com.fmi.basetable.field.Remarks_default_phrase::4C29FA42A76EED1F15FB5343764B4D24" repetition="1" UUID="88582050-70B0-4B97-AA7C-1EBC3E6DE96F">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[1]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="12" name="com.fmi.basetable.field.Remarks_default_phrase::78F61847BA8FCE8D8484599D64C89E11" repetition="1" UUID="75AB73AC-13B2-465B-B3FC-54E98F88F978">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[0]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="13" name="com.fmi.basetable.field.Remarks_default_phrase::B416E01B8B652EF6F1543564F160CD4C" repetition="1" UUID="0B2FFBD5-075F-44A1-9E58-EB3B837CEB8A">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[12]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="14" name="com.fmi.basetable.field.Remarks_default_phrase::C2E73BEE4D742D3657C1537A147DDAF6" repetition="1" UUID="9A104639-6178-4B5B-8702-1356CDE782D1">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[今週もお疲れさまでした！良い週末をお過ごしください。]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Text" id="15" name="com.fmi.basetable.field.Remarks_default_phrase::26A47C6B7672F044F4426397BA1DB3F0" repetition="1" UUID="EAEB6C9B-D698-46E1-B7C3-7154964AE1BE">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[雑談・労い]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Calculated" datatype="Text" id="16" name="com.fmi.basetable.field.Remarks_default_phrase::94721AAE678BF3FAD0C7582D2E6F567B" repetition="1" UUID="DCF27474-DD5F-4623-A446-C9529BF5D819">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[{"phrase":"今週もお疲れさまでした！良い週末をお過ごしください。","title":"雑談・労い"}]]></Data>
							</StyledText>
						</Cell>
						<Cell>
							<FieldReference type="Normal" datatype="Number" id="18" name="com.fmi.basetable.field.Remarks_default_phrase::EB377C6E167892349C86D64B7F965ACA" repetition="1" UUID="F56FF1A7-4779-4D43-BFA8-6732625ABF25">
								<BaseTableReference id="147" name="com.fmi.basetable.894A047D00BBA7C2E8E7F316C9E6CECE" UUID="5FFA7F80-429C-4E2F-84EE-3E3CF5C2E699"></BaseTableReference>
							</FieldReference>
							<StyledText>
								<Data><![CDATA[12]]></Data>
							</StyledText>
						</Cell>
					</Row>
				</RowList>
			</Records>
		</AddAction>
	</Data>
</FMAdd_on>
