<?php
declare(strict_types=1);

require_once __DIR__ . '/_auth.php';
tpcms_require_admin();

/* ---------- ROWS/単項目の旧ファイルを物理削除（保存完了後に実行） ---------- */
// JS から rows_deleted_files[] が POST されていれば /uploads 配下の該当 basename を削除。
// 外部URL(http/https)や /uploads/ 以外、パストラバーサルは安全側で無視します。
if ($_SERVER['REQUEST_METHOD'] === 'POST'
    && !empty($_POST['rows_deleted_files'])
    && is_array($_POST['rows_deleted_files'])) {

  $__rows_deleted_files = array_values(array_filter(array_map(function($v){ return (string)$v; }, $_POST['rows_deleted_files'])));

  // 保存処理が完了した後で確実に走るよう shutdown に登録
  register_shutdown_function(function() use ($__rows_deleted_files) {
    $uploadsDir = realpath(__DIR__ . '/../uploads');
    if ($uploadsDir === false) return;

    $seen = [];
    foreach ($__rows_deleted_files as $raw) {
      $v = trim($raw);
      if ($v === '' || preg_match('~^https?://~i', $v)) continue; // 外部URLは対象外

      if (strpos($v, '/uploads/') === 0) {
        $v = substr($v, 9); // '/uploads/' を除去
      }
      $bn = basename(str_replace('\\','/', $v));
      if ($bn === '' || isset($seen[$bn])) continue;
      $seen[$bn] = true;

      $path = $uploadsDir . DIRECTORY_SEPARATOR . $bn;
      $real = realpath($path);
      if ($real && strpos($real, $uploadsDir) === 0 && is_file($real)) {
        @unlink($real);
      }
    }
  });
}

require_once __DIR__ . '/../config.php';

// フォールバック（存在する場合は既存実装を優先）
if (!function_exists('tpcms_h')) {
    function tpcms_h(?string $s): string { return htmlspecialchars($s ?? '', ENT_QUOTES, 'UTF-8'); }
}
if (!function_exists('tpcms_json_read')) {
    function tpcms_json_read(string $path, $default = []) {
        if (is_file($path)) {
            $json = file_get_contents($path);
            $data = json_decode((string)$json, true);
            if (is_array($data) || is_object($data)) return $data;
        }
        return $default;
    }
}
if (!function_exists('tpcms_json_write')) {
    function tpcms_json_write(string $path, $data): bool {
        $dir = dirname($path);
        if (!is_dir($dir) && !@mkdir($dir, 0777, true)) {
            return false;
        }

        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
        if ($json === false) {
            return false; // JSON化失敗（不正なUTF-8など）
        }

        $tmp = $path . '.tmp';
        $bytes = @file_put_contents($tmp, $json, LOCK_EX);
        if ($bytes === false || $bytes < strlen($json)) {
            @unlink($tmp);
            return false; // 部分書き込み/0バイト書き込みを失敗扱いに
        }

        $ok = @rename($tmp, $path); // アトミック置換
        if (!$ok) {
            @unlink($tmp);
            return false;
        }

        @chmod($path, 0664);
        clearstatcache(true, $path);
        return true;
    }
}

// ------------------------------------------------------------
// アクティブテーマとフォーム定義の取得
// ------------------------------------------------------------
$active = tpcms_json_read(__DIR__ . '/../themes/_active.json', ['theme' => 'beginner9', 'color' => 'white']); // 例：{"theme":"beginner9","color":"white"}
$theme  = (string)($active['theme'] ?? 'beginner9');
$color  = (string)($active['color'] ?? 'white');
$themeKey = $theme;

$formdefPath = __DIR__ . '/../themes/' . $theme . '/' . $color . '/theme.formdef.json';
$formdef = tpcms_json_read($formdefPath, [
    'site_fields'   => [],
    'site_sections' => []
]);

$siteFields = is_array($formdef['site_fields'] ?? null) ? $formdef['site_fields'] : [];

// 現在値（共通）：/data/site.json
$siteCommonPath = __DIR__ . '/../data/site.json';
$siteCommon = tpcms_json_read($siteCommonPath, []);

// テーマ専用（site_sections）の現在値：/data/site.<themeKey>.json
$siteThemePath = __DIR__ . '/../data/site.' . $themeKey . '.json';
$siteTheme = tpcms_json_read($siteThemePath, []);
$siteSections = is_array($formdef['site_sections'] ?? null) ? $formdef['site_sections'] : [];

// ------------------------------------------------------------
// POST 保存（共通設定のみ）
// ------------------------------------------------------------
$saved = false;
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
tpcms_require_post_csrf();
    $posted = $_POST['site'] ?? [];
    $postedTheme = $_POST['theme'] ?? [];
	
    // 削除フラグ（共通／テーマ専用）
    $delSite  = $_POST['del_site']  ?? [];
    $delTheme = $_POST['del_theme'] ?? [];

    // uploads 実体削除用ヘルパ
    $uploadsDir = realpath(__DIR__ . '/../uploads') ?: (__DIR__ . '/../uploads');
    $tryDelete = function ($name) use ($uploadsDir) {
        if (!is_string($name) || $name === '') return;
        if (preg_match('~^https?://~i', $name)) return; // 外部URLは削除しない
        $target = $uploadsDir . '/' . basename($name);  // 保存は basename 想定
        if (is_file($target)) { @unlink($target); }
    };

    if (!is_array($posted)) { $posted = []; }

    // 既存値をベースに上書き（未知キーは温存）
    $updated = $siteCommon;
    $updatedTheme = $siteTheme;

    foreach ($siteFields as $__def) {
        $k = (string)($__def['key'] ?? '');
        if ($k === '') continue;

        $type = (string)($__def['type'] ?? 'text');
        $req  = !empty($__def['required']);
        $maxl = isset($__def['maxlength']) ? (int)$__def['maxlength'] : null;

        // 削除指示がある場合は旧値を削除して空に
        $old  = is_scalar($siteCommon[$k] ?? null) ? (string)$siteCommon[$k] : '';
        if ($type === 'file' && isset($posted[$k.'__delete']) && (string)$posted[$k.'__delete'] === '1') {
            // admin.js が生成する __delete チェック対応（del_site より優先）
            $tryDelete($old);
            $val = '';
        } elseif (isset($delSite[$k])) {
            if ($type === 'file') { $tryDelete($old); }
            $val = '';
        } else {
            if ($type === 'checkbox') {
                $val = isset($posted[$k]) ? '1' : '';
            } else {
                $raw = $posted[$k] ?? '';
                $val = is_scalar($raw) ? (string)$raw : '';
                $val = trim($val);
            }
        }

        // maxlength
        if ($maxl !== null && $maxl > 0) {
            if (function_exists('mb_substr')) {
                $val = mb_substr($val, 0, $maxl, 'UTF-8');
            } else {
                $val = substr($val, 0, $maxl);
            }
        }

        // required
        if ($req && $val === '') {
            $errors[] = "「{$k}」は必須です。";
        }

        // 型ごとの最小整形（現時点は text/textarea/file のみ想定）
        if ($type === 'file') {
            // 画像/動画/URL いずれも文字列として保存（実ファイルは別の統一アップロードUIで扱う方針）
            // 例：basename（uploads内）または https://... のURL を保存
            // 追加の検証は後続STEPで実装可
        } else {
            // text/textarea：特別な処理はなし
        }

        $updated[$k] = $val;
    }

// --- site_sections → $updatedTheme を構築（保存前に実行） ---
if (!is_array($postedTheme)) { $postedTheme = []; }

foreach ($siteSections as $__sec) {
    $secKey = (string)($__sec['key'] ?? '');
    if ($secKey === '') continue;
    $themeSecKey = 'THEME_' . $secKey;

    $secPosted = $postedTheme[$themeSecKey] ?? [];
    if (!is_array($secPosted)) $secPosted = [];

    $fields = is_array($__sec['fields'] ?? null) ? $__sec['fields'] : [];
    foreach ($fields as $__def) {
        $k    = (string)($__def['key'] ?? '');
        if ($k === '') continue;

        $type = (string)($__def['type'] ?? 'text');
        $req  = !empty($__def['required']);
        $maxl = isset($__def['maxlength']) ? (int)$__def['maxlength'] : null;

        // 削除指示がある場合は旧値を削除して空に
        $oldTheme = '';
        if (isset($siteTheme[$themeSecKey]) && is_array($siteTheme[$themeSecKey])) {
            $oldTheme = is_scalar($siteTheme[$themeSecKey][$k] ?? null) ? (string)$siteTheme[$themeSecKey][$k] : '';
        }
        if ($type === 'file' && isset($secPosted[$k.'__delete']) && (string)$secPosted[$k.'__delete'] === '1') {
            // admin.js の __delete チェック対応（del_theme より優先）
            $tryDelete($oldTheme);
            $val = '';
        } elseif (isset($delTheme[$themeSecKey]) && isset($delTheme[$themeSecKey][$k])) {
            if ($type === 'file') { $tryDelete($oldTheme); }
            $val = '';
        } else {
            if ($type === 'table') {
                // 新UI対応：theme[THEME_...][<key>][thead|cols|rows] を受け付ける（互換で旧形式もOK）
                $raw = $secPosted[$k] ?? [];
                $val = [];

                if (is_array($raw)) {
                    // 新形式（thead/cols/rows のいずれかがある）
                    if (isset($raw['rows']) || isset($raw['cols']) || isset($raw['thead'])) {
                        // rows
                        $rows = [];
                        if (isset($raw['rows']) && is_array($raw['rows'])) {
                            foreach ($raw['rows'] as $_r) {
                                if (!is_array($_r)) continue;
                                $clean = [];
                                foreach ($_r as $_ck => $_cv) {
                                    $clean[(string)$_ck] = is_scalar($_cv) ? trim((string)$_cv) : '';
                                }
                                $rows[] = $clean;
                            }
                        }
                        // cols
                        $cols = [];
                        if (isset($raw['cols']) && is_array($raw['cols'])) {
                            foreach ($raw['cols'] as $_c) {
                                $cols[] = [
                                    'key'   => (string)($_c['key'] ?? ''),
                                    'label' => (string)($_c['label'] ?? ''),
                                ];
                            }
                        }
                        // thead
                        $thead = [];
                        if (isset($raw['thead']) && is_array($raw['thead'])) {
                            $thead['top']  = !empty($raw['thead']['top']);
                            $thead['left'] = !empty($raw['thead']['left']);
                        }
                        $val = ['cols' => $cols, 'thead' => $thead, 'rows' => $rows];

                    } else {
                        // 互換：旧形式（[ [col=>val,...], ... ]）
                        foreach ($raw as $__row) {
                            if (!is_array($__row)) continue;
                            $clean = [];
                            foreach ($__row as $__ck => $__cv) {
                                $clean[(string)$__ck] = is_scalar($__cv) ? trim((string)$__cv) : '';
                            }
                            $val[] = $clean;
                        }
                    }
                }
            } else {
                if ($type === 'checkbox') {
                    $val = isset($secPosted[$k]) ? '1' : '';
                } else {
                    $raw = $secPosted[$k] ?? '';
                    $val = is_scalar($raw) ? (string)$raw : '';
                    $val = trim($val);
                }
            }
        }

        if ($maxl !== null && $maxl > 0) {
            if (function_exists('mb_substr')) {
                $val = mb_substr($val, 0, $maxl, 'UTF-8');
            } else {
                $val = substr($val, 0, $maxl);
            }
        }
        if ($req && $val === '') {
            $errors[] = "「{$secKey}.{$k}」は必須です。";
        }

        if (!isset($updatedTheme[$themeSecKey]) || !is_array($updatedTheme[$themeSecKey])) {
            $updatedTheme[$themeSecKey] = [];
        }
        $updatedTheme[$themeSecKey][$k] = $val;
    }
}

    if (empty($errors)) {
        $ok1 = tpcms_json_write($siteCommonPath, $updated);
        $ok2 = tpcms_json_write($siteThemePath,  $updatedTheme);
        if ($ok1 && $ok2) {
            $saved = true;
            $siteCommon = $updated;
            $siteTheme  = $updatedTheme; // 再表示に反映
        } else {
            $errors[] = '保存に失敗しました。書き込み権限を確認してください。';
        }
    }
}

// ------------------------------------------------------------
// HTML 出力
// ------------------------------------------------------------
?><!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>サイト設定（共通）｜Template Party CMS</title>
  <link rel="stylesheet" href="./assets/admin.css">
</head>
<body class="admin site">

<header>
<div>
<a href="./">ダッシュボード</a>
<a href="../" target="_blank" rel="noopener">公開側トップ</a>
</div>
<div>
<a href="./logout.php">ログアウト</a>
</div>
</header>

  <div id="container">
    <h1>サイト基本設定</h1>

    <?php if (!empty($errors)): ?>
      <div class="err">
        <strong>エラー：</strong>
        <ul style="margin:.4rem 0 .2rem .9rem;">
          <?php foreach ($errors as $e): ?>
            <li><?= tpcms_h($e) ?></li>
          <?php endforeach; ?>
        </ul>
      </div>
    <?php elseif ($saved): ?>
      <div class="notice">保存しました。</div>
    <?php endif; ?>

    <div class="card">
      <form method="post" action="./site.php" id="siteForm" data-form="site">
      <input type="hidden" name="_csrf" value="<?= tpcms_h(tpcms_csrf_token()) ?>">

        <!-- 共通設定（site_fields） -->
        <h2>共通設定</h2>

        <section class="sec collapsed" data-sec="SITE_COMMON" id="sec_SITE_COMMON">
          <button type="button" class="sec-toggle" data-sec-toggle="SITE_COMMON">
            <i class="fa-solid fa-circle-chevron-down icon icon-down" aria-hidden="true"></i>
            <i class="fa-solid fa-circle-chevron-up icon icon-up" aria-hidden="true"></i>
            <span>共通設定の編集</span>
          </button>
          <div class="sec-body tpcms-edit-pane">

<?php
// site_fields を順に描画（text / textarea / file を中心に最小表示）
foreach ($siteFields as $__def) {
    $type  = (string)($__def['type'] ?? 'text');
    $label = (string)($__def['label'] ?? '');
    $key   = (string)($__def['key'] ?? '');

    // legend: 見出しのみ（入力なし・保存対象外）※ key 不要なので key チェックより先に判定
    if ($type === 'legend') {
        echo '        <div class="row">'.PHP_EOL;
        echo '          <div class="legend-title">'
            . tpcms_h($label !== '' ? $label : '設定') . '</div>'.PHP_EOL;
        echo '        </div>'.PHP_EOL;
        continue;
    }

    // legend 以外は key 必須
    if ($key === '') continue;

    // ラベル未指定なら key を既定に
    if ($label === '') $label = $key;
    
    // legend: 見出しのみ（入力なし・保存対象外）
    if ($type === 'legend') {
        echo '        <div class="row">'.PHP_EOL;
        echo '          <div class="legend-title"">'.tpcms_h($label).'</div>'.PHP_EOL;
        echo '        </div>'.PHP_EOL;
        continue;
    }

    $req   = !empty($__def['required']);
    $ph    = (string)($__def['placeholder'] ?? '');
    $max   = isset($__def['maxlength']) ? 'maxlength="'.(int)$__def['maxlength'].'"' : '';
    $val   = $siteCommon[$key] ?? '';
    $__imw = (int)($__def['image_max_width']  ?? 0);
    $__imh = (int)($__def['image_max_height'] ?? 0);
    $__iq  = (int)($__def['image_quality']    ?? 0);
    $__attrResize = '';
    if ($__imw > 0) $__attrResize .= ' data-image-max-width="'.$__imw.'"';
    if ($__imh > 0) $__attrResize .= ' data-image-max-height="'.$__imh.'"';
    if ($__iq  > 0) { $__iq = max(1, min(100, $__iq)); $__attrResize .= ' data-image-quality="'.$__iq.'"'; }

    echo '        <div class="row"'.$__attrResize.'>'.PHP_EOL;
    if ($type === 'checkbox') {
        echo '          <label class="label" style="display:none"></label>'.PHP_EOL;
    } else {
        echo '          <label class="label">'.tpcms_h($label).($req ? '（必須）' : '').'</label>'.PHP_EOL;
    }

    if ($type === 'textarea') {
        echo '          <textarea name="site['.tpcms_h($key).']" rows="3" '.$max.' placeholder="'.tpcms_h($ph).'"'.
             '>'.tpcms_h(is_scalar($val)? (string)$val : '').'</textarea>'.PHP_EOL;

} elseif ($type === 'select') {
    // select（optionsは [["value","label"], ...] 形式を推奨）
    $opts        = (isset($__def['options']) && is_array($__def['options'])) ? $__def['options'] : [];
    $allowEmpty  = isset($__def['allow_empty']) ? (bool)$__def['allow_empty'] : true;
    $valStr      = is_scalar($val) ? (string)$val : '';

    echo '          <select name="site['.tpcms_h($key).']" class="input"'.($req ? ' required' : '').'>'.PHP_EOL;
    if ($allowEmpty) {
        echo '            <option value="">'.tpcms_h($ph !== '' ? $ph : '未選択').'</option>'.PHP_EOL;
    }
    foreach ($opts as $op) {
        $ov = (string)($op[0] ?? '');
        $ot = (string)($op[1] ?? $ov);
        $sel = ($valStr === $ov) ? ' selected' : '';
        echo '            <option value="'.tpcms_h($ov).'"'.$sel.'>'.tpcms_h($ot).'</option>'.PHP_EOL;
    }
    echo '          </select>'.PHP_EOL;

} elseif ($type === 'color') {
    // 値の正規化（colorは#RRGGBB固定、空は空のままでもOK）
    $valStr = is_scalar($val) ? (string)$val : '';
    if ($valStr !== '' && !preg_match('/^#[0-9A-Fa-f]{6}$/', $valStr)) {
        $valStr = '#000000';
    }
    // default（任意）… theme.formdef.json に "default":"#EEECE7" のように明示
    $defRaw = isset($__def['default']) ? (string)$__def['default'] : '';
    $defStr = (preg_match('/^#[0-9A-Fa-f]{6}$/', $defRaw) ? strtoupper($defRaw) : '');

    echo '          <div class="color-field">'.PHP_EOL;
    echo '            <input type="color" name="site['.tpcms_h($key).']" value="'.tpcms_h($valStr).'" data-color-input>'.PHP_EOL;
    echo '            <input type="text" value="'.tpcms_h($valStr).'" class="input" style="width:8rem;margin-left:.6rem" data-color-hex>'.PHP_EOL;
    if ($defStr !== '') {
        echo '            <a href="#" class="small" style="margin-left:.6rem" data-color-reset data-default="'.tpcms_h($defStr).'">デフォルトに戻す</a>'.PHP_EOL;
    }
    echo '          </div>'.PHP_EOL;

} elseif ($type === 'file') {
    if ($ph === '') { $ph = 'ファイル名'; }
    // テキスト欄のみ（ドロップゾーン／サムネはJSに任せる）
    $safe = is_scalar($val) ? (string)$val : '';
    $uid  = 'up_site_' . preg_replace('/[^a-z0-9_]+/i', '_', (string)$key);

    echo '          <input id="'.tpcms_h($uid).'" type="text" name="site['.tpcms_h($key).']" value="'.tpcms_h($safe).'" class="input js-upload-url as-plain-text" placeholder="'.tpcms_h($ph).'" data-file="1" readonly aria-readonly="true" tabindex="-1">'.PHP_EOL;

    // （ヒント文を削除）


    } elseif ($type === 'checkbox') {
        $checked = !empty($val) && $val !== '0';
        echo '          <label class="inline"><input type="checkbox" name="site['.tpcms_h($key).']" value="1'.($checked ? '" checked' : '"').'> '.tpcms_h($label).($req ? '（必須）' : '').'</label>'.PHP_EOL;

    } else { // text / その他は text 扱い（最小）

        echo '          <input type="text" name="site['.tpcms_h($key).']" value="'.tpcms_h(is_scalar($val)? (string)$val : '').'" '.
                     $max.' placeholder="'.tpcms_h($ph).'"'.(!empty($__def['link_picker']) ? ' data-link-picker="1"' : '').'>'.PHP_EOL;
    }

    if (!empty($__def['help_html'])) {
        $help = (string)$__def['help_html'];
        $help = strip_tags($help, '<a><strong><em><code><ul><ol><li>');
        echo '          <div class="help">'.$help.'</div>'.PHP_EOL;
    }

    echo '        </div>'.PHP_EOL;
}

?>

          </div><!-- /.sec-body -->
        </section>

        <?php if (!empty($siteSections)): ?>
        <!-- テーマ専用設定（site_sections） -->
        <h2>テーマ専用設定</h2>

<?php
foreach ($siteSections as $__sec) {
    $secKey   = (string)($__sec['key'] ?? '');
    if ($secKey === '') continue;
    $secLabel = (string)($__sec['label'] ?? $secKey);
    $secHelp  = (string)($__sec['help_html'] ?? '');
    $fields   = is_array($__sec['fields'] ?? null) ? $__sec['fields'] : [];

    // 保存規則に合わせたキー名
    $themeSecKey = 'THEME_' . $secKey;
    $cur         = is_array($siteTheme[$themeSecKey] ?? null) ? $siteTheme[$themeSecKey] : [];
    $secId       = 'sec_' . preg_replace('/[^a-z0-9_]+/i', '_', $themeSecKey);
?>
        <section class="sec collapsed" data-sec="<?= tpcms_h($themeSecKey) ?>" id="<?= tpcms_h($secId) ?>">
          <button type="button" class="sec-toggle" data-sec-toggle="<?= tpcms_h($themeSecKey) ?>">
            <i class="fa-solid fa-circle-chevron-down icon icon-down" aria-hidden="true"></i>
            <i class="fa-solid fa-circle-chevron-up icon icon-up" aria-hidden="true"></i>
            <span><?= tpcms_h($secLabel) ?>の編集</span>
          </button>

<?php if ($secHelp !== ''): 
      $help = strip_tags($secHelp, '<a><strong><em><code><ul><ol><li>');
?>
          <div class="help" style="margin:.4rem .8rem 0;"><?= $help ?></div>
<?php endif; ?>

          <div class="sec-body tpcms-edit-pane">
<?php
    // 各フィールドを簡易描画（text / textarea / file の最小対応）
    foreach ($fields as $__def) {
        $type = (string)($__def['type'] ?? 'text');
        $lab  = (string)($__def['label'] ?? '');
        $k    = (string)($__def['key']  ?? '');

        // legend: 見出しのみ（入力なし・保存対象外）※ key 不要なので先に判定
        if ($type === 'legend') {
            echo '            <div class="row">'.PHP_EOL;
            echo '              <div class="legend-title">'
                . tpcms_h($lab !== '' ? $lab : '設定') . '</div>'.PHP_EOL;
            echo '            </div>'.PHP_EOL;
            continue;
        }

        // legend 以外は key 必須
        if ($k === '') continue;

        $req  = !empty($__def['required']);
        $ph   = (string)($__def['placeholder'] ?? '');
        $max  = isset($__def['maxlength']) ? 'maxlength="'.(int)$__def['maxlength'].'"' : '';
        $val  = $cur[$k] ?? '';
        $__imw = (int)($__def['image_max_width']  ?? 0);
        $__imh = (int)($__def['image_max_height'] ?? 0);
        $__iq  = (int)($__def['image_quality']    ?? 0);
        $__attrResize = '';
        if ($__imw > 0) $__attrResize .= ' data-image-max-width="'.$__imw.'"';
        if ($__imh > 0) $__attrResize .= ' data-image-max-height="'.$__imh.'"';
        if ($__iq  > 0) { $__iq = max(1, min(100, $__iq)); $__attrResize .= ' data-image-quality="'.$__iq.'"'; }

        echo '            <div class="row"'.$__attrResize.'>'.PHP_EOL;
        if ($type === 'checkbox') {
            echo '              <label class="label" style="display:none"></label>'.PHP_EOL;
        } else {
            echo '              <label class="label">'.tpcms_h($lab !== '' ? $lab : $k).($req ? '（必須）' : '').'</label>'.PHP_EOL;
        }

        if ($type === 'textarea') {
            echo '              <textarea name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" rows="3" '.$max.' placeholder="'.tpcms_h($ph).'">'.
                 tpcms_h(is_scalar($val)? (string)$val : '').'</textarea>'.PHP_EOL;

        } elseif ($type === 'color') {
            // 値の正規化（colorは#RRGGBB固定、空は空のままでもOK）
            $valStr = is_scalar($val) ? (string)$val : '';
            if ($valStr !== '' && !preg_match('/^#[0-9A-Fa-f]{6}$/', $valStr)) {
                $valStr = '#000000';
            }
            // default（任意）… theme.formdef.json に "default":"#EEECE7" のように明示
            $defRaw = isset($__def['default']) ? (string)$__def['default'] : '';
            $defStr = (preg_match('/^#[0-9A-Fa-f]{6}$/', $defRaw) ? strtoupper($defRaw) : '');

            echo '          <div class="color-field">'.PHP_EOL;
            echo '            <input type="color" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="'.tpcms_h($valStr).'" data-color-input>'.PHP_EOL;
            echo '            <input type="text" value="'.tpcms_h($valStr).'" class="input" style="width:8rem;margin-left:.6rem" data-color-hex>'.PHP_EOL;
            if ($defStr !== '') {
                echo '            <a href="#" class="small" style="margin-left:.6rem" data-color-reset data-default="'.tpcms_h($defStr).'">デフォルトに戻す</a>'.PHP_EOL;
            }
            echo '          </div>'.PHP_EOL;

        } elseif ($type === 'file') {
            if ($ph === '') { $ph = 'ファイル名'; }
            // テキスト欄のみ（ドロップゾーン／サムネはJSに任せる）
            $safe = is_scalar($val) ? (string)$val : '';
            $uid  = 'up_sec_' . preg_replace('/[^a-z0-9_]+/i', '_', (string)$themeSecKey . '_' . (string)$k);

            echo '              <input id="'.tpcms_h($uid).'" type="text" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="'.tpcms_h($safe).'" class="input js-upload-url as-plain-text" placeholder="'.tpcms_h($ph).'" data-file="1" readonly aria-readonly="true" tabindex="-1">'.PHP_EOL;

    } elseif ($type === 'checkbox') {
        $checked = !empty($val) && $val !== '0';
        echo '              <label class="inline"><input type="checkbox" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="1"'.($checked ? ' checked' : '').'> '.tpcms_h($lab).($req ? '（必須）' : '').'</label>'.PHP_EOL;

        } elseif ($type === 'radio') {
            // radio: 未設定なら先頭を checked。保存値があれば一致するものを checked
            $options = is_array($__def['options'] ?? null) ? $__def['options'] : [];
            $hasKey  = array_key_exists($k, $cur ?? []);
            $i = 0;
            foreach ($options as $op) {
                // 2通りの定義を許容: ["value","label"] / {"value":..,"label":..}
                if (is_array($op) && array_key_exists(0, $op)) {
                    $oval = (string)($op[0] ?? '');
                    $olab = (string)($op[1] ?? $oval);
                } else {
                    $oval = (string)($op['value'] ?? '');
                    $olab = (string)($op['label'] ?? $oval);
                }
                $checked = $hasKey ? ((string)$val === $oval) : ($i === 0);
                $id = 'r_' . preg_replace('/[^a-z0-9_]+/i','_', (string)$themeSecKey . '_' . (string)$k . '_' . $i);
                echo '              <label class="inline" for="'.tpcms_h($id).'">'.PHP_EOL;
                echo '                <input id="'.tpcms_h($id).'" type="radio" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="'.tpcms_h($oval).'"'.($checked ? ' checked' : '').'> '.tpcms_h($olab).PHP_EOL;
                echo '              </label>'.PHP_EOL;
                $i++;
            }
        } elseif ($type === 'select') {
            $options     = (isset($__def['options']) && is_array($__def['options'])) ? $__def['options'] : [];
            $allowEmpty  = isset($__def['allow_empty']) ? (bool)$__def['allow_empty'] : true;

            echo '              <select name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']"'.($req ? ' required' : '').'>'.PHP_EOL;

            // allow_empty:true のときだけ「未選択」行を出す
            if ($allowEmpty) {
                echo '                <option value="">'.tpcms_h($ph !== '' ? $ph : '未選択').'</option>'.PHP_EOL;
            }

            foreach ($options as $op) {
                // 配列ペア [value,label] / オブジェクト {value,label} の両方に対応
                $v = isset($op[0]) ? (string)$op[0] : (string)($op['value'] ?? '');
                $l = isset($op[1]) ? (string)$op[1] : (string)($__def['labels'][$v] ?? ($op['label'] ?? $v));
                $sel = ((string)$val === $v) ? ' selected' : '';
                echo '                <option value="'.tpcms_h($v).'"'.$sel.'>'.tpcms_h($l).'</option>'.PHP_EOL;
            }
            echo '              </select>'.PHP_EOL;
        } elseif ($type === 'table') {

            // ---- tpcms-form: table（列の追加/削除対応）----
            $k     = (string)($__def['key'] ?? '');
            $cols  = is_array($__def['cols'] ?? null) ? $__def['cols'] : [];
            $thead = $__def['thead'] ?? ['top' => false, 'left' => false];

            // 現在値（保存済み）取得（旧形式/新形式 両対応）
            $tval   = $cur[$k] ?? [];
            $___tbl = is_array($tval) ? $tval : [];

            // rows 決定：1) 新形式 rows[], 2) 旧形式「配列＝行リスト」、3) 定義 rows ぶん空行
            $___rows = [];
            if (isset($___tbl['rows']) && is_array($___tbl['rows'])) {
                // 新形式
                $___rows = array_values($___tbl['rows']);
            } elseif ($___tbl && array_keys($___tbl) === range(0, count($___tbl) - 1)) {
                // 旧形式（0..Nの数値キーのみ＝行リスト）
                foreach ($___tbl as $_r) {
                    if (!is_array($_r)) continue;
                    $clean = [];
                    foreach ($_r as $_ck => $_cv) {
                        $clean[(string)$_ck] = is_scalar($_cv) ? (string)$_cv : '';
                    }
                    $___rows[] = $clean;
                }
            } else {
                // 初期行（定義 rows）
                $want = (int)($__def['rows'] ?? 0);
                for ($ri = 0; $ri < max(0, $want); $ri++) $___rows[] = [];
            }

            // thead（保存済みがあれば優先）
            $___top  = !empty($thead['top']);
            $___left = !empty($thead['left']);
            if (isset($___tbl['thead']) && is_array($___tbl['thead'])) {
                $___top  = !empty($___tbl['thead']['top']);
                $___left = !empty($___tbl['thead']['left']);
            }

            // cols のアクティブ構成：保存 > 定義 > 先頭行推定
            $___colsActive = [];
            if (isset($___tbl['cols']) && is_array($___tbl['cols']) && $___tbl['cols']) {
                $___colsActive = array_values($___tbl['cols']);
            } elseif ($cols) {
                $___colsActive = array_values($cols);
            } elseif (isset($___rows[0]) && is_array($___rows[0])) {
                foreach (array_keys($___rows[0]) as $ck) {
                    $___colsActive[] = ['key' => (string)$ck, 'label' => ''];
                }
            }

            // name のベース（外側の<label>は既に出力済みなので、ここでは .tpcms-table のみを描画）
            $___base = 'theme['.$themeSecKey.']['.$k.']';
            ?>
            <div class="tpcms-table" data-base="<?= tpcms_h($___base) ?>">

              <div style="margin-bottom:.4rem">
                <label><input type="checkbox" name="<?= tpcms_h($___base) ?>[thead][top]"  value="1" <?= $___top  ? 'checked' : '' ?>> 上部見出し</label>
                <input type="hidden" name="<?= tpcms_h($___base) ?>[thead][left]" value="0">
                <label style="margin-left:1rem;"><input type="checkbox" name="<?= tpcms_h($___base) ?>[thead][left]" value="1" <?= $___left ? 'checked' : '' ?>> 左見出し</label>
              </div>

              <?php // ★ 列設定はUIに出さず hidden で往復保存 ?>
              <?php foreach ($___colsActive as $ci => $cc):
                $ck  = (string)($cc['key']   ?? '');
                $cl  = (string)($cc['label'] ?? '');
                if ($ck === '') continue; ?>
                <input type="hidden" name="<?= tpcms_h($___base) ?>[cols][<?= $ci ?>][key]"   value="<?= tpcms_h($ck) ?>">
                <input type="hidden" name="<?= tpcms_h($___base) ?>[cols][<?= $ci ?>][label]" value="<?= tpcms_h($cl) ?>">
              <?php endforeach; ?>

              <div class="tpcms-table-grid" style="display:flex;gap:.5rem;flex-wrap:nowrap;overflow-x:auto">
                <?php foreach ($___colsActive as $c):
                  $ck = (string)($c['key'] ?? '');
                  $cl = (string)($c['label'] ?? $ck);
                  if ($ck === '') continue; ?>
                  <div class="tpcms-table-col" data-col-key="<?= tpcms_h($ck) ?>">
                    <div class="muted" style="font-weight:bold;"><?= tpcms_h($cl) ?></div>
                    <?php foreach ($___rows as $rIdx => $rVal):
                      $cellName   = $___base.'[rows]['.$rIdx.']['.$ck.']';
                      $cellValRaw = $rVal[$ck] ?? '';
                      $cellVal    = is_scalar($cellValRaw) ? (string)$cellValRaw : ''; ?>
                      <input class="input" type="text"
                             name="<?= htmlspecialchars($cellName, ENT_QUOTES, 'UTF-8') ?>"
                             value="<?= tpcms_h($cellVal) ?>"
                             style="margin:.25rem 0;">
                    <?php endforeach; ?>
                  </div>
                <?php endforeach; ?>
              </div>

              <div style="margin-top:.5rem; display:flex; gap:.5rem; flex-wrap:wrap;">
                <div>
                  <button class="btn js-table-add-row mb1rem" type="button">＋ 行を追加</button>
                  <button class="btn js-table-del-row delete-color" type="button">− 最終行を削除</button>
                </div>
                <div>
                  <button class="btn js-table-add-col mb1rem" type="button">＋ 列を追加</button>
                  <button class="btn js-table-del-col delete-color" type="button">− 最終列を削除</button>
                </div>
              </div>

            </div>
            <?php

        } else {

            // text / その他は text として最小表示
            echo '              <input type="text" name="theme['.tpcms_h($themeSecKey).']['.tpcms_h($k).']" value="'.tpcms_h(is_scalar($val)? (string)$val : '').'" '.$max.' placeholder="'.tpcms_h($ph).'"'.(!empty($__def['link_picker']) ? ' data-link-picker="1"' : '').'>'.PHP_EOL;
        }

        if (!empty($__def['help_html'])) {
            $fhelp = (string)$__def['help_html'];
            $fhelp = strip_tags($fhelp, '<a><strong><em><code><ul><ol><li>');
            echo '              <div class="help">'.$fhelp.'</div>'.PHP_EOL;
        }

        echo '            </div>'.PHP_EOL;
    } // end fields
?>
          </div><!-- /.sec-body -->
        </section>
<?php
} // end foreach sections
?>
        <?php endif; ?>

        <!-- 下部の保存 -->
        <div class="btnbar">
          <button type="submit" class="btn1">保存</button>
        </div>
      </form>
    </div>
  </div>

  <script src="./assets/admin.js"></script>

<script>
(function(){
  // カラーピッカーをデフォルトに戻す
  document.addEventListener('click', function(e){
    var a = e.target && e.target.closest('[data-color-reset]');
    if (!a) return;
    e.preventDefault();
    var def = (a.getAttribute('data-default') || '').trim();
    if (!/^#[0-9A-Fa-f]{6}$/.test(def)) return;

    var wrap   = a.closest('.color-field');
    var picker = wrap && wrap.querySelector('input[type="color"][data-color-input]');
    var hex    = wrap && wrap.querySelector('input[data-color-hex]');
    if (!picker || !hex) return;

    picker.value = def.toUpperCase();
    hex.value    = def.toUpperCase();

    // 同期用のinputイベントを発火（他のリスナがあれば追随）
    try { picker.dispatchEvent(new Event('input', { bubbles: true })); } catch(_){}
  }, false);
})();
</script>

<script>
// カラーピッカー
(function(){
  function normalizeHex(s){
    if (!s) return null;
    s = String(s).trim();
    // 先頭に # が無ければ付与
    if (s[0] !== '#') s = '#' + s;
    // #RGB → #RRGGBB
    var m3 = /^#([0-9a-fA-F]{3})$/.test(s) ? s.match(/^#([0-9a-fA-F]{3})$/) : null;
    if (m3) {
      var a = m3[1];
      return ('#' + a[0]+a[0] + a[1]+a[1] + a[2]+a[2]).toUpperCase();
    }
    // #RRGGBB
    if (/^#([0-9a-fA-F]{6})$/.test(s)) return s.toUpperCase();
    return null; // 未確定・不正は無視
  }

  // color → hex
  document.addEventListener('input', function(e){
    var t = e.target;
    if (!t || t.type !== 'color' || !t.matches('input[type="color"][data-color-input]')) return;
    var wrap = t.closest('.color-field');
    var out  = wrap ? wrap.querySelector('[data-color-hex]') : null;
    if (out) out.value = (t.value || '').toUpperCase();
  });

  // hex → color（有効な #RGB / #RRGGBB のときのみ反映）
  document.addEventListener('input', function(e){
    var t = e.target;
    if (!t || !t.matches('input[data-color-hex]')) return;
    var norm = normalizeHex(t.value);
    if (!norm) return; // 入力途中は無視（エラーにしない）
    var wrap   = t.closest('.color-field');
    var picker = wrap ? wrap.querySelector('input[type="color"][data-color-input]') : null;
    if (picker) picker.value = norm;
    t.value = norm; // 正規化して書き戻し
  });

  // 初期同期（color の値を hex に反映）
  document.addEventListener('DOMContentLoaded', function(){
    document.querySelectorAll('input[type="color"][data-color-input]').forEach(function(inp){
      var wrap = inp.closest('.color-field');
      var out  = wrap ? wrap.querySelector('[data-color-hex]') : null;
      if (out) out.value = (inp.value || '').toUpperCase();
    });
  });
})();
</script>

<script>
(function(){
  var THEME_KEY = <?= json_encode($themeKey) ?>;
  var PREFIX = 'site.sec.' + THEME_KEY + '.';

  // 初期化（デフォルトは閉じる／保存で「1=開」のときだけ開く）
  document.querySelectorAll('.sec[data-sec]').forEach(function(sec){
    var id = sec.getAttribute('data-sec');
    var open = localStorage.getItem(PREFIX + id);
    if (open !== '1') { sec.classList.add('collapsed'); }
  });

  // トグル
  document.addEventListener('click', function(e){
    var btn = e.target.closest('.sec-toggle');
    if (!btn) return;
    var id = btn.getAttribute('data-sec-toggle');
    var sec = btn.closest('.sec');
    if (!sec || !id) return;

    var collapsed = sec.classList.toggle('collapsed');
    localStorage.setItem(PREFIX + id, collapsed ? '0' : '1');
  }, false);
})();
</script>

<script>
/* 置換検知（admin.js の実装に依存せず動く汎用版）
   - アップロード開始前に「現在値」を控える
   - 送信時に「控えた値」と「現在値」が違えば rows_deleted_files[] に積む
*/
(function(){
  function markPrevByInput(inp){
    if (!inp) return;
    inp.dataset.prevUploadValue = (inp.value || '').trim();
  }

  // dropzone（data-upload-drop）経由の操作開始時に以前の値を控える
  document.addEventListener('click', function(e){
    var dz = e.target.closest && e.target.closest('[data-upload-drop]');
    if (!dz) return;
    var sel = dz.getAttribute('data-upload-target') || dz.getAttribute('data-upload-drop');
    if (!sel) return;
    var inp = dz.closest('form')?.querySelector(sel);
    markPrevByInput(inp);
  });
  document.addEventListener('dragenter', function(e){
    var dz = e.target.closest && e.target.closest('[data-upload-drop]');
    if (!dz) return;
    var sel = dz.getAttribute('data-upload-target') || dz.getAttribute('data-upload-drop');
    if (!sel) return;
    var inp = dz.closest('form')?.querySelector(sel);
    markPrevByInput(inp);
  }, true);

  // テキストの file 入力にフォーカスした場合も控えておく（保険）
  document.addEventListener('focusin', function(e){
    var inp = e.target && e.target.matches && e.target.matches('input[type="text"][data-file="1"]') ? e.target : null;
    if (inp) markPrevByInput(inp);
  });

  // フォーム送信時に差分あれば rows_deleted_files[] を積む
  document.addEventListener('submit', function(e){
    var form = e.target && e.target.tagName === 'FORM' ? e.target : null;
    if (!form) return;
    form.querySelectorAll('input[type="text"][data-file="1"]').forEach(function(inp){
      var prev = (inp.dataset.prevUploadValue || '').trim();
      var now  = (inp.value || '').trim();
      if (!prev || prev === now) return;
      if (/^https?:\/\//i.test(prev)) return; // 外部URLは対象外
      var h = document.createElement('input');
      h.type = 'hidden';
      h.name = 'rows_deleted_files[]';
      h.value = prev;
      form.appendChild(h);
    });
  });
})();
</script>

<script>
// site.php: admin.js が生成する「__delete」チェック → rows_deleted_files[] に反映
document.addEventListener('change', function (e) {
  var cb = e.target;
  if (!cb || cb.type !== 'checkbox' || !cb.name || !/__delete$/.test(cb.name)) return;

  var form = cb.closest('form');
  if (!form) return;

  // 対応する file 入力（data-file="1" ＆ __delete を外したname）
  var baseName = cb.name.replace(/__delete$/, '');
  var inp = form.querySelector('input[type="text"][data-file="1"][name="' + baseName.replace(/"/g, '\\"') + '"]');
  if (!inp) return;

  var mark = form.querySelector('input[type="hidden"][name="rows_deleted_files[]"][data-del-for="' + baseName.replace(/"/g, '\\"') + '"]');
  if (cb.checked) {
    var v = (inp.value || '').trim();
    if (!v || /^https?:\/\//i.test(v)) return; // 外部URLは削除対象外
    if (!mark) {
      mark = document.createElement('input');
      mark.type = 'hidden';
      mark.name = 'rows_deleted_files[]';
      mark.setAttribute('data-del-for', baseName);
      form.appendChild(mark);
    }
    mark.value = v;
  } else {
    if (mark) mark.remove();
  }
});
</script>

<script>
// --- table: 行・列の追加/削除 ---
document.addEventListener('click', function (ev) {
  var btn;

  // ===== 行を追加 =====
  btn = ev.target.closest('.js-table-add-row');
  if (btn) {
    ev.preventDefault();
    var wrap = btn.closest('.tpcms-table'); if (!wrap) return;
    var base = wrap.getAttribute('data-base');

    // 既存列ごとに、次の行インデックスで空セルを1つ追加
    wrap.querySelectorAll('.tpcms-table-col').forEach(function (col) {
      var key = col.getAttribute('data-col-key');
      var inputs = col.querySelectorAll('input.input');
      var next = inputs.length; // 0始まりの次インデックス
      var name = base + '[rows][' + next + '][' + key + ']';
      var inp = document.createElement('input');
      inp.className = 'input';
      inp.type = 'text';
      inp.name = name;
      inp.value = '';
      inp.style.margin = '.25rem 0';
      col.appendChild(inp);
    });
    return;
  }

  // ===== 最終行を削除 =====
  btn = ev.target.closest('.js-table-del-row');
  if (btn) {
    ev.preventDefault();
    var wrap = btn.closest('.tpcms-table'); if (!wrap) return;

    // 各列の最終入力を1つずつ削除（0行未満にはしない）
    var canDel = true;
    wrap.querySelectorAll('.tpcms-table-col').forEach(function (col) {
      if (col.querySelectorAll('input.input').length === 0) canDel = false;
    });
    if (!canDel) return;
    wrap.querySelectorAll('.tpcms-table-col').forEach(function (col) {
      var inputs = col.querySelectorAll('input.input');
      var last = inputs[inputs.length - 1];
      if (last) last.remove();
    });
    return;
  }

  // ===== 列を追加 =====
  btn = ev.target.closest('.js-table-add-col');
  if (btn) {
    ev.preventDefault();
    var wrap = btn.closest('.tpcms-table'); if (!wrap) return;
    var base = wrap.getAttribute('data-base');

    var grid = wrap.querySelector('.tpcms-table-grid');
    if (!grid) return;

    // 既存キー集合
    var keys = Array.prototype.map.call(
      wrap.querySelectorAll('.tpcms-table-col'),
      function(col){ return col.getAttribute('data-col-key') || ''; }
    );

    // 新キーを COL1.. で自動採番（重複回避）
    var n = 1, newKey;
    do { newKey = 'COL' + n++; } while (keys.indexOf(newKey) !== -1);

    // 既存行数（任意の列の入力数）を取得
    var rowsCount = 0;
    var anyCol = wrap.querySelector('.tpcms-table-col');
    if (anyCol) rowsCount = anyCol.querySelectorAll('input.input').length;

    // 表示用ラベル：新しい列N
    var label = '新しい列' + (keys.length + 1);

    // --- hidden: cols[][key|label] を末尾に生成
    var idx = keys.length; // 新列は末尾インデックス
    var hKey   = document.createElement('input');
    var hLabel = document.createElement('input');
    hKey.type='hidden';   hLabel.type='hidden';
    hKey.name   = base + '[cols][' + idx + '][key]';
    hLabel.name = base + '[cols][' + idx + '][label]';
    hKey.value   = newKey;
    hLabel.value = label;
    wrap.appendChild(hKey); wrap.appendChild(hLabel);

    // --- 見た目の列DOMを生成
    var col = document.createElement('div');
    col.className = 'tpcms-table-col';
    col.setAttribute('data-col-key', newKey);

    var head = document.createElement('div');
    var sampleHead = grid.querySelector('.tpcms-table-col > div');
    if (sampleHead) {
      head.className = sampleHead.className || 'muted';
      var st = sampleHead.getAttribute('style');
      if (st) head.setAttribute('style', st);
    } else {
      head.className = 'muted';
      head.style.fontWeight = 'bold';
    }
    head.textContent = label;
    col.appendChild(head);

    for (var r=0; r<rowsCount; r++) {
      var inp = document.createElement('input');
      inp.className = 'input';
      inp.type = 'text';
      inp.name = base + '[rows][' + r + '][' + newKey + ']';
      inp.value = '';
      inp.style.margin = '.25rem 0';
      col.appendChild(inp);
    }

    grid.appendChild(col);
    return;
  }

  // ===== 最終列を削除 =====
  btn = ev.target.closest('.js-table-del-col');
  if (btn) {
    ev.preventDefault();
    var wrap = btn.closest('.tpcms-table'); if (!wrap) return;
    var base = wrap.getAttribute('data-base');

    var cols = wrap.querySelectorAll('.tpcms-table-col');
    if (!cols || cols.length <= 1) return; // 最低1列は残す

    var lastCol = cols[cols.length - 1];
    var lastKey = lastCol.getAttribute('data-col-key') || '';

    // hidden の cols[<idx>] を末尾インデックスで削除
    var lastIdx = cols.length - 1;
    var sel = [
      'input[type="hidden"][name="'+ base + '[cols]['+ lastIdx +'][key]"]',
      'input[type="hidden"][name="'+ base + '[cols]['+ lastIdx +'][label]"]'
    ].join(',');
    wrap.querySelectorAll(sel).forEach(function(h){ h.parentNode && h.parentNode.removeChild(h); });

    // 列DOMを削除
    lastCol.parentNode.removeChild(lastCol);
    return;
  }
}, false);
</script>

<?php
// ---------- メニュー候補（内部ページ/アンカーのみ）をJSへ渡す ----------
$__candidates = [];
$__menu = function_exists('tpcms_json_read') ? tpcms_json_read(__DIR__ . '/../data/menu.json', []) : [];
if (isset($__menu['items']) && is_array($__menu['items'])) {
  foreach ($__menu['items'] as $row) {
    if (!is_array($row)) continue;
    $mSlug = isset($row['slug'])  ? (string)$row['slug']  : '';
    $label = isset($row['label']) ? (string)$row['label'] : $mSlug;
    if ($mSlug === '') continue;
    if (preg_match('~^https?://~i', $mSlug)) continue;      // 外部URLは候補外
    if (stripos($label, '<h3') !== false) continue;         // 見出し行は候補外

    // ラベル整形（タグ除去→実体復号→空白整形）
    $label_admin = $label;
    $label_admin = preg_replace('#<[^>]*>#u', ' ', $label_admin);
    $label_admin = html_entity_decode($label_admin, ENT_QUOTES, 'UTF-8');
    $label_admin = str_replace("\xC2\xA0", ' ', $label_admin);
    $label_admin = preg_replace('/\s+/u', ' ', $label_admin);
    $label_admin = trim($label_admin);

    $__candidates[] = ['slug' => $mSlug, 'label' => ($label_admin !== '' ? $label_admin : $mSlug)];
  }
}
?>
<!-- Link Picker popup -->
<style>
  .tpcms-linkpicker-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.15);z-index:9998;display:none}
  .tpcms-linkpicker{position:fixed;z-index:9999;inset:auto 0 0 0;max-width:480px;margin:10vh auto;background:#fff;border:1px solid #ddd;border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,.15);overflow:hidden;display:none}
  .tpcms-linkpicker h3{margin:0;padding:.8rem 1rem;border-bottom:1px solid #eee;font-size:1rem;background:#f7f7f7}
  .tpcms-linkpicker .list{max-height:50vh;overflow:auto;padding:.5rem}
  .tpcms-linkpicker .item{display:block;width:100%;text-align:left;padding:.5rem .75rem;border-radius:6px;border:1px solid transparent;background:#fff;cursor:pointer}
  .tpcms-linkpicker .item:hover{background:#f4f7ff;border-color:#cfe1ff}
  .tpcms-linkpicker .slug{color:#666;font-size:.85em;margin-left:.4rem}
  .tpcms-linkpicker .foot{display:flex;justify-content:flex-end;gap:.5rem;padding:.6rem .8rem;border-top:1px solid #eee;background:#fafafa}
  .tpcms-link-btn{display:inline-flex;align-items:center;justify-content:center;width:auto;padding:.55rem .7rem;margin-left:.5rem;border-radius:6px;vertical-align:middle}
  .tpcms-link-btn i{font-size:1rem;line-height:1}
</style>
<div class="tpcms-linkpicker-backdrop" id="tpcmsLinkBackdrop"></div>
<div class="tpcms-linkpicker" id="tpcmsLinkPicker" role="dialog" aria-modal="true" aria-labelledby="tpcmsLinkPickerTitle">
  <h3 id="tpcmsLinkPickerTitle">リンク先を選択</h3>
  <div class="list" id="tpcmsLinkPickerList" tabindex="0" aria-label="メニュー候補"></div>
  <div class="foot">
    <button type="button" class="btn" id="tpcmsLinkPickerClose">閉じる</button>
  </div>
</div>
<script>
(function(){
  // 候補
  var CANDS = <?= json_encode($__candidates, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
  // popup要素
  var picker   = document.getElementById('tpcmsLinkPicker');
  var listBox  = document.getElementById('tpcmsLinkPickerList');
  var closeBtn = document.getElementById('tpcmsLinkPickerClose');
  var backdrop = document.getElementById('tpcmsLinkBackdrop');
  var targetInput = null;

  function escapeHtml(s){
    return String(s).replace(/[&<>"']/g, function(ch){
      return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[ch]);
    });
  }

  function openPicker(forInput){
    targetInput = forInput || null;
    if (!picker || !listBox) return;

    listBox.innerHTML = '';
    if (!CANDS.length) {
      var p = document.createElement('p');
      p.style.padding = '.8rem';
      p.textContent = '候補がありません（メニュー未設定）';
      listBox.appendChild(p);
    } else {
      CANDS.forEach(function(c){
        var b = document.createElement('button');
        b.type = 'button';
        b.className = 'item';
        b.setAttribute('data-slug', c.slug);
        b.innerHTML = '<span class="label">'+ escapeHtml(c.label || c.slug) +'</span>'
                    + '<span class="slug">'+ escapeHtml(c.slug) +'</span>';
        b.addEventListener('click', function(){
          var v = String(this.getAttribute('data-slug') || '');

          // page_blocks.php と同条件相当：アンカーは ./#xxx に正規化
          if (v.charAt(0) === '#') {
            v = './' + v;
          }

          if (targetInput) {
            targetInput.value = v;
            try { targetInput.dispatchEvent(new Event('input', {bubbles:true})); } catch(_){}
          }
          hidePicker();
        });
        listBox.appendChild(b);
      });
    }

    backdrop.style.display = 'block';
    picker.style.display   = 'block';
    setTimeout(function(){ try{ listBox.focus(); }catch(_){} }, 0);
  }
  function hidePicker(){ picker.style.display = 'none'; backdrop.style.display = 'none'; targetInput = null; }

  if (closeBtn) closeBtn.addEventListener('click', hidePicker);
  if (backdrop) backdrop.addEventListener('click', hidePicker);
  document.addEventListener('keydown', function(e){ if (e.key === 'Escape' && picker.style.display === 'block') hidePicker(); });

  // data-link-picker="1" の text 入力だけにボタンを付与
  function ensureId(el){ if (el.id) return el.id; el.id = 'lp_' + Math.random().toString(36).slice(2); return el.id; }
  function attachButton(input){
    if (!input || input.dataset.linkpickerAttached === '1') return;
    if (input.type !== 'text') return;
    if (!input.hasAttribute('data-link-picker')) return;

    var id = ensureId(input);
    var btn = document.createElement('button');
    btn.type = 'button';
    btn.className = 'btn tpcms-link-btn';
    btn.setAttribute('data-for', '#'+id);
    btn.setAttribute('title', 'メニューから選ぶ');
    btn.innerHTML = '<i class="fa-solid fa-link" aria-hidden="true"></i><span class="sr-only">選択</span>';
    input.insertAdjacentElement('afterend', btn);
    input.dataset.linkpickerAttached = '1';
  }
  function scanAll(root){
    (root || document).querySelectorAll('input[type="text"][data-link-picker]').forEach(attachButton);
  }

  document.addEventListener('DOMContentLoaded', function(){ scanAll(document); });
  var mo = new MutationObserver(function(muts){
    muts.forEach(function(m){
      m.addedNodes && m.addedNodes.forEach(function(n){
        if (n.nodeType !== 1) return;
        if (n.matches && n.matches('input[type="text"][data-link-picker]')) attachButton(n);
        if (n.querySelectorAll) n.querySelectorAll('input[type="text"][data-link-picker]').forEach(attachButton);
      });
    });
  });
  mo.observe(document.body, {childList:true, subtree:true});

  // クリックでポップアップ
  document.addEventListener('click', function(e){
    var btn = e.target && e.target.closest('.tpcms-link-btn');
    if (!btn) return;
    var sel = btn.getAttribute('data-for');
    var input = sel ? document.querySelector(sel) : null;
    openPicker(input || null);
  }, false);
})();
</script>

</body>
</html>
