{"id":2131,"date":"2026-10-30T11:52:54","date_gmt":"2026-10-30T02:52:54","guid":{"rendered":"http:\/\/korean.haus\/?page_id=2131"},"modified":"2026-10-30T11:52:54","modified_gmt":"2026-10-30T02:52:54","slug":"%ea%b0%80%eb%ac%b8-%ec%97%ad%ec%82%ac%ea%b4%80","status":"publish","type":"page","link":"https:\/\/korean.haus\/?page_id=2131","title":{"rendered":"\uac00\ubb38 \uc5ed\uc0ac\uad00"},"content":{"rendered":"<style id=\"fh-app-style\">@import url('https:\/\/fonts.googleapis.com\/css2?family=Noto+Serif+KR:wght@500;600;700&family=Noto+Sans+KR:wght@400;500;600;700&display=swap');\n\n.fh-app{\n  --fh-bg:#F5F6F1;\n  --fh-surface:#FFFFFF;\n  --fh-surface-2:#FBFAF6;\n  --fh-ink:#1E2A25;\n  --fh-ink-soft:#55605A;\n  --fh-muted:#948F81;\n  --fh-jade:#3F6657;\n  --fh-jade-dark:#2C4A3F;\n  --fh-jade-soft:#E7EEE9;\n  --fh-seal:#A63D34;\n  --fh-seal-soft:#F5E4E1;\n  --fh-border:#E4E1D6;\n  --fh-radius:16px;\n  --fh-radius-sm:10px;\n  --fh-font-display:'Noto Serif KR',serif;\n  --fh-font-body:'Noto Sans KR',sans-serif;\n\n  all:initial;\n  *,*::before,*::after{box-sizing:border-box;}\n  display:block;\n  max-width:480px;\n  margin:0 auto;\n  background:var(--fh-bg);\n  font-family:var(--fh-font-body);\n  color:var(--fh-ink);\n  -webkit-font-smoothing:antialiased;\n  position:relative;\n  min-height:80vh;\n  padding-bottom:24px;\n  line-height:1.5;\n}\n.fh-app *{box-sizing:border-box; font-family:inherit;}\n.fh-app button{font-family:var(--fh-font-body); cursor:pointer;}\n.fh-app img{max-width:100%; display:block;}\n\n.fh-app .fh-loading{text-align:center; padding:60px 0; color:var(--fh-muted); font-size:13px;}\n\n\/* ---------- Header ---------- *\/\n\/* [2026-07-27 \uc218\uc815] \ud0ed\ubc14\ub97c \ud5e4\ub354 \ub0b4\ubd80\ub85c \uc774\ub3d9 + overflow:hidden \uc81c\uac70(iOS \uc0ac\ud30c\ub9ac\uc5d0\uc11c\n   \uac00\ub85c \uc2a4\ud06c\ub864 \ud0ed\ubc14\uac00 \ub9c9\ud788\ub294 \ubb38\uc81c \ubc29\uc9c0) + \ubc30\uacbd\uc744 \uc0ac\uc9c4 \ub300\uc2e0 \uc0c9\uc0c1 \uadf8\ub77c\ub370\uc774\uc158\uc73c\ub85c \uad6c\uc131 *\/\n.fh-app header{\n  padding:22px 20px 24px;\n  background:\n    radial-gradient(circle at 88% 8%, rgba(166,61,52,0.35), transparent 55%),\n    radial-gradient(circle at 6% 100%, rgba(63,102,87,0.4), transparent 60%),\n    linear-gradient(135deg, var(--fh-jade-dark) 0%, var(--fh-jade) 65%, #3d5c4f 100%);\n  color:#F4F5F0;\n  position:relative;\n}\n.fh-app header::after{\n  content:\"\";\n  position:absolute; right:10px; top:10px;\n  width:92px; height:92px; border-radius:50%;\n  border:1px solid rgba(244,245,240,0.12);\n  pointer-events:none;\n}\n.fh-app .header-row{display:flex; align-items:center; gap:14px; position:relative; z-index:1;}\n.fh-app .crest{\n  width:52px;height:52px;border-radius:50%;\n  background:var(--fh-seal);\n  display:flex;align-items:center;justify-content:center;\n  font-family:var(--fh-font-display);\n  font-size:22px; font-weight:700; color:#fff;\n  border:2px solid rgba(255,255,255,0.55);\n  flex-shrink:0;\n  box-shadow:0 3px 10px rgba(0,0,0,0.18);\n}\n.fh-app .header-text h1{\n  font-family:var(--fh-font-display);\n  font-size:20px; font-weight:700; margin:0 0 2px;\n  letter-spacing:0.2px; color:#F4F5F0;\n}\n.fh-app .header-text p{margin:0; font-size:12.5px; color:rgba(244,245,240,0.78);}\n.fh-app .settings-btn{\n  margin-left:auto; width:34px;height:34px;border-radius:50%;\n  border:1px solid rgba(244,245,240,0.35);\n  background:rgba(255,255,255,0.08); color:#F4F5F0;\n  display:flex;align-items:center;justify-content:center; flex-shrink:0;\n}\n\n\/* ---------- Main ---------- *\/\n.fh-app main{padding:18px 16px 90px;}\n.fh-app #fhContent{ transition:opacity .22s ease; }\n.fh-app #fhContent.fh-fade-out{ opacity:0; }\n.fh-app #fhContent.fh-fade-in{ opacity:1; }\n\n\/* ---------- \ud648 \ub85c\ub529 \uc2a4\ucf08\ub808\ud1a4 (\ub458\ub7ec\ubcf4\uae30 \uc790\ub9ac\uc5d0 \uba3c\uc800 \ubcf4\uc774\ub294 \ub300\uccb4 \ud654\uba74) ---------- *\/\n.fh-app .fh-skel-line{\n  height:12px; border-radius:6px; margin-top:9px;\n  background:linear-gradient(90deg, var(--fh-border) 25%, var(--fh-surface-2) 37%, var(--fh-border) 63%);\n  background-size:400% 100%;\n  animation:fhShimmer 1.5s ease infinite;\n}\n.fh-app .fh-skel-showcase{\n  background:var(--fh-surface); border:1px solid var(--fh-border); border-radius:var(--fh-radius);\n  padding:30px 20px; text-align:center; display:flex; flex-direction:column; align-items:center; gap:12px;\n  min-height:190px; justify-content:center;\n}\n.fh-app .fh-skel-illust{ width:130px; height:auto; }\n.fh-app .fh-skel-illust circle{ animation:fhPulse 1.8s ease-in-out infinite; transform-origin:100px 52px; }\n.fh-app .fh-skel-showcase p{ margin:0; font-size:12px; color:var(--fh-muted); }\n@keyframes fhShimmer{ 0%{background-position:100% 0;} 100%{background-position:0 0;} }\n@keyframes fhPulse{ 0%,100%{ transform:scale(1); opacity:0.9; } 50%{ transform:scale(1.06); opacity:1; } }\n.fh-app .section-title{\n  font-family:var(--fh-font-display); font-size:17px; font-weight:700;\n  margin:4px 0 14px; display:flex; align-items:center; gap:8px;\n}\n.fh-app .section-title .dot{width:7px;height:7px;border-radius:50%; background:var(--fh-seal); display:inline-block;}\n\n.fh-app .add-fab{\n  position:fixed; right:20px; bottom:20px;\n  width:52px; height:52px; border-radius:50%;\n  background:var(--fh-seal); color:#fff; border:none;\n  font-size:26px; display:flex; align-items:center; justify-content:center;\n  box-shadow:0 6px 16px rgba(166,61,52,0.35);\n  z-index:9030;\n}\n\n.fh-app .empty-state{text-align:center; padding:52px 20px; color:var(--fh-ink-soft);}\n.fh-app .empty-state .glyph{\n  width:56px;height:56px;border-radius:50%;\n  border:1.5px dashed var(--fh-muted); margin:0 auto 14px;\n  display:flex;align-items:center;justify-content:center; color:var(--fh-muted);\n}\n.fh-app .empty-state h3{font-family:var(--fh-font-display); font-size:15.5px; margin:0 0 6px; color:var(--fh-ink);}\n.fh-app .empty-state p{font-size:13px; margin:0; line-height:1.6;}\n\n\/* ---------- Home ---------- *\/\n.fh-app .motto-card{\n  background:var(--fh-surface); border:1px solid var(--fh-border); border-radius:var(--fh-radius);\n  padding:18px; margin-bottom:18px;\n}\n.fh-app .motto-card .label{font-size:11px; color:var(--fh-seal); font-weight:700; letter-spacing:1px;}\n.fh-app .motto-card p{font-family:var(--fh-font-display); font-size:15.5px; line-height:1.7; margin:8px 0 0; color:var(--fh-ink);}\n\n\/* \uac00\ud6c8 \ud788\uc5b4\ub85c: \ub3c4\uc7a5(\ub099\uad00) \ubc30\uc9c0 + \uc778\uc6a9\ubd80\ud638 + \uc720\ub798 \uc5f0\ub3c4 pill *\/\n.fh-app .fh-motto-hero{ position:relative; overflow:hidden; padding-right:76px; }\n.fh-app .fh-motto-hero::before{\n  content:\"\"; position:absolute; inset:0; pointer-events:none;\n  background-image:\n    radial-gradient(circle at 12% 15%, rgba(63,102,87,0.06), transparent 42%),\n    radial-gradient(circle at 92% 88%, rgba(166,61,52,0.05), transparent 46%);\n}\n.fh-app .fh-seal-badge{\n  position:absolute; top:16px; right:16px; width:46px; height:46px; border-radius:50%;\n  background:var(--fh-seal); color:#fff; display:flex; align-items:center; justify-content:center;\n  font-family:var(--fh-font-display); font-size:19px; font-weight:700;\n  border:2px solid rgba(255,255,255,0.5); transform:rotate(-9deg);\n  box-shadow:0 4px 10px rgba(166,61,52,0.32); z-index:1;\n}\n.fh-app .fh-quote-mark{ color:var(--fh-jade); font-family:var(--fh-font-display); font-size:19px; opacity:0.5; }\n.fh-app .fh-origin-pill{\n  display:inline-block; margin-top:12px; font-size:11px; font-weight:700; color:var(--fh-jade);\n  background:var(--fh-jade-soft); padding:4px 12px; border-radius:20px; position:relative; z-index:1;\n}\n\n.fh-app .home-grid{display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-bottom:8px;}\n\/* [2026-07-28 \uc218\uc815] \ud648\uce74\ub4dc\ub97c \uc0c1\ub2e8 \ud070 \ubc30\ub108(\uc0ac\uc9c4 \ubbf8\ub9ac\ubcf4\uae30\/\ub3c4\uc7a5) + \ud558\ub2e8 \ubcf8\ubb38 \uad6c\uc870\ub85c \uc7ac\uc124\uacc4 *\/\n.fh-app .home-card{\n  background:var(--fh-surface); border:1.5px solid var(--fh-border); border-radius:var(--fh-radius);\n  overflow:hidden; padding:0; transition:transform .15s ease, box-shadow .15s ease;\n}\n.fh-app .home-card:active{ transform:scale(0.98); }\n.fh-app .home-card-banner{\n  width:100%; aspect-ratio:1\/1; display:flex; align-items:center; justify-content:center; overflow:hidden;\n}\n.fh-app .home-card-banner img{ width:100%; height:100%; object-fit:cover; display:block; }\n.fh-app .home-card-seal-lg{\n  width:66px; height:66px; border-radius:50%; color:#fff;\n  display:flex; align-items:center; justify-content:center;\n  font-family:var(--fh-font-display); font-size:27px; font-weight:700;\n  border:3px solid rgba(255,255,255,0.55); transform:rotate(-8deg);\n  box-shadow:0 5px 14px rgba(20,28,24,0.22);\n}\n.fh-app .home-card-body{ padding:12px 14px 15px; }\n.fh-app .home-card .ic{\n  width:28px; height:28px; border-radius:50%; background:var(--fh-jade-soft); color:var(--fh-jade);\n  display:flex; align-items:center; justify-content:center; margin-bottom:8px;\n}\n.fh-app .home-card h4{margin:0 0 3px; font-size:14px; font-family:var(--fh-font-display);}\n.fh-app .home-card p{margin:0; font-size:11.5px; color:var(--fh-muted);}\n\n\/* [2026-07-28 \ucd94\uac00] \ud648 \ud654\uba74\uc758 \"+ \uc0c8 \uc2e4 \ucd94\uac00\" \ud0c0\uc77c *\/\n.fh-app .home-card-add{\n  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;\n  border:1.5px dashed var(--fh-border); border-radius:var(--fh-radius); background:transparent;\n  color:var(--fh-muted); min-height:212px; cursor:pointer; font-size:12.5px; font-weight:600;\n  transition:border-color .15s ease, color .15s ease;\n}\n.fh-app .home-card-add:active{ border-color:var(--fh-jade); color:var(--fh-jade); }\n\n\n\/* ---------- [2026-07-25 \ucd94\uac00] \ud3f4\ub354 \uce69 ---------- *\/\n.fh-app .fh-folder-chips{ display:flex; gap:7px; overflow-x:auto; padding:2px 2px 12px; scrollbar-width:none; }\n.fh-app .fh-folder-chips::-webkit-scrollbar{display:none;}\n.fh-app .fh-chip{\n  flex:0 0 auto; border:1px solid var(--fh-border); background:var(--fh-surface); color:var(--fh-ink-soft);\n  font-size:12px; padding:7px 13px; border-radius:20px; white-space:nowrap;\n}\n.fh-app .fh-chip.active{ background:var(--fh-jade); border-color:var(--fh-jade); color:#fff; font-weight:700; }\n.fh-app .fh-chip-manage{ color:var(--fh-seal); border-color:var(--fh-seal-soft); background:var(--fh-seal-soft); font-weight:700; }\n\n.fh-app .fh-folder-row{ display:flex; align-items:center; gap:5px; margin-bottom:8px; flex-wrap:nowrap; }\n.fh-app .fh-folder-row input{\n  flex:1; min-width:0; border:1px solid var(--fh-border); border-radius:10px; padding:10px 11px;\n  font-size:13.5px; background:var(--fh-surface-2); color:var(--fh-ink);\n}\n.fh-app .fh-folder-btn{ flex:0 0 auto; width:auto; padding:8px 9px; font-size:11px; }\n\n\/* ---------- \uce74\ub4dc\ud615 \ub9ac\uc2a4\ud2b8 (\ucd94\uc5b5\uc2e4\/\ubbf8\ub798\uc2e4 \ub4f1, \ub370\uc2a4\ud06c\ud1b1\uc5d0\uc11c 3\uc5f4) ---------- *\/\n.fh-app .fh-card-list{ display:flex; flex-direction:column; }\n\n\/* ---------- \ub370\uc2a4\ud06c\ud1b1 \uc0ac\uc9c4 \uadf8\ub9ac\ub4dc (\ucf54\ubc84\ud50c\ub85c\uc6b0 \ub300\uccb4, 3\uc5f4) ---------- *\/\n.fh-app .fh-photo-grid3{ display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-bottom:8px; }\n.fh-app .fh-grid-card{ background:var(--fh-surface); border:1px solid var(--fh-border); border-radius:var(--fh-radius); padding:12px; cursor:pointer; }\n.fh-app .fh-grid-photo{\n  width:100%; aspect-ratio:1; border-radius:10px; background:var(--fh-jade-soft); overflow:hidden; margin-bottom:10px;\n  display:flex; align-items:center; justify-content:center;\n  font-family:var(--fh-font-display); font-size:28px; color:var(--fh-jade);\n}\n.fh-app .fh-grid-photo img{ width:100%; height:100%; object-fit:cover; }\n.fh-app .fh-grid-card h4{ margin:0; font-size:14px; font-family:var(--fh-font-display); }\n.fh-app .fh-grid-card .cat{ font-size:10.5px; font-weight:700; color:var(--fh-seal); margin-bottom:4px; display:block; }\n\n\/* ---------- People ---------- *\/\n.fh-app .people-grid{display:grid; grid-template-columns:1fr 1fr; gap:12px;}\n.fh-app .person-card{background:var(--fh-surface); border:1px solid var(--fh-border); border-radius:var(--fh-radius); padding:14px;}\n.fh-app .person-photo{\n  width:100%; aspect-ratio:1; border-radius:12px; background:var(--fh-jade-soft);\n  display:flex;align-items:center;justify-content:center; overflow:hidden; margin-bottom:10px;\n  color:var(--fh-jade); font-family:var(--fh-font-display); font-size:26px; font-weight:700;\n}\n.fh-app .person-photo img{width:100%;height:100%;object-fit:cover;}\n.fh-app .person-card h4{margin:0 0 2px; font-size:14.5px; font-family:var(--fh-font-display);}\n.fh-app .person-card .rel{font-size:11.5px; color:var(--fh-jade); font-weight:600;}\n.fh-app .person-card .years{font-size:11px; color:var(--fh-muted); margin-top:2px;}\n\n.fh-app .detail-photo{\n  width:100%; aspect-ratio:4\/3; border-radius:var(--fh-radius); background:var(--fh-jade-soft);\n  display:flex;align-items:center;justify-content:center; overflow:hidden; margin-bottom:16px;\n  color:var(--fh-jade); font-family:var(--fh-font-display); font-size:44px; font-weight:700;\n}\n.fh-app .detail-photo img{width:100%;height:100%;object-fit:cover;}\n.fh-app .detail-block{background:var(--fh-surface); border:1px solid var(--fh-border); border-radius:var(--fh-radius); padding:18px; margin-bottom:14px;}\n.fh-app .detail-block h2{font-family:var(--fh-font-display); margin:0 0 4px; font-size:20px;}\n.fh-app .detail-block .sub{color:var(--fh-jade); font-weight:600; font-size:13px; margin-bottom:10px;}\n.fh-app .detail-block .meta-row{display:flex; gap:16px; margin:10px 0; font-size:12.5px; color:var(--fh-ink-soft); flex-wrap:wrap;}\n.fh-app .detail-block .meta-row div b{display:block; color:var(--fh-muted); font-size:10.5px; font-weight:600;}\n.fh-app .detail-block .bio{font-size:13.5px; line-height:1.8; color:var(--fh-ink); white-space:pre-wrap; margin-top:8px;}\n\n\/* ---------- Timeline ---------- *\/\n.fh-app .timeline{position:relative; padding-left:26px;}\n.fh-app .timeline::before{content:\"\"; position:absolute; left:6px; top:6px; bottom:6px; width:2px; background:var(--fh-border);}\n.fh-app .tl-item{position:relative; margin-bottom:22px;}\n.fh-app .tl-item::before{\n  content:\"\"; position:absolute; left:-26px; top:3px; width:13px; height:13px; border-radius:50%;\n  background:var(--fh-seal); border:3px solid var(--fh-bg);\n}\n.fh-app .tl-year{font-family:var(--fh-font-display); font-weight:700; font-size:15px; color:var(--fh-seal);}\n.fh-app .tl-card{background:var(--fh-surface); border:1px solid var(--fh-border); border-radius:var(--fh-radius-sm); padding:12px 14px; margin-top:6px;}\n.fh-app .tl-card h4{margin:0 0 4px; font-size:14px; font-family:var(--fh-font-display);}\n.fh-app .tl-card p{margin:0; font-size:12.5px; color:var(--fh-ink-soft); line-height:1.6;}\n\n\/* ---------- Gallery (legacy grid, kept for reference\/unused) ---------- *\/\n.fh-app .gallery-grid{display:grid; grid-template-columns:1fr 1fr 1fr; gap:6px;}\n.fh-app .gallery-item{aspect-ratio:1; border-radius:8px; overflow:hidden; background:var(--fh-jade-soft); position:relative;}\n.fh-app .gallery-item img{width:100%;height:100%;object-fit:cover;}\n\n\/* ---------- Coverflow (\uc601\ud654\uad00\uc2dd 3D \uce90\ub7ec\uc140) ---------- *\/\n.fh-app .fh-viewmode-toggle{\n  display:flex; gap:6px; margin:0 0 12px;\n}\n.fh-app .fh-vm-btn{\n  flex:0 0 auto; display:flex; align-items:center; gap:5px; border:1px solid var(--fh-border);\n  background:var(--fh-surface); color:var(--fh-ink-soft); border-radius:20px; padding:6px 13px; font-size:11.5px; font-weight:600;\n}\n.fh-app .fh-vm-btn.active{ background:var(--fh-jade); border-color:var(--fh-jade); color:#fff; }\n.fh-app .fh-cover-stage{\n  position:relative; height:230px; perspective:900px; margin:4px 0 14px;\n}\n.fh-app .fh-cover-track{ position:relative; width:100%; height:100%; transform-style:preserve-3d; }\n.fh-app .fh-cover-item{\n  position:absolute; top:50%; left:50%; width:165px; height:165px;\n  border-radius:14px; overflow:hidden; background:var(--fh-jade-soft);\n  box-shadow:0 14px 30px rgba(30,42,37,0.28);\n  transition:transform .38s cubic-bezier(.22,.68,0,1.21), opacity .38s ease;\n  cursor:pointer; will-change:transform;\n}\n.fh-app .fh-cover-item img{width:100%;height:100%;object-fit:cover;}\n.fh-app .fh-cover-noimg{\n  width:100%;height:100%;display:flex;align-items:center;justify-content:center;\n  font-family:var(--fh-font-display); font-size:40px; color:var(--fh-jade);\n}\n.fh-app .fh-cover-nav{\n  position:absolute; top:50%; transform:translateY(-50%); width:32px; height:32px; border-radius:50%;\n  background:rgba(30,42,37,0.55); color:#fff; border:none; font-size:13px; z-index:200;\n  display:flex; align-items:center; justify-content:center;\n}\n.fh-app .fh-cover-nav.prev{left:2px;}\n.fh-app .fh-cover-nav.next{right:2px;}\n.fh-app .fh-cover-caption{ text-align:center; padding:0 24px; margin-bottom:14px; min-height:60px; }\n.fh-app .fh-cover-caption .cat{ font-size:10.5px; font-weight:700; color:var(--fh-seal); letter-spacing:0.5px; }\n.fh-app .fh-cover-caption h4{ font-family:var(--fh-font-display); font-size:15.5px; margin:4px 0; }\n.fh-app .fh-cover-caption p{ font-size:12px; color:var(--fh-ink-soft); margin:0; line-height:1.6; }\n.fh-app .fh-filmstrip{\n  display:flex; gap:8px; overflow-x:auto; padding:2px 4px 8px; scrollbar-width:none;\n}\n.fh-app .fh-filmstrip::-webkit-scrollbar{display:none;}\n.fh-app .fh-film-thumb{\n  flex:0 0 auto; width:52px; height:52px; border-radius:8px; overflow:hidden; border:2px solid transparent;\n  padding:0; background:var(--fh-jade-soft); opacity:0.55; transition:opacity .2s, border-color .2s, transform .2s;\n}\n.fh-app .fh-film-thumb img{width:100%;height:100%;object-fit:cover;}\n.fh-app .fh-film-thumb span{\n  display:flex;align-items:center;justify-content:center;width:100%;height:100%;\n  font-family:var(--fh-font-display); color:var(--fh-jade);\n}\n.fh-app .fh-film-thumb.active{ opacity:1; border-color:var(--fh-seal); transform:translateY(-3px); }\n\n\/* ---------- Stories ---------- *\/\n.fh-app .story-card{background:var(--fh-surface); border:1px solid var(--fh-border); border-radius:var(--fh-radius); padding:16px; margin-bottom:12px;}\n.fh-app .story-card .cat{font-size:10.5px; font-weight:700; color:var(--fh-seal); letter-spacing:0.5px;}\n.fh-app .story-card h4{margin:6px 0 6px; font-family:var(--fh-font-display); font-size:15.5px;}\n.fh-app .story-card p{margin:0; font-size:12.5px; color:var(--fh-ink-soft); line-height:1.7; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;}\n.fh-app .story-full{font-size:13.5px; line-height:1.85; color:var(--fh-ink); white-space:pre-wrap;}\n\n\/* ---------- Tab bar (\ud5e4\ub354 \ub0b4\ubd80 \ud1b5\uacc4\ubaa9\ub85d \uc790\ub9ac\ub85c \uc774\ub3d9) ---------- *\/\n.fh-app nav.fh-tabbar{\n  display:flex; gap:8px; margin-top:16px; position:relative; z-index:1;\n  overflow-x:auto; overflow-y:hidden;\n  scroll-behavior:smooth;\n  -webkit-overflow-scrolling:touch;\n  touch-action:pan-x;\n  scrollbar-width:none;\n  max-width:100%;\n}\n.fh-app nav.fh-tabbar::-webkit-scrollbar{display:none;}\n.fh-app .tab-btn{\n  flex:0 0 auto; min-width:60px; min-height:44px; background:rgba(255,255,255,0.1); border:1px solid rgba(255,255,255,0.16);\n  border-radius:12px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px;\n  color:rgba(244,245,240,0.78); font-size:10.5px; padding:8px 9px; white-space:nowrap;\n  transition:background .15s ease, color .15s ease, border-color .15s ease;\n}\n.fh-app .tab-btn.active{\n  background:var(--fh-seal); border-color:var(--fh-seal); color:#fff; font-weight:700;\n}\n.fh-app .tab-btn svg{width:19px;height:19px; flex-shrink:0;}\n\n\/* ---------- Modal ---------- *\/\n.fh-app .modal-backdrop{\n  position:fixed; inset:0; background:rgba(20,24,20,0.5); display:flex; align-items:flex-end;\n  z-index:9100; justify-content:center;\n}\n.fh-app .modal-sheet{\n  background:var(--fh-surface); width:100%; max-width:480px; border-radius:20px 20px 0 0;\n  padding:20px 20px 28px; max-height:88vh; overflow-y:auto;\n  animation:fhSlideUp .22s ease-out; position:relative;\n}\n@keyframes fhSlideUp{from{transform:translateY(24px); opacity:0;} to{transform:translateY(0); opacity:1;}}\n.fh-app .modal-handle{width:36px;height:4px;border-radius:2px;background:var(--fh-border); margin:0 auto 16px;}\n.fh-app .modal-sheet h3{font-family:var(--fh-font-display); margin:0 0 16px; font-size:17px;}\n.fh-app .field{margin-bottom:14px;}\n.fh-app .field label{display:block; font-size:12px; font-weight:600; color:var(--fh-ink-soft); margin-bottom:6px;}\n.fh-app .field input, .fh-app .field textarea, .fh-app .field select{\n  width:100%; border:1px solid var(--fh-border); border-radius:10px; padding:11px 12px;\n  font-size:14px; background:var(--fh-surface-2); color:var(--fh-ink);\n}\n.fh-app .field textarea{min-height:90px; resize:vertical;}\n.fh-app .field input:focus, .fh-app .field textarea:focus, .fh-app .field select:focus{outline:2px solid var(--fh-jade); outline-offset:1px;}\n.fh-app .field-row{display:flex; gap:10px;}\n.fh-app .field-row .field{flex:1;}\n.fh-app .photo-upload{\n  border:1.5px dashed var(--fh-border); border-radius:12px; padding:16px; text-align:center;\n  color:var(--fh-muted); font-size:12.5px; position:relative; overflow:hidden;\n}\n.fh-app .photo-upload img{max-width:100%; max-height:140px; border-radius:8px; margin:0 auto;}\n.fh-app .photo-upload input{position:absolute; inset:0; opacity:0; cursor:pointer;}\n.fh-app .btn-row{display:flex; gap:10px; margin-top:18px;}\n.fh-app .btn{flex:1; border:none; border-radius:12px; padding:13px; font-size:14px; font-weight:700;}\n.fh-app .btn-primary{background:var(--fh-jade); color:#fff;}\n.fh-app .btn-primary:disabled{opacity:0.6;}\n.fh-app .btn-ghost{background:var(--fh-surface-2); color:var(--fh-ink-soft); border:1px solid var(--fh-border);}\n.fh-app .btn-danger{background:var(--fh-seal-soft); color:var(--fh-seal);}\n.fh-app .close-x{position:absolute; top:16px; right:16px; background:none; border:none; color:var(--fh-muted); font-size:20px;}\n.fh-app .back-btn{background:none; border:none; color:var(--fh-jade); font-size:13.5px; font-weight:600; padding:0 0 14px; display:flex; align-items:center; gap:4px;}\n.fh-app .login-note{\n  background:var(--fh-jade-soft); color:var(--fh-jade-dark); border-radius:var(--fh-radius-sm);\n  padding:12px 14px; font-size:12px; margin-bottom:14px; line-height:1.6;\n}\n\n\/* ---------- \uc2b9\uc778 \ub300\uae30 \/ \uae00\uc4f0\uae30 \uc720\uc758\uc0ac\ud56d ---------- *\/\n.fh-app .fh-pending-badge{\n  display:inline-block; font-size:10.5px; font-weight:700; color:var(--fh-seal);\n  background:var(--fh-seal-soft); padding:4px 10px; border-radius:20px; margin:0 0 8px;\n}\n.fh-app .fh-moderation-notice{\n  background:var(--fh-surface-2); border:1px solid var(--fh-border); border-radius:var(--fh-radius-sm);\n  padding:11px 13px; font-size:11.5px; color:var(--fh-ink-soft); line-height:1.6; margin-bottom:16px;\n}\n\n\/* ---------- \ub313\uae00 ---------- *\/\n.fh-app .fh-comments{ margin-top:20px; padding-top:16px; border-top:1px solid var(--fh-border); }\n.fh-app .fh-comments h5{ margin:0 0 10px; font-size:13px; font-family:var(--fh-font-display); color:var(--fh-ink); }\n.fh-app .fh-comment-item{ background:var(--fh-surface-2); border-radius:10px; padding:10px 12px; margin-bottom:8px; }\n.fh-app .fh-comment-meta{ display:flex; justify-content:space-between; gap:8px; font-size:11px; color:var(--fh-muted); margin-bottom:4px; }\n.fh-app .fh-comment-meta b{ color:var(--fh-ink); font-weight:700; }\n.fh-app .fh-comment-item p{ margin:0; font-size:12.5px; color:var(--fh-ink); line-height:1.6; white-space:pre-wrap; }\n.fh-app .fh-comment-del{ margin-top:6px; background:none; border:none; color:var(--fh-seal); font-size:11px; font-weight:600; padding:0; }\n.fh-app .fh-comment-empty{ font-size:12px; color:var(--fh-muted); margin:0 0 10px; }\n.fh-app .fh-comment-form textarea{\n  width:100%; min-height:60px; border:1px solid var(--fh-border); border-radius:10px;\n  padding:9px 11px; font-size:13px; background:var(--fh-surface-2); color:var(--fh-ink); resize:vertical;\n}\n.fh-app .fh-comment-form .btn{ margin-top:8px; width:auto; padding:9px 16px; }\n\n\/* ---------- \uc5ed\uc0ac\uad00\ubcc4 \uad00\ub9ac\uc790 ---------- *\/\n.fh-app .fh-admin-row{\n  display:flex; align-items:center; justify-content:space-between; background:var(--fh-surface-2);\n  border-radius:8px; padding:8px 10px; margin-bottom:6px; font-size:12.5px;\n}\n.fh-app .fh-admin-row .btn-danger{ width:auto; padding:6px 12px; font-size:11.5px; }\n\n.fh-app .fh-toast{\n  position:fixed; bottom:96px; left:50%; transform:translateX(-50%);\n  background:var(--fh-ink); color:#fff; padding:10px 18px; border-radius:24px; font-size:12.5px;\n  z-index:9200; opacity:0; transition:opacity .25s; pointer-events:none;\n}\n.fh-app .fh-toast.show{opacity:1;}\n\n@media (max-width:380px){\n  .fh-app .people-grid, .fh-app .gallery-grid{gap:8px;}\n  .fh-app .fh-photo-grid3{ gap:8px; }\n  .fh-app .fh-grid-card{ padding:7px; border-radius:10px; }\n  .fh-app .fh-grid-photo{ border-radius:7px; margin-bottom:5px; font-size:18px; }\n  .fh-app .fh-grid-card h4{ font-size:11px; line-height:1.3; }\n  .fh-app .fh-grid-card .cat{ font-size:9px; margin-bottom:2px; }\n  .fh-app header{ padding:18px 14px 18px; }\n  .fh-app .crest{ width:44px; height:44px; font-size:18px; }\n  .fh-app .header-text h1{ font-size:17px; }\n  .fh-app .header-text p{ font-size:11px; }\n  .fh-app nav.fh-tabbar{ gap:6px; margin-top:12px; }\n  .fh-app .tab-btn{ min-width:52px; padding:7px 7px; font-size:9.5px; }\n  .fh-app .tab-btn svg{ width:17px; height:17px; }\n  .fh-app .home-card-seal-lg{ width:52px; height:52px; font-size:22px; }\n}\n\n\/* ================= \ub370\uc2a4\ud06c\ud1b1 (880px \uc774\uc0c1): 3x3 \uadf8\ub9ac\ub4dc \ub808\uc774\uc544\uc6c3 =================\n   \ubaa8\ubc14\uc77c(\uae30\ubcf8) \uc2a4\ud0c0\uc77c\uc740 \uc704\uc5d0\uc11c \uadf8\ub300\ub85c \uc720\uc9c0\ub418\uace0, \uc774 \uad6c\uac04\ub9cc \ud3ed\uc744 \ub113\ud788\uace0\n   \uce74\ub4dc\ud615 \ubaa9\ub85d\ub4e4\uc744 3\uc5f4 \uadf8\ub9ac\ub4dc\ub85c \ubc14\uafc9\ub2c8\ub2e4. \ud5e4\ub354 \ub0b4\ubd80 \ud0ed \ubaa9\ub85d\ub3c4 \uac00\uc6b4\ub370 \uc815\ub82c\ud569\ub2c8\ub2e4. *\/\n@media (min-width:880px){\n  .fh-app{ max-width:1080px; padding-bottom:40px; }\n  .fh-app nav.fh-tabbar{ justify-content:center; flex-wrap:wrap; overflow:visible; }\n  .fh-app .tab-btn{ min-width:76px; }\n  .fh-app main{ padding:24px 28px 60px; }\n  .fh-app header{ padding:26px 32px 26px; }\n\n  .fh-app .home-grid,\n  .fh-app .people-grid,\n  .fh-app .fh-card-list{\n    grid-template-columns:repeat(3,1fr);\n    display:grid;\n    gap:18px;\n  }\n  .fh-app .fh-photo-grid3{ grid-template-columns:repeat(3,1fr); gap:18px; }\n  .fh-app .home-card-banner{ aspect-ratio:16\/9; }\n  .fh-app .home-card-seal-lg{ width:78px; height:78px; font-size:32px; }\n\n  .fh-app .add-fab{\n    right:auto; bottom:24px;\n    left:calc(50% + 540px - 76px);\n  }\n\n  \/* \ud3f4\ub354\ub97c \uc5f4\ub78c \uc911\uc77c \ub54c(=\ucf54\ubc84\ud50c\ub85c\uc6b0\uac00 \ub098\ud0c0\ub0a0 \ub54c) \ub354 \ud06c\uace0 \uc6c5\uc7a5\ud558\uac8c *\/\n  .fh-app .fh-cover-stage{ height:320px; margin:8px 0 20px; }\n  .fh-app .fh-cover-item{ width:230px; height:230px; border-radius:18px; }\n  .fh-app .fh-cover-nav{ width:42px; height:42px; font-size:16px; }\n  .fh-app .fh-cover-caption{ max-width:520px; margin-left:auto; margin-right:auto; }\n  .fh-app .fh-cover-caption h4{ font-size:18px; }\n  .fh-app .fh-filmstrip{ justify-content:center; }\n  .fh-app .fh-film-thumb{ width:64px; height:64px; }\n}\n@media (min-width:1180px){\n  .fh-app{ max-width:1240px; }\n  .fh-app .add-fab{ left:calc(50% + 620px - 76px); }\n}\n\n\/* [2026-10-28 \ucd94\uac00] \uc885\uc911 \uc7ac\uc0b0\uc2e4 *\/\n.fh-app .fh-asset-lock{background:var(--fh-seal-soft);color:var(--fh-seal);font-size:12px;font-weight:600;padding:9px 12px;border-radius:var(--fh-radius-sm);margin-bottom:12px;line-height:1.5;}\n.fh-app .fh-asset-stats{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:14px;}\n.fh-app .fh-asset-stat{background:var(--fh-surface);border:1px solid var(--fh-border);border-radius:var(--fh-radius-sm);padding:12px;display:flex;flex-direction:column;gap:3px;min-width:0;}\n.fh-app .fh-asset-stat span{font-size:11px;color:var(--fh-muted);font-weight:600;}\n.fh-app .fh-asset-stat b{font-family:var(--fh-font-display);font-size:16px;color:var(--fh-jade-dark);word-break:keep-all;}\n.fh-app .fh-asset-stat i{font-style:normal;font-size:11px;color:var(--fh-ink-soft);}\n.fh-app .fh-asset-tools{display:flex;gap:8px;margin-bottom:12px;}\n.fh-app .fh-asset-tools input{flex:1;min-width:0;padding:10px 12px;border:1px solid var(--fh-border);border-radius:var(--fh-radius-sm);font-size:13px;background:var(--fh-surface);font-family:inherit;}\n.fh-app .fh-asset-tools .btn{white-space:nowrap;font-size:12px;padding:0 12px;}\n.fh-app .fh-asset-list{display:grid;grid-template-columns:1fr;gap:10px;}\n.fh-app .fh-asset-card{background:var(--fh-surface);border:1px solid var(--fh-border);border-left:4px solid #6B6B2A;border-radius:var(--fh-radius-sm);padding:13px 14px;cursor:pointer;}\n.fh-app .fh-asset-card-top{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:4px;}\n.fh-app .fh-asset-kind{font-size:11px;font-weight:700;color:#6B6B2A;}\n.fh-app .fh-asset-card h4{margin:0 0 3px;font-family:var(--fh-font-display);font-size:15px;}\n.fh-app .fh-asset-addr{font-size:12px;color:var(--fh-ink-soft);margin-bottom:8px;}\n.fh-app .fh-asset-nums{display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--fh-ink-soft);}\n.fh-app .fh-asset-nums b{color:var(--fh-jade-dark);font-size:13px;}\n.fh-app .fh-asset-badge{display:inline-block;font-size:10.5px;font-weight:700;padding:2px 8px;border-radius:99px;background:var(--fh-seal-soft);color:var(--fh-seal);}\n.fh-app .fh-asset-row{display:flex;gap:10px;padding:8px 0;border-bottom:1px solid var(--fh-border);font-size:13px;}\n.fh-app .fh-asset-row b{flex:0 0 88px;color:var(--fh-muted);font-weight:600;}\n.fh-app .fh-asset-row span{flex:1;word-break:break-all;}\n@media (min-width:880px){\n  .fh-app .fh-asset-stats{grid-template-columns:repeat(4,1fr);}\n  .fh-app .fh-asset-list{grid-template-columns:repeat(3,1fr);}\n}\n\n\/* [2026-10-28 \ucd94\uac00] \ud68c\ucc28\ubcc4 \uae30\ub85d *\/\n.fh-app .fh-asset-round{display:flex;align-items:center;gap:8px;margin-bottom:10px;font-size:12px;font-weight:600;color:var(--fh-ink-soft);}\n.fh-app .fh-asset-round select{flex:1;padding:9px 10px;border:1px solid var(--fh-border);border-radius:var(--fh-radius-sm);background:var(--fh-surface);font:inherit;font-size:13px;}\n.fh-app .fh-asset-roundnote{background:var(--fh-jade-soft);color:var(--fh-jade-dark);font-size:12px;font-weight:600;padding:8px 12px;border-radius:var(--fh-radius-sm);margin-bottom:10px;}\n.fh-app .fh-round-box{margin-top:16px;border-top:2px solid var(--fh-border);padding-top:12px;}\n.fh-app .fh-round-title{display:flex;justify-content:space-between;align-items:center;font-weight:700;font-size:13px;margin-bottom:8px;}\n.fh-app .fh-round-item{border:1px solid var(--fh-border);border-left:4px solid var(--fh-jade);border-radius:var(--fh-radius-sm);padding:9px 11px;margin-bottom:8px;background:var(--fh-surface-2);}\n.fh-app .fh-round-head{display:flex;align-items:center;gap:8px;font-size:12.5px;}\n.fh-app .fh-round-head span{color:var(--fh-muted);flex:1;}\n.fh-app .fh-round-edit{border:0;background:none;color:var(--fh-jade);font-size:12px;font-weight:700;}\n.fh-app .fh-round-body{font-size:12.5px;color:var(--fh-ink-soft);margin-top:4px;line-height:1.6;}\n.fh-app .fh-round-memo{font-size:12.5px;margin-top:4px;white-space:pre-wrap;}\n.fh-app .fh-round-empty{font-size:12.5px;color:var(--fh-muted);padding:8px 0;}\n.fh-app .fh-delta{font-size:11.5px;font-weight:700;}\n.fh-app .fh-delta.up{color:#C0392B;} .fh-app .fh-delta.down{color:#2F6FB0;}\n.fh-app .fh-check{display:flex;gap:8px;align-items:flex-start;font-size:12px;color:var(--fh-ink-soft);line-height:1.5;}\n\n.fh-app .fh-asset-badge.pub{background:var(--fh-jade-soft);color:var(--fh-jade-dark);}\n.fh-app .fh-vis-box{border:1px dashed var(--fh-jade);border-radius:var(--fh-radius-sm);padding:12px;margin-bottom:14px;background:var(--fh-surface-2);}\n.fh-app .fh-check-note{font-size:11.5px;color:var(--fh-ink-soft);line-height:1.5;margin-bottom:8px;}\n\n.fh-app .fh-backup-box{margin-top:22px;padding:14px;border:1px solid var(--fh-border);border-radius:var(--fh-radius-sm);background:var(--fh-surface-2);}\n.fh-app .fh-backup-box p{font-size:12px;color:var(--fh-ink-soft);line-height:1.6;margin:6px 0 10px;}\n.fh-app .fh-backup-box .fh-asset-tools{margin-bottom:0;}\n.fh-app .fh-backup-box .btn{flex:1;}\n\n\/* [2026-10-28 \ucd94\uac00] \uc18c\uacc4\u00b7\ud569\uacc4 \ud45c *\/\n.fh-app .fh-sum-box{background:var(--fh-surface);border:1px solid var(--fh-border);border-radius:var(--fh-radius-sm);padding:12px;margin-bottom:14px;}\n.fh-app .fh-sum-head{display:flex;justify-content:space-between;align-items:center;gap:8px;font-weight:700;font-size:13px;margin-bottom:8px;flex-wrap:wrap;}\n.fh-app .fh-sum-toggle{display:flex;gap:4px;}\n.fh-app .fh-sum-toggle button{border:1px solid var(--fh-border);background:var(--fh-surface-2);padding:4px 10px;border-radius:99px;font-size:11.5px;color:var(--fh-ink-soft);}\n.fh-app .fh-sum-toggle button.on{background:var(--fh-jade);border-color:var(--fh-jade);color:#fff;font-weight:700;}\n.fh-app .fh-sum-scroll{overflow-x:auto;}\n.fh-app .fh-sum-table{width:100%;border-collapse:collapse;font-size:12px;min-width:360px;}\n.fh-app .fh-sum-table th,.fh-app .fh-sum-table td{padding:7px 6px;border-bottom:1px solid var(--fh-border);text-align:right;white-space:nowrap;}\n.fh-app .fh-sum-table th:first-child{text-align:left;font-weight:600;}\n.fh-app .fh-sum-table thead th{color:var(--fh-muted);font-size:11px;font-weight:600;}\n.fh-app .fh-sum-table tr.total th,.fh-app .fh-sum-table tr.total td{background:var(--fh-jade-soft);color:var(--fh-jade-dark);font-weight:700;border-bottom:0;}\n.fh-app .fh-sum-foot{font-size:11.5px;color:var(--fh-ink-soft);margin-top:8px;line-height:1.6;}\n\n.fh-app .fh-sum-table th small{display:block;font-weight:400;font-size:10.5px;color:var(--fh-muted);}\n.fh-app .fh-trend-table{min-width:420px;}\n.fh-app .fh-trend-val{position:relative;min-width:110px;}\n.fh-app .fh-trend-val i{position:absolute;left:0;top:4px;bottom:4px;background:var(--fh-jade-soft);border-radius:4px;z-index:0;}\n.fh-app .fh-trend-val span{position:relative;z-index:1;font-weight:700;}<\/style><div id=\"fh-app-root\" class=\"fh-app\"><\/div><script>window.FH_CONFIG = {\"hallId\":\"1\",\"restUrl\":\"https:\\\/\\\/korean.haus\\\/index.php?rest_route=\\\/family-hall\\\/v1\\\/\",\"mediaRestUrl\":\"https:\\\/\\\/korean.haus\\\/index.php?rest_route=\\\/wp\\\/v2\\\/media\",\"nonce\":\"64849e3b78\",\"canEdit\":false,\"isLoggedIn\":false,\"currentUserId\":0};<\/script><script>(function(){\n\"use strict\";\n\nvar CFG = window.FH_CONFIG || {};\nif(!CFG.hallId) CFG.hallId = '1'; \/* [2026-07-26 \ucd94\uac00] \uba40\ud2f0 \uc5ed\uc0ac\uad00(hall id) \uc9c0\uc6d0 *\/\nvar ROOT = document.getElementById('fh-app-root');\nif(!ROOT){ return; }\n\nvar State = {\n  tab:'home',\n  meta:{ familyName:'\uc6b0\ub9ac \uac00\ubb38', crestChar:'\u5bb6', originYear:'', motto:'' },\n  people:[], timeline:[], gallery:[], stories:[],\n  founder:[], branch:[], sites:[], heritage:[], future:[],\n  folders:{},          \/* stateKey -> [{id,name}, ...] (\ud3b8\uc9d1\uc790\uac00 \ub9cc\ub4e0 \ud558\uc704 \ud3f4\ub354) *\/\n  activeFolder:{},      \/* stateKey -> \ud604\uc7ac \uc120\ud0dd\ub41c folderId ('' = \uc804\uccb4, \ud654\uba74\uc5d0\ub9cc \uc874\uc7ac) *\/\n  comments:{},          \/* \"sectionKey:itemId\" -> [{id,author,authorId,text,date}, ...] *\/\n  assets:[],            \/* [2026-10-28 \ucd94\uac00] \uc885\uc911 \uc7ac\uc0b0(\ubd80\ub3d9\uc0b0\u00b7\ud1a0\uc9c0) - \uad00\ub9ac\uc790 \uc804\uc6a9 *\/\n  customSections:[]     \/* [2026-07-28 \ucd94\uac00] \uad00\ub9ac\uc790\uac00 \uc790\uc720\ub86d\uac8c \ucd94\uac00\ud55c \uc2e4: [{id,name,items:[...]}, ...] *\/\n};\n\n\/* \uc0c1\ub2e8~\ud558\ub2e8 \ud0ed \uc815\uc758 (\uc544\uc774\ucf58\uc740 24x24 stroke \uae30\uc900 path) *\/\nvar TABS = [\n  { id:'home', label:'\ud648', icon:'<path d=\"M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"\/><path d=\"M9 22V12h6v10\"\/>' },\n  { id:'founder', label:'\uc2dc\uc870\uc2e4', icon:'<path d=\"M12 2l2.6 5.9L21 9l-4.9 4.1L17.6 20 12 16.6 6.4 20l1.5-6.9L3 9l6.4-1.1z\"\/>' },\n  { id:'branch', label:'\ubd84\ud30c\uc2e4', icon:'<circle cx=\"6\" cy=\"6\" r=\"2.4\"\/><circle cx=\"18\" cy=\"6\" r=\"2.4\"\/><circle cx=\"12\" cy=\"18\" r=\"2.4\"\/><path d=\"M6 8.4V12a3 3 0 0 0 3 3h1M18 8.4V12a3 3 0 0 1-3 3h-1\"\/>' },\n  { id:'people', label:'\uc778\ubb3c', icon:'<path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"\/><circle cx=\"9\" cy=\"7\" r=\"4\"\/><path d=\"M23 21v-2a4 4 0 0 0-3-3.87\"\/><path d=\"M16 3.13a4 4 0 0 1 0 7.75\"\/>' },\n  { id:'timeline', label:'\uc5f0\ud45c', icon:'<line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"22\"\/><circle cx=\"12\" cy=\"6\" r=\"2.4\"\/><circle cx=\"12\" cy=\"14\" r=\"2.4\"\/><circle cx=\"12\" cy=\"20.5\" r=\"1.3\"\/>' },\n  { id:'gallery', label:'\uac24\ub7ec\ub9ac', icon:'<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"\/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"\/><path d=\"M21 15l-5-5L5 21\"\/>' },\n  { id:'stories', label:'\ucd94\uc5b5\uc2e4', icon:'<path d=\"M4 19.5A2.5 2.5 0 0 1 6.5 17H20\"\/><path d=\"M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z\"\/>' },\n  { id:'sites', label:'\uc720\uc801\uc9c0', icon:'<path d=\"M3 21h18\"\/><path d=\"M5 21V7l7-4 7 4v14\"\/><path d=\"M9 21v-9h6v9\"\/>' },\n  { id:'heritage', label:'\ubb38\ud654\uc7ac\uc2e4', icon:'<rect x=\"3\" y=\"8\" width=\"18\" height=\"12\" rx=\"2\"\/><path d=\"M8 8V6a4 4 0 0 1 8 0v2\"\/><circle cx=\"12\" cy=\"14\" r=\"1.6\"\/>' },\n  { id:'future', label:'\ubbf8\ub798\uc2e4', icon:'<circle cx=\"12\" cy=\"12\" r=\"4\"\/><path d=\"M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1\"\/>' }\n];\n\n\/* [2026-10-28 \ucd94\uac00] \uc885\uc911 \uc7ac\uc0b0\uc2e4 \ud0ed - \uad00\ub9ac\uc790\uc5d0\uac8c\ub9cc \ubcf4\uc785\ub2c8\ub2e4 *\/\nif(CFG.canEdit){\n  TABS.push({ id:'assets', label:'\uc885\uc911 \uc7ac\uc0b0\uc2e4', icon:'<path d=\"M3 21h18\"\/><path d=\"M5 21V10l7-6 7 6v11\"\/><path d=\"M9 21v-6h6v6\"\/><path d=\"M9 11h6\"\/>' });\n}\n\n\/* \uc2dc\uc870\uc2e4\u00b7\ubd84\ud30c\uc2e4\u00b7\uc720\uc801\uc9c0\u00b7\ubb38\ud654\uc7ac\uc2e4\u00b7\ubbf8\ub798\uc2e4\uc740 \uad6c\uc870\uac00 \ube44\uc2b7\ud574 \uacf5\ud1b5 \uc124\uc815\uc73c\ub85c \ucc98\ub9ac\ud569\ub2c8\ub2e4.\n   \ubaa8\ub450 \ud3b8\uc9d1\uc790\uac00 \ud558\uc704 \ud3f4\ub354\ub97c \ub9cc\ub4e4\uc5b4 \ud56d\ubaa9\uc744 \ubd84\ub958\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4 (folderEnabled). *\/\nvar SIMPLE_SECTIONS = {\n  founder: {\n    label:'\uc2dc\uc870\uc2e4', stateKey:'founder',\n    emptyTitle:'\uc544\uc9c1 \ub4f1\ub85d\ub41c \uc2dc\uc870 \uae30\ub85d\uc774 \uc5c6\uc5b4\uc694', emptyDesc:'\uac00\ubb38\uc744 \uc774\ub8e8\uc2e0 \uc2dc\uc870\ub2d8\uc758 \uae30\ub85d\uc744 \ub0a8\uaca8\ubcf4\uc138\uc694.',\n    fields:[\n      { key:'name', label:'\uc774\ub984', placeholder:'\uc608: \uc774\uc131\ubb38', required:true },\n      { key:'title', label:'\ud638\uce6d\u00b7\uc9c1\ud568', placeholder:'\uc608: \uc0ac\uc9c1\uacf5' },\n      { key:'era', label:'\uc2dc\ub300\u00b7\uc5f0\ub300', placeholder:'\uc608: 1392\ub144' }\n    ],\n    bodyKey:'desc', bodyLabel:'\uc124\uba85', bodyPlaceholder:'\uc2dc\uc870\uc5d0 \ub300\ud55c \uae30\ub85d\uc744 \uc801\uc5b4\uc8fc\uc138\uc694',\n    hasPhoto:true, folderEnabled:true, folderLabel:'\ubd84\ub958'\n  },\n  branch: {\n    label:'\ubd84\ud30c\uc2e4', stateKey:'branch',\n    emptyTitle:'\uc544\uc9c1 \ub4f1\ub85d\ub41c \ubd84\ud30c\uac00 \uc5c6\uc5b4\uc694', emptyDesc:'\ubb38\uc911\uc5d0\uc11c \uac08\ub77c\uc838 \ub098\uc628 \ubd84\ud30c\ub97c \uae30\ub85d\ud574 \ubcf4\uc138\uc694.',\n    fields:[\n      { key:'name', label:'\ubd84\ud30c\uba85', placeholder:'\uc608: OO\uacf5\ud30c', required:true },\n      { key:'location', label:'\uc138\uac70\uc9c0', placeholder:'\uc608: \uacbd\ubd81 OO' },\n      { key:'era', label:'\ubd84\ud30c \uc2dc\uae30', placeholder:'\uc608: 17\uc138\uae30' }\n    ],\n    bodyKey:'desc', bodyLabel:'\uc124\uba85', bodyPlaceholder:'\ubd84\ud30c\uc5d0 \ub300\ud55c \uc124\uba85\uc744 \uc801\uc5b4\uc8fc\uc138\uc694',\n    hasPhoto:true, folderEnabled:true, folderLabel:'\ubd84\ub958'\n  },\n  sites: {\n    label:'\uc720\uc801\uc9c0', stateKey:'sites',\n    emptyTitle:'\uc544\uc9c1 \ub4f1\ub85d\ub41c \uc720\uc801\uc9c0\uac00 \uc5c6\uc5b4\uc694', emptyDesc:'\uac00\ubb38\uacfc \uad00\ub828\ub41c \uace0\ud0dd\u00b7\ubb18\uc5ed\u00b7\uc0ac\ub2f9 \ub4f1\uc758 \uc7a5\uc18c\ub97c \uae30\ub85d\ud574 \ubcf4\uc138\uc694.',\n    fields:[\n      { key:'name', label:'\uc774\ub984', placeholder:'\uc608: \uc885\uac00 \uace0\ud0dd', required:true },\n      { key:'location', label:'\uc704\uce58', placeholder:'\uc608: \uacbd\ubd81 \uacbd\uc8fc\uc2dc OO\uba74' },\n      { key:'era', label:'\uc2dc\ub300\u00b7\uc5f0\ub300', placeholder:'\uc608: \uc870\uc120 \uc911\uae30' }\n    ],\n    bodyKey:'desc', bodyLabel:'\uc124\uba85', bodyPlaceholder:'\uc720\uc801\uc9c0\uc5d0 \ub300\ud55c \uc124\uba85\uc744 \uc801\uc5b4\uc8fc\uc138\uc694',\n    hasPhoto:true, folderEnabled:true, folderLabel:'\uc9c0\uc5ed\u00b7\ubd84\ub958'\n  },\n  heritage: {\n    label:'\ubb38\ud654\uc7ac\uc2e4', stateKey:'heritage',\n    emptyTitle:'\uc544\uc9c1 \ub4f1\ub85d\ub41c \ubb38\ud654\uc7ac\uac00 \uc5c6\uc5b4\uc694', emptyDesc:'\uac00\ubb38\uc774 \ubcf4\uc720\ud558\uac70\ub098 \uc804\ud574 \ub0b4\ub824\uc624\ub294 \uc720\ubb3c\u00b7\ubb38\uc11c\ub97c \uae30\ub85d\ud574 \ubcf4\uc138\uc694.',\n    fields:[\n      { key:'name', label:'\uc774\ub984', placeholder:'\uc608: OO \uace0\ubb38\uc11c', required:true },\n      { key:'designation', label:'\uc9c0\uc815 \ud604\ud669', placeholder:'\uc608: \uc2dc\ub3c4 \uc720\ud615\ubb38\ud654\uc7ac O\ud638' },\n      { key:'era', label:'\uc2dc\ub300\u00b7\uc5f0\ub300', placeholder:'\uc608: 18\uc138\uae30' }\n    ],\n    bodyKey:'desc', bodyLabel:'\uc124\uba85', bodyPlaceholder:'\ubb38\ud654\uc7ac\uc5d0 \ub300\ud55c \uc124\uba85\uc744 \uc801\uc5b4\uc8fc\uc138\uc694',\n    hasPhoto:true, folderEnabled:true, folderLabel:'\ubd84\ub958'\n  },\n  future: {\n    label:'\ubbf8\ub798\uc2e4', stateKey:'future',\n    emptyTitle:'\uc544\uc9c1 \uae30\ub85d\ub41c \ub2e4\uc9d0\uc774 \uc5c6\uc5b4\uc694', emptyDesc:'\ud6c4\uc190\uc5d0\uac8c \uc804\ud558\uace0 \uc2f6\uc740 \ubc14\ub78c\uc774\ub098 \uac00\ubb38\uc758 \ubbf8\ub798 \ube44\uc804\uc744 \ub0a8\uaca8\ubcf4\uc138\uc694.',\n    fields:[\n      { key:'title', label:'\uc81c\ubaa9', placeholder:'\uc608: \ub2e4\uc74c \uc138\ub300\uc5d0\uac8c', required:true },\n      { key:'author', label:'\uc791\uc131\uc790', placeholder:'\uc608: 12\ub300\uc190 \uc774OO' }\n    ],\n    bodyKey:'content', bodyLabel:'\ub0b4\uc6a9', bodyPlaceholder:'\ud6c4\uc190\uc5d0\uac8c \uc804\ud558\uace0 \uc2f6\uc740 \uc774\uc57c\uae30\ub97c \uc790\uc720\ub86d\uac8c \uc801\uc5b4\uc8fc\uc138\uc694',\n    hasPhoto:false, folderEnabled:true, folderLabel:'\ubd84\ub958'\n  }\n};\n\n\/* \ud3f4\ub354(\ud558\uc704 \ubd84\ub958) \uae30\ub2a5\uc744 \uc9c0\uc6d0\ud558\ub294 \uc139\uc158\uacfc \ud45c\uc2dc \ub77c\ubca8\n   [2026-07-29 \uc218\uc815] \ud648 \ud654\uba74(\"\ub458\ub7ec\ubcf4\uae30\")\uc758 \ud3f4\ub354 \uae30\ub2a5\uc740 \uc81c\uac70\ud558\uace0,\n   \uac01 \uc2e4 \ub0b4\ubd80(\ubd84\ud30c\uc2e4 \ub4f1)\uc5d0\uc11c\ub9cc \ud3f4\ub354\ub97c \uc4f8 \uc218 \uc788\ub3c4\ub85d \uc815\ub9ac\ud588\uc2b5\ub2c8\ub2e4. *\/\nvar FOLDER_SECTIONS = {\n  people:'\uc778\ubb3c', timeline:'\uc5f0\ud45c', gallery:'\uac24\ub7ec\ub9ac', stories:'\ucd94\uc5b5\uc2e4',\n  founder:'\uc2dc\uc870\uc2e4', branch:'\ubd84\ud30c\uc2e4', sites:'\uc720\uc801\uc9c0', heritage:'\ubb38\ud654\uc7ac\uc2e4', future:'\ubbf8\ub798\uc2e4'\n};\n\n\/* ========================================================================\n   [2026-07-28 \ucd94\uac00] \ucee4\uc2a4\ud140 \uc2e4(\uad00\ub9ac\uc790\uac00 9\uac1c \uae30\ubcf8 \uc2e4 \uc678\uc5d0 \uc790\uc720\ub86d\uac8c \ucd94\uac00\ud558\ub294 \uc0c8 \uc2e4)\n   \uae30\uc874 SIMPLE_SECTIONS\/FOLDER_SECTIONS\/TABS\ub294 \uc6d0\ub798 \uace0\uc815\ub41c \uac1d\uccb4\uc600\uc9c0\ub9cc,\n   \uc5ec\uae30\uc11c\ub294 \ud56d\ubaa9\uc744 \"\ub4f1\ub85d(register)\"\ud574\uc11c \ub3d9\uc801\uc73c\ub85c \ucd94\uac00\ud569\ub2c8\ub2e4.\n   \uc774\ub807\uac8c \ud558\uba74 renderSimpleList\u00b7openSimpleForm\u00b7\ud3f4\ub354\u00b7\ucf54\ubc84\ud50c\ub85c\uc6b0\u00b7\ub313\uae00 \ub4f1\n   \uae30\uc874\uc5d0 \ub9cc\ub4e4\uc5b4\ub454 \ubc94\uc6a9 \ub85c\uc9c1\uc744 \uc0c8 \uc2e4\uc5d0\ub3c4 \uadf8\ub300\ub85c \uc7ac\uc0ac\uc6a9\ud560 \uc218 \uc788\uc5b4\uc694.\n   ======================================================================== *\/\nvar CUSTOM_ICON = '<circle cx=\"12\" cy=\"12\" r=\"9\"\/><path d=\"M12 8v8M8 12h8\"\/>'; \/* \uc0c8 \uc2e4 \uae30\ubcf8 \uc544\uc774\ucf58 (\uc6d0 \uc548\uc5d0 +) *\/\n\nfunction customSectionKey(id){ return 'custom_' + id; }\n\nfunction registerCustomSection(cs){\n  var key = customSectionKey(cs.id);\n  if(!State[key]) State[key] = cs.items || [];\n  SIMPLE_SECTIONS[key] = {\n    label: cs.name, stateKey: key,\n    emptyTitle: '\uc544\uc9c1 \ub4f1\ub85d\ub41c \uae30\ub85d\uc774 \uc5c6\uc5b4\uc694',\n    emptyDesc: '\uc774 \uc2e4\uc758 \uccab \uae30\ub85d\uc744 \ub0a8\uaca8\ubcf4\uc138\uc694.',\n    fields: [ { key:'title', label:'\uc81c\ubaa9', placeholder:'\uc81c\ubaa9\uc744 \uc785\ub825\ud558\uc138\uc694', required:true } ],\n    bodyKey:'desc', bodyLabel:'\ub0b4\uc6a9', bodyPlaceholder:'\ub0b4\uc6a9\uc744 \uc790\uc720\ub86d\uac8c \uc801\uc5b4\uc8fc\uc138\uc694',\n    hasPhoto:true, folderEnabled:true, folderLabel:'\ubd84\ub958'\n  };\n  FOLDER_SECTIONS[key] = cs.name;\n  if(!TABS.some(function(t){ return t.id===key; })){\n    TABS.push({ id:key, label:cs.name, icon:CUSTOM_ICON });\n  }\n}\n\n\/* \ud0ed \ubaa9\ub85d\uc774 \ub370\uc774\ud130 \ub85c\ub4dc \ud6c4(\ucee4\uc2a4\ud140 \uc2e4 \ud3ec\ud568) \ubc14\ub014 \uc218 \uc788\uc5b4 \ud5e4\ub354\uc758 \ud0ed\ubc14\ub97c \ub2e4\uc2dc \uadf8\ub9bd\ub2c8\ub2e4 *\/\nfunction refreshTabbar(){\n  var nav = document.getElementById('fhTabbar');\n  if(!nav) return;\n  var activeId = State.tab;\n  nav.innerHTML = TABS.map(function(t){ return tabButton(t.id, t.label, t.icon); }).join('');\n  nav.querySelectorAll('.tab-btn').forEach(function(btn){\n    if(btn.getAttribute('data-tab') === activeId) btn.classList.add('active');\n    btn.addEventListener('click', function(){\n      nav.querySelectorAll('.tab-btn').forEach(function(b){ b.classList.remove('active'); });\n      btn.classList.add('active');\n      btn.scrollIntoView({ behavior:'smooth', inline:'center', block:'nearest' });\n      State.tab = btn.getAttribute('data-tab');\n      render();\n    });\n  });\n}\n\n\/* \uc2e4 \ucd94\uac00\u00b7\uc774\ub984 \ubcc0\uacbd\u00b7\uc0ad\uc81c\ub97c \ud55c \ud654\uba74\uc5d0\uc11c \ucc98\ub9ac\ud558\ub294 \uad00\ub9ac \ubaa8\ub2ec (\ud648 \ud654\uba74\uc758 \"+ \uc0c8 \uc2e4 \ucd94\uac00\" \ud0c0\uc77c\uc5d0\uc11c \uc5f4\ub9bc) *\/\nfunction openCustomSectionManager(){\n  function bodyHtml(){\n    var list = State.customSections;\n    return (list.length ? list.map(function(cs){\n      return '<div class=\"fh-folder-row\" data-cs-row=\"' + cs.id + '\">' +\n        '<input value=\"' + escapeHtml(cs.name) + '\" data-cs-input=\"' + cs.id + '\">' +\n        '<button class=\"btn btn-ghost fh-folder-btn\" data-cs-rename=\"' + cs.id + '\">\uc218\uc815<\/button>' +\n        '<button class=\"btn btn-danger fh-folder-btn\" data-cs-del=\"' + cs.id + '\">\uc0ad\uc81c<\/button>' +\n      '<\/div>';\n    }).join('') : '<p style=\"font-size:12.5px;color:var(--fh-muted);margin:0 0 14px;\">\uc544\uc9c1 \ucd94\uac00\ud55c \uc2e4\uc774 \uc5c6\uc5b4\uc694.<\/p>') +\n    '<div class=\"field-row\" style=\"margin-top:14px;\">' +\n      '<div class=\"field\" style=\"margin-bottom:0;\"><input id=\"fhNewSectionName\" placeholder=\"\uc0c8 \uc2e4 \uc774\ub984 (\uc608: \uc885\uc911 \uc7ac\uc0b0\uc2e4)\"><\/div>' +\n      '<button class=\"btn btn-ghost\" style=\"flex:0 0 auto; padding:11px 14px;\" id=\"fhAddSection\">\ucd94\uac00<\/button>' +\n    '<\/div>';\n  }\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>\uc2e4 \ucd94\uac00 \u00b7 \uad00\ub9ac<\/h3>' +\n    '<p style=\"font-size:11.5px;color:var(--fh-muted);margin:0 0 14px;\">\uc0ac\uc9c4\u00b7\uae00\u00b7\ub313\uae00\u00b7\ud3f4\ub354\ub97c \ubaa8\ub450 \uc4f8 \uc218 \uc788\ub294 \uc0c8\ub85c\uc6b4 \uc2e4\uc744 \uc790\uc720\ub86d\uac8c \ucd94\uac00\ud560 \uc218 \uc788\uc5b4\uc694. \ucd94\uac00\ud55c \uc2e4\uc740 \ud648 \ud654\uba74\uacfc \uc0c1\ub2e8 \uba54\ub274\uc5d0 \ubc14\ub85c \ub098\ud0c0\ub098\uc694.<\/p>' +\n    '<div id=\"fhCsList\">' + bodyHtml() + '<\/div>' +\n    '<div class=\"btn-row\"><button class=\"btn btn-primary\" id=\"fhCsDone\">\uc644\ub8cc<\/button><\/div>'\n  );\n  function rebind(){\n    document.getElementById('fhCsList').innerHTML = bodyHtml();\n    document.querySelectorAll('[data-cs-rename]').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        var id = btn.getAttribute('data-cs-rename');\n        var input = document.querySelector('[data-cs-input=\"' + id + '\"]');\n        var newName = input ? input.value.trim() : '';\n        if(!newName){ showToast('\uc774\ub984\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n        var cs = State.customSections.find(function(c){ return c.id===id; });\n        if(!cs) return;\n        cs.name = newName;\n        var key = customSectionKey(id);\n        SIMPLE_SECTIONS[key].label = newName;\n        FOLDER_SECTIONS[key] = newName;\n        var t = TABS.find(function(x){ return x.id===key; });\n        if(t) t.label = newName;\n        apiSave('customSections', State.customSections).then(function(){\n          refreshTabbar(); rebind(); render(); showToast('\uc774\ub984\uc744 \uc218\uc815\ud588\uc5b4\uc694');\n        });\n      });\n    });\n    document.querySelectorAll('[data-cs-del]').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        var id = btn.getAttribute('data-cs-del');\n        if(!confirm('\uc774 \uc2e4\uc744 \uc0ad\uc81c\ud560\uae4c\uc694? \uc548\uc5d0 \uc788\ub294 \ubaa8\ub4e0 \uae30\ub85d\uc774 \ud568\uaed8 \uc0ad\uc81c\ub418\uace0 \ub418\ub3cc\ub9b4 \uc218 \uc5c6\uc5b4\uc694.')) return;\n        var key = customSectionKey(id);\n        State.customSections = State.customSections.filter(function(c){ return c.id!==id; });\n        delete State[key];\n        delete SIMPLE_SECTIONS[key];\n        delete FOLDER_SECTIONS[key];\n        TABS = TABS.filter(function(t){ return t.id!==key; });\n        if(State.tab === key) State.tab = 'home';\n        apiSave('customSections', State.customSections).then(function(){\n          refreshTabbar(); rebind(); render(); showToast('\uc0ad\uc81c\ud588\uc5b4\uc694');\n        });\n      });\n    });\n    document.getElementById('fhAddSection').addEventListener('click', function(){\n      var name = document.getElementById('fhNewSectionName').value.trim();\n      if(!name){ showToast('\uc2e4 \uc774\ub984\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n      var cs = { id: uid(), name: name, items: [] };\n      State.customSections.push(cs);\n      State[customSectionKey(cs.id)] = cs.items;\n      registerCustomSection(cs);\n      apiSave('customSections', State.customSections).then(function(){\n        refreshTabbar(); rebind(); render(); showToast('\uc0c8 \uc2e4\uc744 \ucd94\uac00\ud588\uc5b4\uc694');\n      });\n    });\n  }\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhCsDone').addEventListener('click', closeModal);\n  rebind();\n}\n\/* ===================== [2026-07-28 \ucd94\uac00] \ucee4\uc2a4\ud140 \uc2e4 \uae30\ub2a5 \ub05d ===================== *\/\n\nfunction getFolders(key){ return State.folders[key] || []; }\n\n\/* [2026-07-29 \ucd94\uac00] \ub2e4\ub2e8\uacc4(\ubb34\uc81c\ud55c \uae4a\uc774) \ud3f4\ub354 \uc9c0\uc6d0 \ud5ec\ud37c\n   \ud3f4\ub354 \uac1d\uccb4\ub294 {id, name, parent} \ud615\ud0dc\uc774\uba70 parent=''\ub294 \ucd5c\uc0c1\uc704 \ud3f4\ub354\uc785\ub2c8\ub2e4. *\/\nfunction getChildFolders(key, parentId){\n  return getFolders(key).filter(function(f){ return (f.parent||'') === (parentId||''); });\n}\n\/* \ud3f4\ub354 \ud2b8\ub9ac\ub97c \ubd80\ubaa8\u2192\uc790\uc2dd \uc21c\uc11c\ub85c \ud3c9\ud0c4\ud654\ud558\uace0, \ub4e4\uc5ec\uc4f0\uae30\uc5d0 \uc4f8 depth \uac12\uc744 \ubd99\uc5ec\uc90d\ub2c8\ub2e4 *\/\nfunction flattenFolderTree(key, parentId, depth, out){\n  parentId = parentId || ''; depth = depth || 0; out = out || [];\n  getChildFolders(key, parentId).forEach(function(f){\n    out.push({ id:f.id, name:f.name, parent:f.parent||'', depth:depth });\n    flattenFolderTree(key, f.id, depth+1, out);\n  });\n  return out;\n}\n\/* \ud3f4\ub354 \uc790\uc2e0 + \ubaa8\ub4e0 \ud558\uc704(\uc790\uc190) \ud3f4\ub354 id \ubaa9\ub85d (\uc0ad\uc81c \uc2dc \ud568\uaed8 \uc81c\uac70\ud558\uae30 \uc704\ud568) *\/\nfunction getDescendantFolderIds(key, folderId){\n  var ids = [folderId];\n  getChildFolders(key, folderId).forEach(function(f){\n    ids = ids.concat(getDescendantFolderIds(key, f.id));\n  });\n  return ids;\n}\n\/* \ub4e4\uc5ec\uc4f0\uae30\uac00 \uc801\uc6a9\ub41c <option> \ubaa9\ub85d \ubb38\uc790\uc5f4 \uc0dd\uc131 (\ud3f4\ub354 \uc120\ud0dd \ub4dc\ub86d\ub2e4\uc6b4\u00b7\ubc30\uc815 \ubaa9\ub85d\uc5d0\uc11c \uacf5\uc6a9\uc73c\ub85c \uc0ac\uc6a9) *\/\nfunction folderOptionsHtml(key, selectedId){\n  var indent = function(depth){ return depth>0 ? '\\u2003'.repeat(depth) + '\u2514 ' : ''; };\n  return '<option value=\"\">\ubbf8\uc9c0\uc815(\uc804\uccb4)<\/option>' +\n    flattenFolderTree(key).map(function(f){\n      return '<option value=\"' + f.id + '\"' + (f.id===selectedId?' selected':'') + '>' + indent(f.depth) + escapeHtml(f.name) + '<\/option>';\n    }).join('');\n}\n\n\/* \ud68c\uc6d0\uc774 \uc9c1\uc811 \uc81c\ucd9c\ud560 \uc218 \uc788\ub294 \uc2e4\uacfc \uadf8 \ubc29\uc2dd.\n   'approval' = \uad00\ub9ac\uc790 \uc2b9\uc778 \uc804\uae4c\uc9c0 \uc815\uc2dd \ubaa9\ub85d\uc5d0 \ubcf4\uc774\uc9c0 \uc54a\uc74c (\uc791\uc131\uc790 \ubcf8\uc778\u00b7\uad00\ub9ac\uc790\uc5d0\uac8c\ub9cc \"\uc2b9\uc778 \ub300\uae30\" \ud45c\uc2dc\ub85c \ubcf4\uc784)\n   'direct'   = \uc2b9\uc778 \uc808\ucc28 \uc5c6\uc774 \ub85c\uadf8\uc778 \ud68c\uc6d0\uc774 \ubc14\ub85c \uc815\uc2dd \ub4f1\ub85d *\/\nvar MEMBER_SUBMIT_SECTIONS = {\n  people:'approval', heritage:'approval', sites:'approval', gallery:'approval',\n  stories:'direct', future:'direct'\n};\n\nfunction isOwnSubmission(it){\n  return !!(CFG.currentUserId && it.submittedBy && String(it.submittedBy) === String(CFG.currentUserId));\n}\nfunction isVisibleItem(it){\n  if(!it.status || it.status === 'approved') return true;\n  return CFG.canEdit || isOwnSubmission(it);\n}\nfunction filteredItems(key){\n  var arr = (State[key] || []).filter(isVisibleItem);\n  var active = State.activeFolder[key] || '';\n  if(!active) return arr;\n  return arr.filter(function(it){ return it.folder === active; });\n}\n\n\/* \uc2b9\uc778 \ub300\uae30 \ubc30\uc9c0: \uad00\ub9ac\uc790\uc5d0\uac8c\ub294 \uc81c\ucd9c\uc790 \uc774\ub984\uc744, \uc81c\ucd9c\uc790 \ubcf8\uc778\uc5d0\uac8c\ub294 \uc548\ub0b4 \ubb38\uad6c\ub97c \ubcf4\uc5ec\uc90d\ub2c8\ub2e4 *\/\nfunction pendingBadgeHtml(it){\n  if(!it || !it.status || it.status !== 'pending') return '';\n  var label = CFG.canEdit ? ('\uc2b9\uc778 \ub300\uae30 \u00b7 ' + escapeHtml(it.submittedByName||'\ud68c\uc6d0')) : '\uc2b9\uc778 \ub300\uae30 \uc911 (\uad00\ub9ac\uc790 \ud655\uc778 \ud6c4 \uacf5\uac1c\ub3fc\uc694)';\n  return '<span class=\"fh-pending-badge\">' + label + '<\/span>';\n}\n\/* \uad00\ub9ac\uc790\uac00 \uc0c1\uc138\ubcf4\uae30\uc5d0\uc11c \ubc14\ub85c \uc2b9\uc778\ud560 \uc218 \uc788\ub294 \ubc84\ud2bc *\/\nfunction approveButtonHtml(key, it){\n  if(!CFG.canEdit || !it || it.status !== 'pending') return '';\n  return '<button class=\"btn btn-primary\" data-approve=\"' + key + ':' + it.id + '\">\uc2b9\uc778\ud558\uae30<\/button>';\n}\n\/* \uc2b9\uc778 \ucc98\ub9ac \uacf5\ud1b5 \ud568\uc218: \ubaa9\ub85d\uc5d0\uc11c \ud574\ub2f9 \ud56d\ubaa9\uc758 status\ub97c approved\ub85c \ubc14\uafb8\uace0 \uc800\uc7a5\ud569\ub2c8\ub2e4 *\/\nfunction approveItem(key, itemId){\n  var item = (State[key]||[]).find(function(x){ return x.id===itemId; });\n  if(!item) return Promise.resolve();\n  item.status = 'approved';\n  return apiSave(key, State[key]).then(function(){ applyMeta(); showToast('\uc2b9\uc778\ud588\uc5b4\uc694'); });\n}\n\n\/* \uc885\uce5c\ud68c \ud648\ud398\uc774\uc9c0 \ucde8\uc9c0\uc5d0 \ub9de\uc9c0 \uc54a\ub294 \uae00\uc5d0 \ub300\ud55c \uc548\ub0b4 \ubb38\uad6c (\uae00\uc4f0\uae30 \ud654\uba74 \uc0c1\ub2e8 \ud45c\uc2dc) *\/\nvar MODERATION_NOTICE = '\uc774 \uacf5\uac04\uc740 \uc885\uce5c\ud68c \uac00\ubb38 \uc5ed\uc0ac\uad00\uc758 \ucde8\uc9c0\uc5d0 \ub9de\ub294 \uae30\ub85d\uc744 \uc704\ud55c \uacf3\uc774\uc5d0\uc694. \ucde8\uc9c0\uc5d0 \uc5b4\uae0b\ub098\uac70\ub098 \ubd80\uc801\uc808\ud55c \ub0b4\uc6a9\uc740 \uad00\ub9ac\uc790 \ud655\uc778 \ud6c4 \uc0ac\uc804 \uc548\ub0b4 \uc5c6\uc774 \uc989\uc2dc \uc0ad\uc81c\ub420 \uc218 \uc788\uc5b4\uc694.';\nfunction moderationNoticeHtml(){\n  return '<div class=\"fh-moderation-notice\">' + escapeHtml(MODERATION_NOTICE) + '<\/div>';\n}\n\nfunction renderFolderChips(key){\n  if(!FOLDER_SECTIONS[key]) return '';\n  var flat = flattenFolderTree(key);\n  if(flat.length===0 && !CFG.canEdit) return '';\n  var active = State.activeFolder[key] || '';\n  var indent = function(depth){ return depth>0 ? '\\u2003'.repeat(depth) + '\u203a ' : ''; };\n  var chips = '<button class=\"fh-chip' + (active===''?' active':'') + '\" data-folder-chip=\"\">\uc804\uccb4<\/button>' +\n    flat.map(function(f){\n      return '<button class=\"fh-chip' + (active===f.id?' active':'') + '\" data-folder-chip=\"' + f.id + '\">' + indent(f.depth) + escapeHtml(f.name) + '<\/button>';\n    }).join('') +\n    (CFG.canEdit ? '<button class=\"fh-chip fh-chip-manage\" data-folder-manage=\"' + key + '\">+ \ud3f4\ub354<\/button>' : '');\n  return '<div class=\"fh-folder-chips\" data-folder-section=\"' + key + '\">' + chips + '<\/div>';\n}\n\nfunction bindFolderChips(key){\n  var row = document.querySelector('.fh-folder-chips[data-folder-section=\"' + key + '\"]');\n  if(!row) return;\n  row.querySelectorAll('[data-folder-chip]').forEach(function(btn){\n    btn.addEventListener('click', function(){\n      State.activeFolder[key] = btn.getAttribute('data-folder-chip');\n      render();\n    });\n  });\n  var manageBtn = row.querySelector('[data-folder-manage]');\n  if(manageBtn) manageBtn.addEventListener('click', function(){ openFolderManager(key); });\n}\n\n\/* [2026-07-29 \ucd94\uac00] \uc2e4 \uc548\uc758 \uac01 \ud56d\ubaa9 \uc774\ub984\uc744 \ud45c\uc2dc\ud560 \ub54c \uc4f8 \ub300\ud45c \ud14d\uc2a4\ud2b8\ub97c \uace8\ub77c\uc90d\ub2c8\ub2e4 *\/\nfunction itemDisplayTitle(key, item){\n  if(key === 'people') return item.name || '\uc774\ub984 \uc5c6\uc74c';\n  if(key === 'timeline') return item.title || '\uc81c\ubaa9 \uc5c6\uc74c';\n  if(key === 'gallery') return item.caption || '\uc124\uba85 \uc5c6\uc74c';\n  if(key === 'stories') return item.title || '\uc81c\ubaa9 \uc5c6\uc74c';\n  if(SIMPLE_SECTIONS[key]) return item[SIMPLE_SECTIONS[key].fields[0].key] || '\uc81c\ubaa9 \uc5c6\uc74c';\n  return item.title || item.name || '\ud56d\ubaa9';\n}\n\nfunction openFolderManager(key){\n  var label = FOLDER_SECTIONS[key] || '';\n  var openAddUnder = null; \/* \uc9c0\uae08 \"+ \ud558\uc704 \ud3f4\ub354\" \uc785\ub825\ucc3d\uc774 \uc5f4\ub824 \uc788\ub294 \ubd80\ubaa8 \ud3f4\ub354 id ('' = \ucd5c\uc0c1\uc704 \ucd94\uac00\ucc3d) *\/\n\n  \/* [2026-07-29 \ucd94\uac00] \uc2e4 \uc548\uc758 \uac01 \ud56d\ubaa9\uc744 \uc6d0\ud558\ub294(\ub2e4\ub2e8\uacc4) \ud3f4\ub354\ub85c \ubc30\uc815 *\/\n  function assignRowsHtml(){\n    var flat = flattenFolderTree(key);\n    var items = State[key] || [];\n    if(!flat.length || !items.length) return '';\n    return '<div style=\"margin-top:22px;padding-top:16px;border-top:1px solid var(--fh-border);\">' +\n      '<label style=\"display:block;font-size:12px;font-weight:600;color:var(--fh-ink-soft);margin-bottom:10px;\">\ud56d\ubaa9\uc744 \ud3f4\ub354\uc5d0 \ubc30\uc815<\/label>' +\n      items.map(function(it){\n        return '<div class=\"fh-folder-row\"><div style=\"flex:1;font-size:13px;color:var(--fh-ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;\">' + escapeHtml(itemDisplayTitle(key, it)) + '<\/div>' +\n          '<select data-item-assign=\"' + it.id + '\" style=\"flex:1;border:1px solid var(--fh-border);border-radius:8px;padding:8px 9px;font-size:12.5px;background:var(--fh-surface-2);color:var(--fh-ink);\">' + folderOptionsHtml(key, it.folder||'') + '<\/select><\/div>';\n      }).join('') +\n    '<\/div>';\n  }\n\n  \/* \ud3f4\ub354 \ud2b8\ub9ac\ub97c \ubd80\ubaa8\u2192\uc790\uc2dd \uc21c\uc11c\ub85c, \ub4e4\uc5ec\uc4f0\uae30\uc640 \ud568\uaed8 \uadf8\ub9bd\ub2c8\ub2e4. \uac01 \ud589\uc5d0\ub294\n     \uc774\ub984 \uc218\uc815 \uc785\ub825\ucc3d, \"+ \ud558\uc704\" (\ud558\uc704 \ud3f4\ub354 \ucd94\uac00) \ubc84\ud2bc, \uc0ad\uc81c \ubc84\ud2bc\uc774 \uc788\uc5b4\uc694. *\/\n  function treeHtml(){\n    var flat = flattenFolderTree(key);\n    if(!flat.length) return '<p style=\"font-size:12.5px;color:var(--fh-muted);margin:0 0 14px;\">\uc544\uc9c1 \ub9cc\ub4e0 \ud3f4\ub354\uac00 \uc5c6\uc5b4\uc694.<\/p>';\n    return flat.map(function(f){\n      var indentPx = f.depth * 18;\n      var row = '<div class=\"fh-folder-row\" data-folder-row=\"' + f.id + '\" style=\"padding-left:' + indentPx + 'px;\">' +\n        (f.depth>0 ? '<span style=\"color:var(--fh-muted);font-size:12px;flex-shrink:0;\">\u2514<\/span>' : '') +\n        '<input value=\"' + escapeHtml(f.name) + '\" data-folder-input=\"' + f.id + '\">' +\n        '<button class=\"btn btn-ghost fh-folder-btn\" data-folder-rename=\"' + f.id + '\">\uc218\uc815<\/button>' +\n        '<button class=\"btn btn-ghost fh-folder-btn\" data-folder-addsub=\"' + f.id + '\">+ \ud558\uc704<\/button>' +\n        '<button class=\"btn btn-danger fh-folder-btn\" data-folder-del=\"' + f.id + '\">\uc0ad\uc81c<\/button>' +\n      '<\/div>';\n      if(openAddUnder === f.id){\n        row += '<div class=\"field-row\" style=\"padding-left:' + (indentPx+18) + 'px;margin-bottom:10px;\">' +\n          '<div class=\"field\" style=\"margin-bottom:0;\"><input id=\"fhSubFolderName\" placeholder=\"' + escapeHtml(f.name) + ' \uc548\uc5d0 \ub9cc\ub4e4 \ud3f4\ub354 \uc774\ub984\" autofocus><\/div>' +\n          '<button class=\"btn btn-ghost\" style=\"flex:0 0 auto;padding:11px 14px;\" id=\"fhAddSubFolderConfirm\" data-parent=\"' + f.id + '\">\ucd94\uac00<\/button>' +\n        '<\/div>';\n      }\n      return row;\n    }).join('');\n  }\n\n  function bodyHtml(){\n    return treeHtml() +\n    '<div class=\"field-row\" style=\"margin-top:14px;\">' +\n      '<div class=\"field\" style=\"margin-bottom:0;\"><input id=\"fhNewFolderName\" placeholder=\"\uc0c8 \ud3f4\ub354 \uc774\ub984(\ucd5c\uc0c1\uc704)\"><\/div>' +\n      '<button class=\"btn btn-ghost\" style=\"flex:0 0 auto; padding:11px 14px;\" id=\"fhAddFolder\">\ucd94\uac00<\/button>' +\n    '<\/div>' +\n    assignRowsHtml();\n  }\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>' + label + ' \ud3f4\ub354 \uad00\ub9ac<\/h3>' +\n    '<p style=\"font-size:11.5px;color:var(--fh-muted);margin:0 0 14px;\">\ud3f4\ub354\ub294 \uac1c\uc218 \uc81c\ud55c \uc5c6\uc774 \ub9cc\ub4e4 \uc218 \uc788\uace0, \uac01 \ud3f4\ub354 \uc548\uc5d0 \ub610 \ud558\uc704 \ud3f4\ub354\ub97c \ub9cc\ub4e4\uc5b4 \uc5ec\ub7ec \ub2e8\uacc4\ub85c \uc815\ub9ac\ud560 \uc218 \uc788\uc5b4\uc694. \ud3f4\ub354\ub97c \ub9cc\ub4e0 \ub4a4 \uc544\ub798\uc5d0\uc11c \uc774 \uc2e4\uc758 \ud56d\ubaa9\uc744 \uc6d0\ud558\ub294 \ud3f4\ub354\ub85c \ubc14\ub85c \ubc30\uc815\ud560 \uc218 \uc788\uc5b4\uc694.<\/p>' +\n    '<div id=\"fhFolderList\">' + bodyHtml() + '<\/div>' +\n    '<div class=\"btn-row\"><button class=\"btn btn-primary\" id=\"fhFolderDone\">\uc644\ub8cc<\/button><\/div>'\n  );\n  function rebind(){\n    document.getElementById('fhFolderList').innerHTML = bodyHtml();\n    document.querySelectorAll('[data-folder-rename]').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        var fid = btn.getAttribute('data-folder-rename');\n        var input = document.querySelector('[data-folder-input=\"' + fid + '\"]');\n        var newName = input ? input.value.trim() : '';\n        if(!newName){ showToast('\ud3f4\ub354 \uc774\ub984\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n        var f = getFolders(key).find(function(x){ return x.id===fid; });\n        if(f){\n          f.name = newName;\n          apiSave('folders', State.folders).then(function(){ rebind(); render(); showToast('\ud3f4\ub354 \uc774\ub984\uc744 \uc218\uc815\ud588\uc5b4\uc694'); });\n        }\n      });\n    });\n    document.querySelectorAll('[data-folder-del]').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        var fid = btn.getAttribute('data-folder-del');\n        var doomed = getDescendantFolderIds(key, fid); \/* \uc790\uae30 \uc790\uc2e0 + \ubaa8\ub4e0 \ud558\uc704 \ud3f4\ub354 *\/\n        var msg = doomed.length > 1\n          ? '\uc774 \ud3f4\ub354\uc640 \uadf8 \uc548\uc758 \ud558\uc704 \ud3f4\ub354 ' + (doomed.length-1) + '\uac1c\uac00 \ubaa8\ub450 \uc0ad\uc81c\ub429\ub2c8\ub2e4. \ud3f4\ub354 \uc548\uc758 \ud56d\ubaa9\uc740 \"\uc804\uccb4\"\ub85c \ub0a8\uc544\uc788\uc5b4\uc694. \uacc4\uc18d\ud560\uae4c\uc694?'\n          : '\uc774 \ud3f4\ub354\ub97c \uc0ad\uc81c\ud560\uae4c\uc694? \ud3f4\ub354 \uc548\uc758 \ud56d\ubaa9\uc740 \"\uc804\uccb4\"\ub85c \ub0a8\uc544\uc788\uc5b4\uc694.';\n        if(!confirm(msg)) return;\n        State.folders[key] = getFolders(key).filter(function(f){ return doomed.indexOf(f.id) === -1; });\n        (State[key]||[]).forEach(function(it){ if(doomed.indexOf(it.folder) !== -1) it.folder=''; });\n        if(doomed.indexOf(State.activeFolder[key]) !== -1) State.activeFolder[key]='';\n        if(openAddUnder && doomed.indexOf(openAddUnder) !== -1) openAddUnder = null;\n        Promise.all([\n          apiSave('folders', State.folders),\n          apiSave(key, State[key])\n        ]).then(function(){\n          rebind(); render();\n          showToast('\ud3f4\ub354\ub97c \uc0ad\uc81c\ud588\uc5b4\uc694');\n        });\n      });\n    });\n    document.querySelectorAll('[data-folder-addsub]').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        var fid = btn.getAttribute('data-folder-addsub');\n        openAddUnder = (openAddUnder === fid) ? null : fid;\n        rebind();\n        if(openAddUnder){\n          var input = document.getElementById('fhSubFolderName');\n          if(input) input.focus();\n        }\n      });\n    });\n    var subConfirm = document.getElementById('fhAddSubFolderConfirm');\n    if(subConfirm) subConfirm.addEventListener('click', function(){\n      var name = document.getElementById('fhSubFolderName').value.trim();\n      if(!name){ showToast('\ud3f4\ub354 \uc774\ub984\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n      var parentId = subConfirm.getAttribute('data-parent');\n      if(!State.folders[key]) State.folders[key]=[];\n      State.folders[key].push({ id:uid(), name:name, parent:parentId });\n      openAddUnder = null;\n      apiSave('folders', State.folders).then(function(){ rebind(); render(); showToast('\ud558\uc704 \ud3f4\ub354\ub97c \ucd94\uac00\ud588\uc5b4\uc694'); });\n    });\n    document.getElementById('fhAddFolder').addEventListener('click', function(){\n      var name = document.getElementById('fhNewFolderName').value.trim();\n      if(!name){ showToast('\ud3f4\ub354 \uc774\ub984\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n      if(!State.folders[key]) State.folders[key]=[];\n      State.folders[key].push({ id:uid(), name:name, parent:'' });\n      apiSave('folders', State.folders).then(function(){ rebind(); render(); showToast('\ud3f4\ub354\ub97c \ucd94\uac00\ud588\uc5b4\uc694'); });\n    });\n    \/* [2026-07-29 \ucd94\uac00] \ud56d\ubaa9 \u2192 \ud3f4\ub354 \ubc30\uc815 \ub4dc\ub86d\ub2e4\uc6b4 \ubcc0\uacbd \uc2dc \uc989\uc2dc \uc800\uc7a5 *\/\n    document.querySelectorAll('[data-item-assign]').forEach(function(sel){\n      sel.addEventListener('change', function(){\n        var itemId = sel.getAttribute('data-item-assign');\n        var it = (State[key]||[]).find(function(x){ return x.id===itemId; });\n        if(it){\n          it.folder = sel.value;\n          apiSave(key, State[key]).then(function(){ render(); showToast('\ubc30\uc815\uc744 \uc800\uc7a5\ud588\uc5b4\uc694'); });\n        }\n      });\n    });\n  }\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhFolderDone').addEventListener('click', closeModal);\n  rebind();\n}\n\n\/* \ub4f1\ub85d\/\uc218\uc815 \ud3fc\uc5d0 \ub123\uc744 \ud3f4\ub354 \uc120\ud0dd select *\/\nfunction folderFieldHtml(key, selectedId){\n  if(!FOLDER_SECTIONS[key]) return '';\n  return '<div class=\"field\"><label>\ud3f4\ub354<\/label><select id=\"f-folder\">' + folderOptionsHtml(key, selectedId) + '<\/select><\/div>';\n}\n\nfunction uid(){ return Date.now().toString(36) + Math.random().toString(36).slice(2,7); }\n\nfunction escapeHtml(str){\n  if(!str) return '';\n  return String(str).replace(\/[&<>\"']\/g, function(c){\n    return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c];\n  });\n}\n\n\/* ================= [2026-07-26 \ucd94\uac00] REST helpers (hall = \uc5ed\uc0ac\uad00 id\ub85c \ub370\uc774\ud130 \ubd84\ub9ac) ================= *\/\nfunction apiGetAll(){\n  return fetch(CFG.restUrl + 'data?hall=' + encodeURIComponent(CFG.hallId), { headers:{ 'X-WP-Nonce': CFG.nonce } })\n    .then(function(r){ if(!r.ok) throw new Error('load failed'); return r.json(); });\n}\nfunction apiSave(key, value){\n  return fetch(CFG.restUrl + 'data', {\n    method:'POST',\n    headers:{ 'Content-Type':'application\/json', 'X-WP-Nonce': CFG.nonce },\n    body: JSON.stringify({ key:key, value:value, hall:CFG.hallId })\n  }).then(function(r){\n    if(!r.ok){\n      return r.json().catch(function(){ return null; }).then(function(body){\n        var reason = body && body.message ? body.message : ('\uc624\ub958 \ucf54\ub4dc ' + r.status);\n        throw new Error(reason);\n      });\n    }\n    return r.json();\n  }).catch(function(e){\n    showToast('\uc800\uc7a5\uc5d0 \uc2e4\ud328\ud588\uc5b4\uc694: ' + (e && e.message ? e.message : '\ub2e4\uc2dc \uc2dc\ub3c4\ud574 \uc8fc\uc138\uc694.'));\n    throw e;\n  });\n}\nfunction uploadMedia(blob, filename){\n  var form = new FormData();\n  form.append('file', blob, filename);\n  return fetch(CFG.mediaRestUrl, {\n    method:'POST',\n    headers:{ 'X-WP-Nonce': CFG.nonce },\n    body: form\n  }).then(function(r){\n    if(!r.ok) throw new Error('upload failed');\n    return r.json();\n  }).then(function(data){ return data.source_url; });\n}\n\n\/* [2026-07-27 \ucd94\uac00] \ud68c\uc6d0 \uc9c1\uc811 \uc81c\ucd9c(\uc804\uccb4 \ud3b8\uc9d1 \uad8c\ud55c \uc5c6\uc774\ub3c4 \uc0c8 \ud56d\ubaa9\ub9cc \ucd94\uac00) *\/\nfunction apiSubmitItem(key, item){\n  return fetch(CFG.restUrl + 'submit', {\n    method:'POST',\n    headers:{ 'Content-Type':'application\/json', 'X-WP-Nonce': CFG.nonce },\n    body: JSON.stringify({ hall:CFG.hallId, key:key, item:item })\n  }).then(function(r){\n    if(!r.ok){\n      return r.json().catch(function(){ return null; }).then(function(body){\n        var reason = body && body.message ? body.message : ('\uc624\ub958 \ucf54\ub4dc ' + r.status);\n        throw new Error(reason);\n      });\n    }\n    return r.json();\n  }).catch(function(e){\n    showToast('\ub4f1\ub85d\uc5d0 \uc2e4\ud328\ud588\uc5b4\uc694: ' + (e && e.message ? e.message : '\ub2e4\uc2dc \uc2dc\ub3c4\ud574 \uc8fc\uc138\uc694.'));\n    throw e;\n  });\n}\n\n\/* \uad00\ub9ac\uc790\ub294 \uc804\uccb4 \ubc30\uc5f4\uc744 \ubc14\ub85c \uc800\uc7a5\ud558\uace0(add\/edit \uacb8\uc6a9), \uad00\ub9ac\uc790\uac00 \uc544\ub2cc \ub85c\uadf8\uc778 \ud68c\uc6d0\uc740\n   \uc0c8 \ud56d\ubaa9\ub9cc \/submit\uc73c\ub85c \uc81c\ucd9c\ud569\ub2c8\ub2e4(\uc2b9\uc778 \ud544\uc694 \uc2e4\uc740 \ub300\uae30 \uc0c1\ud0dc\ub85c \ub4f1\ub85d). *\/\nfunction saveOrSubmitItem(key, item, isEdit){\n  if(CFG.canEdit){\n    if(!item.status) item.status = 'approved';\n    var idx = State[key].findIndex(function(x){ return x.id===item.id; });\n    if(idx>=0) State[key][idx] = item; else State[key].push(item);\n    return apiSave(key, State[key]).then(function(){ return { pending:false }; });\n  }\n  return apiSubmitItem(key, item).then(function(res){\n    State[key].push(res.item);\n    return { pending: res.item && res.item.status === 'pending' };\n  });\n}\n\n\/* ---- \ub313\uae00 ---- *\/\nfunction apiAddComment(sectionKey, itemId, text){\n  return fetch(CFG.restUrl + 'comment', {\n    method:'POST',\n    headers:{ 'Content-Type':'application\/json', 'X-WP-Nonce': CFG.nonce },\n    body: JSON.stringify({ hall:CFG.hallId, sectionKey:sectionKey, itemId:itemId, text:text })\n  }).then(function(r){ if(!r.ok) throw new Error('comment failed'); return r.json(); });\n}\nfunction apiDeleteComment(sectionKey, itemId, commentId){\n  return fetch(CFG.restUrl + 'comment\/delete', {\n    method:'POST',\n    headers:{ 'Content-Type':'application\/json', 'X-WP-Nonce': CFG.nonce },\n    body: JSON.stringify({ hall:CFG.hallId, sectionKey:sectionKey, itemId:itemId, commentId:commentId })\n  }).then(function(r){ if(!r.ok) throw new Error('delete failed'); return r.json(); });\n}\n\n\/* ---- \uc5ed\uc0ac\uad00\ubcc4 \uad00\ub9ac\uc790 ---- *\/\nfunction apiGetAdmins(){\n  return fetch(CFG.restUrl + 'admins?hall=' + encodeURIComponent(CFG.hallId), { headers:{ 'X-WP-Nonce': CFG.nonce } })\n    .then(function(r){ if(!r.ok) throw new Error('load failed'); return r.json(); });\n}\nfunction apiAddAdmin(identifier){\n  return fetch(CFG.restUrl + 'admins', {\n    method:'POST',\n    headers:{ 'Content-Type':'application\/json', 'X-WP-Nonce': CFG.nonce },\n    body: JSON.stringify({ hall:CFG.hallId, action:'add', identifier:identifier })\n  }).then(function(r){ if(!r.ok) throw new Error('add admin failed'); return r.json(); });\n}\nfunction apiRemoveAdmin(id){\n  return fetch(CFG.restUrl + 'admins', {\n    method:'POST',\n    headers:{ 'Content-Type':'application\/json', 'X-WP-Nonce': CFG.nonce },\n    body: JSON.stringify({ hall:CFG.hallId, action:'remove', id:id })\n  }).then(function(r){ if(!r.ok) throw new Error('remove admin failed'); return r.json(); });\n}\n\n\/* ================= [2026-07-26 \ucd94\uac00] \ub313\uae00 (\ubaa8\ub4e0 \ud56d\ubaa9 \uacf5\ud1b5) ================= *\/\nfunction renderComments(sectionKey, itemId){\n  var key = sectionKey + ':' + itemId;\n  var list = State.comments[key] || [];\n  var rows = list.length ? list.map(function(c){\n    var canDel = CFG.isLoggedIn && (CFG.canEdit || (CFG.currentUserId && String(c.authorId) === String(CFG.currentUserId)));\n    return '<div class=\"fh-comment-item\">' +\n      '<div class=\"fh-comment-meta\"><b>' + escapeHtml(c.author || '\uc775\uba85') + '<\/b><span>' + escapeHtml((c.date||'').slice(0,16)) + '<\/span><\/div>' +\n      '<p>' + escapeHtml(c.text) + '<\/p>' +\n      (canDel ? '<button class=\"fh-comment-del\" data-del-comment=\"' + sectionKey + '|' + itemId + '|' + c.id + '\">\uc0ad\uc81c<\/button>' : '') +\n    '<\/div>';\n  }).join('') : '<p class=\"fh-comment-empty\">\uc544\uc9c1 \ub313\uae00\uc774 \uc5c6\uc5b4\uc694.<\/p>';\n\n  var form = CFG.isLoggedIn ?\n    '<div class=\"fh-comment-form\"><textarea id=\"fhCommentInput\" placeholder=\"\ub313\uae00\uc744 \ub0a8\uaca8\ubcf4\uc138\uc694\"><\/textarea>' +\n    '<button class=\"btn btn-primary\" data-add-comment=\"' + sectionKey + '|' + itemId + '\">\ub313\uae00 \ub4f1\ub85d<\/button><\/div>' :\n    '<p class=\"fh-comment-empty\">\ub85c\uadf8\uc778\ud558\uba74 \ub313\uae00\uc744 \ub0a8\uae38 \uc218 \uc788\uc5b4\uc694.<\/p>';\n\n  return '<div class=\"fh-comments\"><h5>\ub313\uae00 ' + list.length + '<\/h5>' + rows + form + '<\/div>';\n}\n\nfunction bindComments(sectionKey, itemId, containerEl){\n  var holder = containerEl.querySelector('.fh-comments-holder');\n  if(!holder) return;\n  var addBtn = holder.querySelector('[data-add-comment]');\n  if(addBtn) addBtn.addEventListener('click', function(){\n    var ta = holder.querySelector('#fhCommentInput');\n    var text = ta.value.trim();\n    if(!text){ showToast('\ub313\uae00 \ub0b4\uc6a9\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n    addBtn.disabled = true;\n    apiAddComment(sectionKey, itemId, text).then(function(res){\n      var key = sectionKey + ':' + itemId;\n      if(!State.comments[key]) State.comments[key] = [];\n      State.comments[key].push(res.comment);\n      holder.innerHTML = renderComments(sectionKey, itemId);\n      bindComments(sectionKey, itemId, containerEl);\n      showToast('\ub313\uae00\uc744 \ub4f1\ub85d\ud588\uc5b4\uc694');\n    }).catch(function(){\n      showToast('\ub313\uae00 \ub4f1\ub85d\uc5d0 \uc2e4\ud328\ud588\uc5b4\uc694');\n      addBtn.disabled = false;\n    });\n  });\n  holder.querySelectorAll('[data-del-comment]').forEach(function(btn){\n    btn.addEventListener('click', function(){\n      if(!confirm('\ub313\uae00\uc744 \uc0ad\uc81c\ud560\uae4c\uc694?')) return;\n      var parts = btn.getAttribute('data-del-comment').split('|');\n      apiDeleteComment(parts[0], parts[1], parts[2]).then(function(){\n        var key = parts[0] + ':' + parts[1];\n        State.comments[key] = (State.comments[key]||[]).filter(function(c){ return c.id !== parts[2]; });\n        holder.innerHTML = renderComments(parts[0], parts[1]);\n        bindComments(parts[0], parts[1], containerEl);\n        showToast('\uc0ad\uc81c\ud588\uc5b4\uc694');\n      }).catch(function(){ showToast('\uc0ad\uc81c\uc5d0 \uc2e4\ud328\ud588\uc5b4\uc694'); });\n    });\n  });\n}\n\nfunction showToast(msg){\n  var t = ROOT.querySelector('.fh-toast');\n  if(!t) return;\n  t.textContent = msg; t.classList.add('show');\n  clearTimeout(showToast._h);\n  showToast._h = setTimeout(function(){ t.classList.remove('show'); }, 1800);\n}\n\n\/* ================= Shell ================= *\/\nfunction buildShell(){\n  ROOT.innerHTML =\n    '<header>' +\n      '<div class=\"header-row\">' +\n        '<div class=\"crest\" id=\"fhCrest\">\u5bb6<\/div>' +\n        '<div class=\"header-text\">' +\n          '<h1 id=\"fhFamilyName\">\uc6b0\ub9ac \uac00\ubb38<\/h1>' +\n          '<p id=\"fhMottoShort\">\uac00\ubb38\uc758 \uc5ed\uc0ac\ub97c \uae30\ub85d\ud558\ub294 \uacf5\uac04<\/p>' +\n        '<\/div>' +\n        (CFG.canEdit ? '<button class=\"settings-btn\" id=\"fhSettingsBtn\" aria-label=\"\uc124\uc815\">' +\n          '<svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"3\"\/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"\/><\/svg>' +\n        '<\/button>' : '') +\n      '<\/div>' +\n      '<nav class=\"fh-tabbar\" id=\"fhTabbar\">' +\n        TABS.map(function(t){ return tabButton(t.id, t.label, t.icon); }).join('') +\n      '<\/nav>' +\n    '<\/header>' +\n    '<main id=\"fhContent\">' + renderHomeSkeleton() + '<\/main>' +\n    (CFG.canEdit || CFG.isLoggedIn ? '<button class=\"add-fab\" id=\"fhFab\" style=\"display:none;\">+<\/button>' : '') +\n    '<div id=\"fhModalRoot\"><\/div>' +\n    '<div class=\"fh-toast\"><\/div>';\n\n  ROOT.querySelectorAll('.tab-btn').forEach(function(btn){\n    btn.addEventListener('click', function(){\n      ROOT.querySelectorAll('.tab-btn').forEach(function(b){ b.classList.remove('active'); });\n      btn.classList.add('active');\n      btn.scrollIntoView({ behavior:'smooth', inline:'center', block:'nearest' });\n      State.tab = btn.getAttribute('data-tab');\n      render();\n    });\n  });\n  if(CFG.canEdit || CFG.isLoggedIn){\n    var fab = document.getElementById('fhFab');\n    fab.addEventListener('click', function(){\n      if(!(CFG.canEdit || MEMBER_SUBMIT_SECTIONS[State.tab])) return;\n      if(State.tab==='people') openPersonForm();\n      else if(State.tab==='timeline' && CFG.canEdit) openTimelineForm();\n      else if(State.tab==='gallery') openGalleryForm();\n      else if(State.tab==='stories') openStoryForm();\n      else if(State.tab==='assets') openAssetForm();\n      else if(SIMPLE_SECTIONS[State.tab] && (CFG.canEdit || MEMBER_SUBMIT_SECTIONS[State.tab])) openSimpleForm(SIMPLE_SECTIONS[State.tab]);\n    });\n  }\n  if(CFG.canEdit){\n    document.getElementById('fhSettingsBtn').addEventListener('click', openSettingsForm);\n  }\n}\nfunction tabButton(id,label,svgInner){\n  return '<button class=\"tab-btn' + (id==='home'?' active':'') + '\" data-tab=\"' + id + '\">' +\n    '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">' + svgInner + '<\/svg>' + label +\n  '<\/button>';\n}\n\n\/* ================= Init ================= *\/\nfunction init(){\n  buildShell();\n  apiGetAll().then(function(data){\n    State.meta = data.meta || State.meta;\n    State.people = data.people || [];\n    State.timeline = data.timeline || [];\n    State.gallery = data.gallery || [];\n    State.stories = data.stories || [];\n    State.sites = data.sites || [];\n    State.heritage = data.heritage || [];\n    State.future = data.future || [];\n    State.founder = data.founder || [];\n    State.assets = data.assets || []; \/* [2026-10-28 \ucd94\uac00] \uad00\ub9ac\uc790 \uc678\uc5d0\ub294 \uc11c\ubc84\uac00 \uacf5\uac1c \ud5c8\uc6a9\ub41c \uc7ac\uc0b0\ub9cc \ubcf4\ub0c4 *\/\n    \/* \uad00\ub9ac\uc790\uac00 \uc544\ub2c8\uc5b4\ub3c4 \uacf5\uac1c\ub41c \uc7ac\uc0b0\uc774 \uc788\uc73c\uba74 '\uc885\uc911 \uc7ac\uc0b0' \ud0ed\uc744 \ubcf4\uc5ec\uc90d\ub2c8\ub2e4 *\/\n    if(!CFG.canEdit && State.assets.length && !TABS.some(function(t){ return t.id==='assets'; })){\n      TABS.push({ id:'assets', label:'\uc885\uc911 \uc7ac\uc0b0', icon:ASSET_ICON });\n    }\n    State.branch = data.branch || [];\n    State.folders = data.folders || {};\n    State.comments = data.comments || {};\n    \/* [2026-07-28 \ucd94\uac00] \ucee4\uc2a4\ud140 \uc2e4 \ub85c\ub4dc \ubc0f \ub4f1\ub85d (SIMPLE_SECTIONS\/FOLDER_SECTIONS\/TABS\uc5d0 \ub3d9\uc801 \ubc18\uc601) *\/\n    State.customSections = data.customSections || [];\n    State.customSections.forEach(function(cs){\n      State[customSectionKey(cs.id)] = cs.items || [];\n      registerCustomSection(cs);\n    });\n    refreshTabbar();\n    applyMeta();\n    if(State.tab === 'home'){\n      swapHomeContent();\n    } else {\n      render();\n    }\n  }).catch(function(){\n    document.getElementById('fhContent').innerHTML = '<div class=\"fh-loading\">\ub370\uc774\ud130\ub97c \ubd88\ub7ec\uc624\uc9c0 \ubabb\ud588\uc5b4\uc694. \uc0c8\ub85c\uace0\uce68\ud574 \uc8fc\uc138\uc694.<\/div>';\n  });\n}\n\n\/* \ud648 \ud654\uba74\uc758 \ub85c\ub529 \uc790\ub9ac(\uc2a4\ucf08\ub808\ud1a4)\ub97c \uc2e4\uc81c \"\ub458\ub7ec\ubcf4\uae30\" \ucf58\ud150\uce20\ub85c \ubd80\ub4dc\ub7fd\uac8c \uc790\ub9ac\ubc14\uafc8\ud569\ub2c8\ub2e4 *\/\nfunction swapHomeContent(){\n  var c = document.getElementById('fhContent');\n  if(!c){ return; }\n  c.classList.add('fh-fade-out');\n  setTimeout(function(){\n    c.innerHTML = renderHome();\n    bindDynamicEvents();\n    bindSectionCoverflow();\n    c.classList.remove('fh-fade-out');\n    c.classList.add('fh-fade-in');\n    setTimeout(function(){ c.classList.remove('fh-fade-in'); }, 320);\n  }, 180);\n}\n\nfunction applyMeta(){\n  var crest = document.getElementById('fhCrest');\n  var name = document.getElementById('fhFamilyName');\n  var motto = document.getElementById('fhMottoShort');\n  if(crest) crest.textContent = State.meta.crestChar || '\u5bb6';\n  if(name) name.textContent = State.meta.familyName || '\uc6b0\ub9ac \uac00\ubb38';\n  if(motto) motto.textContent = State.meta.originYear ? (State.meta.originYear + '\ub144\ubd80\ud130 \uc774\uc5b4\uc628 \uc774\uc57c\uae30') : '\uac00\ubb38\uc758 \uc5ed\uc0ac\ub97c \uae30\ub85d\ud558\ub294 \uacf5\uac04';\n}\nfunction setText(id, val){ var el = document.getElementById(id); if(el) el.textContent = val; }\n\n\/* ================= Render ================= *\/\nfunction render(){\n  var c = document.getElementById('fhContent');\n  var fab = document.getElementById('fhFab');\n  if(fab){\n    var canUseFab = State.tab!=='home' && (CFG.canEdit || !!MEMBER_SUBMIT_SECTIONS[State.tab]);\n    fab.style.display = canUseFab ? 'flex' : 'none';\n  }\n\n  if(State.tab==='home') c.innerHTML = renderHome();\n  else if(State.tab==='people') c.innerHTML = renderPeople();\n  else if(State.tab==='timeline') c.innerHTML = renderTimeline();\n  else if(State.tab==='gallery') c.innerHTML = renderGallery();\n  else if(State.tab==='stories') c.innerHTML = renderStories();\n  else if(State.tab==='assets') c.innerHTML = renderAssets(); \/* [2026-10-28 \ucd94\uac00] *\/\n  else if(SIMPLE_SECTIONS[State.tab]) c.innerHTML = renderSimpleList(SIMPLE_SECTIONS[State.tab]);\n  bindDynamicEvents();\n  bindSectionCoverflow();\n  if(FOLDER_SECTIONS[State.tab]) bindFolderChips(State.tab);\n  if(State.tab==='assets') bindAssets();\n}\n\nfunction tabIcon(id){ var t = TABS.find(function(x){ return x.id===id; }); return t ? t.icon : ''; }\n\n\/* \ub370\uc774\ud130\uac00 \ub85c\ub4dc\ub418\uae30 \uc804, \"\ub458\ub7ec\ubcf4\uae30\" \uc790\ub9ac\uc5d0 \uba3c\uc800 \ubcf4\uc5ec\uc904 \ub300\uccb4 \ud654\uba74.\n   \uc2e4\uc81c \ub370\uc774\ud130\uac00 \ub3c4\ucc29\ud558\uba74 renderHome()\uc73c\ub85c \ubd80\ub4dc\ub7fd\uac8c \uc790\ub9ac\ubc14\uafc8\ub429\ub2c8\ub2e4. *\/\n\/* [2026-07-28 \ucd94\uac00] \ud648 \ub85c\ub529 \uc2a4\ucf08\ub808\ud1a4 + \uc544\ub798 \ud648 \ud654\uba74 \uc0c9\uc0c1\u00b7\uc0ac\uc9c4 \ubbf8\ub9ac\ubcf4\uae30 \uc7ac\uad6c\uc131 *\/\nfunction renderHomeSkeleton(){\n  return '<div class=\"motto-card\">' +\n      '<div class=\"label\">\uac00\ud6c8<\/div>' +\n      '<div class=\"fh-skel-line\" style=\"width:92%\"><\/div>' +\n      '<div class=\"fh-skel-line\" style=\"width:68%\"><\/div>' +\n    '<\/div>' +\n    '<div class=\"section-title\"><span class=\"dot\"><\/span>\ub458\ub7ec\ubcf4\uae30<\/div>' +\n    '<div class=\"fh-skel-showcase\">' +\n      '<svg class=\"fh-skel-illust\" viewBox=\"0 0 200 150\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">' +\n        '<circle cx=\"100\" cy=\"52\" r=\"34\" fill=\"var(--fh-jade-soft)\"\/>' +\n        '<text x=\"100\" y=\"64\" text-anchor=\"middle\" font-family=\"Georgia, serif\" font-size=\"30\" fill=\"var(--fh-jade)\">\u5bb6<\/text>' +\n        '<rect x=\"34\" y=\"106\" width=\"132\" height=\"9\" rx=\"4.5\" fill=\"var(--fh-border)\"\/>' +\n        '<rect x=\"58\" y=\"123\" width=\"84\" height=\"7\" rx=\"3.5\" fill=\"var(--fh-border)\"\/>' +\n      '<\/svg>' +\n      '<p>\uac00\ubb38\uc758 \uae30\ub85d\uc744 \ubd88\ub7ec\uc624\ub294 \uc911\uc774\uc5d0\uc694\u2026<\/p>' +\n    '<\/div>';\n}\n\nfunction renderHome(){\n  var loginNote = (!CFG.isLoggedIn) ? '<div class=\"login-note\">\ub85c\uadf8\uc778\ud558\uba74 \uc778\ubb3c, \uc5f0\ud45c, \uc0ac\uc9c4, \uc774\uc57c\uae30\ub97c \uc9c1\uc811 \uae30\ub85d\ud560 \uc218 \uc788\uc5b4\uc694.<\/div>' : '';\n  var cards = [\n    { tab:'founder', title:'\uc2dc\uc870\uc2e4', desc: State.founder.length + '\uac74 \uae30\ub85d\ub428' },\n    { tab:'branch', title:'\ubd84\ud30c\uc2e4', desc: State.branch.length + '\uac1c \ubd84\ud30c' },\n    { tab:'people', title:'\uc778\ubb3c', desc: State.people.length + '\uba85 \uae30\ub85d\ub428' },\n    { tab:'timeline', title:'\uc5f0\ud45c', desc: State.timeline.length + '\uac74\uc758 \uc0ac\uac74' },\n    { tab:'gallery', title:'\uac24\ub7ec\ub9ac', desc: State.gallery.length + '\uc7a5\uc758 \uc0ac\uc9c4' },\n    { tab:'stories', title:'\ucd94\uc5b5\uc2e4', desc: State.stories.length + '\ud3b8\uc758 \uc774\uc57c\uae30' },\n    { tab:'sites', title:'\uc720\uc801\uc9c0', desc: State.sites.length + '\uacf3 \uae30\ub85d\ub428' },\n    { tab:'heritage', title:'\ubb38\ud654\uc7ac\uc2e4', desc: State.heritage.length + '\uac74 \uae30\ub85d\ub428' },\n    { tab:'future', title:'\ubbf8\ub798\uc2e4', desc: State.future.length + '\ud3b8\uc758 \ub2e4\uc9d0' }\n  ];\n  \/* [2026-10-28 \ucd94\uac00] \uc885\uc911 \uc7ac\uc0b0\uc2e4 \uce74\ub4dc\ub294 \uad00\ub9ac\uc790\uc5d0\uac8c\ub9cc *\/\n  if(CFG.canEdit) cards.push({ tab:'assets', title:'\uc885\uc911 \uc7ac\uc0b0\uc2e4', desc:(State.assets||[]).length + '\uac74 \u00b7 ' + fmtWon(assetStats(State.assets||[]).value) });\n  else if((State.assets||[]).length) cards.push({ tab:'assets', title:'\uc885\uc911 \uc7ac\uc0b0', desc:State.assets.length + '\uac74 \uacf5\uac1c' });\n  \/* [2026-07-28 \ucd94\uac00] \ucee4\uc2a4\ud140 \uc2e4\ub3c4 \ub458\ub7ec\ubcf4\uae30 \uce74\ub4dc\ub85c \ud568\uaed8 \ub178\ucd9c *\/\n  State.customSections.forEach(function(cs){\n    var key = customSectionKey(cs.id);\n    cards.push({ tab:key, title:cs.name, desc:(State[key]||[]).length + '\uac74 \uae30\ub85d\ub428' });\n  });\n  var motto = escapeHtml(State.meta.motto);\n  \/* [2026-07-28 \ucd94\uac00] \uad00\ub9ac\uc790\uc5d0\uac8c\ub9cc \ubcf4\uc774\ub294 \"+ \uc0c8 \uc2e4 \ucd94\uac00\" \ud0c0\uc77c *\/\n  var addTile = CFG.canEdit\n    ? '<div class=\"home-card-add\" data-open-section-manager=\"1\">' +\n        '<svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12h14\"\/><\/svg>' +\n        '<span>\uc0c8 \uc2e4 \ucd94\uac00<\/span>' +\n      '<\/div>'\n    : '';\n  return loginNote +\n    '<div class=\"motto-card fh-motto-hero\">' +\n      '<div class=\"fh-seal-badge\" aria-hidden=\"true\">' + escapeHtml(State.meta.crestChar||'\u5bb6') + '<\/div>' +\n      '<div class=\"label\">\uac00\ud6c8<\/div>' +\n      (motto ? '<p><span class=\"fh-quote-mark\">\u300c<\/span>' + motto + '<span class=\"fh-quote-mark\">\u300d<\/span><\/p>' : '<p>\uc544\uc9c1 \uac00\ud6c8\uc774 \uc124\uc815\ub418\uc9c0 \uc54a\uc558\uc5b4\uc694.<\/p>') +\n      (State.meta.originYear ? '<div class=\"fh-origin-pill\">' + escapeHtml(State.meta.originYear) + '\ub144\ubd80\ud130 \uc774\uc5b4\uc628 \uc774\uc57c\uae30<\/div>' : '') +\n    '<\/div>' +\n    '<div class=\"section-title\"><span class=\"dot\"><\/span>\ub458\ub7ec\ubcf4\uae30<\/div>' +\n    '<div class=\"home-grid\">' + cards.map(function(c){ return homeCard(c.tab, c.title, c.desc, tabIcon(c.tab), findSectionThumb(c.tab)); }).join('') + addTile + '<\/div>';\n}\n\n\/* \uc0ac\uc9c4\uc774 \uc788\ub294 \uc2e4\uc740 \ucd5c\uadfc \ub4f1\ub85d\ub41c \uc0ac\uc9c4 \ud55c \uc7a5\uc744 \ubbf8\ub9ac\ubcf4\uae30\ub85c \uace8\ub77c\uc635\ub2c8\ub2e4 (\uc2b9\uc778\ub41c \ud56d\ubaa9\ub9cc) *\/\nvar FH_PHOTO_SECTIONS = { people:1, gallery:1, sites:1, heritage:1, founder:1, branch:1 };\nfunction findSectionThumb(tab){\n  var hasPhoto = FH_PHOTO_SECTIONS[tab] || (SIMPLE_SECTIONS[tab] && SIMPLE_SECTIONS[tab].hasPhoto); \/* [2026-07-28 \uc218\uc815] \ucee4\uc2a4\ud140 \uc2e4\ub3c4 \ud3ec\ud568 *\/\n  if(!hasPhoto) return null;\n  var arr = (State[tab]||[]).filter(isVisibleItem).filter(function(it){ return it.photo; });\n  if(!arr.length) return null;\n  return arr[arr.length-1].photo;\n}\n\n\/* \ub458\ub7ec\ubcf4\uae30 \uce74\ub4dc \uc0c9\uc0c1 \ud314\ub808\ud2b8 - \uc624\ubc29\uc0c9(\uccad\u00b7\uc801\u00b7\ud669\u00b7\ubc31\u00b7\ud751)\uc5d0\uc11c \uc601\uac10\uc744 \ubc1b\uc544 \uac01 \uc2e4\ub9c8\ub2e4 \ub2e4\ub978 \ud1a4\uc744 \ubd80\uc5ec\ud569\ub2c8\ub2e4 *\/\nvar FH_HOME_COLORS = {\n  founder:  { soft:'#F5EDD9', deep:'#B8860B' }, \/* \ud669 - \uc2dc\uc870\uc2e4 *\/\n  branch:   { soft:'#E3EBF5', deep:'#3B6FA0' }, \/* \uccad - \ubd84\ud30c\uc2e4 *\/\n  people:   { soft:'#E7EEE9', deep:'#3F6657' }, \/* \ucd08\ub85d - \uc778\ubb3c *\/\n  timeline: { soft:'#F5E4E1', deep:'#A63D34' }, \/* \uc801 - \uc5f0\ud45c *\/\n  gallery:  { soft:'#F0E5F2', deep:'#7A4B8C' }, \/* \uc790 - \uac24\ub7ec\ub9ac *\/\n  stories:  { soft:'#F0E6DC', deep:'#8B5E34' }, \/* \uac08 - \ucd94\uc5b5\uc2e4 *\/\n  sites:    { soft:'#E6EAEE', deep:'#55677A' }, \/* \ud68c\uccad - \uc720\uc801\uc9c0 *\/\n  heritage: { soft:'#E3E6F5', deep:'#4B4B96' }, \/* \ub0a8 - \ubb38\ud654\uc7ac\uc2e4 *\/\n  future:   { soft:'#DFF0F0', deep:'#2E8B8B' }, \/* \ud558\ub298 - \ubbf8\ub798\uc2e4 *\/\n  assets:   { soft:'#EDEBDD', deep:'#6B6B2A' }  \/* \uc62c\ub9ac\ube0c - \uc885\uc911 \uc7ac\uc0b0\uc2e4 [2026-10-28 \ucd94\uac00] *\/\n};\nfunction homeCard(tab,title,desc,svgInner,thumb){\n  var c = FH_HOME_COLORS[tab] || { soft:'var(--fh-jade-soft)', deep:'var(--fh-jade)' };\n  var banner = thumb\n    ? '<div class=\"home-card-banner\"><img decoding=\"async\" src=\"' + escapeHtml(thumb) + '\" alt=\"\"><\/div>'\n    : '<div class=\"home-card-banner\" style=\"background:' + c.soft + ';\">' +\n        '<div class=\"home-card-seal-lg\" style=\"background:' + c.deep + ';\">' + escapeHtml(State.meta.crestChar||'\u5bb6') + '<\/div>' +\n      '<\/div>';\n  return '<div class=\"home-card\" data-nav=\"' + tab + '\" style=\"border-color:' + c.deep + '55;\">' +\n    banner +\n    '<div class=\"home-card-body\">' +\n      '<div class=\"ic\" style=\"background:' + c.soft + ';color:' + c.deep + ';\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">' + svgInner + '<\/svg><\/div>' +\n      '<h4>' + title + '<\/h4><p>' + desc + '<\/p>' +\n    '<\/div><\/div>';\n}\n\nfunction renderPeople(){\n  var chips = renderFolderChips('people');\n  var list = filteredItems('people');\n  if(list.length===0) return '<div class=\"section-title\"><span class=\"dot\"><\/span>\uc778\ubb3c<\/div>' + chips + emptyStateBody('\uc544\uc9c1 \ub4f1\ub85d\ub41c \uc778\ubb3c\uc774 \uc5c6\uc5b4\uc694','\uac00\ubb38\uc758 \uccab \uc778\ubb3c\uc744 \uae30\ub85d\ud558\uba74 \uc774\uacf3\uc5d0 \ud504\ub85c\ud544\uc774 \ub9cc\ub4e4\uc5b4\uc838\uc694.');\n  var sorted = list.slice().sort(function(a,b){ return (a.generation||0)-(b.generation||0); });\n  return '<div class=\"section-title\"><span class=\"dot\"><\/span>\uc778\ubb3c ' + list.length + '<\/div>' + chips + '<div class=\"people-grid\">' +\n    sorted.map(function(p){\n      return '<div class=\"person-card\" data-open-person=\"' + p.id + '\">' +\n        '<div class=\"person-photo\">' + (p.photo ? '<img decoding=\"async\" src=\"' + escapeHtml(p.photo) + '\" alt=\"\">' : escapeHtml((p.name||'?')[0])) + '<\/div>' +\n        '<h4>' + escapeHtml(p.name||'\uc774\ub984 \uc5c6\uc74c') + '<\/h4>' +\n        '<div class=\"rel\">' + escapeHtml(p.relation||'') + '<\/div>' +\n        '<div class=\"years\">' + escapeHtml(p.birth||'') + (p.death ? ' ~ ' + escapeHtml(p.death) : (p.birth ? ' ~' : '')) + '<\/div>' +\n        pendingBadgeHtml(p) +\n      '<\/div>';\n    }).join('') + '<\/div>';\n}\n\nfunction personDetailHtml(p){\n  return '<button class=\"back-btn\" data-back=\"people\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg>\ubaa9\ub85d\uc73c\ub85c<\/button>' +\n    '<div class=\"detail-photo\">' + (p.photo ? '<img decoding=\"async\" src=\"' + escapeHtml(p.photo) + '\" alt=\"\">' : escapeHtml((p.name||'?')[0])) + '<\/div>' +\n    '<div class=\"detail-block\">' +\n      pendingBadgeHtml(p) +\n      '<h2>' + escapeHtml(p.name||'\uc774\ub984 \uc5c6\uc74c') + (p.hanja ? ' <span style=\"font-size:14px;color:var(--fh-muted);\">(' + escapeHtml(p.hanja) + ')<\/span>' : '') + '<\/h2>' +\n      '<div class=\"sub\">' + escapeHtml(p.relation||'') + (p.title ? ' \u00b7 ' + escapeHtml(p.title) : '') + '<\/div>' +\n      '<div class=\"meta-row\">' +\n        '<div><b>\uc138\ub300<\/b>' + (p.generation ? p.generation + '\uc138' : '-') + '<\/div>' +\n        '<div><b>\ucd9c\uc0dd<\/b>' + (escapeHtml(p.birth) || '-') + '<\/div>' +\n        '<div><b>\uc0ac\ub9dd<\/b>' + (escapeHtml(p.death) || '-') + '<\/div>' +\n      '<\/div>' +\n      '<div class=\"bio\">' + (escapeHtml(p.bio) || '\uc544\uc9c1 \uae30\ub85d\ub41c \uc774\ub825\uc774 \uc5c6\uc5b4\uc694.') + '<\/div>' +\n    '<\/div>' +\n    (CFG.canEdit ? '<div class=\"btn-row\">' + approveButtonHtml('people', p) + '<button class=\"btn btn-ghost\" data-edit-person=\"' + p.id + '\">\uc218\uc815<\/button><button class=\"btn btn-danger\" data-del-person=\"' + p.id + '\">\uc0ad\uc81c<\/button><\/div>' : '') +\n    '<div class=\"fh-comments-holder\">' + renderComments('people', p.id) + '<\/div>';\n}\n\nfunction renderTimeline(){\n  var chips = renderFolderChips('timeline');\n  var list = filteredItems('timeline');\n  if(list.length===0) return '<div class=\"section-title\"><span class=\"dot\"><\/span>\uac00\ubb38 \uc5f0\ud45c<\/div>' + chips + emptyStateBody('\uc544\uc9c1 \uae30\ub85d\ub41c \uc0ac\uac74\uc774 \uc5c6\uc5b4\uc694','\uac00\ubb38\uc758 \uc2dc\uc791\uc774\ub098 \uc911\uc694\ud55c \uc0ac\uac74\uc744 \uc5f0\ud45c\uc5d0 \ub0a8\uaca8\ubcf4\uc138\uc694.');\n  var sorted = list.slice().sort(function(a,b){ return (a.year||'').localeCompare(b.year||'', undefined, {numeric:true}); });\n  return '<div class=\"section-title\"><span class=\"dot\"><\/span>\uac00\ubb38 \uc5f0\ud45c<\/div>' + chips + '<div class=\"timeline\">' +\n    sorted.map(function(t){\n      var short = (t.desc||'').slice(0,60);\n      return '<div class=\"tl-item\" data-open-timeline=\"' + t.id + '\">' +\n        '<div class=\"tl-year\">' + escapeHtml(t.year||'') + '<\/div>' +\n        '<div class=\"tl-card\"><h4>' + escapeHtml(t.title||'\uc81c\ubaa9 \uc5c6\uc74c') + '<\/h4>' +\n        '<p>' + escapeHtml(short) + ((t.desc||'').length>60 ? '\u2026' : '') + '<\/p><\/div><\/div>';\n    }).join('') + '<\/div>';\n}\n\n\/* ================= [2026-07-25 \ucd94\uac00] Coverflow (\uc601\ud654\uad00 \uc2a4\ud0c0\uc77c 3D \uce90\ub7ec\uc140) \/ 3x3 \uaca9\uc790 \ubdf0 \uc804\ud658 ================= *\/\nvar CoverState = {}; \/\/ sectionKey -> \ud604\uc7ac \uc778\ub371\uc2a4\nvar ViewMode = {};   \/\/ sectionKey -> 'cover' | 'grid' (\ud3b8\uc9d1\uc790\u00b7\ubc29\ubb38\uc790 \ubaa8\ub450 \ud654\uba74\uc5d0\uc11c \uc804\ud658 \uac00\ub2a5, \ube0c\ub77c\uc6b0\uc800 \uc0c8\ub85c\uace0\uce68 \uc2dc \ucd08\uae30\ud654)\n\nfunction normalizeGalleryItems(){\n  var sorted = filteredItems('gallery').slice().sort(function(a,b){ return (b.date||'').localeCompare(a.date||''); });\n  return sorted.map(function(g){\n    return { id:g.id, photo:g.photo, title:g.caption||'\uc124\uba85 \uc5c6\uc74c', subtitle:g.date||'', desc:'', raw:g };\n  });\n}\nfunction normalizeSimpleItems(cfg){\n  var sorted = filteredItems(cfg.stateKey).slice().sort(function(a,b){ return (b.id||'').localeCompare(a.id||''); });\n  return sorted.map(function(item){\n    var subtitleParts = cfg.fields.slice(1).map(function(f){ return item[f.key]; }).filter(Boolean);\n    return { id:item.id, photo:item.photo, title:item[cfg.fields[0].key]||'\uc81c\ubaa9 \uc5c6\uc74c', subtitle:subtitleParts.join(' \u00b7 '), desc:item[cfg.bodyKey]||'', raw:item };\n  });\n}\n\nfunction isDesktop(){ return window.innerWidth >= 880; }\nfunction getViewMode(sectionKey){ return ViewMode[sectionKey] || 'cover'; }\n\n\/* \ucef4\ud4e8\ud130\u00b7\ubaa8\ubc14\uc77c \uad6c\ubd84 \uc5c6\uc774 \ub3d9\uc77c\ud55c \ub450 \uac00\uc9c0 \ubcf4\uae30 \ubc29\uc2dd\uc744 \uc81c\uacf5\ud569\ub2c8\ub2e4:\n   \"\uc785\uccb4\" = \uc601\ud654\uad00\uc2dd \ud55c \uc904 \ucf54\ubc84\ud50c\ub85c\uc6b0, \"\uaca9\uc790\" = 3\uc5f4(3x3) \uc815\uc9c0 \uadf8\ub9ac\ub4dc *\/\nfunction viewModeToggleHtml(sectionKey){\n  var mode = getViewMode(sectionKey);\n  return '<div class=\"fh-viewmode-toggle\" data-viewmode-section=\"' + sectionKey + '\">' +\n    '<button class=\"fh-vm-btn' + (mode==='cover'?' active':'') + '\" data-viewmode=\"cover\" aria-label=\"\uc785\uccb4 \ubcf4\uae30\">' +\n      '<svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"3\" y=\"6\" width=\"10\" height=\"12\" rx=\"1.5\" transform=\"skewY(-6)\"\/><rect x=\"9\" y=\"4\" width=\"12\" height=\"16\" rx=\"1.5\"\/><\/svg>\uc785\uccb4' +\n    '<\/button>' +\n    '<button class=\"fh-vm-btn' + (mode==='grid'?' active':'') + '\" data-viewmode=\"grid\" aria-label=\"\uaca9\uc790 \ubcf4\uae30\">' +\n      '<svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"2\" y=\"2\" width=\"6\" height=\"6\" rx=\"1\"\/><rect x=\"9\" y=\"2\" width=\"6\" height=\"6\" rx=\"1\"\/><rect x=\"16\" y=\"2\" width=\"6\" height=\"6\" rx=\"1\"\/><rect x=\"2\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"\/><rect x=\"9\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"\/><rect x=\"16\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"\/><rect x=\"2\" y=\"16\" width=\"6\" height=\"6\" rx=\"1\"\/><rect x=\"9\" y=\"16\" width=\"6\" height=\"6\" rx=\"1\"\/><rect x=\"16\" y=\"16\" width=\"6\" height=\"6\" rx=\"1\"\/><\/svg>\uaca9\uc790' +\n    '<\/button>' +\n  '<\/div>';\n}\n\nfunction renderCoverflowSection(sectionKey, items, label, emptyTitle, emptyDesc){\n  var chips = renderFolderChips(sectionKey);\n  var head = '<div class=\"section-title\"><span class=\"dot\"><\/span>' + label + '<\/div>' + chips;\n  if(items.length===0) return head + emptyStateBody(emptyTitle, emptyDesc);\n\n  var toggle = viewModeToggleHtml(sectionKey);\n  if(getViewMode(sectionKey) === 'grid'){\n    return head + toggle + renderGridView(sectionKey, items);\n  }\n  return head + toggle + renderCoverView(sectionKey, items);\n}\n\n\/* ---- \uc785\uccb4(\ucf54\ubc84\ud50c\ub85c\uc6b0) \ubdf0: \ud55c \uc904\ub85c \ud750\ub974\ub294 \uc601\ud654\uad00 \ud6a8\uacfc ---- *\/\nfunction renderCoverView(sectionKey, items){\n  if(CoverState[sectionKey]===undefined || CoverState[sectionKey] >= items.length) CoverState[sectionKey] = 0;\n  var cur = CoverState[sectionKey];\n  return '<div class=\"fh-cover-stage\" id=\"fhCoverStage-' + sectionKey + '\">' +\n      '<div class=\"fh-cover-track\" id=\"fhCoverTrack-' + sectionKey + '\">' +\n        items.map(function(it,i){\n          return '<div class=\"fh-cover-item\" data-idx=\"' + i + '\">' +\n            (it.photo ? '<img decoding=\"async\" src=\"' + escapeHtml(it.photo) + '\" alt=\"\">' : '<div class=\"fh-cover-noimg\">' + escapeHtml((it.title||'?')[0]) + '<\/div>') +\n          '<\/div>';\n        }).join('') +\n      '<\/div>' +\n      (items.length>1 ? '<button class=\"fh-cover-nav prev\" id=\"fhCoverPrev-' + sectionKey + '\" aria-label=\"\uc774\uc804\">&#10094;<\/button><button class=\"fh-cover-nav next\" id=\"fhCoverNext-' + sectionKey + '\" aria-label=\"\ub2e4\uc74c\">&#10095;<\/button>' : '') +\n    '<\/div>' +\n    '<div class=\"fh-cover-caption\" id=\"fhCoverCaption-' + sectionKey + '\">' + coverCaptionHtml(items[cur]) + '<\/div>' +\n    (items.length>1 ? '<div class=\"fh-filmstrip\" id=\"fhFilmstrip-' + sectionKey + '\">' +\n      items.map(function(it,i){\n        return '<button class=\"fh-film-thumb' + (i===cur?' active':'') + '\" data-idx=\"' + i + '\">' +\n          (it.photo ? '<img decoding=\"async\" src=\"' + escapeHtml(it.photo) + '\" alt=\"\">' : '<span>' + escapeHtml((it.title||'?')[0]) + '<\/span>') +\n        '<\/button>';\n      }).join('') +\n    '<\/div>' : '');\n}\n\n\/* ---- \uaca9\uc790(3x3) \ubdf0: \uc0ac\uc9c4\uc744 3\uc5f4\ub85c \uc815\ub82c\ud574 \ub298\uc5b4\ub193\uc740 \uc815\uc801\uc778 \uadf8\ub9ac\ub4dc (\ucef4\ud4e8\ud130\u00b7\ubaa8\ubc14\uc77c \ub3d9\uc77c) ---- *\/\nfunction renderGridView(sectionKey, items){\n  return '<div class=\"fh-photo-grid3\">' +\n    items.map(function(it){\n      return '<div class=\"fh-grid-card\" data-open-grid=\"' + sectionKey + ':' + it.id + '\">' +\n        '<div class=\"fh-grid-photo\">' + (it.photo ? '<img decoding=\"async\" src=\"' + escapeHtml(it.photo) + '\" alt=\"\">' : '<span>' + escapeHtml((it.title||'?')[0]) + '<\/span>') + '<\/div>' +\n        (it.subtitle ? '<div class=\"cat\">' + escapeHtml(it.subtitle) + '<\/div>' : '') +\n        '<h4>' + escapeHtml(it.title) + '<\/h4>' +\n        pendingBadgeHtml(it.raw) +\n      '<\/div>';\n    }).join('') + '<\/div>';\n}\n\nfunction coverCaptionHtml(it){\n  return (it.subtitle ? '<div class=\"cat\">' + escapeHtml(it.subtitle) + '<\/div>' : '') +\n    '<h4>' + escapeHtml(it.title) + '<\/h4>' +\n    (it.desc ? '<p>' + escapeHtml(it.desc.slice(0,70)) + (it.desc.length>70 ? '\u2026' : '') + '<\/p>' : '') +\n    pendingBadgeHtml(it.raw);\n}\n\n\nfunction bindCoverflow(sectionKey, items, onOpenCurrent){\n  var stage = document.getElementById('fhCoverStage-' + sectionKey);\n  if(!stage || items.length===0) return;\n  var track = document.getElementById('fhCoverTrack-' + sectionKey);\n  var caption = document.getElementById('fhCoverCaption-' + sectionKey);\n  var filmstrip = document.getElementById('fhFilmstrip-' + sectionKey);\n\n  function applyTransforms(){\n    var cur = CoverState[sectionKey] || 0;\n    var spread = isDesktop() ? 105 : 72;\n    track.querySelectorAll('.fh-cover-item').forEach(function(el){\n      var i = parseInt(el.getAttribute('data-idx'), 10);\n      var offset = i - cur;\n      var abs = Math.abs(offset);\n      if(abs > 3){ el.style.opacity = '0'; el.style.pointerEvents = 'none'; return; }\n      el.style.pointerEvents = 'auto';\n      var tx = offset * spread, tz = -abs * 90, ry = offset * -32;\n      el.style.transform = 'translate(-50%,-50%) translateX(' + tx + 'px) translateZ(' + tz + 'px) rotateY(' + ry + 'deg)';\n      el.style.zIndex = String(100 - abs);\n      el.style.opacity = String(1 - abs * 0.28);\n    });\n    if(caption) caption.innerHTML = coverCaptionHtml(items[cur]);\n    if(filmstrip){\n      filmstrip.querySelectorAll('.fh-film-thumb').forEach(function(t){\n        t.classList.toggle('active', parseInt(t.getAttribute('data-idx'), 10) === cur);\n      });\n      var activeThumb = filmstrip.querySelector('.fh-film-thumb.active');\n      if(activeThumb) activeThumb.scrollIntoView({ behavior:'smooth', inline:'center', block:'nearest' });\n    }\n  }\n  function goTo(i){\n    if(i < 0) i = 0; if(i > items.length - 1) i = items.length - 1;\n    CoverState[sectionKey] = i;\n    applyTransforms();\n  }\n  applyTransforms();\n\n  track.querySelectorAll('.fh-cover-item').forEach(function(el){\n    el.addEventListener('click', function(){\n      var i = parseInt(el.getAttribute('data-idx'), 10);\n      if(i === CoverState[sectionKey]) onOpenCurrent(items[i].raw);\n      else goTo(i);\n    });\n  });\n  var prevBtn = document.getElementById('fhCoverPrev-' + sectionKey);\n  var nextBtn = document.getElementById('fhCoverNext-' + sectionKey);\n  if(prevBtn) prevBtn.addEventListener('click', function(){ goTo(CoverState[sectionKey] - 1); });\n  if(nextBtn) nextBtn.addEventListener('click', function(){ goTo(CoverState[sectionKey] + 1); });\n  if(filmstrip){\n    filmstrip.querySelectorAll('.fh-film-thumb').forEach(function(t){\n      t.addEventListener('click', function(){ goTo(parseInt(t.getAttribute('data-idx'), 10)); });\n    });\n  }\n  var startX = null, startY = null;\n  stage.addEventListener('touchstart', function(e){\n    startX = e.touches[0].clientX; startY = e.touches[0].clientY;\n  }, { passive:true });\n  stage.addEventListener('touchend', function(e){\n    if(startX === null) return;\n    var dx = e.changedTouches[0].clientX - startX;\n    var dy = e.changedTouches[0].clientY - startY;\n    if(Math.abs(dx) > 40 && Math.abs(dx) > Math.abs(dy)){\n      if(dx < 0) goTo(CoverState[sectionKey] + 1); else goTo(CoverState[sectionKey] - 1);\n    }\n    startX = null;\n  }, { passive:true });\n}\n\nfunction bindSectionCoverflow(){\n  var c = document.getElementById('fhContent');\n  var key = null;\n  var openViewerFn = null;\n  if(State.tab === 'gallery'){\n    key = 'gallery';\n    openViewerFn = function(raw){ openGalleryViewer(raw); };\n  } else if(SIMPLE_SECTIONS[State.tab] && SIMPLE_SECTIONS[State.tab].hasPhoto){\n    key = SIMPLE_SECTIONS[State.tab].stateKey;\n    var cfgForOpen = SIMPLE_SECTIONS[State.tab];\n    openViewerFn = function(raw){ openSimpleViewer(cfgForOpen, raw); };\n  }\n  if(!key) return;\n\n  var toggleRow = c.querySelector('.fh-viewmode-toggle[data-viewmode-section=\"' + key + '\"]');\n  if(toggleRow){\n    toggleRow.querySelectorAll('[data-viewmode]').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        var mode = btn.getAttribute('data-viewmode');\n        if(getViewMode(key) === mode) return;\n        ViewMode[key] = mode;\n        render();\n      });\n    });\n  }\n\n  if(getViewMode(key) === 'grid'){\n    c.querySelectorAll('[data-open-grid]').forEach(function(el){\n      el.addEventListener('click', function(){\n        var parts = el.getAttribute('data-open-grid').split(':');\n        if(parts[0]==='gallery'){\n          var g = State.gallery.find(function(x){ return x.id===parts[1]; });\n          openGalleryViewer(g);\n        } else {\n          var cfg = SIMPLE_SECTIONS[parts[0]];\n          var item = State[cfg.stateKey].find(function(x){ return x.id===parts[1]; });\n          openSimpleViewer(cfg, item);\n        }\n      });\n    });\n    return;\n  }\n\n  var items = (key === 'gallery') ? normalizeGalleryItems() : normalizeSimpleItems(SIMPLE_SECTIONS[State.tab]);\n  if(!items.length) return;\n  bindCoverflow(key, items, openViewerFn);\n}\n\nfunction renderGallery(){\n  var items = normalizeGalleryItems();\n  return renderCoverflowSection('gallery', items, '\uc0ac\uc9c4\ucca9', '\uc544\uc9c1 \ub4f1\ub85d\ub41c \uc0ac\uc9c4\uc774 \uc5c6\uc5b4\uc694', '\uac00\ubb38\uc758 \uc18c\uc911\ud55c \uc21c\uac04\uc744 \uc0ac\uc9c4\uc73c\ub85c \ub0a8\uaca8\ubcf4\uc138\uc694.');\n}\n\nfunction renderStories(){\n  var chips = renderFolderChips('stories');\n  var list = filteredItems('stories');\n  if(list.length===0) return '<div class=\"section-title\"><span class=\"dot\"><\/span>\ucd94\uc5b5\uc2e4<\/div>' + chips + emptyStateBody('\uc544\uc9c1 \uae30\ub85d\ub41c \uc774\uc57c\uae30\uac00 \uc5c6\uc5b4\uc694','\uac00\ubb38\uc758 \uc720\ub798\ub098 \uc804\ud574 \ub0b4\ub824\uc624\ub294 \uc774\uc57c\uae30, \ucd94\uc5b5\uc744 \uc801\uc5b4\ubcf4\uc138\uc694.');\n  var sorted = list.slice().sort(function(a,b){ return (b.id||'').localeCompare(a.id||''); });\n  return '<div class=\"section-title\"><span class=\"dot\"><\/span>\ucd94\uc5b5\uc2e4<\/div>' + chips + '<div class=\"fh-card-list\">' +\n    sorted.map(function(s){\n      return '<div class=\"story-card\" data-open-story=\"' + s.id + '\">' +\n        '<div class=\"cat\">' + escapeHtml(s.category||'\uc774\uc57c\uae30') + '<\/div>' +\n        '<h4>' + escapeHtml(s.title||'\uc81c\ubaa9 \uc5c6\uc74c') + '<\/h4>' +\n        '<p>' + escapeHtml(s.content||'') + '<\/p><\/div>';\n    }).join('') + '<\/div>';\n}\n\nfunction emptyStateBody(title,desc){\n  return '<div class=\"empty-state\"><div class=\"glyph\"><svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12h14\"\/><\/svg><\/div>' +\n    '<h3>' + title + '<\/h3><p>' + desc + '<\/p><\/div>';\n}\nfunction emptyState(label,title,desc){\n  return '<div class=\"section-title\"><span class=\"dot\"><\/span>' + label + '<\/div>' + emptyStateBody(title,desc);\n}\n\n\/* ---------- \uc720\uc801\uc9c0 \/ \ubb38\ud654\uc7ac\uc2e4 \/ \ubbf8\ub798\uc2e4 (\uacf5\ud1b5 \ub9ac\uc2a4\ud2b8\ud615 \uc139\uc158) ---------- *\/\nfunction renderSimpleList(cfg){\n  if(cfg.hasPhoto){\n    var covItems = normalizeSimpleItems(cfg);\n    return renderCoverflowSection(cfg.stateKey, covItems, cfg.label, cfg.emptyTitle, cfg.emptyDesc);\n  }\n  var chips = renderFolderChips(cfg.stateKey);\n  var list = filteredItems(cfg.stateKey);\n  if(list.length===0) return '<div class=\"section-title\"><span class=\"dot\"><\/span>' + cfg.label + '<\/div>' + chips + emptyStateBody(cfg.emptyTitle, cfg.emptyDesc);\n  var sorted = list.slice().sort(function(a,b){ return (b.id||'').localeCompare(a.id||''); });\n  return '<div class=\"section-title\"><span class=\"dot\"><\/span>' + cfg.label + '<\/div>' + chips + '<div class=\"fh-card-list\">' +\n    sorted.map(function(item){\n      var subtitleParts = cfg.fields.slice(1).map(function(f){ return item[f.key]; }).filter(Boolean);\n      var body = item[cfg.bodyKey] || '';\n      return '<div class=\"story-card\" data-open-simple=\"' + cfg.stateKey + ':' + item.id + '\">' +\n        (subtitleParts.length ? '<div class=\"cat\">' + escapeHtml(subtitleParts.join(' \u00b7 ')) + '<\/div>' : '') +\n        '<h4>' + escapeHtml(item[cfg.fields[0].key] || '\uc81c\ubaa9 \uc5c6\uc74c') + '<\/h4>' +\n        '<p>' + escapeHtml(body) + '<\/p><\/div>';\n    }).join('') + '<\/div>';\n}\n\nfunction openSimpleViewer(cfg, item){\n  var subtitleParts = cfg.fields.slice(1).map(function(f){ return item[f.key]; }).filter(Boolean);\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    (cfg.hasPhoto && item.photo ? '<img decoding=\"async\" src=\"' + escapeHtml(item.photo) + '\" style=\"width:100%;border-radius:12px;margin-bottom:14px;\">' : '') +\n    pendingBadgeHtml(item) +\n    (subtitleParts.length ? '<div style=\"font-size:11px;font-weight:700;color:var(--fh-seal);letter-spacing:0.5px;margin-bottom:6px;\">' + escapeHtml(subtitleParts.join(' \u00b7 ')) + '<\/div>' : '') +\n    '<h3 style=\"margin-top:0;\">' + escapeHtml(item[cfg.fields[0].key] || '\uc81c\ubaa9 \uc5c6\uc74c') + '<\/h3>' +\n    '<div class=\"story-full\">' + escapeHtml(item[cfg.bodyKey] || '') + '<\/div>' +\n    (CFG.canEdit ? '<div class=\"btn-row\">' + approveButtonHtml(cfg.stateKey, item) + '<button class=\"btn btn-ghost\" id=\"fhEditSimple\">\uc218\uc815<\/button><button class=\"btn btn-danger\" id=\"fhDelSimple\">\uc0ad\uc81c<\/button><\/div>' : '') +\n    '<div class=\"fh-comments-holder\">' + renderComments(cfg.stateKey, item.id) + '<\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  var approve = document.querySelector('[data-approve]');\n  if(approve) approve.addEventListener('click', function(){\n    var parts = approve.getAttribute('data-approve').split(':');\n    approveItem(parts[0], parts[1]).then(function(){ closeModal(); render(); });\n  });\n  var editBtn = document.getElementById('fhEditSimple');\n  if(editBtn) editBtn.addEventListener('click', function(){ openSimpleForm(cfg, item); });\n  var delBtn = document.getElementById('fhDelSimple');\n  if(delBtn) delBtn.addEventListener('click', function(){\n    if(!confirm('\uc0ad\uc81c\ud560\uae4c\uc694? \ub418\ub3cc\ub9b4 \uc218 \uc5c6\uc5b4\uc694.')) return;\n    State[cfg.stateKey] = State[cfg.stateKey].filter(function(x){ return x.id!==item.id; });\n    apiSave(cfg.stateKey, State[cfg.stateKey]).then(function(){ applyMeta(); closeModal(); render(); showToast('\uc0ad\uc81c\ud588\uc5b4\uc694'); });\n  });\n  bindComments(cfg.stateKey, item.id, document.getElementById('fhModalRoot'));\n}\n\nfunction openSimpleForm(cfg, item){\n  var isEdit = !!item;\n  var it = item || { id:uid(), photo:'', folder:(State.activeFolder[cfg.stateKey]||'') };\n  cfg.fields.forEach(function(f){ if(it[f.key]===undefined) it[f.key]=''; });\n  if(it[cfg.bodyKey]===undefined) it[cfg.bodyKey]='';\n  var pendingBlob = null;\n\n  var fieldsHtml = cfg.fields.map(function(f){\n    return '<div class=\"field\"><label>' + f.label + '<\/label><input id=\"sf-' + f.key + '\" value=\"' + escapeHtml(it[f.key]) + '\" placeholder=\"' + (f.placeholder||'') + '\"><\/div>';\n  }).join('');\n\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>' + (isEdit ? cfg.label + ' \uc218\uc815' : cfg.label + ' \ucd94\uac00') + '<\/h3>' +\n    (isEdit ? '' : moderationNoticeHtml()) +\n    (cfg.hasPhoto ? '<div class=\"field\"><label>\uc0ac\uc9c4<\/label><div class=\"photo-upload\" id=\"fhPhotoUpload\">' +\n      (it.photo ? '<img decoding=\"async\" src=\"' + escapeHtml(it.photo) + '\">' : '\ud0ed\ud558\uc5ec \uc0ac\uc9c4 \uc120\ud0dd') +\n      '<input type=\"file\" id=\"fhPhotoInput\" accept=\"image\/*\"><\/div><\/div>' : '') +\n    fieldsHtml +\n    folderFieldHtml(cfg.stateKey, it.folder||'') +\n    '<div class=\"field\"><label>' + cfg.bodyLabel + '<\/label><textarea id=\"sf-body\" style=\"min-height:110px;\" placeholder=\"' + cfg.bodyPlaceholder + '\">' + escapeHtml(it[cfg.bodyKey]) + '<\/textarea><\/div>' +\n    '<div class=\"btn-row\"><button class=\"btn btn-ghost\" id=\"fhCancel\">\ucde8\uc18c<\/button><button class=\"btn btn-primary\" id=\"fhSave\">\uc800\uc7a5<\/button><\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhCancel').addEventListener('click', closeModal);\n  if(cfg.hasPhoto){\n    document.getElementById('fhPhotoInput').addEventListener('change', function(e){\n      var file = e.target.files[0]; if(!file) return;\n      resizeImageToBlob(file, 1000, 0.82).then(function(blob){\n        pendingBlob = blob;\n        var url = URL.createObjectURL(blob);\n        document.getElementById('fhPhotoUpload').innerHTML = '<img decoding=\"async\" src=\"' + url + '\"><input type=\"file\" id=\"fhPhotoInput\" accept=\"image\/*\">';\n        document.getElementById('fhPhotoInput').addEventListener('change', arguments.callee);\n      });\n    });\n  }\n  document.getElementById('fhSave').addEventListener('click', function(){\n    var titleVal = document.getElementById('sf-' + cfg.fields[0].key).value.trim();\n    if(!titleVal){ showToast(cfg.fields[0].label + '\uc744(\ub97c) \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n    var saveBtn = document.getElementById('fhSave');\n    saveBtn.disabled = true; saveBtn.textContent = pendingBlob ? '\uc5c5\ub85c\ub4dc \uc911...' : '\uc800\uc7a5 \uc911...';\n\n    var uploadPromise = (cfg.hasPhoto && pendingBlob) ? uploadMedia(pendingBlob, cfg.stateKey + '-' + it.id + '.jpg') : Promise.resolve(it.photo);\n    uploadPromise.then(function(photoUrl){\n      var updated = { id: it.id };\n      cfg.fields.forEach(function(f){ updated[f.key] = document.getElementById('sf-' + f.key).value.trim(); });\n      updated[cfg.bodyKey] = document.getElementById('sf-body').value.trim();\n      var folderSel = document.getElementById('f-folder');\n      updated.folder = folderSel ? folderSel.value : '';\n      if(cfg.hasPhoto) updated.photo = photoUrl || '';\n      if(isEdit && it.status) updated.status = it.status;\n\n      return saveOrSubmitItem(cfg.stateKey, updated, isEdit).then(function(result){\n        applyMeta(); closeModal(); render();\n        showToast(result.pending ? '\ub4f1\ub85d \uc694\uccad\uc744 \ubcf4\ub0c8\uc5b4\uc694. \uad00\ub9ac\uc790 \uc2b9\uc778 \ud6c4 \ubaa9\ub85d\uc5d0 \uacf5\uac1c\ub3fc\uc694.' : (isEdit ? '\uc218\uc815\ud588\uc5b4\uc694' : '\ucd94\uac00\ud588\uc5b4\uc694'));\n      });\n    }).catch(function(){\n      saveBtn.disabled = false; saveBtn.textContent = '\uc800\uc7a5';\n      showToast('\uc800\uc7a5\uc5d0 \uc2e4\ud328\ud588\uc5b4\uc694');\n    });\n  });\n}\n\n\/* ========================================================================\n   [2026-10-28 \ucd94\uac00] \uc885\uc911 \uc7ac\uc0b0\uc2e4 (\uc885\uce5c\ud68c \ubd80\ub3d9\uc0b0\u00b7\ud1a0\uc9c0 \uad00\ub9ac)\n   - \uad00\ub9ac\uc790(\ud3b8\uc9d1 \uad8c\ud55c\uc790)\uc5d0\uac8c\ub9cc \ud0ed\u00b7\ud648 \uce74\ub4dc\uac00 \ubcf4\uc774\uace0, \uc11c\ubc84(\/data GET)\ub3c4 \uad00\ub9ac\uc790 \uc678\uc5d0\ub294\n     assets \ub370\uc774\ud130\ub97c \uc544\uc608 \ub0b4\ub824\ubcf4\ub0b4\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. (\ud654\uba74\uc5d0\uc11c \uc228\uae30\uae30\ub9cc \ud55c \uac83\uc774 \uc544\ub2d8)\n   - \ubcc4\ub3c4 \ud654\uba74: \uc694\uc57d \uce74\ub4dc + \uc885\ub958\ubcc4 \ud544\ud130 + \uac80\uc0c9 + \ubaa9\ub85d + \uc0c1\uc138\/\ub4f1\ub85d\/\uc218\uc815 + CSV \ub0b4\ub824\ubc1b\uae30\n   ======================================================================== *\/\nvar ASSET_ICON = '<path d=\"M3 21h18\"\/><path d=\"M5 21V10l7-6 7 6v11\"\/><path d=\"M9 21v-6h6v6\"\/><path d=\"M9 11h6\"\/>';\nvar ASSET_KINDS = ['\ud1a0\uc9c0(\ub300\uc9c0)','\uc804\u00b7\ub2f5(\ub18d\uc9c0)','\uc784\uc57c','\uac74\ubb3c','\ubb18\uc5ed\u00b7\uc704\ud1a0','\uae30\ud0c0'];\nvar ASSET_USAGES = ['\uc9c1\uc811 \uc0ac\uc6a9','\uc784\ub300 \uc911','\ubb18\uc5ed\u00b7\uc704\ud1a0','\uacf5\uc2e4\u00b7\uc720\ud734','\ub9e4\uac01 \uc608\uc815'];\nvar AssetView = { kind:'', query:'', round:'', group:'kind', trend:'round' }; \/* round: '' = \ud604\uc7ac \uc815\ubcf4, \uc22b\uc790 = \ud574\ub2f9 \ud68c\ucc28 \uae30\uc900 \ubcf4\uae30 [2026-10-28 \ucd94\uac00] *\/\n\nfunction toNum(v){\n  var n = parseFloat(String(v==null?'':v).replace(\/[,\\s\uc6d0\u33a1]\/g,''));\n  return isFinite(n) ? n : 0;\n}\nfunction fmtNum(n){ return Math.round(n*100)\/100 === Math.round(n) ? Math.round(n).toLocaleString('ko-KR') : (Math.round(n*100)\/100).toLocaleString('ko-KR'); }\nfunction fmtWon(n){\n  n = Math.round(n||0);\n  if(!n) return '0\uc6d0';\n  var eok = Math.floor(n\/100000000), man = Math.floor((n%100000000)\/10000), rest = n%10000;\n  var out = [];\n  if(eok) out.push(eok.toLocaleString('ko-KR') + '\uc5b5');\n  if(man) out.push(man.toLocaleString('ko-KR') + '\ub9cc');\n  if(!eok && !man && rest) out.push(rest.toLocaleString('ko-KR'));\n  return out.join(' ') + '\uc6d0';\n}\nfunction fmtArea(m2){\n  if(!m2) return '-';\n  return fmtNum(m2) + '\u33a1 (' + fmtNum(m2*0.3025) + '\ud3c9)';\n}\nfunction daysUntil(dateStr){\n  if(!dateStr) return null;\n  var d = new Date(dateStr + 'T00:00:00'); if(isNaN(d)) return null;\n  var t = new Date(); t.setHours(0,0,0,0);\n  return Math.round((d - t) \/ 86400000);\n}\nfunction leaseBadgeHtml(a){\n  if(a.usage !== '\uc784\ub300 \uc911' || !a.leaseEnd) return '';\n  var d = daysUntil(a.leaseEnd);\n  if(d === null) return '';\n  if(d < 0) return '<span class=\"fh-asset-badge warn\">\uc784\ub300 \ub9cc\ub8cc \uc9c0\ub0a8<\/span>';\n  if(d <= 90) return '<span class=\"fh-asset-badge warn\">\uc784\ub300 \ub9cc\ub8cc ' + d + '\uc77c \uc804<\/span>';\n  return '';\n}\n\nfunction assetStats(list){\n  var area = 0, value = 0, rent = 0, rentCnt = 0, soon = 0;\n  list.forEach(function(a){\n    area += toNum(a.area); value += toNum(a.value);\n    if(a.usage === '\uc784\ub300 \uc911'){ rent += toNum(a.rent); rentCnt++; }\n    if(a.usage === '\uc784\ub300 \uc911' && a.leaseEnd){ var d = daysUntil(a.leaseEnd); if(d !== null && d <= 90) soon++; }\n  });\n  return { count:list.length, area:area, value:value, rent:rent, rentCnt:rentCnt, soon:soon };\n}\n\n\/* ---- \ud68c\ucc28\ubcc4 \uae30\ub85d [2026-10-28 \ucd94\uac00] ----\n   \uac01 \uc7ac\uc0b0(a.rounds)\uc5d0 '\uc81cN\ud68c' \uc810\uac80\u00b7\ud3c9\uac00 \uae30\ub85d\uc744 \uc313\uace0, \ud68c\ucc28\ub97c \uace0\ub974\uba74 \uadf8 \ud68c\ucc28 \uae30\uc900\uc73c\ub85c \uc804\uccb4\ub97c \uc5f4\ub78c\ud569\ub2c8\ub2e4. *\/\nfunction allRoundNums(){\n  var set = {};\n  (State.assets||[]).forEach(function(a){ (a.rounds||[]).forEach(function(r){ set[r.no] = 1; }); });\n  return Object.keys(set).map(Number).sort(function(x,y){ return y-x; });\n}\nfunction assetBase(){\n  var list = State.assets || [];\n  if(!AssetView.round) return list;\n  var n = Number(AssetView.round), out = [];\n  list.forEach(function(a){\n    var r = (a.rounds||[]).find(function(x){ return Number(x.no) === n; });\n    if(!r) return;\n    var m = {}; for(var k in a) m[k] = a[k];\n    if(r.value) m.value = r.value;\n    if(r.rent !== '' && r.rent != null) m.rent = r.rent;\n    if(r.usage) m.usage = r.usage;\n    if(r.tenant) m.tenant = r.tenant;\n    m._round = r; out.push(m);\n  });\n  return out;\n}\nfunction sortedRounds(a){ return (a.rounds||[]).slice().sort(function(x,y){ return Number(y.no)-Number(x.no); }); }\n\nfunction filteredAssets(){\n  var q = AssetView.query.trim().toLowerCase();\n  return assetBase().filter(function(a){\n    if(AssetView.kind && a.kind !== AssetView.kind) return false;\n    if(!q) return true;\n    return [a.name,a.address,a.lot,a.owner,a.tenant,a.note].join(' ').toLowerCase().indexOf(q) !== -1;\n  });\n}\n\nfunction renderAssets(){\n  if(!CFG.canEdit){\n    var pub = State.assets || [];\n    if(!pub.length) return emptyState('\uc885\uc911 \uc7ac\uc0b0','\uacf5\uac1c\ub41c \uc7ac\uc0b0\uc774 \uc5c6\uc5b4\uc694','\uacf5\uac1c\ub85c \uc9c0\uc815\ub41c \uc885\uc911 \uc7ac\uc0b0\uc774 \uc5ec\uae30\uc5d0 \ub098\ud0c0\ub098\uc694.');\n    var pst = assetStats(pub);\n    return '<div class=\"section-title\"><span class=\"dot\"><\/span>\uc885\uc911 \uc7ac\uc0b0<\/div>' +\n      '<div class=\"fh-asset-stats\"><div class=\"fh-asset-stat\"><span>\uacf5\uac1c \uc7ac\uc0b0<\/span><b>' + pst.count + '\uac74<\/b><\/div>' +\n      '<div class=\"fh-asset-stat\"><span>\ucd1d \uba74\uc801<\/span><b>' + (pst.area ? fmtNum(pst.area) + '\u33a1' : '-') + '<\/b><i>' + (pst.area ? fmtNum(pst.area*0.3025) + '\ud3c9' : '') + '<\/i><\/div><\/div>' +\n      '<div class=\"fh-asset-tools\"><input id=\"fhAssetSearch\" type=\"search\" placeholder=\"\uc7ac\uc0b0\uba85\u00b7\uc18c\uc7ac\uc9c0\u00b7\uc9c0\ubc88 \uac80\uc0c9\" value=\"' + escapeHtml(AssetView.query) + '\"><\/div>' +\n      '<div id=\"fhAssetList\">' + assetListHtml() + '<\/div>';\n  }\n  var all = State.assets || [];\n  var base = assetBase();\n  var st = assetStats(base);\n  var rnums = allRoundNums();\n  var kinds = ASSET_KINDS.filter(function(k){ return all.some(function(a){ return a.kind===k; }); });\n  var chips = '<div class=\"fh-folder-chips\"><button class=\"fh-chip' + (AssetView.kind?'':' active') + '\" data-asset-kind=\"\">\uc804\uccb4 ' + all.length + '<\/button>' +\n    kinds.map(function(k){\n      var n = all.filter(function(a){ return a.kind===k; }).length;\n      return '<button class=\"fh-chip' + (AssetView.kind===k?' active':'') + '\" data-asset-kind=\"' + escapeHtml(k) + '\">' + escapeHtml(k) + ' ' + n + '<\/button>';\n    }).join('') + '<\/div>';\n  return '<div class=\"section-title\"><span class=\"dot\"><\/span>\uc885\uc911 \uc7ac\uc0b0\uc2e4<\/div>' +\n    '<div class=\"fh-asset-lock\">\ud83d\udd12 \uae30\ubcf8\uc740 \uad00\ub9ac\uc790 \uc804\uc6a9\uc774\uc5d0\uc694. \uc7ac\uc0b0\ub9c8\ub2e4 \uacf5\uac1c \ubc94\uc704\ub97c \uace8\ub77c \ud68c\uc6d0\u00b7\ubc29\ubb38\uc790\uc5d0\uac8c \ubcf4\uc5ec\uc904 \uc218 \uc788\uace0, \uacf5\uac1c\ud574\ub3c4 \ud3c9\uac00\uc561\u00b7\uc784\ub300\u00b7\uba85\uc758\u00b7\uba54\ubaa8\ub294 \ub530\ub85c \ud5c8\uc6a9\ud558\uc9c0 \uc54a\uc73c\uba74 \uc11c\ubc84\uc5d0\uc11c \uc804\ub2ec\ub418\uc9c0 \uc54a\uc544\uc694.<\/div>' +\n    '<div class=\"fh-asset-stats\">' +\n      '<div class=\"fh-asset-stat\"><span>' + (AssetView.round ? '\uc81c' + AssetView.round + '\ud68c \uae30\ub85d \uc7ac\uc0b0' : '\ub4f1\ub85d \uc7ac\uc0b0') + '<\/span><b>' + st.count + '\uac74<\/b><\/div>' +\n      '<div class=\"fh-asset-stat\"><span>\ucd1d \uba74\uc801<\/span><b>' + (st.area ? fmtNum(st.area) + '\u33a1' : '-') + '<\/b><i>' + (st.area ? fmtNum(st.area*0.3025) + '\ud3c9' : '') + '<\/i><\/div>' +\n      '<div class=\"fh-asset-stat\"><span>\ucd1d \ud3c9\uac00\uc561<\/span><b>' + fmtWon(st.value) + '<\/b><\/div>' +\n      '<div class=\"fh-asset-stat\"><span>\uc6d4 \uc784\ub300\uc218\uc785<\/span><b>' + fmtWon(st.rent) + '<\/b><i>\uc784\ub300 ' + st.rentCnt + '\uac74' + (st.soon ? ' \u00b7 \ub9cc\ub8cc\uc784\ubc15 ' + st.soon : '') + '<\/i><\/div>' +\n    '<\/div>' +\n    (all.length ? '<div class=\"fh-asset-round\"><label for=\"fhAssetRound\">\uc5f4\ub78c \uae30\uc900<\/label><select id=\"fhAssetRound\"><option value=\"\">\ud604\uc7ac \uc815\ubcf4<\/option>' +\n      rnums.map(function(n){ return '<option value=\"' + n + '\"' + (String(AssetView.round)===String(n)?' selected':'') + '>\uc81c' + n + '\ud68c \uae30\ub85d<\/option>'; }).join('') +\n      '<\/select><\/div>' + chips +\n      '<div class=\"fh-asset-tools\"><input id=\"fhAssetSearch\" type=\"search\" placeholder=\"\uc7ac\uc0b0\uba85\u00b7\uc18c\uc7ac\uc9c0\u00b7\uc9c0\ubc88\u00b7\uba85\uc758\u00b7\uc784\ucc28\uc778 \uac80\uc0c9\" value=\"' + escapeHtml(AssetView.query) + '\">' +\n      '<button class=\"btn btn-ghost\" id=\"fhAssetCsv\">\uc7ac\uc0b0 CSV<\/button><button class=\"btn btn-ghost\" id=\"fhAssetRoundCsv\">\ud68c\ucc28 CSV<\/button><\/div>' : '') +\n    (all.length ? assetSummaryHtml(base) + assetTrendHtml() : '') +\n    '<div id=\"fhAssetList\">' + assetListHtml() + '<\/div>' +\n    '<div class=\"fh-backup-box\"><b>\ud83d\udcbe \ub370\uc774\ud130 \ubcf4\uad00<\/b><p>\uc7ac\uc0b0 \uc815\ubcf4\ub294 \uc6cc\ub4dc\ud504\ub808\uc2a4 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ub418\uc5b4 \uc0c8\ub85c\uace0\uce68\u00b7\ud50c\ub7ec\uadf8\uc778 \ube44\ud65c\uc131\ud654\u00b7\uc5c5\ub370\uc774\ud2b8\ub85c \uc0ac\ub77c\uc9c0\uc9c0 \uc54a\uc544\uc694. \uadf8\ub798\ub3c4 \ub9cc\uc77c\uc744 \uc704\ud574 \uac00\ub054 \ubc31\uc5c5 \ud30c\uc77c\uc744 \ub0b4\ub824\ubc1b\uc544 \ubcf4\uad00\ud558\uc138\uc694.<\/p>' +\n      '<div class=\"fh-asset-tools\"><button class=\"btn btn-ghost\" id=\"fhAssetBackup\">\ubc31\uc5c5 \ud30c\uc77c \uc800\uc7a5<\/button><button class=\"btn btn-ghost\" id=\"fhAssetRestore\">\ubc31\uc5c5 \ubd88\ub7ec\uc624\uae30<\/button>' +\n      '<input type=\"file\" id=\"fhAssetRestoreFile\" accept=\".json,application\/json\" style=\"display:none\"><\/div><\/div>';\n}\n\n\/* [2026-10-28 \ucd94\uac00] \uc18c\uacc4\u00b7\ud569\uacc4 \ud45c: \uc885\ub958\ubcc4 \ub610\ub294 \uc0ac\uc6a9\ud604\ud669\ubcc4\ub85c \ubb36\uc5b4 \uac74\uc218\u00b7\uba74\uc801\u00b7\ud3c9\uac00\uc561\u00b7\uc784\ub300\ub8cc\ub97c \uc9d1\uacc4\ud569\ub2c8\ub2e4 *\/\nfunction assetSummaryHtml(base){\n  if(!base.length) return '';\n  var byUsage = AssetView.group === 'usage';\n  var order = byUsage ? ASSET_USAGES : ASSET_KINDS, field = byUsage ? 'usage' : 'kind';\n  var groups = {};\n  base.forEach(function(a){ var g = a[field] || '(\ubbf8\ubd84\ub958)'; (groups[g] = groups[g] || []).push(a); });\n  var names = order.filter(function(n){ return groups[n]; }).concat(Object.keys(groups).filter(function(n){ return order.indexOf(n) === -1; }));\n  function row(label, list, cls){\n    var st = assetStats(list);\n    return '<tr class=\"' + (cls||'') + '\"><th>' + escapeHtml(label) + '<\/th><td>' + st.count + '<\/td><td>' + (st.area ? fmtNum(st.area) : '-') + '<\/td><td>' + (st.value ? fmtWon(st.value) : '-') + '<\/td><td>' + (st.rent ? fmtWon(st.rent) : '-') + '<\/td><\/tr>';\n  }\n  var total = assetStats(base);\n  return '<div class=\"fh-sum-box\"><div class=\"fh-sum-head\"><span>\uc18c\uacc4 \u00b7 \ud569\uacc4' + (AssetView.round ? ' (\uc81c' + AssetView.round + '\ud68c \uae30\uc900)' : '') + '<\/span>' +\n    '<div class=\"fh-sum-toggle\"><button class=\"' + (byUsage?'':'on') + '\" data-asset-group=\"kind\">\uc885\ub958\ubcc4<\/button><button class=\"' + (byUsage?'on':'') + '\" data-asset-group=\"usage\">\uc0ac\uc6a9\ud604\ud669\ubcc4<\/button><\/div><\/div>' +\n    '<div class=\"fh-sum-scroll\"><table class=\"fh-sum-table\"><thead><tr><th>' + (byUsage?'\uc0ac\uc6a9\ud604\ud669':'\uc885\ub958') + '<\/th><th>\uac74\uc218<\/th><th>\uba74\uc801(\u33a1)<\/th><th>\ud3c9\uac00\uc561<\/th><th>\uc6d4 \uc784\ub300\ub8cc<\/th><\/tr><\/thead><tbody>' +\n    names.map(function(n){ return row(n, groups[n]); }).join('') +\n    row('\ud569\uacc4', base, 'total') + '<\/tbody><\/table><\/div>' +\n    '<div class=\"fh-sum-foot\">\uc5f0 \uc784\ub300\uc218\uc785(\uc6d4 \uc784\ub300\ub8cc\u00d712): <b>' + fmtWon(total.rent*12) + '<\/b> \u00b7 \ucd1d \uba74\uc801 ' + fmtArea(total.area) + ' \u00b7 \uc6d4 \uc784\ub300\ub8cc\ub294 \"\uc784\ub300 \uc911\"\uc778 \uc7ac\uc0b0\ub9cc \ud569\uc0b0<\/div><\/div>';\n}\n\n\/* [2026-10-28 \ucd94\uac00] \ud68c\ucc28\ubcc4\u00b7\uc6d4\ubcc4 \ud569\uacc4: \ud68c\ucc28(\ub610\ub294 \uc6d4)\ub9c8\ub2e4 \ubaa8\ub4e0 \uc7ac\uc0b0\uc758 \ud569\uacc4\ub97c \ud55c \uc904\ub85c \ubaa8\uc544 \ubcc0\ub3d9\uc744 \ube44\uad50\ud569\ub2c8\ub2e4.\n   \ud574\ub2f9 \uc2dc\uc810\uc5d0 \uae30\ub85d\uc774 \uc5c6\ub294 \uc7ac\uc0b0\uc740 \uadf8 \uc774\uc804\uc758 \uac00\uc7a5 \ucd5c\uadfc \uae30\ub85d \uac12\uc744 \uc774\uc5b4\uc11c \uc0ac\uc6a9\ud569\ub2c8\ub2e4. *\/\nfunction assetTrendRows(mode){\n  var assets = State.assets || [], keys = {};\n  assets.forEach(function(a){ (a.rounds||[]).forEach(function(r){\n    if(mode === 'month'){ if(r.date) keys[String(r.date).slice(0,7)] = 1; } else { keys[Number(r.no)] = 1; }\n  }); });\n  var list = Object.keys(keys);\n  list = mode === 'month' ? list.sort() : list.map(Number).sort(function(x,y){ return x-y; });\n  return list.map(function(k){\n    var row = { key:k, count:0, area:0, value:0, rent:0, dates:[] };\n    assets.forEach(function(a){\n      var recs = (a.rounds||[]).slice().sort(function(x,y){\n        return mode === 'month' ? String(x.date||'').localeCompare(String(y.date||'')) || (Number(x.no)-Number(y.no)) : Number(x.no)-Number(y.no);\n      }).filter(function(r){ return mode === 'month' ? (r.date && String(r.date).slice(0,7) <= k) : Number(r.no) <= Number(k); });\n      if(!recs.length) return;\n      function pick(f){ for(var i=recs.length-1;i>=0;i--){ if(recs[i][f] !== '' && recs[i][f] != null) return recs[i][f]; } return ''; }\n      var usage = pick('usage') || a.usage;\n      row.count++; row.area += toNum(a.area); row.value += toNum(pick('value'));\n      if(usage === '\uc784\ub300 \uc911') row.rent += toNum(pick('rent'));\n      var last = recs[recs.length-1];\n      if(mode !== 'month' && Number(last.no) === Number(k) && last.date) row.dates.push(last.date);\n    });\n    row.label = mode === 'month' ? (k.slice(0,4) + '\ub144 ' + Number(k.slice(5,7)) + '\uc6d4') : ('\uc81c' + k + '\ud68c');\n    row.sub = mode === 'month' ? '' : (row.dates.length ? row.dates.sort()[row.dates.length-1] : '');\n    return row;\n  });\n}\nfunction assetTrendHtml(){\n  var mode = AssetView.trend;\n  var rows = assetTrendRows(mode);\n  var toggle = '<div class=\"fh-sum-toggle\"><button class=\"' + (mode==='round'?'on':'') + '\" data-asset-trend=\"round\">\ud68c\ucc28\ubcc4<\/button><button class=\"' + (mode==='month'?'on':'') + '\" data-asset-trend=\"month\">\uc6d4\ubcc4<\/button><\/div>';\n  if(!rows.length) return '<div class=\"fh-sum-box\"><div class=\"fh-sum-head\"><span>\ud68c\ucc28\ubcc4\u00b7\uc6d4\ubcc4 \ud569\uacc4<\/span>' + toggle + '<\/div><div class=\"fh-round-empty\">\uc7ac\uc0b0\uc744 \uc5f4\uc5b4 \"+ \ud68c\ucc28 \ucd94\uac00\"\ub85c \uae30\ub85d\uc744 \ub0a8\uae30\uba74 \ud68c\ucc28\ubcc4\u00b7\uc6d4\ubcc4 \ud569\uacc4\uac00 \uc5ec\uae30\uc5d0 \ub098\ud0c0\ub098\uc694.<\/div><\/div>';\n  var maxV = Math.max.apply(null, rows.map(function(r){ return r.value; })) || 1;\n  var body = rows.map(function(r, i){\n    var prev = rows[i-1], d = prev ? r.value - prev.value : 0;\n    var delta = prev && d ? '<span class=\"fh-delta ' + (d>0?'up':'down') + '\">' + (d>0?'\u25b2':'\u25bc') + fmtWon(Math.abs(d)) + '<\/span>' : '<span class=\"fh-delta\">-<\/span>';\n    return '<tr><th>' + escapeHtml(r.label) + (r.sub ? '<small>' + escapeHtml(r.sub) + '<\/small>' : '') + '<\/th><td>' + r.count + '<\/td>' +\n      '<td class=\"fh-trend-val\"><i style=\"width:' + Math.round(r.value \/ maxV * 100) + '%\"><\/i><span>' + (r.value ? fmtWon(r.value) : '-') + '<\/span><\/td>' +\n      '<td>' + delta + '<\/td><td>' + (r.rent ? fmtWon(r.rent) : '-') + '<\/td><\/tr>';\n  }).join('');\n  return '<div class=\"fh-sum-box\"><div class=\"fh-sum-head\"><span>' + (mode==='month' ? '\uc6d4\ubcc4' : '\ud68c\ucc28\ubcc4') + ' \ud569\uacc4<\/span>' + toggle + '<\/div>' +\n    '<div class=\"fh-sum-scroll\"><table class=\"fh-sum-table fh-trend-table\"><thead><tr><th>' + (mode==='month' ? '\uc6d4' : '\ud68c\ucc28') + '<\/th><th>\uac74\uc218<\/th><th>\ud3c9\uac00\uc561 \ud569\uacc4<\/th><th>\uc804 \uae30\uac04 \ub300\ube44<\/th><th>\uc6d4 \uc784\ub300\ub8cc \ud569\uacc4<\/th><\/tr><\/thead><tbody>' + body + '<\/tbody><\/table><\/div>' +\n    '<div class=\"fh-sum-foot\">\uae30\ub85d\uc774 \uc5c6\ub294 \uc7ac\uc0b0\uc740 \uc774\uc804 \uae30\ub85d \uac12\uc744 \uc774\uc5b4\uc11c \uacc4\uc0b0\ud574\uc694. \uc6d4\ubcc4\uc740 \uc810\uac80\u00b7\ud3c9\uac00\uc77c \uae30\uc900\uc774\uc5d0\uc694.<\/div><\/div>';\n}\n\nfunction assetListHtml(){\n  var all = State.assets || [];\n  if(!all.length) return emptyStateBody('\uc544\uc9c1 \ub4f1\ub85d\ub41c \uc885\uc911 \uc7ac\uc0b0\uc774 \uc5c6\uc5b4\uc694','\uc624\ub978\ucabd \uc544\ub798 + \ubc84\ud2bc\uc73c\ub85c \ud1a0\uc9c0\u00b7\uac74\ubb3c \ub4f1 \uccab \uc7ac\uc0b0\uc744 \ub4f1\ub85d\ud574 \ubcf4\uc138\uc694.');\n  var list = filteredAssets();\n  if(!list.length) return emptyStateBody('\uc870\uac74\uc5d0 \ub9de\ub294 \uc7ac\uc0b0\uc774 \uc5c6\uc5b4\uc694','\uac80\uc0c9\uc5b4\ub098 \uc885\ub958 \uc120\ud0dd\uc744 \ubc14\uafd4 \ubcf4\uc138\uc694.');\n  list = list.slice().sort(function(a,b){ return toNum(b.value) - toNum(a.value); });\n  var fst = assetStats(list);\n  var listSum = (AssetView.kind || AssetView.query.trim())\n    ? '<div class=\"fh-asset-roundnote\">\uc870\ud68c \uacb0\uacfc \uc18c\uacc4 \u00b7 ' + fst.count + '\uac74 \u00b7 ' + (fst.area ? fmtNum(fst.area) + '\u33a1 \u00b7 ' : '') + '\ud3c9\uac00\uc561 ' + fmtWon(fst.value) + (fst.rent ? ' \u00b7 \uc6d4 \uc784\ub300\ub8cc ' + fmtWon(fst.rent) : '') + '<\/div>' : '';\n  var roundNote = listSum + (AssetView.round ? '<div class=\"fh-asset-roundnote\">\uc81c' + AssetView.round + '\ud68c \uae30\ub85d \uae30\uc900\uc73c\ub85c \ubcf4\uace0 \uc788\uc5b4\uc694<\/div>' : '');\n  return roundNote + '<div class=\"fh-asset-list\">' + list.map(function(a){\n    var meta = [a.kind, a.usage].filter(Boolean).join(' \u00b7 ');\n    return '<div class=\"fh-asset-card\" data-open-asset=\"' + escapeHtml(a.id) + '\">' +\n      '<div class=\"fh-asset-card-top\"><span class=\"fh-asset-kind\">' + escapeHtml(meta || '') + '<\/span>' + (a._round ? '' : leaseBadgeHtml(a)) + visBadgeHtml(a) + '<\/div>' +\n      '<h4>' + escapeHtml(a.name || '\uc774\ub984 \uc5c6\uc74c') + '<\/h4>' +\n      '<div class=\"fh-asset-addr\">' + escapeHtml([a.address, a.lot].filter(Boolean).join(' ')) + '<\/div>' +\n      (a._round && a._round.memo ? '<div class=\"fh-asset-addr\">\ud83d\udcdd ' + escapeHtml(a._round.memo) + '<\/div>' : '') +\n      '<div class=\"fh-asset-nums\"><span>' + (toNum(a.area) ? fmtArea(toNum(a.area)) : '\uba74\uc801 -') + '<\/span><b>' + (toNum(a.value) ? fmtWon(toNum(a.value)) : (CFG.canEdit ? '\ud3c9\uac00\uc561 -' : '')) + '<\/b><\/div>' +\n    '<\/div>';\n  }).join('') + '<\/div>';\n}\n\nfunction bindAssets(){\n  var c = document.getElementById('fhContent');\n  c.querySelectorAll('[data-asset-kind]').forEach(function(b){\n    b.addEventListener('click', function(){ AssetView.kind = b.getAttribute('data-asset-kind'); render(); });\n  });\n  var s = document.getElementById('fhAssetSearch');\n  if(s) s.addEventListener('input', function(){\n    AssetView.query = s.value;\n    document.getElementById('fhAssetList').innerHTML = assetListHtml();\n    bindAssetCards();\n  });\n  c.querySelectorAll('[data-asset-group]').forEach(function(b){\n    b.addEventListener('click', function(){ AssetView.group = b.getAttribute('data-asset-group'); render(); });\n  });\n  c.querySelectorAll('[data-asset-trend]').forEach(function(b){\n    b.addEventListener('click', function(){ AssetView.trend = b.getAttribute('data-asset-trend'); render(); });\n  });\n  var rs = document.getElementById('fhAssetRound');\n  if(rs) rs.addEventListener('change', function(){ AssetView.round = rs.value; render(); });\n  var csv = document.getElementById('fhAssetCsv');\n  if(csv) csv.addEventListener('click', exportAssetsCsv);\n  var bk = document.getElementById('fhAssetBackup');\n  if(bk) bk.addEventListener('click', backupAssets);\n  var rst = document.getElementById('fhAssetRestore'), rf = document.getElementById('fhAssetRestoreFile');\n  if(rst && rf){\n    rst.addEventListener('click', function(){ rf.click(); });\n    rf.addEventListener('change', function(){ if(rf.files[0]) restoreAssets(rf.files[0]); rf.value = ''; });\n  }\n  var rcsv = document.getElementById('fhAssetRoundCsv');\n  if(rcsv) rcsv.addEventListener('click', exportAssetRoundsCsv);\n  bindAssetCards();\n}\nfunction bindAssetCards(){\n  document.querySelectorAll('[data-open-asset]').forEach(function(el){\n    el.addEventListener('click', function(){\n      var a = (State.assets||[]).find(function(x){ return x.id === el.getAttribute('data-open-asset'); });\n      if(a) openAssetViewer(a);\n    });\n  });\n}\n\nfunction visBadgeHtml(a){\n  if(!CFG.canEdit || a._round) return '';\n  if(a.visibility === 'public') return '<span class=\"fh-asset-badge pub\">\ubc29\ubb38\uc790 \uacf5\uac1c<\/span>';\n  if(a.visibility === 'member') return '<span class=\"fh-asset-badge pub\">\ud68c\uc6d0 \uacf5\uac1c<\/span>';\n  return '';\n}\nfunction assetRow(label, val){\n  if(val === undefined || val === null || val === '') return '';\n  return '<div class=\"fh-asset-row\"><b>' + label + '<\/b><span>' + escapeHtml(String(val)) + '<\/span><\/div>';\n}\n\nfunction assetRoundsHtml(a){\n  var rs = sortedRounds(a);\n  var rows = rs.map(function(r, i){\n    var prev = rs[i+1], delta = '';\n    if(prev && toNum(r.value) && toNum(prev.value)){\n      var d = toNum(r.value) - toNum(prev.value);\n      if(d) delta = '<span class=\"fh-delta ' + (d>0?'up':'down') + '\">' + (d>0?'\u25b2 ':'\u25bc ') + fmtWon(Math.abs(d)) + '<\/span>';\n    }\n    return '<div class=\"fh-round-item\"><div class=\"fh-round-head\"><b>\uc81c' + escapeHtml(String(r.no)) + '\ud68c<\/b><span>' + escapeHtml(r.date||'') + '<\/span>' +\n      '<button class=\"fh-round-edit\" data-edit-round=\"' + escapeHtml(r.id) + '\">\uc218\uc815<\/button><\/div>' +\n      '<div class=\"fh-round-body\">' +\n        (toNum(r.value) ? '\ud3c9\uac00\uc561 <b>' + fmtWon(toNum(r.value)) + '<\/b> ' + delta : '') +\n        (r.usage ? ' \u00b7 ' + escapeHtml(r.usage) : '') +\n        (toNum(r.rent) ? ' \u00b7 \uc6d4 ' + fmtWon(toNum(r.rent)) : '') +\n        (r.tenant ? ' \u00b7 ' + escapeHtml(r.tenant) : '') +\n      '<\/div>' + (r.memo ? '<div class=\"fh-round-memo\">' + escapeHtml(r.memo) + '<\/div>' : '') + '<\/div>';\n  }).join('');\n  return '<div class=\"fh-round-box\"><div class=\"fh-round-title\"><span>\ud68c\ucc28\ubcc4 \uae30\ub85d ' + rs.length + '\uac74<\/span><button class=\"btn btn-primary\" id=\"fhAddRound\" style=\"padding:6px 12px;font-size:12px;\">+ \ud68c\ucc28 \ucd94\uac00<\/button><\/div>' +\n    (rows || '<div class=\"fh-round-empty\">\uc544\uc9c1 \ud68c\ucc28 \uae30\ub85d\uc774 \uc5c6\uc5b4\uc694. \uc810\uac80\u00b7\ud3c9\uac00\ud560 \ub54c\ub9c8\ub2e4 \ud55c \ud68c\uc529 \ub0a8\uaca8 \ubcf4\uc138\uc694.<\/div>') + '<\/div>';\n}\n\nfunction openRoundForm(a, round){\n  var isEdit = !!round;\n  var nums = (a.rounds||[]).map(function(r){ return Number(r.no)||0; });\n  var rd = round || { id:uid(), no:(nums.length ? Math.max.apply(null, nums) + 1 : 1), date:new Date().toISOString().slice(0,10), value:a.value||'', rent:a.rent||'', usage:a.usage||'', tenant:a.tenant||'', memo:'' };\n  function inp(id,label,val,type,ph){ return '<div class=\"field\"><label>' + label + '<\/label><input id=\"rd-' + id + '\" type=\"' + (type||'text') + '\" value=\"' + escapeHtml(String(val==null?'':val)) + '\" placeholder=\"' + (ph||'') + '\"><\/div>'; }\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>' + escapeHtml(a.name||'\uc7ac\uc0b0') + ' \u00b7 ' + (isEdit ? '\ud68c\ucc28 \uc218\uc815' : '\ud68c\ucc28 \ucd94\uac00') + '<\/h3>' +\n    '<div class=\"field-row\">' + inp('no','\ud68c\ucc28 (\uc22b\uc790)',rd.no,'number') + inp('date','\uc810\uac80\u00b7\ud3c9\uac00\uc77c',rd.date,'date') + '<\/div>' +\n    '<div class=\"field-row\">' + inp('value','\ud3c9\uac00\uc561 (\uc6d0)',rd.value,'text') + inp('rent','\uc6d4 \uc784\ub300\ub8cc (\uc6d0)',rd.rent,'text') + '<\/div>' +\n    '<div class=\"field-row\"><div class=\"field\"><label>\uc0ac\uc6a9 \ud604\ud669<\/label><select id=\"rd-usage\"><option value=\"\">(\ubcc0\uacbd \uc5c6\uc74c)<\/option>' +\n      ASSET_USAGES.map(function(o){ return '<option' + (o===rd.usage?' selected':'') + '>' + o + '<\/option>'; }).join('') + '<\/select><\/div>' + inp('tenant','\uc784\ucc28\uc778',rd.tenant) + '<\/div>' +\n    '<div class=\"field\"><label>\uba54\ubaa8<\/label><textarea id=\"rd-memo\" placeholder=\"\uc810\uac80 \ub0b4\uc6a9, \ubcc0\ub3d9 \uc0ac\ud56d \ub4f1\">' + escapeHtml(rd.memo||'') + '<\/textarea><\/div>' +\n    '<label class=\"fh-check\"><input type=\"checkbox\" id=\"rd-apply\" checked> \uac00\uc7a5 \ucd5c\uadfc \ud68c\ucc28\uc774\uba74 \ud604\uc7ac \uc815\ubcf4(\ud3c9\uac00\uc561\u00b7\uc784\ub300\ub8cc\u00b7\uc0ac\uc6a9\ud604\ud669)\uc5d0\ub3c4 \ubc18\uc601<\/label>' +\n    '<div class=\"btn-row\">' + (isEdit ? '<button class=\"btn btn-danger\" id=\"fhRdDel\">\uc0ad\uc81c<\/button>' : '') + '<button class=\"btn btn-ghost\" id=\"fhCancel\">\ucde8\uc18c<\/button><button class=\"btn btn-primary\" id=\"fhSave\">\uc800\uc7a5<\/button><\/div>'\n  );\n  function back(){ var cur = State.assets.find(function(x){ return x.id===a.id; }); if(cur) openAssetViewer(cur); else closeModal(); render(); }\n  document.getElementById('fhClose').addEventListener('click', back);\n  document.getElementById('fhCancel').addEventListener('click', back);\n  function persist(list, msg){\n    var all = State.assets.map(function(x){ return x.id===a.id ? list : x; });\n    apiSave('assets', all).then(function(){ State.assets = all; showToast(msg); back(); }).catch(function(){});\n  }\n  function cloneAsset(){ var m = {}; for(var k in a) m[k] = a[k]; m.rounds = (a.rounds||[]).slice(); return m; }\n  if(isEdit) document.getElementById('fhRdDel').addEventListener('click', function(){\n    if(!confirm('\uc774 \ud68c\ucc28 \uae30\ub85d\uc744 \uc0ad\uc81c\ud560\uae4c\uc694?')) return;\n    var m = cloneAsset(); m.rounds = m.rounds.filter(function(r){ return r.id !== rd.id; }); persist(m, '\uc0ad\uc81c\ud588\uc5b4\uc694');\n  });\n  document.getElementById('fhSave').addEventListener('click', function(){\n    function v(id){ return document.getElementById('rd-' + id).value.trim(); }\n    var no = parseInt(v('no'), 10);\n    if(!no || no < 1){ showToast('\ud68c\ucc28\ub294 1 \uc774\uc0c1\uc758 \uc22b\uc790\ub85c \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n    var dup = (a.rounds||[]).some(function(r){ return Number(r.no) === no && r.id !== rd.id; });\n    if(dup){ showToast('\uc81c' + no + '\ud68c \uae30\ub85d\uc774 \uc774\ubbf8 \uc788\uc5b4\uc694'); return; }\n    var u = { id:rd.id, no:no, date:v('date'), value:toNum(v('value')) || '', rent:toNum(v('rent')) || '', usage:v('usage'), tenant:v('tenant'), memo:v('memo') };\n    var m = cloneAsset();\n    var idx = m.rounds.findIndex(function(r){ return r.id === u.id; });\n    if(idx >= 0) m.rounds[idx] = u; else m.rounds.push(u);\n    var maxNo = Math.max.apply(null, m.rounds.map(function(r){ return Number(r.no); }));\n    if(document.getElementById('rd-apply').checked && no === maxNo){\n      if(u.value) m.value = u.value;\n      if(u.rent !== '') m.rent = u.rent;\n      if(u.usage) m.usage = u.usage;\n      if(u.tenant) m.tenant = u.tenant;\n      if(u.date) m.valueDate = u.date;\n    }\n    persist(m, isEdit ? '\uc218\uc815\ud588\uc5b4\uc694' : '\uc81c' + no + '\ud68c \uae30\ub85d\uc744 \ub0a8\uacbc\uc5b4\uc694');\n  });\n}\n\nfunction openAssetViewerPublic(a){\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<div style=\"font-size:11px;font-weight:700;color:var(--fh-seal);margin-bottom:6px;\">' + escapeHtml(a.kind||'') + '<\/div>' +\n    '<h3 style=\"margin-top:0;\">' + escapeHtml(a.name || '\uc774\ub984 \uc5c6\uc74c') + '<\/h3>' +\n    '<div class=\"fh-asset-detail\">' + assetRow('\uc18c\uc7ac\uc9c0', a.address) + assetRow('\uc9c0\ubc88', a.lot) +\n      assetRow('\uba74\uc801', toNum(a.area) ? fmtArea(toNum(a.area)) : '') +\n      assetRow('\ud3c9\uac00\uc561', toNum(a.value) ? fmtWon(toNum(a.value)) : '') + assetRow('\ud3c9\uac00 \uae30\uc900\uc77c', toNum(a.value) ? a.valueDate : '') + '<\/div>' +\n    (a.publicNote ? '<div class=\"story-full\" style=\"margin-top:12px;\">' + escapeHtml(a.publicNote) + '<\/div>' : '')\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n}\n\nfunction openAssetViewer(a){\n  if(!CFG.canEdit){ openAssetViewerPublic(a); return; }\n  var area = toNum(a.area), value = toNum(a.value), rent = toNum(a.rent);\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<div style=\"font-size:11px;font-weight:700;color:var(--fh-seal);margin-bottom:6px;\">' + escapeHtml([a.kind,a.usage].filter(Boolean).join(' \u00b7 ')) + ' ' + leaseBadgeHtml(a) + '<\/div>' +\n    '<h3 style=\"margin-top:0;\">' + escapeHtml(a.name || '\uc774\ub984 \uc5c6\uc74c') + '<\/h3>' +\n    '<div class=\"fh-asset-detail\">' +\n      assetRow('\uc18c\uc7ac\uc9c0', a.address) + assetRow('\uc9c0\ubc88', a.lot) +\n      assetRow('\uba74\uc801', area ? fmtArea(area) : '') +\n      assetRow('\ud3c9\uac00\uc561', value ? fmtWon(value) + (value>=10000 ? ' (' + fmtNum(value) + '\uc6d0)' : '') : '') +\n      assetRow('\ud3c9\uac00 \uae30\uc900\uc77c', a.valueDate) +\n      assetRow('\ub4f1\uae30 \uba85\uc758', a.owner) + assetRow('\ucde8\ub4dd\uc77c\u00b7\uc0ac\uc720', a.acquired) +\n      assetRow('\uc0ac\uc6a9 \ud604\ud669', a.usage) + assetRow('\uc784\ucc28\uc778', a.tenant) +\n      assetRow('\uc6d4 \uc784\ub300\ub8cc', rent ? fmtWon(rent) : '') + assetRow('\uc784\ub300 \ub9cc\ub8cc\uc77c', a.leaseEnd) +\n      assetRow('\uc11c\ub958 \ubcf4\uad00\ucc98', a.docs) +\n    '<\/div>' +\n    (a.note ? '<div class=\"story-full\" style=\"margin-top:12px;\">' + escapeHtml(a.note) + '<\/div>' : '') +\n    assetRoundsHtml(a) +\n    '<div class=\"btn-row\"><button class=\"btn btn-ghost\" id=\"fhEditAsset\">\uc218\uc815<\/button><button class=\"btn btn-danger\" id=\"fhDelAsset\">\uc0ad\uc81c<\/button><\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhAddRound').addEventListener('click', function(){ openRoundForm(a); });\n  document.querySelectorAll('[data-edit-round]').forEach(function(b){\n    b.addEventListener('click', function(){\n      var r = (a.rounds||[]).find(function(x){ return x.id === b.getAttribute('data-edit-round'); });\n      if(r) openRoundForm(a, r);\n    });\n  });\n  document.getElementById('fhEditAsset').addEventListener('click', function(){ openAssetForm(a); });\n  document.getElementById('fhDelAsset').addEventListener('click', function(){\n    if(!confirm('\uc774 \uc7ac\uc0b0 \uae30\ub85d\uc744 \uc0ad\uc81c\ud560\uae4c\uc694? \ub418\ub3cc\ub9b4 \uc218 \uc5c6\uc5b4\uc694.')) return;\n    State.assets = State.assets.filter(function(x){ return x.id !== a.id; });\n    apiSave('assets', State.assets).then(function(){ closeModal(); render(); showToast('\uc0ad\uc81c\ud588\uc5b4\uc694'); });\n  });\n}\n\nfunction openAssetForm(item){\n  if(!CFG.canEdit) return;\n  var isEdit = !!item;\n  var a = item || { id:uid() };\n  function opts(list, sel){ return list.map(function(o){ return '<option' + (o===sel?' selected':'') + '>' + o + '<\/option>'; }).join(''); }\n  function inp(id, label, ph, type){\n    return '<div class=\"field\"><label>' + label + '<\/label><input id=\"as-' + id + '\" type=\"' + (type||'text') + '\" value=\"' + escapeHtml(a[id]||'') + '\" placeholder=\"' + (ph||'') + '\"><\/div>';\n  }\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>' + (isEdit ? '\uc885\uc911 \uc7ac\uc0b0 \uc218\uc815' : '\uc885\uc911 \uc7ac\uc0b0 \ub4f1\ub85d') + '<\/h3>' +\n    inp('name','\uc7ac\uc0b0\uba85 *','\uc608: OO\ub9ac \uc885\uc911 \uc704\ud1a0') +\n    '<div class=\"field-row\"><div class=\"field\"><label>\uc885\ub958<\/label><select id=\"as-kind\">' + opts(ASSET_KINDS, a.kind||ASSET_KINDS[0]) + '<\/select><\/div>' +\n    '<div class=\"field\"><label>\uc0ac\uc6a9 \ud604\ud669<\/label><select id=\"as-usage\">' + opts(ASSET_USAGES, a.usage||ASSET_USAGES[0]) + '<\/select><\/div><\/div>' +\n    inp('address','\uc18c\uc7ac\uc9c0','\uc608: \uacbd\ubd81 \uacbd\uc8fc\uc2dc OO\uba74 OO\ub9ac') +\n    inp('lot','\uc9c0\ubc88','\uc608: 123-4\ubc88\uc9c0') +\n    '<div class=\"field-row\">' + inp('area','\uba74\uc801 (\u33a1)','\uc608: 1650','text') + inp('value','\ud3c9\uac00\uc561 (\uc6d0)','\uc608: 350000000','text') + '<\/div>' +\n    inp('valueDate','\ud3c9\uac00 \uae30\uc900\uc77c','','date') +\n    inp('owner','\ub4f1\uae30 \uba85\uc758','\uc608: \uc885\uc911 \ub300\ud45c \ub610\ub294 OO\uc885\uc911') +\n    inp('acquired','\ucde8\ub4dd\uc77c\u00b7\ucde8\ub4dd \uc0ac\uc720','\uc608: 1975\ub144 \/ \ubb38\uc911 \ub9e4\uc785') +\n    '<div class=\"field-row\">' + inp('tenant','\uc784\ucc28\uc778','\uc784\ub300 \uc911\uc77c \ub54c') + inp('rent','\uc6d4 \uc784\ub300\ub8cc (\uc6d0)','\uc608: 1000000') + '<\/div>' +\n    inp('leaseEnd','\uc784\ub300 \ub9cc\ub8cc\uc77c','','date') +\n    inp('docs','\uc11c\ub958 \ubcf4\uad00\ucc98','\uc608: \ub4f1\uae30\uad8c\ub9ac\uc99d - \ucd1d\ubb34 \uae08\uace0') +\n    '<div class=\"field\"><label>\uba54\ubaa8 (\uad00\ub9ac\uc790 \uc804\uc6a9)<\/label><textarea id=\"as-note\" placeholder=\"\ubd84\uc7c1\u00b7\uc138\uae08\u00b7\uc774\uc804 \uacc4\ud68d \ub4f1 \ucc38\uace0\uc0ac\ud56d\">' + escapeHtml(a.note||'') + '<\/textarea><\/div>' +\n    '<div class=\"fh-vis-box\"><div class=\"field\"><label>\ud83d\udd13 \uc5f4\ub78c \ubc94\uc704<\/label><select id=\"as-visibility\">' +\n      [['admin','\uad00\ub9ac\uc790\ub9cc (\uae30\ubcf8)'],['member','\ub85c\uadf8\uc778 \ud68c\uc6d0\uc5d0\uac8c \uacf5\uac1c'],['public','\ubc29\ubb38\uc790\uae4c\uc9c0 \ubaa8\ub450 \uacf5\uac1c']].map(function(o){ return '<option value=\"' + o[0] + '\"' + ((a.visibility||'admin')===o[0]?' selected':'') + '>' + o[1] + '<\/option>'; }).join('') +\n    '<\/select><\/div>' +\n    '<div class=\"fh-check-note\">\uacf5\uac1c\ud574\ub3c4 \uc7ac\uc0b0\uba85\u00b7\uc885\ub958\u00b7\uc18c\uc7ac\uc9c0\u00b7\uc9c0\ubc88\u00b7\uba74\uc801\uacfc \uc544\ub798 \uacf5\uac1c\uc6a9 \uc124\uba85\ub9cc \ubcf4\uc5ec\uc694. \uc784\ub300\u00b7\uba85\uc758\u00b7\uc11c\ub958\u00b7\uba54\ubaa8\u00b7\ud68c\ucc28\uae30\ub85d\uc740 \ud56d\uc0c1 \uad00\ub9ac\uc790 \uc804\uc6a9\uc774\uc5d0\uc694.<\/div>' +\n    '<label class=\"fh-check\"><input type=\"checkbox\" id=\"as-showValue\"' + (a.showValue?' checked':'') + '> \ud3c9\uac00\uc561\u00b7\ud3c9\uac00 \uae30\uc900\uc77c\ub3c4 \uacf5\uac1c<\/label>' +\n    '<div class=\"field\" style=\"margin-top:10px;\"><label>\uacf5\uac1c\uc6a9 \uc124\uba85<\/label><textarea id=\"as-publicNote\" placeholder=\"\ud68c\uc6d0\u00b7\ubc29\ubb38\uc790\uc5d0\uac8c \ubcf4\uc5ec\uc904 \uc124\uba85 (\uc608: \uc120\uc0b0 \uad00\ub9ac \uc548\ub0b4)\">' + escapeHtml(a.publicNote||'') + '<\/textarea><\/div><\/div>' +\n    '<div class=\"btn-row\"><button class=\"btn btn-ghost\" id=\"fhCancel\">\ucde8\uc18c<\/button><button class=\"btn btn-primary\" id=\"fhSave\">\uc800\uc7a5<\/button><\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhCancel').addEventListener('click', closeModal);\n  document.getElementById('fhSave').addEventListener('click', function(){\n    function v(id){ return document.getElementById('as-' + id).value.trim(); }\n    if(!v('name')){ showToast('\uc7ac\uc0b0\uba85\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n    var u = { id:a.id, rounds:a.rounds || [] };\n    ['name','kind','usage','address','lot','valueDate','owner','acquired','tenant','leaseEnd','docs','note'].forEach(function(k){ u[k] = v(k); });\n    u.visibility = v('visibility') || 'admin';\n    u.showValue = document.getElementById('as-showValue').checked;\n    u.publicNote = v('publicNote');\n    u.area = toNum(v('area')) || ''; u.value = toNum(v('value')) || ''; u.rent = toNum(v('rent')) || '';\n    var list = (State.assets||[]).slice();\n    var idx = list.findIndex(function(x){ return x.id === u.id; });\n    if(idx >= 0) list[idx] = u; else list.push(u);\n    var btn = document.getElementById('fhSave'); btn.disabled = true; btn.textContent = '\uc800\uc7a5 \uc911...';\n    apiSave('assets', list).then(function(){\n      State.assets = list; closeModal(); render(); showToast(isEdit ? '\uc218\uc815\ud588\uc5b4\uc694' : '\ub4f1\ub85d\ud588\uc5b4\uc694');\n    }).catch(function(){ btn.disabled = false; btn.textContent = '\uc800\uc7a5'; });\n  });\n}\n\n\/* \ubc31\uc5c5(JSON): \ud68c\ucc28 \uae30\ub85d\u00b7\uacf5\uac1c \uc124\uc815\uae4c\uc9c0 \uc804\ubd80 \ub2f4\uc544 \uc800\uc7a5 \/ \ubd88\ub7ec\uc624\uba74 \ud604\uc7ac \ub370\uc774\ud130\ub97c \uad50\uccb4 *\/\nfunction backupAssets(){\n  var d = new Date(), stamp = d.getFullYear() + String(d.getMonth()+1).padStart(2,'0') + String(d.getDate()).padStart(2,'0');\n  var blob = new Blob([JSON.stringify({ type:'fh-assets-backup', version:1, savedAt:d.toISOString(), assets:State.assets||[] }, null, 2)], { type:'application\/json' });\n  var url = URL.createObjectURL(blob), link = document.createElement('a');\n  link.href = url; link.download = '\uc885\uc911\uc7ac\uc0b0_\ubc31\uc5c5_' + stamp + '.json';\n  document.body.appendChild(link); link.click(); document.body.removeChild(link);\n  setTimeout(function(){ URL.revokeObjectURL(url); }, 1000);\n  showToast('\ubc31\uc5c5 \ud30c\uc77c\uc744 \uc800\uc7a5\ud588\uc5b4\uc694');\n}\nfunction restoreAssets(file){\n  var fr = new FileReader();\n  fr.onload = function(){\n    var data;\n    try{ data = JSON.parse(fr.result); }catch(e){ showToast('\uc62c\ubc14\ub978 \ubc31\uc5c5 \ud30c\uc77c\uc774 \uc544\ub2c8\uc5d0\uc694'); return; }\n    var list = Array.isArray(data) ? data : (data && data.type==='fh-assets-backup' ? data.assets : null);\n    if(!Array.isArray(list) || list.some(function(a){ return !a || typeof a!=='object' || !a.id; })){ showToast('\uc62c\ubc14\ub978 \ubc31\uc5c5 \ud30c\uc77c\uc774 \uc544\ub2c8\uc5d0\uc694'); return; }\n    if(!confirm('\ud604\uc7ac ' + (State.assets||[]).length + '\uac74\uc744 \ubc31\uc5c5 \ud30c\uc77c\uc758 ' + list.length + '\uac74\uc73c\ub85c \uad50\uccb4\ud560\uae4c\uc694?\\n(\uad50\uccb4 \uc804\uc5d0 \ud604\uc7ac \ub370\uc774\ud130\ub97c \uba3c\uc800 \ubc31\uc5c5\ud574 \ub450\ub294 \uac83\uc744 \uad8c\uc7a5\ud574\uc694)')) return;\n    apiSave('assets', list).then(function(){ State.assets = list; render(); showToast('\ubc31\uc5c5\uc744 \ubd88\ub7ec\uc654\uc5b4\uc694'); }).catch(function(){});\n  };\n  fr.readAsText(file);\n}\n\nfunction csvDownload(cols, rows, filename){\n  function cell(x){\n    var t = String(x==null?'':x);\n    if(\/^[=+\\-@]\/.test(t)) t = \"'\" + t; \/* \uc5d1\uc140 \uc218\uc2dd \uc2e4\ud589 \ubc29\uc9c0 *\/\n    return '\"' + t.replace(\/\"\/g,'\"\"') + '\"';\n  }\n  var lines = [cols.map(function(c){ return cell(c[1]); }).join(',')];\n  rows.forEach(function(r){ lines.push(cols.map(function(c){ return cell(r[c[0]]); }).join(',')); });\n  var blob = new Blob(['\\ufeff' + lines.join('\\r\\n')], { type:'text\/csv;charset=utf-8' });\n  var url = URL.createObjectURL(blob);\n  var link = document.createElement('a');\n  link.href = url; link.download = filename;\n  document.body.appendChild(link); link.click(); document.body.removeChild(link);\n  setTimeout(function(){ URL.revokeObjectURL(url); }, 1000);\n}\nfunction withTotalRow(list){\n  var st = assetStats(list);\n  return list.concat([{ name:'\ud569\uacc4', kind:st.count + '\uac74', area:st.area || '', value:st.value || '', rent:st.rent || '' }]);\n}\nfunction exportAssetsCsv(){\n  csvDownload([['name','\uc7ac\uc0b0\uba85'],['kind','\uc885\ub958'],['usage','\uc0ac\uc6a9\ud604\ud669'],['address','\uc18c\uc7ac\uc9c0'],['lot','\uc9c0\ubc88'],['area','\uba74\uc801(\u33a1)'],['value','\ud3c9\uac00\uc561(\uc6d0)'],['valueDate','\ud3c9\uac00\uae30\uc900\uc77c'],['owner','\ub4f1\uae30\uba85\uc758'],['acquired','\ucde8\ub4dd\uc77c\u00b7\uc0ac\uc720'],['tenant','\uc784\ucc28\uc778'],['rent','\uc6d4\uc784\ub300\ub8cc(\uc6d0)'],['leaseEnd','\uc784\ub300\ub9cc\ub8cc\uc77c'],['docs','\uc11c\ub958\ubcf4\uad00\ucc98'],['note','\uba54\ubaa8']], withTotalRow(State.assets||[]), '\uc885\uc911\uc7ac\uc0b0\ubaa9\ub85d.csv');\n}\nfunction exportAssetRoundsCsv(){\n  var rows = [];\n  (State.assets||[]).forEach(function(a){\n    sortedRounds(a).slice().reverse().forEach(function(r){\n      rows.push({ name:a.name, kind:a.kind, no:r.no, date:r.date, value:r.value, rent:r.rent, usage:r.usage, tenant:r.tenant, memo:r.memo });\n    });\n  });\n  csvDownload([['name','\uc7ac\uc0b0\uba85'],['kind','\uc885\ub958'],['no','\ud68c\ucc28'],['date','\uc810\uac80\u00b7\ud3c9\uac00\uc77c'],['value','\ud3c9\uac00\uc561(\uc6d0)'],['rent','\uc6d4\uc784\ub300\ub8cc(\uc6d0)'],['usage','\uc0ac\uc6a9\ud604\ud669'],['tenant','\uc784\ucc28\uc778'],['memo','\uba54\ubaa8']], rows, '\uc885\uc911\uc7ac\uc0b0_\ud68c\ucc28\uae30\ub85d.csv');\n}\n\n\/* ================= Events ================= *\/\nfunction bindDynamicEvents(){\n  var c = document.getElementById('fhContent');\n  c.querySelectorAll('[data-nav]').forEach(function(el){\n    el.addEventListener('click', function(){\n      var tab = el.getAttribute('data-nav');\n      ROOT.querySelectorAll('.tab-btn').forEach(function(b){ b.classList.toggle('active', b.getAttribute('data-tab')===tab); });\n      State.tab = tab; render();\n    });\n  });\n  var addSectionTile = c.querySelector('[data-open-section-manager]');\n  if(addSectionTile) addSectionTile.addEventListener('click', function(){ openCustomSectionManager(); });\n  c.querySelectorAll('[data-open-person]').forEach(function(el){\n    el.addEventListener('click', function(){\n      var p = State.people.find(function(x){ return x.id===el.getAttribute('data-open-person'); });\n      c.innerHTML = personDetailHtml(p);\n      bindDetailEvents();\n      bindComments('people', p.id, c);\n    });\n  });\n  c.querySelectorAll('[data-open-timeline]').forEach(function(el){\n    el.addEventListener('click', function(){\n      var t = State.timeline.find(function(x){ return x.id===el.getAttribute('data-open-timeline'); });\n      openTimelineViewer(t);\n    });\n  });\n  c.querySelectorAll('[data-open-gallery]').forEach(function(el){\n    el.addEventListener('click', function(){\n      var g = State.gallery.find(function(x){ return x.id===el.getAttribute('data-open-gallery'); });\n      openGalleryViewer(g);\n    });\n  });\n  c.querySelectorAll('[data-open-story]').forEach(function(el){\n    el.addEventListener('click', function(){\n      var s = State.stories.find(function(x){ return x.id===el.getAttribute('data-open-story'); });\n      openStoryViewer(s);\n    });\n  });\n  c.querySelectorAll('[data-open-simple]').forEach(function(el){\n    el.addEventListener('click', function(){\n      var parts = el.getAttribute('data-open-simple').split(':');\n      var cfg = SIMPLE_SECTIONS[parts[0]];\n      var item = State[cfg.stateKey].find(function(x){ return x.id===parts[1]; });\n      openSimpleViewer(cfg, item);\n    });\n  });\n}\nfunction bindDetailEvents(){\n  var c = document.getElementById('fhContent');\n  var back = c.querySelector('[data-back]');\n  if(back) back.addEventListener('click', function(){ render(); });\n  var approve = c.querySelector('[data-approve]');\n  if(approve) approve.addEventListener('click', function(){\n    var parts = approve.getAttribute('data-approve').split(':');\n    approveItem(parts[0], parts[1]).then(function(){ render(); });\n  });\n  var edit = c.querySelector('[data-edit-person]');\n  if(edit) edit.addEventListener('click', function(){\n    var p = State.people.find(function(x){ return x.id===edit.getAttribute('data-edit-person'); });\n    openPersonForm(p);\n  });\n  var del = c.querySelector('[data-del-person]');\n  if(del) del.addEventListener('click', function(){\n    if(!confirm('\uc774 \uc778\ubb3c\uc744 \uc0ad\uc81c\ud560\uae4c\uc694? \ub418\ub3cc\ub9b4 \uc218 \uc5c6\uc5b4\uc694.')) return;\n    State.people = State.people.filter(function(x){ return x.id!==del.getAttribute('data-del-person'); });\n    apiSave('people', State.people).then(function(){ applyMeta(); render(); showToast('\uc0ad\uc81c\ud588\uc5b4\uc694'); });\n  });\n}\n\n\/* ================= Modal helpers ================= *\/\nfunction openModal(html){\n  var root = document.getElementById('fhModalRoot');\n  root.innerHTML = '<div class=\"modal-backdrop\" id=\"fhBackdrop\"><div class=\"modal-sheet\">' + html + '<\/div><\/div>';\n  document.getElementById('fhBackdrop').addEventListener('click', function(e){\n    if(e.target.id==='fhBackdrop') closeModal();\n  });\n}\nfunction closeModal(){ document.getElementById('fhModalRoot').innerHTML = ''; }\n\nfunction resizeImageToBlob(file, maxDim, quality){\n  return new Promise(function(resolve, reject){\n    var reader = new FileReader();\n    reader.onload = function(e){\n      var img = new Image();\n      img.onload = function(){\n        var w = img.width, h = img.height;\n        if(w>h && w>maxDim){ h = Math.round(h*maxDim\/w); w = maxDim; }\n        else if(h>=w && h>maxDim){ w = Math.round(w*maxDim\/h); h = maxDim; }\n        var canvas = document.createElement('canvas');\n        canvas.width = w; canvas.height = h;\n        canvas.getContext('2d').drawImage(img,0,0,w,h);\n        canvas.toBlob(function(blob){ resolve(blob); }, 'image\/jpeg', quality);\n      };\n      img.onerror = reject;\n      img.src = e.target.result;\n    };\n    reader.onerror = reject;\n    reader.readAsDataURL(file);\n  });\n}\n\n\/* ---------- Settings ---------- *\/\nfunction openSettingsForm(){\n  var m = State.meta;\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>\uac00\ubb38 \uc124\uc815 (\uc5ed\uc0ac\uad00 #' + escapeHtml(CFG.hallId) + ')<\/h3>' +\n    '<div class=\"field\"><label>\uac00\ubb38 \uc774\ub984<\/label><input id=\"f-familyName\" value=\"' + escapeHtml(m.familyName) + '\" placeholder=\"\uc608: \uae40\ud574 \uae40\uc528 \ubb38\uc911\"><\/div>' +\n    '<div class=\"field-row\">' +\n      '<div class=\"field\"><label>\uc778\uc7a5 \ubb38\uc790 (1\uae00\uc790)<\/label><input id=\"f-crest\" maxlength=\"2\" value=\"' + escapeHtml(m.crestChar) + '\" placeholder=\"\u5bb6\"><\/div>' +\n      '<div class=\"field\"><label>\uc2dc\uc870\/\uae30\uc6d0 \uc5f0\ub3c4<\/label><input id=\"f-origin\" value=\"' + escapeHtml(m.originYear) + '\" placeholder=\"\uc608: 1450\"><\/div>' +\n    '<\/div>' +\n    '<div class=\"field\"><label>\uac00\ud6c8<\/label><textarea id=\"f-motto\" placeholder=\"\uac00\ubb38\uc758 \uc815\uc2e0\uc744 \ub2f4\uc740 \ubb38\uad6c\">' + escapeHtml(m.motto) + '<\/textarea><\/div>' +\n    '<div class=\"btn-row\"><button class=\"btn btn-ghost\" id=\"fhCancel\">\ucde8\uc18c<\/button><button class=\"btn btn-primary\" id=\"fhSave\">\uc800\uc7a5<\/button><\/div>' +\n    '<div id=\"fhAdminsSection\" style=\"margin-top:8px;\"><p style=\"font-size:12px;color:var(--fh-muted);\">\uad00\ub9ac\uc790 \ubaa9\ub85d \ubd88\ub7ec\uc624\ub294 \uc911...<\/p><\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhCancel').addEventListener('click', closeModal);\n  document.getElementById('fhSave').addEventListener('click', function(){\n    var newMeta = {\n      familyName: document.getElementById('f-familyName').value.trim() || '\uc6b0\ub9ac \uac00\ubb38',\n      crestChar: document.getElementById('f-crest').value.trim() || '\u5bb6',\n      originYear: document.getElementById('f-origin').value.trim(),\n      motto: document.getElementById('f-motto').value.trim()\n    };\n    apiSave('meta', newMeta).then(function(){\n      State.meta = newMeta; applyMeta(); closeModal(); showToast('\uc124\uc815\uc744 \uc800\uc7a5\ud588\uc5b4\uc694');\n    });\n  });\n  loadAdminsIntoSettings();\n}\n\nfunction loadAdminsIntoSettings(){\n  apiGetAdmins().then(function(list){\n    var el = document.getElementById('fhAdminsSection');\n    if(!el) return;\n    el.innerHTML =\n      '<label style=\"display:block;font-size:12px;font-weight:600;color:var(--fh-ink-soft);margin:6px 0 8px;\">\uc774 \uc5ed\uc0ac\uad00 \uad00\ub9ac\uc790<\/label>' +\n      '<p style=\"font-size:11.5px;color:var(--fh-muted);margin:0 0 10px;\">\uc5ec\uae30\uc5d0 \ub4f1\ub85d\ub41c \ud68c\uc6d0\uc740 \uc0ac\uc774\ud2b8 \uc804\uccb4 \uad8c\ud55c\uc774 \uc5c6\uc5b4\ub3c4 \uc774 \uc5ed\uc0ac\uad00(#' + escapeHtml(CFG.hallId) + ')\ub9cc \ud3b8\uc9d1\ud560 \uc218 \uc788\uc5b4\uc694.<\/p>' +\n      (list.length ? list.map(function(a){\n        return '<div class=\"fh-admin-row\"><span>' + escapeHtml(a.name || a.login || ('\uc0ac\uc6a9\uc790#' + a.id)) + '<\/span>' +\n          '<button class=\"btn btn-danger\" data-remove-admin=\"' + a.id + '\">\uc0ad\uc81c<\/button><\/div>';\n      }).join('') : '<p style=\"font-size:12px;color:var(--fh-muted);margin:0 0 8px;\">\uc9c0\uc815\ub41c \uad00\ub9ac\uc790\uac00 \uc5c6\uc5b4\uc694.<\/p>') +\n      '<div class=\"field-row\" style=\"margin-top:8px;\">' +\n        '<div class=\"field\" style=\"flex:1;margin-bottom:0;\"><input id=\"fhNewAdminId\" placeholder=\"\ud68c\uc6d0 \uc544\uc774\ub514 \ub610\ub294 \uc774\uba54\uc77c\"><\/div>' +\n        '<button class=\"btn btn-ghost\" id=\"fhAddAdminBtn\" style=\"flex:0 0 auto;\">\ucd94\uac00<\/button>' +\n      '<\/div>';\n    el.querySelectorAll('[data-remove-admin]').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        btn.disabled = true;\n        apiRemoveAdmin(btn.getAttribute('data-remove-admin')).then(function(){\n          showToast('\uad00\ub9ac\uc790\ub97c \uc0ad\uc81c\ud588\uc5b4\uc694'); loadAdminsIntoSettings();\n        });\n      });\n    });\n    var addBtn = document.getElementById('fhAddAdminBtn');\n    if(addBtn) addBtn.addEventListener('click', function(){\n      var idf = document.getElementById('fhNewAdminId').value.trim();\n      if(!idf){ showToast('\uc544\uc774\ub514 \ub610\ub294 \uc774\uba54\uc77c\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n      addBtn.disabled = true;\n      apiAddAdmin(idf).then(function(){\n        showToast('\uad00\ub9ac\uc790\ub97c \ucd94\uac00\ud588\uc5b4\uc694'); loadAdminsIntoSettings();\n      }).catch(function(){\n        showToast('\ud574\ub2f9 \ud68c\uc6d0\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc5b4\uc694'); addBtn.disabled = false;\n      });\n    });\n  }).catch(function(){\n    var el = document.getElementById('fhAdminsSection');\n    if(el) el.innerHTML = '<p style=\"font-size:12px;color:var(--fh-muted);\">\uad00\ub9ac\uc790 \ubaa9\ub85d\uc744 \ubd88\ub7ec\uc624\uc9c0 \ubabb\ud588\uc5b4\uc694.<\/p>';\n  });\n}\n\n\/* ---------- Person form ---------- *\/\nfunction openPersonForm(person){\n  var isEdit = !!person;\n  var p = person || { id:uid(), name:'', hanja:'', relation:'', title:'', generation:'', birth:'', death:'', bio:'', photo:'', folder:(State.activeFolder.people||'') };\n  var pendingBlob = null;\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>' + (isEdit ? '\uc778\ubb3c \uc218\uc815' : '\uc778\ubb3c \ucd94\uac00') + '<\/h3>' +\n    (isEdit ? '' : moderationNoticeHtml()) +\n    '<div class=\"field\"><label>\uc0ac\uc9c4<\/label><div class=\"photo-upload\" id=\"fhPhotoUpload\">' +\n      (p.photo ? '<img decoding=\"async\" src=\"' + escapeHtml(p.photo) + '\">' : '\ud0ed\ud558\uc5ec \uc0ac\uc9c4 \uc120\ud0dd') +\n      '<input type=\"file\" id=\"fhPhotoInput\" accept=\"image\/*\"><\/div><\/div>' +\n    '<div class=\"field-row\"><div class=\"field\"><label>\uc774\ub984<\/label><input id=\"f-name\" value=\"' + escapeHtml(p.name) + '\" placeholder=\"\ud64d\uae38\ub3d9\"><\/div>' +\n    '<div class=\"field\"><label>\ud55c\uc790<\/label><input id=\"f-hanja\" value=\"' + escapeHtml(p.hanja) + '\" placeholder=\"\u6d2a\u5409\u7ae5\"><\/div><\/div>' +\n    '<div class=\"field-row\"><div class=\"field\"><label>\uad00\uacc4<\/label><input id=\"f-relation\" value=\"' + escapeHtml(p.relation) + '\" placeholder=\"\uc608: 5\ub300\uc870, \uc870\ubd80\"><\/div>' +\n    '<div class=\"field\"><label>\uc138\ub300(\ub300\uc218)<\/label><input id=\"f-generation\" type=\"number\" value=\"' + escapeHtml(String(p.generation||'')) + '\" placeholder=\"5\"><\/div><\/div>' +\n    '<div class=\"field\"><label>\uc9c1\ud568\/\ud638<\/label><input id=\"f-title\" value=\"' + escapeHtml(p.title) + '\" placeholder=\"\uc608: \ubcd1\uc870\ud310\uc11c, \ud638\ub294 OO\"><\/div>' +\n    '<div class=\"field-row\"><div class=\"field\"><label>\ucd9c\uc0dd<\/label><input id=\"f-birth\" value=\"' + escapeHtml(p.birth) + '\" placeholder=\"1850\"><\/div>' +\n    '<div class=\"field\"><label>\uc0ac\ub9dd<\/label><input id=\"f-death\" value=\"' + escapeHtml(p.death) + '\" placeholder=\"1920\"><\/div><\/div>' +\n    '<div class=\"field\"><label>\uc774\ub825\/\uc57d\ub825<\/label><textarea id=\"f-bio\" placeholder=\"\uc8fc\uc694 \ud589\uc801, \uc5c5\uc801, \uc77c\ud654 \ub4f1\uc744 \uc801\uc5b4\uc8fc\uc138\uc694\">' + escapeHtml(p.bio) + '<\/textarea><\/div>' +\n    folderFieldHtml('people', p.folder||'') +\n    '<div class=\"btn-row\"><button class=\"btn btn-ghost\" id=\"fhCancel\">\ucde8\uc18c<\/button><button class=\"btn btn-primary\" id=\"fhSave\">\uc800\uc7a5<\/button><\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhCancel').addEventListener('click', closeModal);\n  document.getElementById('fhPhotoInput').addEventListener('change', function(e){\n    var file = e.target.files[0]; if(!file) return;\n    resizeImageToBlob(file, 600, 0.85).then(function(blob){\n      pendingBlob = blob;\n      var url = URL.createObjectURL(blob);\n      document.getElementById('fhPhotoUpload').innerHTML = '<img decoding=\"async\" src=\"' + url + '\"><input type=\"file\" id=\"fhPhotoInput\" accept=\"image\/*\">';\n      document.getElementById('fhPhotoInput').addEventListener('change', arguments.callee);\n    });\n  });\n  document.getElementById('fhSave').addEventListener('click', function(){\n    var name = document.getElementById('f-name').value.trim();\n    if(!name){ showToast('\uc774\ub984\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n    var saveBtn = document.getElementById('fhSave');\n    saveBtn.disabled = true; saveBtn.textContent = pendingBlob ? '\uc5c5\ub85c\ub4dc \uc911...' : '\uc800\uc7a5 \uc911...';\n\n    var uploadPromise = pendingBlob ? uploadMedia(pendingBlob, 'person-' + p.id + '.jpg') : Promise.resolve(p.photo);\n    uploadPromise.then(function(photoUrl){\n      var updated = {\n        id: p.id, name: name,\n        hanja: document.getElementById('f-hanja').value.trim(),\n        relation: document.getElementById('f-relation').value.trim(),\n        generation: document.getElementById('f-generation').value.trim(),\n        title: document.getElementById('f-title').value.trim(),\n        birth: document.getElementById('f-birth').value.trim(),\n        death: document.getElementById('f-death').value.trim(),\n        bio: document.getElementById('f-bio').value.trim(),\n        folder: (document.getElementById('f-folder') ? document.getElementById('f-folder').value : ''),\n        photo: photoUrl || ''\n      };\n      if(isEdit && p.status) updated.status = p.status;\n      return saveOrSubmitItem('people', updated, isEdit).then(function(result){\n        applyMeta(); closeModal(); render();\n        showToast(result.pending ? '\ub4f1\ub85d \uc694\uccad\uc744 \ubcf4\ub0c8\uc5b4\uc694. \uad00\ub9ac\uc790 \uc2b9\uc778 \ud6c4 \ubaa9\ub85d\uc5d0 \uacf5\uac1c\ub3fc\uc694.' : (isEdit ? '\uc218\uc815\ud588\uc5b4\uc694' : '\ucd94\uac00\ud588\uc5b4\uc694'));\n      });\n    }).catch(function(){\n      saveBtn.disabled = false; saveBtn.textContent = '\uc800\uc7a5';\n      showToast('\uc0ac\uc9c4 \uc5c5\ub85c\ub4dc\uc5d0 \uc2e4\ud328\ud588\uc5b4\uc694');\n    });\n  });\n}\n\n\/* ---------- Timeline viewer (\uc77d\uae30 + \ub313\uae00, \ubaa8\ub450\uc5d0\uac8c \uacf5\uac1c) ---------- *\/\nfunction openTimelineViewer(t){\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<div class=\"tl-year\" style=\"margin-bottom:6px;\">' + escapeHtml(t.year||'') + '<\/div>' +\n    '<h3 style=\"margin-top:0;\">' + escapeHtml(t.title||'\uc81c\ubaa9 \uc5c6\uc74c') + '<\/h3>' +\n    '<div class=\"story-full\">' + escapeHtml(t.desc||'') + '<\/div>' +\n    (CFG.canEdit ? '<div class=\"btn-row\"><button class=\"btn btn-ghost\" id=\"fhEditTl\">\uc218\uc815<\/button><button class=\"btn btn-danger\" id=\"fhDelTl\">\uc0ad\uc81c<\/button><\/div>' : '') +\n    '<div class=\"fh-comments-holder\">' + renderComments('timeline', t.id) + '<\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  var editBtn = document.getElementById('fhEditTl');\n  if(editBtn) editBtn.addEventListener('click', function(){ openTimelineForm(t); });\n  var delBtn = document.getElementById('fhDelTl');\n  if(delBtn) delBtn.addEventListener('click', function(){\n    if(!confirm('\uc774 \uc0ac\uac74\uc744 \uc0ad\uc81c\ud560\uae4c\uc694?')) return;\n    State.timeline = State.timeline.filter(function(x){ return x.id!==t.id; });\n    apiSave('timeline', State.timeline).then(function(){ applyMeta(); closeModal(); render(); showToast('\uc0ad\uc81c\ud588\uc5b4\uc694'); });\n  });\n  bindComments('timeline', t.id, document.getElementById('fhModalRoot'));\n}\n\n\/* ---------- Timeline form ---------- *\/\nfunction openTimelineForm(item){\n  var isEdit = !!item;\n  var t = item || { id:uid(), year:'', title:'', desc:'', folder:(State.activeFolder.timeline||'') };\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>' + (isEdit ? '\uc0ac\uac74 \uc218\uc815' : '\uc5f0\ud45c \ucd94\uac00') + '<\/h3>' +\n    '<div class=\"field\"><label>\uc5f0\ub3c4<\/label><input id=\"f-year\" value=\"' + escapeHtml(t.year) + '\" placeholder=\"\uc608: 1592 \ub610\ub294 \uc870\uc120 \ucd08\uae30\"><\/div>' +\n    '<div class=\"field\"><label>\uc81c\ubaa9<\/label><input id=\"f-title\" value=\"' + escapeHtml(t.title) + '\" placeholder=\"\uc608: \uac00\ubb38\uc758 \uc2dc\uc870 \uc815\ucc29\"><\/div>' +\n    '<div class=\"field\"><label>\ub0b4\uc6a9<\/label><textarea id=\"f-desc\" placeholder=\"\uc0ac\uac74\uc5d0 \ub300\ud55c \uc124\uba85\uc744 \uc801\uc5b4\uc8fc\uc138\uc694\">' + escapeHtml(t.desc) + '<\/textarea><\/div>' +\n    folderFieldHtml('timeline', t.folder||'') +\n    '<div class=\"btn-row\">' +\n      (isEdit ? '<button class=\"btn btn-danger\" id=\"fhDelete\">\uc0ad\uc81c<\/button>' : '<button class=\"btn btn-ghost\" id=\"fhCancel\">\ucde8\uc18c<\/button>') +\n      '<button class=\"btn btn-primary\" id=\"fhSave\">\uc800\uc7a5<\/button><\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  var cancelBtn = document.getElementById('fhCancel');\n  if(cancelBtn) cancelBtn.addEventListener('click', closeModal);\n  var delBtn = document.getElementById('fhDelete');\n  if(delBtn) delBtn.addEventListener('click', function(){\n    if(!confirm('\uc774 \uc0ac\uac74\uc744 \uc0ad\uc81c\ud560\uae4c\uc694?')) return;\n    State.timeline = State.timeline.filter(function(x){ return x.id!==t.id; });\n    apiSave('timeline', State.timeline).then(function(){ applyMeta(); closeModal(); render(); showToast('\uc0ad\uc81c\ud588\uc5b4\uc694'); });\n  });\n  document.getElementById('fhSave').addEventListener('click', function(){\n    var year = document.getElementById('f-year').value.trim();\n    var title = document.getElementById('f-title').value.trim();\n    if(!year || !title){ showToast('\uc5f0\ub3c4\uc640 \uc81c\ubaa9\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n    var updated = { id:t.id, year:year, title:title, desc:document.getElementById('f-desc').value.trim(),\n      folder: (document.getElementById('f-folder') ? document.getElementById('f-folder').value : '') };\n    var idx = State.timeline.findIndex(function(x){ return x.id===t.id; });\n    if(idx>=0) State.timeline[idx] = updated; else State.timeline.push(updated);\n    apiSave('timeline', State.timeline).then(function(){\n      applyMeta(); closeModal(); render();\n      showToast(isEdit ? '\uc218\uc815\ud588\uc5b4\uc694' : '\ucd94\uac00\ud588\uc5b4\uc694');\n    });\n  });\n}\n\n\/* ---------- Gallery form \/ viewer ---------- *\/\nfunction openGalleryForm(){\n  var pendingBlob = null;\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>\uc0ac\uc9c4 \ucd94\uac00<\/h3>' +\n    moderationNoticeHtml() +\n    '<div class=\"field\"><label>\uc0ac\uc9c4<\/label><div class=\"photo-upload\" id=\"fhPhotoUpload\">\ud0ed\ud558\uc5ec \uc0ac\uc9c4 \uc120\ud0dd<input type=\"file\" id=\"fhPhotoInput\" accept=\"image\/*\"><\/div><\/div>' +\n    '<div class=\"field\"><label>\uc124\uba85<\/label><input id=\"f-caption\" placeholder=\"\uc608: 1985\ub144 \uc885\uce5c\ud68c \ubaa8\uc784\"><\/div>' +\n    '<div class=\"field\"><label>\ub0a0\uc9dc<\/label><input id=\"f-date\" placeholder=\"\uc608: 1985-05\"><\/div>' +\n    folderFieldHtml('gallery', State.activeFolder.gallery||'') +\n    '<div class=\"btn-row\"><button class=\"btn btn-ghost\" id=\"fhCancel\">\ucde8\uc18c<\/button><button class=\"btn btn-primary\" id=\"fhSave\">\uc800\uc7a5<\/button><\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhCancel').addEventListener('click', closeModal);\n  document.getElementById('fhPhotoInput').addEventListener('change', function(e){\n    var file = e.target.files[0]; if(!file) return;\n    resizeImageToBlob(file, 1000, 0.82).then(function(blob){\n      pendingBlob = blob;\n      var url = URL.createObjectURL(blob);\n      document.getElementById('fhPhotoUpload').innerHTML = '<img decoding=\"async\" src=\"' + url + '\">';\n    });\n  });\n  document.getElementById('fhSave').addEventListener('click', function(){\n    if(!pendingBlob){ showToast('\uc0ac\uc9c4\uc744 \uc120\ud0dd\ud574 \uc8fc\uc138\uc694'); return; }\n    var saveBtn = document.getElementById('fhSave');\n    saveBtn.disabled = true; saveBtn.textContent = '\uc5c5\ub85c\ub4dc \uc911...';\n    uploadMedia(pendingBlob, 'gallery-' + uid() + '.jpg').then(function(url){\n      var item = { id:uid(), photo:url, caption:document.getElementById('f-caption').value.trim(), date:document.getElementById('f-date').value.trim(),\n        folder: (document.getElementById('f-folder') ? document.getElementById('f-folder').value : '') };\n      return saveOrSubmitItem('gallery', item, false).then(function(result){\n        applyMeta(); closeModal(); render();\n        showToast(result.pending ? '\ub4f1\ub85d \uc694\uccad\uc744 \ubcf4\ub0c8\uc5b4\uc694. \uad00\ub9ac\uc790 \uc2b9\uc778 \ud6c4 \ubaa9\ub85d\uc5d0 \uacf5\uac1c\ub3fc\uc694.' : '\ucd94\uac00\ud588\uc5b4\uc694');\n      });\n    }).catch(function(){\n      saveBtn.disabled = false; saveBtn.textContent = '\uc800\uc7a5';\n      showToast('\uc0ac\uc9c4 \uc5c5\ub85c\ub4dc\uc5d0 \uc2e4\ud328\ud588\uc5b4\uc694');\n    });\n  });\n}\nfunction openGalleryViewer(g){\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<img decoding=\"async\" src=\"' + escapeHtml(g.photo) + '\" style=\"width:100%;border-radius:12px;margin-bottom:14px;\">' +\n    pendingBadgeHtml(g) +\n    '<p style=\"font-size:13.5px;color:var(--fh-ink-soft);margin:0 0 4px;\">' + escapeHtml(g.caption||'\uc124\uba85 \uc5c6\uc74c') + '<\/p>' +\n    '<p style=\"font-size:11.5px;color:var(--fh-muted);margin:0 0 16px;\">' + escapeHtml(g.date||'') + '<\/p>' +\n    (CFG.canEdit ? '<div class=\"btn-row\">' + approveButtonHtml('gallery', g) + '<button class=\"btn btn-danger\" id=\"fhDelGallery\">\uc0ad\uc81c<\/button><\/div>' : '') +\n    '<div class=\"fh-comments-holder\">' + renderComments('gallery', g.id) + '<\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  var approve = document.querySelector('[data-approve]');\n  if(approve) approve.addEventListener('click', function(){\n    var parts = approve.getAttribute('data-approve').split(':');\n    approveItem(parts[0], parts[1]).then(function(){ closeModal(); render(); });\n  });\n  var delBtn = document.getElementById('fhDelGallery');\n  if(delBtn) delBtn.addEventListener('click', function(){\n    if(!confirm('\uc774 \uc0ac\uc9c4\uc744 \uc0ad\uc81c\ud560\uae4c\uc694?')) return;\n    State.gallery = State.gallery.filter(function(x){ return x.id!==g.id; });\n    apiSave('gallery', State.gallery).then(function(){ applyMeta(); closeModal(); render(); showToast('\uc0ad\uc81c\ud588\uc5b4\uc694'); });\n  });\n  bindComments('gallery', g.id, document.getElementById('fhModalRoot'));\n}\n\n\/* ---------- Story form \/ viewer ---------- *\/\nfunction openStoryForm(){\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<h3>\ucd94\uc5b5\uc2e4\uc5d0 \uae30\ub85d \ucd94\uac00<\/h3>' +\n    moderationNoticeHtml() +\n    '<div class=\"field\"><label>\ubd84\ub958<\/label><select id=\"f-category\">' +\n      '<option value=\"\uc720\ub798\">\uc720\ub798<\/option><option value=\"\uc804\uc124\">\uc804\uc124<\/option><option value=\"\uc77c\ud654\">\uc77c\ud654<\/option><option value=\"\uac00\ud48d\">\uac00\ud48d\u00b7\uc804\ud1b5<\/option>' +\n    '<\/select><\/div>' +\n    '<div class=\"field\"><label>\uc81c\ubaa9<\/label><input id=\"f-title\" placeholder=\"\uc608: \uc2dc\uc870 \ud560\uc544\ubc84\uc9c0\uc640 \uc740\ud589\ub098\ubb34\"><\/div>' +\n    '<div class=\"field\"><label>\ub0b4\uc6a9<\/label><textarea id=\"f-content\" style=\"min-height:140px;\" placeholder=\"\uc774\uc57c\uae30\ub97c \uc790\uc720\ub86d\uac8c \uc801\uc5b4\uc8fc\uc138\uc694\"><\/textarea><\/div>' +\n    folderFieldHtml('stories', State.activeFolder.stories||'') +\n    '<div class=\"btn-row\"><button class=\"btn btn-ghost\" id=\"fhCancel\">\ucde8\uc18c<\/button><button class=\"btn btn-primary\" id=\"fhSave\">\uc800\uc7a5<\/button><\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  document.getElementById('fhCancel').addEventListener('click', closeModal);\n  document.getElementById('fhSave').addEventListener('click', function(){\n    var title = document.getElementById('f-title').value.trim();\n    var content = document.getElementById('f-content').value.trim();\n    if(!title || !content){ showToast('\uc81c\ubaa9\uacfc \ub0b4\uc6a9\uc744 \uc785\ub825\ud574 \uc8fc\uc138\uc694'); return; }\n    var saveBtn = document.getElementById('fhSave');\n    saveBtn.disabled = true;\n    var item = { id:uid(), category:document.getElementById('f-category').value, title:title, content:content,\n      folder: (document.getElementById('f-folder') ? document.getElementById('f-folder').value : '') };\n    saveOrSubmitItem('stories', item, false).then(function(result){\n      applyMeta(); closeModal(); render();\n      showToast(result.pending ? '\ub4f1\ub85d \uc694\uccad\uc744 \ubcf4\ub0c8\uc5b4\uc694. \uad00\ub9ac\uc790 \uc2b9\uc778 \ud6c4 \ubaa9\ub85d\uc5d0 \uacf5\uac1c\ub3fc\uc694.' : '\ucd94\uac00\ud588\uc5b4\uc694');\n    }).catch(function(){ saveBtn.disabled = false; });\n  });\n}\nfunction openStoryViewer(s){\n  openModal(\n    '<div class=\"modal-handle\"><\/div><button class=\"close-x\" id=\"fhClose\">&times;<\/button>' +\n    '<div style=\"font-size:11px;font-weight:700;color:var(--fh-seal);letter-spacing:0.5px;margin-bottom:6px;\">' + escapeHtml(s.category||'\uc774\uc57c\uae30') + '<\/div>' +\n    pendingBadgeHtml(s) +\n    '<h3 style=\"margin-top:0;\">' + escapeHtml(s.title) + '<\/h3>' +\n    '<div class=\"story-full\">' + escapeHtml(s.content) + '<\/div>' +\n    (CFG.canEdit ? '<div class=\"btn-row\"><button class=\"btn btn-danger\" id=\"fhDelStory\">\uc0ad\uc81c<\/button><\/div>' : '') +\n    '<div class=\"fh-comments-holder\">' + renderComments('stories', s.id) + '<\/div>'\n  );\n  document.getElementById('fhClose').addEventListener('click', closeModal);\n  var delBtn = document.getElementById('fhDelStory');\n  if(delBtn) delBtn.addEventListener('click', function(){\n    if(!confirm('\uc774 \uc774\uc57c\uae30\ub97c \uc0ad\uc81c\ud560\uae4c\uc694?')) return;\n    State.stories = State.stories.filter(function(x){ return x.id!==s.id; });\n    apiSave('stories', State.stories).then(function(){ applyMeta(); closeModal(); render(); showToast('\uc0ad\uc81c\ud588\uc5b4\uc694'); });\n  });\n  bindComments('stories', s.id, document.getElementById('fhModalRoot'));\n}\n\n\/* ================= Responsive ================= *\/\nvar _lastIsDesktop = null;\nwindow.addEventListener('resize', debounce(function(){\n  var now = isDesktop();\n  if(_lastIsDesktop !== null && _lastIsDesktop !== now && State.tab){ render(); }\n  _lastIsDesktop = now;\n}, 200));\nfunction debounce(fn, wait){\n  var t; return function(){ var args = arguments; clearTimeout(t); t = setTimeout(function(){ fn.apply(null, args); }, wait); };\n}\n\n\/* ================= Start ================= *\/\nif(document.readyState === 'loading'){\n  document.addEventListener('DOMContentLoaded', init);\n}else{\n  init();\n}\n})();<\/script>","protected":false},"excerpt":{"rendered":"","protected":false},"author":0,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"om_disable_all_campaigns":false,"footnotes":""},"class_list":["post-2131","page","type-page","status-publish","hentry"],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/korean.haus\/index.php?rest_route=\/wp\/v2\/pages\/2131","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/korean.haus\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/korean.haus\/index.php?rest_route=\/wp\/v2\/types\/page"}],"replies":[{"embeddable":true,"href":"https:\/\/korean.haus\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2131"}],"version-history":[{"count":0,"href":"https:\/\/korean.haus\/index.php?rest_route=\/wp\/v2\/pages\/2131\/revisions"}],"wp:attachment":[{"href":"https:\/\/korean.haus\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}