{"id":26357,"date":"2026-10-23T12:07:02","date_gmt":"2026-10-23T03:07:02","guid":{"rendered":"https:\/\/www.tyosuke20xx.com\/blog\/?p=26357"},"modified":"2026-10-23T12:07:03","modified_gmt":"2026-10-23T03:07:03","slug":"rss%e4%b8%80%e8%a6%a7","status":"publish","type":"post","link":"https:\/\/www.tyosuke20xx.com\/blog\/?p=26357","title":{"rendered":"RSS\u4e00\u89a7"},"content":{"rendered":"\n\n\n<pre class=\"wp-block-code\"><code>\n&lt;!DOCTYPE html>\n&lt;html lang=\"ja\">\n&lt;head>\n  &lt;meta charset=\"UTF-8\" \/>\n  &lt;meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" \/>\n  &lt;title>Tsumugi&lt;\/title>\n\n  &lt;!-- \u2705 Favicon\uff08\u521d\u671f\uff09: \u30d6\u30e9\u30a6\u30b6\u4e92\u63db\u306e\u305f\u3081\u8907\u6570rel\u3067\u7528\u610f\uff08\u78ba\u5b9f\u7248\uff09 -->\n  &lt;!-- \u2605\u3053\u3053\u306b\u8cbc\u308b\u2605 -->\n  &lt;link rel=\"icon\" type=\"image\/svg+xml\"\n  href=\"data:image\/svg+xml,\n  &lt;svg xmlns='http:\/\/www.w3.org\/2000\/svg' viewBox='0 0 64 64'>\n  &lt;defs>\n  &lt;linearGradient id='g' x1='0' y1='0' x2='1' y2='1'>\n  &lt;stop offset='0' stop-color='%2300eaff'\/>\n  &lt;stop offset='1' stop-color='%238a5cff'\/>\n  &lt;\/linearGradient>\n  &lt;\/defs>\n  &lt;polygon points='32,4 56,18 56,46 32,60 8,46 8,18' fill='url(%23g)'\/>\n  &lt;circle cx='32' cy='32' r='10' fill='%230b0f2a'\/>\n  &lt;circle cx='32' cy='32' r='16' fill='none' stroke='%23ffffff' stroke-width='2' opacity='0.7'\/>\n  &lt;line x1='32' y1='12' x2='32' y2='20' stroke='%23ffffff' stroke-width='2'\/>\n  &lt;line x1='32' y1='44' x2='32' y2='52' stroke='%23ffffff' stroke-width='2'\/>\n  &lt;line x1='12' y1='32' x2='20' y2='32' stroke='%23ffffff' stroke-width='2'\/>\n  &lt;line x1='44' y1='32' x2='52' y2='32' stroke='%23ffffff' stroke-width='2'\/>\n  &lt;\/svg>\">\n\n  &lt;link href=\"https:\/\/cdn.jsdelivr.net\/npm\/tailwindcss@2.2.19\/dist\/tailwind.min.css\" rel=\"stylesheet\"\/>\n\n  &lt;!-- Tailwind CSS v2 -->\n  &lt;link href=\"https:\/\/cdn.jsdelivr.net\/npm\/tailwindcss@2.2.19\/dist\/tailwind.min.css\" rel=\"stylesheet\"\/>\n  &lt;!-- Font Awesome -->\n  &lt;link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/@fortawesome\/fontawesome-free@6.4.0\/css\/all.min.css\"\/>\n  &lt;!-- A-Frame (VR \u8a66\u9a13) -->\n  &lt;script src=\"https:\/\/aframe.io\/releases\/1.5.0\/aframe.min.js\">&lt;\/script>\n\n  &lt;!-- \u2705 Favicon -->\n  &lt;link id=\"dynamic-favicon\" rel=\"shortcut icon\"\n    href=\"data:image\/svg+xml,%3Csvg xmlns='https:\/\/www.w3.org\/2000\/svg' width='64' height='64' viewBox='0 0 64 64'%3E%3Cdefs%3E%3CradialGradient id='g' cx='30%25' cy='20%25' r='90%25'%3E%3Cstop offset='0%25' stop-color='%237c3aed'\/%3E%3Cstop offset='55%25' stop-color='%235b5fe6'\/%3E%3Cstop offset='100%25' stop-color='%230b1020'\/%3E%3C\/radialGradient%3E%3ClinearGradient id='glow' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%23a78bfa'\/%3E%3Cstop offset='100%25' stop-color='%2360a5fa'\/%3E%3C\/linearGradient%3E%3C\/defs%3E%3Crect x='2' y='2' width='60' height='60' rx='16' fill='url(%23g)'\/%3E%3Cpath d='M20 18h24a2 2 0 0 1 0 4H36v26a2 2 0 0 1-4 0V22h-8a2 2 0 0 1 0-4z' fill='white'\/%3E%3Cpath d='M18 20h28' stroke='url(%23glow)' stroke-width='3' stroke-linecap='round' opacity='.85'\/%3E%3Cpath d='M32 22v26' stroke='url(%23glow)' stroke-width='3' stroke-linecap='round' opacity='.65'\/%3E%3Crect x='2' y='2' width='60' height='60' rx='16' fill='none' stroke='rgba(255,255,255,.22)'\/%3E%3C\/svg%3E\"\/>\n\n  &lt;link id=\"favicon-shortcut\" rel=\"shortcut icon\" href=\"\">\n  &lt;link id=\"favicon-any\" rel=\"icon\" sizes=\"any\" href=\"\">\n  &lt;link id=\"apple-touch\" rel=\"apple-touch-icon\" href=\"\">\n  &lt;meta name=\"theme-color\" content=\"#5b5fe6\">\n\n  &lt;!-- Tailwind CSS v2 -->\n  &lt;link href=\"https:\/\/cdn.jsdelivr.net\/npm\/tailwindcss@2.2.19\/dist\/tailwind.min.css\" rel=\"stylesheet\"\/>\n  &lt;!-- Font Awesome -->\n  &lt;link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/@fortawesome\/fontawesome-free@6.4.0\/css\/all.min.css\"\/>\n  &lt;!-- A-Frame (VR \u8a66\u9a13) -->\n  &lt;script src=\"https:\/\/aframe.io\/releases\/1.5.0\/aframe.min.js\">&lt;\/script>\n\n  &lt;style>\n    :root{\n      --grad-a:#5b5fe6; --grad-b:#7c3aed;\n      --bg-a:#f4f7ff; --bg-b:#eef2ff;\n      --ink:#0f172a; --muted:#64748b;\n    }\n    body{\n      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\",\"Segoe UI Emoji\";\n      background: radial-gradient(circle at top left,var(--bg-a) 0%,var(--bg-b) 45%,#ffffff 100%);\n      color: var(--ink);\n      min-height:100vh;\n    }\n    .glass{ background: rgba(255,255,255,.78); backdrop-filter: blur(18px); border: 1px solid rgba(15,23,42,.10); border-radius: 18px; }\n    .card-hover{ transition:.18s ease; }\n    .card-hover:hover{ transform: translateY(-2px); box-shadow: 0 18px 35px -18px rgba(2,6,23,.25); }\n    .gradient-text{ background: linear-gradient(90deg,var(--grad-a),var(--grad-b)); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }\n    .btn-primary{ background: linear-gradient(135deg,var(--grad-a),var(--grad-b)); color:#fff; border:none; transition:.18s ease; }\n    .btn-primary:hover{ transform: translateY(-1px); box-shadow: 0 14px 26px -18px rgba(2,6,23,.35); }\n    .pill{ border:1px solid rgba(15,23,42,.10); background: rgba(255,255,255,.9); border-radius: 999px; }\n    .mini-avatar{ width:44px;height:44px;border-radius:999px;object-fit:cover; border:2px solid rgba(255,255,255,.95); box-shadow: 0 10px 22px rgba(2,6,23,.14); }\n    .avatar{ width:96px;height:96px;border-radius:999px;object-fit:cover; border:3px solid rgba(255,255,255,.95); box-shadow: 0 14px 30px rgba(2,6,23,.18); }\n    .timeline-post{ background: rgba(255,255,255,.96); border-radius: 18px; border:1px solid rgba(15,23,42,.08); box-shadow: 0 16px 30px -20px rgba(2,6,23,.25); border-left: 4px solid var(--grad-a); transition:.18s ease; }\n    .timeline-post:hover{ transform: translateX(3px); }\n    .badge{ font-size: 11px; padding: 2px 10px; border-radius: 999px; background: rgba(91,95,230,.10); border:1px solid rgba(91,95,230,.25); color: #3730a3; display:inline-flex;align-items:center;gap:6px; white-space:nowrap; }\n    .status-dot{ width:8px;height:8px;border-radius:999px; display:inline-block; }\n    .on{ background:#10b981; box-shadow:0 0 0 3px rgba(16,185,129,.15); }\n    .off{ background:#94a3b8; }\n    .log{ max-height: 190px; overflow:auto; background: rgba(241,245,249,.85); border: 1px solid rgba(15,23,42,.12); border-radius: 12px; padding: 10px; font-size: 12px; color:#0f172a; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; }\n    .log .err{ background: rgba(254,202,202,.8); color:#7f1d1d; padding:6px 8px; border-radius:10px; margin:6px 0; }\n    .log .ok{ background: rgba(187,247,208,.8); color:#065f46; padding:6px 8px; border-radius:10px; margin:6px 0; }\n    .log .info{ color:#334155; padding:2px 0; }\n    .header-bg{ background-size: cover; background-position: center; background-repeat: no-repeat; }\n    .soft-input{ background:#fff; border:1px solid rgba(15,23,42,.14); border-radius: 12px; padding: 10px 12px; outline:none; width:100%; color:#0f172a; }\n    .soft-input:focus{ box-shadow: 0 0 0 3px rgba(91,95,230,.18); border-color: rgba(91,95,230,.35); }\n    .muted{ color: var(--muted); }\n\n    \/* Toggle (ON\/OFF) *\/\n    .toggle{\n      width: 54px; height: 30px; border-radius: 999px;\n      border: 1px solid rgba(15,23,42,.16);\n      background: rgba(148,163,184,.35);\n      position: relative; cursor: pointer; transition:.16s ease;\n      display:inline-flex; align-items:center;\n    }\n    .toggle.on{ background: rgba(16,185,129,.22); border-color: rgba(16,185,129,.35); }\n    .toggle > span{\n      width: 24px; height: 24px; border-radius: 999px;\n      background: #fff; position:absolute; left: 3px; transition:.16s ease;\n      box-shadow: 0 12px 20px -14px rgba(2,6,23,.55);\n      border: 1px solid rgba(15,23,42,.12);\n    }\n    .toggle.on > span{ left: 27px; }\n\n    \/* VR overlay *\/\n    #vr-overlay{ position:fixed; inset:0; background:#000; z-index:9999; display:none; }\n    #vr-topbar{ position:absolute; top:10px; left:10px; right:10px; display:flex; align-items:center; justify-content:space-between; z-index:10000; pointer-events:auto; }\n    #vr-topbar .btn{ background: rgba(255,255,255,.10); border:1px solid rgba(255,255,255,.18); color:#fff; padding:8px 12px; border-radius:999px; font-size:12px; backdrop-filter: blur(10px); }\n    #vr-topbar .btn:hover{ background: rgba(255,255,255,.16); }\n    #vr-hint{ color:#fff; font-size:12px; opacity:.85; background: rgba(255,255,255,.08); padding:8px 12px; border-radius:999px; border:1px solid rgba(255,255,255,.14); backdrop-filter: blur(10px); }\n\n    \/* \u2705 Auth overlay (Login\/Register\/Logout) *\/\n    #auth-overlay{\n      position:fixed; inset:0;\n      background: rgba(2,6,23,.62);\n      backdrop-filter: blur(10px);\n      z-index: 100000;\n      display:none;\n    }\n    #auth-card{\n      max-width: 520px;\n      margin: 6vh auto;\n      padding: 22px;\n      border-radius: 22px;\n      background: rgba(255,255,255,.92);\n      border: 1px solid rgba(148,163,184,.35);\n      box-shadow: 0 30px 70px rgba(2,6,23,.35);\n    }\n    .tab-btn{\n      border:1px solid rgba(15,23,42,.10);\n      background: rgba(255,255,255,.9);\n      border-radius: 999px;\n      padding: 8px 12px;\n      font-size: 12px;\n      transition:.12s ease;\n    }\n    .tab-btn.active{\n      background: linear-gradient(135deg,var(--grad-a),var(--grad-b));\n      color:#fff; border-color: transparent;\n    }\n\n    \/* \u2705 WEB\u30a2\u30a4\u30b3\u30f3 *\/\n    .icon-tile{\n      border-radius: 16px;\n      border: 1px solid rgba(15,23,42,.10);\n      background: rgba(255,255,255,.92);\n      padding: 10px;\n      transition: .14s ease;\n      cursor: pointer;\n      position: relative;\n      overflow: hidden;\n    }\n    .icon-tile:hover{\n      transform: translateY(-2px);\n      box-shadow: 0 18px 32px -22px rgba(2,6,23,.35);\n    }\n    .icon-tile.active{\n      outline: 3px solid rgba(91,95,230,.28);\n      box-shadow: 0 0 0 6px rgba(91,95,230,.10);\n    }\n    .icon-preview{\n      width: 44px; height: 44px;\n      border-radius: 14px;\n      border: 1px solid rgba(15,23,42,.10);\n      background: rgba(241,245,249,.8);\n      display:flex; align-items:center; justify-content:center;\n    }\n    .icon-preview img{ width: 36px; height: 36px; }\n    .icon-tag{\n      font-size: 11px;\n      color: #0f172a;\n      font-weight: 700;\n      letter-spacing: .02em;\n    }\n    .icon-sub{\n      font-size: 11px;\n      color: var(--muted);\n    }\n    .icon-apply{\n      display:flex;\n      gap:8px;\n      margin-top:8px;\n      flex-wrap: wrap;\n    }\n    .mini-btn{\n      border-radius: 999px;\n      border: 1px solid rgba(15,23,42,.12);\n      background: rgba(255,255,255,.9);\n      padding: 6px 10px;\n      font-size: 11px;\n      transition: .12s ease;\n      display:inline-flex; align-items:center; gap:6px;\n    }\n    .mini-btn:hover{ background: rgba(248,250,252,1); }\n    .mini-btn.primary{\n      border-color: transparent;\n      color:#fff;\n      background: linear-gradient(135deg,var(--grad-a),var(--grad-b));\n    }\n\n    @media (max-width: 768px){\n      .mini-avatar{ width:40px;height:40px; }\n      .avatar{ width:88px;height:88px; }\n      #auth-card{ margin: 4vh 14px; }\n    }\n  &lt;\/style>\n&lt;\/head>\n\n&lt;body class=\"text-gray-900\">\n\n  &lt;!-- Hidden file inputs -->\n  &lt;input id=\"file-profile\" type=\"file\" accept=\"image\/*\" class=\"hidden\">\n  &lt;input id=\"file-headerbg\" type=\"file\" accept=\"image\/*\" class=\"hidden\">\n  &lt;input id=\"file-appicon\" type=\"file\" accept=\"image\/*\" class=\"hidden\">\n  &lt;input id=\"file-feedicon\" type=\"file\" accept=\"image\/*\" class=\"hidden\">\n  &lt;input id=\"file-media\" type=\"file\" accept=\"image\/*,video\/*\" multiple class=\"hidden\">\n\n  &lt;!-- \u2705 Auth Overlay -->\n  &lt;div id=\"auth-overlay\">\n    &lt;div id=\"auth-card\">\n      &lt;div class=\"flex items-center justify-between\">\n        &lt;div class=\"flex items-center gap-3\">\n          &lt;div class=\"w-10 h-10 rounded-2xl flex items-center justify-center\"\n               style=\"background:linear-gradient(135deg,var(--grad-a),var(--grad-b)); color:#fff;\">\n            &lt;i class=\"fas fa-comments\">&lt;\/i>\n          &lt;\/div>\n          &lt;div>\n            &lt;div class=\"text-xl font-extrabold tracking-tight\">&lt;span class=\"gradient-text\">Tsumugi&lt;\/span>&lt;\/div>\n            &lt;div class=\"text-xs muted\">\u30ed\u30b0\u30a4\u30f3 \/ \u4f1a\u54e1\u767b\u9332 \/ \u30ed\u30b0\u30a2\u30a6\u30c8\uff08localStorage\u4fdd\u6301\uff09&lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n        &lt;button id=\"btn-auth-close\" class=\"pill px-4 py-2 text-xs hover:bg-slate-50\">\n          &lt;i class=\"fas fa-times mr-2\">&lt;\/i>\u9589\u3058\u308b\n        &lt;\/button>\n      &lt;\/div>\n\n      &lt;div class=\"mt-4 flex items-center gap-2\">\n        &lt;button id=\"tab-login\" class=\"tab-btn active\">&lt;i class=\"fas fa-right-to-bracket mr-2\">&lt;\/i>\u30ed\u30b0\u30a4\u30f3&lt;\/button>\n        &lt;button id=\"tab-register\" class=\"tab-btn\">&lt;i class=\"fas fa-user-plus mr-2\">&lt;\/i>\u4f1a\u54e1\u767b\u9332&lt;\/button>\n        &lt;div class=\"ml-auto flex items-center gap-2\">\n          &lt;button id=\"btn-guest\" class=\"pill px-4 py-2 text-xs hover:bg-slate-50\" title=\"\u30b2\u30b9\u30c8\u3067\u5165\u308b\uff08\u767b\u9332\u4e0d\u8981\uff09\">\n            &lt;i class=\"fas fa-user-ninja mr-2\">&lt;\/i>\u30b2\u30b9\u30c8\n          &lt;\/button>\n        &lt;\/div>\n      &lt;\/div>\n\n      &lt;div class=\"mt-4 glass p-4 border border-slate-200\">\n        &lt;!-- Login -->\n        &lt;div id=\"panel-login\">\n          &lt;div class=\"text-sm font-bold mb-2\">&lt;i class=\"fas fa-right-to-bracket mr-2 text-indigo-600\">&lt;\/i>\u30ed\u30b0\u30a4\u30f3&lt;\/div>\n          &lt;div class=\"space-y-3\">\n            &lt;div>\n              &lt;label class=\"block text-xs font-semibold text-slate-700 mb-1\">\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9&lt;\/label>\n              &lt;input id=\"login-email\" type=\"email\" class=\"soft-input\" placeholder=\"you@example.com\" autocomplete=\"email\">\n            &lt;\/div>\n            &lt;div>\n              &lt;label class=\"block text-xs font-semibold text-slate-700 mb-1\">\u30d1\u30b9\u30ef\u30fc\u30c9&lt;\/label>\n              &lt;input id=\"login-pass\" type=\"password\" class=\"soft-input\" placeholder=\"password\" autocomplete=\"current-password\">\n            &lt;\/div>\n            &lt;div class=\"flex items-center justify-between gap-3\">\n              &lt;label class=\"flex items-center gap-2 text-xs\">\n                &lt;input id=\"login-remember\" type=\"checkbox\" class=\"mr-1\">\n                \u30ed\u30b0\u30a4\u30f3\u72b6\u614b\u3092\u4fdd\u6301\n              &lt;\/label>\n              &lt;button id=\"btn-login\" class=\"btn-primary px-5 py-2 rounded-lg text-xs font-semibold\">\n                &lt;i class=\"fas fa-unlock mr-2\">&lt;\/i>\u30ed\u30b0\u30a4\u30f3\n              &lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n\n        &lt;!-- Register -->\n        &lt;div id=\"panel-register\" style=\"display:none;\">\n          &lt;div class=\"text-sm font-bold mb-2\">&lt;i class=\"fas fa-user-plus mr-2 text-indigo-600\">&lt;\/i>\u4f1a\u54e1\u767b\u9332&lt;\/div>\n          &lt;div class=\"space-y-3\">\n            &lt;div>\n              &lt;label class=\"block text-xs font-semibold text-slate-700 mb-1\">\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9&lt;\/label>\n              &lt;input id=\"reg-email\" type=\"email\" class=\"soft-input\" placeholder=\"you@example.com\" autocomplete=\"email\">\n            &lt;\/div>\n            &lt;div class=\"grid grid-cols-1 md:grid-cols-2 gap-2\">\n              &lt;div>\n                &lt;label class=\"block text-xs font-semibold text-slate-700 mb-1\">\u30d1\u30b9\u30ef\u30fc\u30c9&lt;\/label>\n                &lt;input id=\"reg-pass\" type=\"password\" class=\"soft-input\" placeholder=\"password\" autocomplete=\"new-password\">\n              &lt;\/div>\n              &lt;div>\n                &lt;label class=\"block text-xs font-semibold text-slate-700 mb-1\">\u78ba\u8a8d&lt;\/label>\n                &lt;input id=\"reg-pass2\" type=\"password\" class=\"soft-input\" placeholder=\"confirm\" autocomplete=\"new-password\">\n              &lt;\/div>\n            &lt;\/div>\n            &lt;div>\n              &lt;label class=\"block text-xs font-semibold text-slate-700 mb-1\">\u30e6\u30fc\u30b6\u30fc\u540d\uff0820\u6587\u5b57\u307e\u3067\uff09&lt;\/label>\n              &lt;input id=\"reg-username\" type=\"text\" maxlength=\"20\" class=\"soft-input\" placeholder=\"\u8868\u793a\u540d\">\n            &lt;\/div>\n            &lt;div class=\"flex items-center justify-between gap-3\">\n              &lt;div class=\"text-xs muted\">\u203b\u30d1\u30b9\u30ef\u30fc\u30c9\u306f\u7aef\u672b(localStorage)\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059\uff08\u7c21\u6613SNS\uff09&lt;\/div>\n              &lt;button id=\"btn-register\" class=\"btn-primary px-5 py-2 rounded-lg text-xs font-semibold\">\n                &lt;i class=\"fas fa-check mr-2\">&lt;\/i>\u767b\u9332\n              &lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n      &lt;\/div>\n\n      &lt;div class=\"mt-3\">\n        &lt;div id=\"auth-msg\" class=\"text-xs muted\">&lt;\/div>\n      &lt;\/div>\n    &lt;\/div>\n  &lt;\/div>\n\n  &lt;!-- VR Overlay (\u8a66\u9a13) -->\n  &lt;div id=\"vr-overlay\">\n    &lt;div id=\"vr-topbar\">\n      &lt;div class=\"flex items-center gap-2\">\n        &lt;span id=\"vr-hint\">&lt;i class=\"fas fa-vr-cardboard mr-2\">&lt;\/i>VR\u8a66\u9a13\u30e2\u30fc\u30c9\uff08\u6295\u7a3f\u30ab\u30fc\u30c9\u30923D\u8868\u793a\uff09&lt;\/span>\n      &lt;\/div>\n      &lt;div class=\"flex items-center gap-2\">\n        &lt;button id=\"btn-vr-enter\" class=\"btn\">&lt;i class=\"fas fa-play mr-2\">&lt;\/i>VR\u958b\u59cb&lt;\/button>\n        &lt;button id=\"btn-vr-exit\" class=\"btn\">&lt;i class=\"fas fa-times mr-2\">&lt;\/i>\u7d42\u4e86&lt;\/button>\n      &lt;\/div>\n    &lt;\/div>\n\n    &lt;a-scene id=\"vr-scene\" embedded background=\"color: #0b1020\">\n      &lt;a-entity position=\"0 1.6 3\">\n        &lt;a-camera wasd-controls-enabled=\"true\" look-controls=\"pointerLockEnabled: true\">&lt;\/a-camera>\n      &lt;\/a-entity>\n      &lt;a-entity light=\"type: ambient; intensity: 0.7\">&lt;\/a-entity>\n      &lt;a-entity light=\"type: directional; intensity: 0.8\" position=\"-1 2 1\">&lt;\/a-entity>\n\n      &lt;a-entity id=\"vr-wall\">&lt;\/a-entity>\n\n      &lt;a-plane position=\"0 0 -3\" rotation=\"-90 0 0\" width=\"30\" height=\"30\" color=\"#111827\" opacity=\"0.9\">&lt;\/a-plane>\n      &lt;a-sky color=\"#0b1020\">&lt;\/a-sky>\n    &lt;\/a-scene>\n  &lt;\/div>\n\n  &lt;!-- HEADER -->\n  &lt;header id=\"app-header\" class=\"glass mx-4 mt-4 p-6 border border-indigo-500\/20 header-bg\">\n    &lt;div class=\"flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4\">\n      &lt;div>\n        &lt;div class=\"flex items-center gap-3\">\n          &lt;i class=\"fas fa-comments text-indigo-500 text-2xl\">&lt;\/i>\n          &lt;h1 class=\"text-3xl md:text-4xl font-extrabold tracking-tight\">\n            &lt;span class=\"gradient-text\">Tsumugi&lt;\/span>\n            &lt;span class=\"badge ml-2\">&lt;i class=\"fas fa-bolt\">&lt;\/i> LocalSNS&lt;\/span>\n          &lt;\/h1>\n        &lt;\/div>\n        &lt;p class=\"text-sm md:text-base muted mt-1\">\n          \u30d5\u30a9\u30ed\u30fcTL \/ \u30c7\u30fc\u30bfDL \/ \u6295\u7a3fURL \/ RSS\u6295\u7a3fON\/OFF \/ VR\u8a66\u9a13 \/ \u30ed\u30b0\u30a4\u30f3\n        &lt;\/p>\n      &lt;\/div>\n\n      &lt;div class=\"flex flex-wrap items-center justify-end gap-2\">\n        &lt;div class=\"pill px-3 py-2 flex items-center gap-3\">\n          &lt;img id=\"header-avatar\" class=\"mini-avatar\" src=\"https:\/\/via.placeholder.com\/80\" alt=\"avatar\">\n          &lt;div class=\"text-left leading-tight\">\n            &lt;div class=\"font-semibold text-sm\" id=\"header-username\">\u672a\u8a2d\u5b9a&lt;\/div>\n            &lt;div class=\"text-xs muted\" id=\"header-email\">&lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n\n        &lt;button id=\"btn-auth-open\" class=\"pill px-4 py-2 text-xs flex items-center gap-2 hover:bg-slate-50\" title=\"\u30ed\u30b0\u30a4\u30f3\/\u767b\u9332\">\n          &lt;i class=\"fas fa-user-shield text-slate-700\">&lt;\/i> \u30a2\u30ab\u30a6\u30f3\u30c8\n        &lt;\/button>\n\n        &lt;button id=\"btn-logout\" class=\"pill px-4 py-2 text-xs flex items-center gap-2 hover:bg-slate-50\" title=\"\u30ed\u30b0\u30a2\u30a6\u30c8\">\n          &lt;i class=\"fas fa-right-from-bracket text-slate-700\">&lt;\/i> \u30ed\u30b0\u30a2\u30a6\u30c8\n        &lt;\/button>\n\n        &lt;button id=\"btn-vr\" class=\"pill px-4 py-2 text-xs flex items-center gap-2 hover:bg-slate-50\">\n          &lt;i class=\"fas fa-vr-cardboard text-slate-700\">&lt;\/i> VR\u8a66\u9a13\n        &lt;\/button>\n\n        &lt;button id=\"btn-clear-all\" class=\"bg-yellow-300 hover:bg-yellow-400 px-4 py-2 rounded-full text-black text-xs flex items-center gap-2\">\n          &lt;i class=\"fas fa-broom\">&lt;\/i> \u5168\u6d88\u53bb\n        &lt;\/button>\n      &lt;\/div>\n    &lt;\/div>\n  &lt;\/header>\n\n  &lt;!-- MAIN -->\n  &lt;main class=\"max-w-6xl mx-auto px-4 py-6\" id=\"main-content\">\n    &lt;div class=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n\n      &lt;!-- LEFT -->\n      &lt;section class=\"lg:col-span-1 space-y-6\">\n\n        &lt;!-- Account \/ Profile -->\n        &lt;div class=\"glass p-6 card-hover border border-slate-200\">\n          &lt;div class=\"flex items-center justify-between mb-4\">\n            &lt;h3 class=\"text-xl font-bold flex items-center gap-2\">\n              &lt;i class=\"fas fa-user-circle text-indigo-500\">&lt;\/i>\n              \u30a2\u30ab\u30a6\u30f3\u30c8\n            &lt;\/h3>\n            &lt;span class=\"badge\">&lt;i class=\"fas fa-home\">&lt;\/i> Home=Follow&lt;\/span>\n          &lt;\/div>\n\n          &lt;div class=\"text-center mb-5\">\n            &lt;img id=\"profile-avatar\" class=\"avatar mx-auto mb-3\" src=\"https:\/\/via.placeholder.com\/100\" alt=\"avatar\">\n            &lt;button id=\"btn-change-profile\" class=\"btn-primary px-4 py-2 rounded-full text-white text-sm\">\n              &lt;i class=\"fas fa-camera mr-2\">&lt;\/i>\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u753b\u50cf\n            &lt;\/button>\n          &lt;\/div>\n\n          &lt;div class=\"grid grid-cols-3 gap-2 mb-4\">\n            &lt;div class=\"glass p-3 text-center border border-slate-200\">\n              &lt;div class=\"text-xs muted\">\u30d5\u30a9\u30ed\u30fc&lt;\/div>\n              &lt;div id=\"stat-following\" class=\"text-lg font-extrabold\">0&lt;\/div>\n            &lt;\/div>\n            &lt;div class=\"glass p-3 text-center border border-slate-200\">\n              &lt;div class=\"text-xs muted\">\u30d5\u30a9\u30ed\u30ef\u30fc&lt;\/div>\n              &lt;div id=\"stat-followers\" class=\"text-lg font-extrabold\">0&lt;\/div>\n            &lt;\/div>\n            &lt;div class=\"glass p-3 text-center border border-slate-200\">\n              &lt;div class=\"text-xs muted\">\u6295\u7a3f&lt;\/div>\n              &lt;div id=\"stat-posts\" class=\"text-lg font-extrabold\">0&lt;\/div>\n            &lt;\/div>\n          &lt;\/div>\n\n          &lt;div class=\"space-y-3\">\n            &lt;div>\n              &lt;label class=\"block text-xs font-semibold text-slate-700 mb-1\">\u30e6\u30fc\u30b6\u30fc\u540d\uff0820\u6587\u5b57\u307e\u3067\uff09&lt;\/label>\n              &lt;input id=\"inp-username\" type=\"text\" maxlength=\"20\" class=\"soft-input\" placeholder=\"\u30e6\u30fc\u30b6\u30fc\u540d\">\n            &lt;\/div>\n            &lt;div>\n              &lt;label class=\"block text-xs font-semibold text-slate-700 mb-1\">\u81ea\u5df1\u7d39\u4ecb&lt;\/label>\n              &lt;textarea id=\"inp-bio\" rows=\"4\" class=\"soft-input\" placeholder=\"\u81ea\u5df1\u7d39\u4ecb\">&lt;\/textarea>\n            &lt;\/div>\n\n            &lt;button id=\"btn-save-profile\" class=\"btn-primary w-full py-2 rounded-lg text-white text-sm font-semibold\">\n              &lt;i class=\"fas fa-save mr-2\">&lt;\/i>\u4fdd\u5b58\n            &lt;\/button>\n\n            &lt;div class=\"glass p-3 border border-slate-200\">\n              &lt;div class=\"text-xs font-semibold text-slate-800 mb-1\">\u30d7\u30ec\u30d3\u30e5\u30fc&lt;\/div>\n              &lt;div id=\"preview-username\" class=\"font-semibold\">\u672a\u8a2d\u5b9a&lt;\/div>\n              &lt;div id=\"preview-bio\" class=\"text-xs muted whitespace-pre-line mt-1\">\u307e\u3060\u81ea\u5df1\u7d39\u4ecb\u304c\u3042\u308a\u307e\u305b\u3093&lt;\/div>\n            &lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n\n        &lt;!-- Header \/ Icons -->\n        &lt;div class=\"glass p-6 card-hover border border-slate-200\">\n          &lt;div class=\"flex items-center justify-between mb-3\">\n            &lt;h3 class=\"text-lg font-bold flex items-center gap-2\">\n              &lt;i class=\"fas fa-palette text-indigo-500\">&lt;\/i> \u898b\u305f\u76ee\uff08\u30d5\u30a1\u30a4\u30eb\uff09\n            &lt;\/h3>\n            &lt;span class=\"text-xs muted\">localStorage\u4fdd\u5b58&lt;\/span>\n          &lt;\/div>\n\n          &lt;div class=\"space-y-2 text-sm\">\n            &lt;button id=\"btn-change-headerbg\" class=\"pill w-full px-4 py-2 text-left hover:bg-slate-50\">\n              &lt;i class=\"fas fa-image mr-2 text-slate-700\">&lt;\/i> \u30d8\u30c3\u30c0\u30fc\u80cc\u666f\u3092\u5909\u66f4\n            &lt;\/button>\n            &lt;button id=\"btn-change-appicon\" class=\"pill w-full px-4 py-2 text-left hover:bg-slate-50\">\n              &lt;i class=\"fas fa-icons mr-2 text-slate-700\">&lt;\/i> Favicon\u3092\u5909\u66f4\n            &lt;\/button>\n            &lt;button id=\"btn-change-feedicon\" class=\"pill w-full px-4 py-2 text-left hover:bg-slate-50\">\n              &lt;i class=\"fas fa-rss mr-2 text-slate-700\">&lt;\/i> Feed\u30a2\u30a4\u30b3\u30f3\u5909\u66f4\n            &lt;\/button>\n\n            &lt;div class=\"grid grid-cols-2 gap-2 mt-2\">\n              &lt;button id=\"btn-reset-headerbg\" class=\"pill px-4 py-2 hover:bg-slate-50\">\n                &lt;i class=\"fas fa-undo mr-2\">&lt;\/i>\u30d8\u30c3\u30c0\u30fc\u623b\u3059\n              &lt;\/button>\n              &lt;button id=\"btn-reset-appicon\" class=\"pill px-4 py-2 hover:bg-slate-50\">\n                &lt;i class=\"fas fa-undo mr-2\">&lt;\/i>Favicon\u623b\u3059\n              &lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n\n          &lt;!-- \u2705 WEB\u30a2\u30a4\u30b3\u30f3 -->\n          &lt;div class=\"mt-5\">\n            &lt;div class=\"flex items-center justify-between\">\n              &lt;div class=\"text-sm font-extrabold flex items-center gap-2\">\n                &lt;i class=\"fas fa-sparkles text-indigo-600\">&lt;\/i> WEB\u30a2\u30a4\u30b3\u30f3\n              &lt;\/div>\n              &lt;span class=\"text-xs muted\">\u30af\u30ea\u30c3\u30af\u3067\u9078\u629e \u2192 \u9069\u7528&lt;\/span>\n            &lt;\/div>\n\n            &lt;div class=\"mt-3 grid grid-cols-2 gap-2\" id=\"icon-grid\">&lt;\/div>\n\n            &lt;div class=\"mt-3 glass p-4 border border-slate-200\">\n              &lt;div class=\"flex items-center justify-between gap-2\">\n                &lt;div class=\"text-xs font-semibold flex items-center gap-2\">\n                  &lt;i class=\"fas fa-wand-magic-sparkles text-indigo-600\">&lt;\/i>\n                  \u9078\u629e\u4e2d\u30a2\u30a4\u30b3\u30f3\n                &lt;\/div>\n                &lt;button id=\"btn-icon-random\" class=\"pill px-3 py-2 text-xs hover:bg-slate-50\" title=\"\u30e9\u30f3\u30c0\u30e0\u306b\u9078\u3076\">\n                  &lt;i class=\"fas fa-dice mr-2\">&lt;\/i>\u30e9\u30f3\u30c0\u30e0\n                &lt;\/button>\n              &lt;\/div>\n\n              &lt;div class=\"mt-3 flex items-center gap-3\">\n                &lt;div class=\"icon-preview\">\n                  &lt;img id=\"icon-selected-preview\" src=\"\" alt=\"selected\">\n                &lt;\/div>\n                &lt;div class=\"flex-1\">\n                  &lt;div id=\"icon-selected-name\" class=\"icon-tag\">\u672a\u9078\u629e&lt;\/div>\n                  &lt;div id=\"icon-selected-desc\" class=\"icon-sub\">\u30a2\u30a4\u30b3\u30f3\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044&lt;\/div>\n                &lt;\/div>\n              &lt;\/div>\n\n              &lt;div class=\"icon-apply mt-3\">\n                &lt;button id=\"btn-apply-favicon\" class=\"mini-btn primary\">\n                  &lt;i class=\"fas fa-icons\">&lt;\/i>Favicon\u306b\u9069\u7528\n                &lt;\/button>\n                &lt;button id=\"btn-apply-feed\" class=\"mini-btn\">\n                  &lt;i class=\"fas fa-rss\">&lt;\/i>Feed\u306b\u9069\u7528\n                &lt;\/button>\n                &lt;button id=\"btn-apply-profile\" class=\"mini-btn\">\n                  &lt;i class=\"fas fa-user-circle\">&lt;\/i>\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u306b\u9069\u7528\n                &lt;\/button>\n              &lt;\/div>\n\n              &lt;div class=\"mt-3 text-&#91;11px] muted\">\n                \u203bSVG(dataURL)\u306a\u306e\u3067\u3001\u7aef\u672b\u5185(localStorage)\u3060\u3051\u3067\u5b8c\u7d50\u3057\u307e\u3059\uff08\u5916\u90e8\u753b\u50cf\u4e0d\u8981\uff09\n              &lt;\/div>\n            &lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n\n        &lt;!-- \u2705 RSS\uff08\u6295\u7a3f\u6a5f\u80fd ON\/OFF \u5b8c\u5168\uff09 -->\n        &lt;div class=\"glass p-6 card-hover border border-amber-500\/20\">\n          &lt;div class=\"flex items-center justify-between mb-2\">\n            &lt;h3 class=\"text-lg font-bold flex items-center gap-2\">\n              &lt;i class=\"fas fa-rss text-amber-500\">&lt;\/i> RSS\u6295\u7a3f\n            &lt;\/h3>\n            &lt;span class=\"badge\">\n              &lt;span class=\"status-dot\" id=\"rss-dot\">&lt;\/span>\n              &lt;span id=\"rss-status-text\">\u505c\u6b62\u4e2d&lt;\/span>\n            &lt;\/span>\n          &lt;\/div>\n\n          &lt;div class=\"flex items-center justify-between gap-3 mb-3\">\n            &lt;div class=\"text-xs muted\">\n              &lt;b>ON&lt;\/b>\uff1a\u4e00\u5b9a\u9593\u9694\u3067RSS\u3092\u53d6\u5f97\u3057\u3001&lt;b>Feed\u6295\u7a3f&lt;\/b>\u3068\u3057\u3066\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3078\u81ea\u52d5\u6295\u7a3f\u3057\u307e\u3059\n            &lt;\/div>\n\n            &lt;div class=\"flex items-center gap-2\">\n              &lt;div class=\"text-xs font-bold text-slate-700\">\u81ea\u52d5&lt;\/div>\n              &lt;div id=\"rss-toggle\" class=\"toggle\" role=\"switch\" aria-checked=\"false\" tabindex=\"0\" title=\"RSS\u81ea\u52d5\u6295\u7a3f ON\/OFF\">\n                &lt;span>&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          &lt;\/div>\n\n          &lt;div class=\"space-y-2\">\n            &lt;input id=\"rss-url\" class=\"soft-input text-xs\" placeholder=\"RSS URL\uff08https:\/\/...\uff09\">\n            &lt;div class=\"grid grid-cols-2 gap-2\">\n              &lt;button id=\"btn-add-rss\" class=\"btn-primary py-2 rounded-lg text-xs\">\n                &lt;i class=\"fas fa-plus mr-2\">&lt;\/i>\u8ffd\u52a0\n              &lt;\/button>\n              &lt;button id=\"btn-fetch-rss\" class=\"pill py-2 text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-sync mr-2\">&lt;\/i>\u4eca\u3059\u3050\u53d6\u5f97\n              &lt;\/button>\n            &lt;\/div>\n\n            &lt;div class=\"grid grid-cols-2 gap-2\">\n              &lt;input id=\"rss-interval\" type=\"number\" min=\"10\" max=\"3600\" value=\"120\" class=\"soft-input text-xs\" placeholder=\"\u9593\u9694(\u79d2)\">\n              &lt;button id=\"btn-set-rss-interval\" class=\"pill py-2 text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-clock mr-2\">&lt;\/i>\u9593\u9694\u8a2d\u5b9a\n              &lt;\/button>\n            &lt;\/div>\n\n            &lt;div class=\"grid grid-cols-2 gap-2\">\n              &lt;button id=\"btn-rss-clear-seen\" class=\"pill py-2 text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-eraser mr-2\">&lt;\/i>\u65e2\u8aad\u30ea\u30bb\u30c3\u30c8\n              &lt;\/button>\n              &lt;button id=\"btn-rss-test-one\" class=\"pill py-2 text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-flask mr-2\">&lt;\/i>URL\u691c\u67fb\n              &lt;\/button>\n            &lt;\/div>\n\n            &lt;div class=\"mt-2\">\n              &lt;div class=\"text-xs font-semibold mb-1\">RSS\u4e00\u89a7&lt;\/div>\n              &lt;div id=\"rss-list\" class=\"space-y-2 text-xs\">&lt;\/div>\n            &lt;\/div>\n\n            &lt;div class=\"mt-3\">\n              &lt;div class=\"text-xs font-semibold mb-1\">RSS\u30ed\u30b0&lt;\/div>\n              &lt;div id=\"rss-log\" class=\"log\">&lt;\/div>\n            &lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n\n      &lt;\/section>\n\n      &lt;!-- RIGHT -->\n      &lt;section class=\"lg:col-span-2 space-y-6\">\n\n        &lt;!-- New Post -->\n        &lt;div class=\"glass p-6 card-hover border border-slate-200\">\n          &lt;div class=\"flex items-center justify-between mb-3\">\n            &lt;h3 class=\"text-xl font-bold flex items-center gap-2\">\n              &lt;i class=\"fas fa-edit text-indigo-500\">&lt;\/i> \u65b0\u898f\u6295\u7a3f\n            &lt;\/h3>\n            &lt;div class=\"flex gap-2\">\n              &lt;button id=\"btn-home\" class=\"btn-primary px-3 py-2 rounded-full text-xs\">\n                &lt;i class=\"fas fa-house mr-2\">&lt;\/i>Home\n              &lt;\/button>\n              &lt;button id=\"btn-explore\" class=\"pill px-3 py-2 rounded-full text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-compass mr-2\">&lt;\/i>\u63a2\u7d22\n              &lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n\n          &lt;textarea id=\"post-text\" class=\"soft-input text-sm\" rows=\"4\"\n            placeholder=\"\u3044\u307e\u3069\u3046\u3057\u3066\u308b\uff1f\uff08Ctrl+Enter\u3067\u6295\u7a3f\uff09\" maxlength=\"600\">&lt;\/textarea>\n\n          &lt;div class=\"mt-3 flex flex-col md:flex-row md:items-center md:justify-between gap-3\">\n            &lt;div class=\"text-xs muted flex items-center gap-2 flex-wrap\">\n              &lt;i class=\"fas fa-circle-info\">&lt;\/i>\n              &lt;span id=\"char-count\">(0\/600)&lt;\/span>\n              &lt;label class=\"pill px-3 py-2 text-xs cursor-pointer hover:bg-slate-50\" id=\"btn-add-media\">\n                &lt;i class=\"fas fa-paperclip mr-2\">&lt;\/i>\u753b\u50cf\/\u52d5\u753b\n              &lt;\/label>\n              &lt;button id=\"btn-clear-media\" class=\"pill px-3 py-2 text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-trash mr-2\">&lt;\/i>\u30af\u30ea\u30a2\n              &lt;\/button>\n              &lt;span class=\"badge\">&lt;i class=\"fas fa-link\">&lt;\/i>\u6295\u7a3fURL&lt;\/span>\n              &lt;span class=\"badge\">&lt;i class=\"fas fa-rss\">&lt;\/i>RSS\u6295\u7a3f&lt;\/span>\n            &lt;\/div>\n\n            &lt;div class=\"flex gap-2\">\n              &lt;button id=\"btn-post\" class=\"btn-primary px-5 py-2 rounded-lg text-xs font-semibold\">\n                &lt;i class=\"fas fa-paper-plane mr-2\">&lt;\/i>\u6295\u7a3f\n              &lt;\/button>\n              &lt;button id=\"btn-delete-all-posts\" class=\"bg-red-600 hover:bg-red-700 px-5 py-2 rounded-lg text-white text-xs font-semibold\">\n                &lt;i class=\"fas fa-trash mr-2\">&lt;\/i>\u5168\u6295\u7a3f\u524a\u9664\n              &lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n\n          &lt;div class=\"mt-4 glass p-4 border border-slate-200\">\n            &lt;div class=\"flex items-center justify-between\">\n              &lt;div class=\"text-xs font-semibold flex items-center gap-2\">\n                &lt;i class=\"fas fa-link text-indigo-600\">&lt;\/i> \u76f4\u8fd1\u306e\u6295\u7a3fURL\n              &lt;\/div>\n              &lt;button id=\"btn-copy-post-url\" class=\"pill px-3 py-2 text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-copy mr-2\">&lt;\/i>\u30b3\u30d4\u30fc\n              &lt;\/button>\n            &lt;\/div>\n            &lt;div class=\"mt-2 text-sm\">\n              &lt;div id=\"latest-post-url\" class=\"muted break-all\">(\u307e\u3060\u6295\u7a3f\u3057\u3066\u3044\u307e\u305b\u3093)&lt;\/div>\n            &lt;\/div>\n            &lt;div class=\"mt-3 flex flex-wrap gap-2\">\n              &lt;button id=\"btn-open-post-url\" class=\"pill px-4 py-2 rounded-lg text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-arrow-up-right-from-square mr-2\">&lt;\/i>\u958b\u304f\n              &lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n\n          &lt;div class=\"mt-4\">\n            &lt;div class=\"text-xs font-semibold mb-1\">\u6dfb\u4ed8&lt;\/div>\n            &lt;div id=\"media-preview\" class=\"grid grid-cols-2 md:grid-cols-3 gap-3\">&lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n\n        &lt;!-- Timeline -->\n        &lt;div class=\"glass p-6 border border-slate-200\">\n          &lt;div class=\"flex flex-col md:flex-row md:items-center md:justify-between gap-3 mb-3\">\n            &lt;h3 class=\"text-xl font-bold flex items-center gap-2\">\n              &lt;i class=\"fas fa-stream text-indigo-500\">&lt;\/i> \u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\n              &lt;span id=\"post-count\" class=\"text-sm font-normal muted\">(0\u4ef6)&lt;\/span>\n            &lt;\/h3>\n            &lt;div class=\"flex items-center gap-2\">\n              &lt;input id=\"search\" class=\"soft-input text-xs w-full md:w-72\" placeholder=\"\u691c\u7d22\uff08\u672c\u6587\/\u30e6\u30fc\u30b6\u30fc\uff09\">\n              &lt;button id=\"btn-refresh\" class=\"pill px-4 py-2 text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-rotate mr-2\">&lt;\/i>\u66f4\u65b0\n              &lt;\/button>\n              &lt;button id=\"btn-export\" class=\"bg-emerald-600 hover:bg-emerald-700 px-4 py-2 rounded-lg text-white text-xs\">\n                &lt;i class=\"fas fa-download mr-2\">&lt;\/i>\u30c7\u30fc\u30bfDL\n              &lt;\/button>\n              &lt;button id=\"btn-import\" class=\"pill px-4 py-2 text-xs hover:bg-slate-50\">\n                &lt;i class=\"fas fa-upload mr-2\">&lt;\/i>\u8aad\u307f\u8fbc\u307f\n              &lt;\/button>\n            &lt;\/div>\n          &lt;\/div>\n\n          &lt;div class=\"flex flex-wrap gap-2 text-xs mb-3\">\n            &lt;button class=\"btn-primary px-3 py-2 rounded-full\" data-filter=\"all\">&lt;i class=\"fas fa-globe mr-2\">&lt;\/i>\u3059\u3079\u3066&lt;\/button>\n            &lt;button class=\"pill px-3 py-2 rounded-full hover:bg-slate-50\" data-filter=\"user\">&lt;i class=\"fas fa-user mr-2\">&lt;\/i>\u30e6\u30fc\u30b6\u30fc&lt;\/button>\n            &lt;button class=\"pill px-3 py-2 rounded-full hover:bg-slate-50\" data-filter=\"feed\">&lt;i class=\"fas fa-rss mr-2\">&lt;\/i>Feed&lt;\/button>\n          &lt;\/div>\n\n          &lt;div id=\"timeline\" class=\"space-y-4\">&lt;\/div>\n          &lt;div id=\"empty\" class=\"text-center py-14 muted\">\n            &lt;i class=\"fas fa-comments text-4xl mb-4 text-indigo-500\">&lt;\/i>\n            &lt;div class=\"text-lg font-semibold\">\u307e\u3060\u6295\u7a3f\u304c\u3042\u308a\u307e\u305b\u3093&lt;\/div>\n            &lt;div class=\"text-xs mt-1\">\u6295\u7a3f\u304bRSS\u53d6\u5f97\u3092\u3059\u308b\u3068\u3001\u3053\u3053\u306b\u8868\u793a\u3055\u308c\u307e\u3059&lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n\n      &lt;\/section>\n    &lt;\/div>\n  &lt;\/main>\n\n  &lt;footer class=\"glass mx-4 mb-4 p-4 text-center border border-slate-200\">\n    &lt;div class=\"text-xs muted\">\n      &lt;i class=\"fas fa-copyright mr-1\">&lt;\/i> 2026 Tsumugi \u2013 Local SNS\uff08\u30ed\u30b0\u30a4\u30f3\/\u767b\u9332\/\u30ed\u30b0\u30a2\u30a6\u30c8 + RSS\u6295\u7a3fON\/OFF\uff09\n    &lt;\/div>\n  &lt;\/footer>\n\n&lt;script>\n\/* =========================================================\n   \u2705 \u4f1a\u54e1\u767b\u9332\u304c\u6a5f\u80fd\u3057\u306a\u3044\u554f\u984c\u3092\u4fee\u6b63\uff08\u6b20\u843d\u306a\u3057\u5168\u4f53\u30b3\u30fc\u30c9\uff09\n   \u4e3b\u56e0\u306b\u306a\u308a\u3084\u3059\u3044\u30dd\u30a4\u30f3\u30c8\u3092\u6f70\u3057\u3066\u3044\u307e\u3059\uff1a\n   1) crypto.subtle \u304c http \/ file:\/\/ \u3067\u4f7f\u3048\u305a\u4f8b\u5916 \u2192 \u767b\u9332\u304c\u6b62\u307e\u308b\n      \u2192 \u2705 sha256 \u5931\u6557\u6642\u306f\u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\uff08\u7c21\u6613\u30cf\u30c3\u30b7\u30e5\uff09\u3067\u5fc5\u305a\u9032\u3080\n   2) users \u8aad\u307f\u8fbc\u307f\u524d\u306b findUser \u304c\u8d70\u308b \/ \u7834\u640d\u30c7\u30fc\u30bf\u3067\u843d\u3061\u308b\n      \u2192 \u2705 loadUsers \u3092\u6700\u521d\u306b\u5b9f\u884c\uff06sanitize\n   3) \u767b\u9332\u5f8c\u306b\u30a2\u30d7\u30ea\u304c\u30ed\u30c3\u30af\u89e3\u9664\u3055\u308c\u306a\u3044\/ UI\u672a\u66f4\u65b0\n      \u2192 \u2705 afterAuthBootstrap \u3092\u78ba\u5b9f\u306b\u547c\u3076\n========================================================= *\/\n\n\/* ---------- Storage helpers ---------- *\/\nconst LS = {\n  get(k, fb=null){ try{ const v=localStorage.getItem(k); return (v===null||v===undefined)?fb:v; }catch(e){ return fb; } },\n  set(k, v){ try{ localStorage.setItem(k, v); }catch(e){} },\n  del(k){ try{ localStorage.removeItem(k); }catch(e){} }\n};\n\nconst KEY = {\n  users:\"ts_users\",\n  current:\"ts_current_user\",          \/\/ {email}\n  session:\"ts_session\",               \/\/ {email, remember, ts}\n  posts:\"ts_posts\",\n  icons:\"ts_icons\",\n  headerBg:\"ts_header_bg\",\n  favicon:\"ts_favicon\",\n  rss:\"ts_rss\",\n  rssSeen:\"ts_rss_seen\",\n  latest:\"ts_latest_post_url\",\n  webIconSelected:\"ts_webicon_selected\" \/\/ {id}\n};\n\nconst PRESET_RSS = &#91;\n  \"https:\/\/uploadvr.com\/feed\/\",\n  \"https:\/\/www.theverge.com\/rss\/index.xml\",\n  \"https:\/\/www3.nhk.or.jp\/rss\/news\/cat0.xml\"\n];\n\n\/* ---------- State ---------- *\/\nlet users = &#91;];\nlet currentUser = null;   \/\/ {email}\nlet posts = &#91;];\nlet icons = {};\nlet homeMode = \"home\";\nlet currentFilter = \"all\";\nlet searchQuery = \"\";\nlet pendingMedia = &#91;];\nlet rssTimer = null;\n\n\/* \u2705 WEB\u30a2\u30a4\u30b3\u30f3 state *\/\nlet WEB_ICONS = &#91;];\nlet selectedWebIconId = null;\n\n\/* ---------- Utilities ---------- *\/\nfunction uid(){ return Date.now().toString(36)+\"-\"+Math.random().toString(36).slice(2,8); }\nfunction escapeHtml(s){ return (s||\"\").replace(\/&#91;&amp;&lt;>\"']\/g, m => ({'&amp;':'&amp;amp;','&lt;':'&amp;lt;','>':'&amp;gt;','\"':'&amp;quot;',\"'\":'&amp;#39;'}&#91;m])); }\nfunction nowJp(){ return new Date().toLocaleString(\"ja-JP\"); }\nfunction safeUrl(u){\n  const s = String(u||\"\").trim();\n  if(!s) return \"\";\n  if(\/^https?:\\\/\\\/\/i.test(s)) return s;\n  return \"\";\n}\nasync function copyText(t){\n  try{\n    if(navigator.clipboard){ await navigator.clipboard.writeText(t); return true; }\n  }catch(e){}\n  const ta=document.createElement(\"textarea\"); ta.value=t; document.body.appendChild(ta);\n  ta.select(); try{ document.execCommand(\"copy\"); }catch(e){}\n  document.body.removeChild(ta);\n  return true;\n}\nfunction openPopup(url){ window.open(url, \"_blank\", \"noopener,noreferrer,width=980,height=740\"); }\nfunction bytesToMB(b){ return Math.round((b\/1024\/1024)*10)\/10; }\n\nfunction addLog(msg, type=\"info\"){\n  const el = document.getElementById(\"rss-log\");\n  if(!el) return;\n  const div = document.createElement(\"div\");\n  div.className = type===\"err\" ? \"err\" : type===\"ok\" ? \"ok\" : \"info\";\n  div.textContent = `&#91;${new Date().toLocaleTimeString(\"ja-JP\")}] ${msg}`;\n  el.appendChild(div);\n  el.scrollTop = el.scrollHeight;\n  while(el.children.length > 160) el.removeChild(el.firstChild);\n}\n\n\/* =========================================================\n   \u2705 WEB\u30a2\u30a4\u30b3\u30f3\uff08SVG\u751f\u6210\uff09\n========================================================= *\/\nfunction toDataUrlSvg(svg){\n  const enc = encodeURIComponent(svg)\n    .replace(\/%0A\/g,\"\")\n    .replace(\/%20\/g,\" \")\n    .replace(\/%3D\/g,\"=\")\n    .replace(\/%3A\/g,\":\")\n    .replace(\/%2F\/g,\"\/\")\n    .replace(\/%2C\/g,\",\");\n  return \"data:image\/svg+xml;charset=utf-8,\" + enc;\n}\nfunction makeIconSvg({bg1,bg2,shape,mark,accent1,accent2}){\n  const bg = `\n    &lt;defs>\n      &lt;radialGradient id=\"rg\" cx=\"30%\" cy=\"20%\" r=\"90%\">\n        &lt;stop offset=\"0%\" stop-color=\"${bg2}\"\/>\n        &lt;stop offset=\"55%\" stop-color=\"${bg1}\"\/>\n        &lt;stop offset=\"100%\" stop-color=\"#0b1020\"\/>\n      &lt;\/radialGradient>\n      &lt;linearGradient id=\"lg\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n        &lt;stop offset=\"0%\" stop-color=\"${accent1}\"\/>\n        &lt;stop offset=\"100%\" stop-color=\"${accent2}\"\/>\n      &lt;\/linearGradient>\n      &lt;filter id=\"shadow\" x=\"-30%\" y=\"-30%\" width=\"160%\" height=\"160%\">\n        &lt;feDropShadow dx=\"0\" dy=\"10\" stdDeviation=\"8\" flood-color=\"rgba(2,6,23,.45)\"\/>\n      &lt;\/filter>\n    &lt;\/defs>\n  `;\n  let base;\n  if(shape===\"hex\"){\n    base = `&lt;path d=\"M32 4 54 16v32L32 60 10 48V16Z\" fill=\"url(#rg)\" \/>`;\n  }else if(shape===\"round\"){\n    base = `&lt;circle cx=\"32\" cy=\"32\" r=\"28\" fill=\"url(#rg)\" \/>`;\n  }else{\n    base = `&lt;rect x=\"4\" y=\"4\" width=\"56\" height=\"56\" rx=\"16\" fill=\"url(#rg)\" \/>`;\n  }\n  let glyph=\"\";\n  if(mark===\"T\"){\n    glyph = `&lt;path d=\"M20 18h24a2 2 0 0 1 0 4H36v26a2 2 0 0 1-4 0V22h-8a2 2 0 0 1 0-4z\" fill=\"white\"\/>`;\n  }else if(mark===\"V\"){\n    glyph = `&lt;path d=\"M18 18h8l6 22 6-22h8L36 50h-8L18 18z\" fill=\"white\"\/>`;\n  }else if(mark===\"C\"){\n    glyph = `&lt;path d=\"M44.5 22.5c-2.3-2.3-5.5-3.7-9-3.7-6.9 0-12.5 5.6-12.5 12.5S28.6 43.8 35.5 43.8c3.5 0 6.7-1.4 9-3.7l-3.2-3.2c-1.5 1.5-3.6 2.4-5.8 2.4-4.4 0-8-3.6-8-8s3.6-8 8-8c2.3 0 4.3.9 5.8 2.4l3.2-3.2z\" fill=\"white\"\/>`;\n  }else if(mark===\"star\"){\n    glyph = `&lt;path d=\"M32 18l3.9 8.2 9.1 1.3-6.6 6.4 1.6 9.1L32 39.7 23.9 43l1.6-9.1-6.6-6.4 9.1-1.3L32 18z\" fill=\"white\"\/>`;\n  }else if(mark===\"bolt\"){\n    glyph = `&lt;path d=\"M36 14 20 36h10l-2 14 16-22H34l2-14z\" fill=\"white\"\/>`;\n  }else if(mark===\"eye\"){\n    glyph = `&lt;path d=\"M32 22c-10 0-18 10-18 10s8 10 18 10 18-10 18-10-8-10-18-10zm0 16a6 6 0 1 1 0-12 6 6 0 0 1 0 12z\" fill=\"white\"\/>&lt;circle cx=\"32\" cy=\"32\" r=\"2.6\" fill=\"white\"\/>`;\n  }else if(mark===\"crown\"){\n    glyph = `&lt;path d=\"M20 40l-2-16 8 8 6-10 6 10 8-8-2 16H20z\" fill=\"white\"\/>&lt;path d=\"M22 42h20\" stroke=\"url(#lg)\" stroke-width=\"3\" stroke-linecap=\"round\" opacity=\".8\"\/>`;\n  }\n  const accents = `\n    &lt;path d=\"M18 20h28\" stroke=\"url(#lg)\" stroke-width=\"3\" stroke-linecap=\"round\" opacity=\".85\"\/>\n    &lt;path d=\"M32 22v26\" stroke=\"url(#lg)\" stroke-width=\"3\" stroke-linecap=\"round\" opacity=\".65\"\/>\n  `;\n  const border = shape===\"round\"\n    ? `&lt;circle cx=\"32\" cy=\"32\" r=\"28\" fill=\"none\" stroke=\"rgba(255,255,255,.24)\"\/>`\n    : shape===\"hex\"\n      ? `&lt;path d=\"M32 4 54 16v32L32 60 10 48V16Z\" fill=\"none\" stroke=\"rgba(255,255,255,.24)\"\/>`\n      : `&lt;rect x=\"4\" y=\"4\" width=\"56\" height=\"56\" rx=\"16\" fill=\"none\" stroke=\"rgba(255,255,255,.22)\"\/>`;\n  return `\n    &lt;svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"96\" height=\"96\" viewBox=\"0 0 64 64\">\n      ${bg}\n      &lt;g filter=\"url(#shadow)\">\n        ${base}\n        ${accents}\n        ${glyph}\n        ${border}\n      &lt;\/g>\n    &lt;\/svg>\n  `.trim();\n}\nfunction buildWebIconPack(){\n  const presets = &#91;\n    { id:\"tsumugi_T\", name:\"Tsumugi T\", desc:\"\u30b7\u30b0\u30cd\u30c1\u30e3\u30fc \/ Squircle\", shape:\"squircle\", mark:\"T\", bg1:\"#5b5fe6\", bg2:\"#7c3aed\", accent1:\"#a78bfa\", accent2:\"#60a5fa\" },\n    { id:\"verse_V\",  name:\"Verse V\",  desc:\"SNS\u611f \/ Round\", shape:\"round\", mark:\"V\", bg1:\"#06b6d4\", bg2:\"#3b82f6\", accent1:\"#67e8f9\", accent2:\"#a78bfa\" },\n    { id:\"chron_C\",  name:\"Chronicle C\", desc:\"\u30a2\u30fc\u30ab\u30a4\u30d6 \/ Squircle\", shape:\"squircle\", mark:\"C\", bg1:\"#22c55e\", bg2:\"#10b981\", accent1:\"#bbf7d0\", accent2:\"#60a5fa\" },\n    { id:\"spark_star\", name:\"Spark Star\", desc:\"\u30ad\u30e9\u30c3 \/ Round\", shape:\"round\", mark:\"star\", bg1:\"#f59e0b\", bg2:\"#ef4444\", accent1:\"#fde68a\", accent2:\"#fca5a5\" },\n    { id:\"bolt_hex\", name:\"Neon Bolt\", desc:\"\u7a32\u59bb \/ Hex\", shape:\"hex\", mark:\"bolt\", bg1:\"#a855f7\", bg2:\"#ec4899\", accent1:\"#c4b5fd\", accent2:\"#93c5fd\" },\n    { id:\"eye_squ\",  name:\"Observer\", desc:\"\u53ce\u96c6 \/ Squircle\", shape:\"squircle\", mark:\"eye\", bg1:\"#0ea5e9\", bg2:\"#22c55e\", accent1:\"#7dd3fc\", accent2:\"#86efac\" },\n    { id:\"crown_squ\", name:\"Crown\", desc:\"\u4e0a\u54c1 \/ Squircle\", shape:\"squircle\", mark:\"crown\", bg1:\"#f97316\", bg2:\"#a855f7\", accent1:\"#fdba74\", accent2:\"#c4b5fd\" }\n  ];\n  WEB_ICONS = presets.map(p=>{\n    const svg = makeIconSvg(p);\n    return { id:p.id, name:p.name, desc:p.desc, dataUrl: toDataUrlSvg(svg) };\n  });\n}\nfunction saveSelectedWebIcon(id){\n  selectedWebIconId = id;\n  LS.set(KEY.webIconSelected, JSON.stringify({id}));\n}\nfunction loadSelectedWebIcon(){\n  const obj = JSON.parse(LS.get(KEY.webIconSelected, \"null\") || \"null\");\n  selectedWebIconId = (obj &amp;&amp; obj.id) ? obj.id : (WEB_ICONS&#91;0]?.id || null);\n}\nfunction getSelectedWebIcon(){\n  if(!selectedWebIconId) return WEB_ICONS&#91;0] || null;\n  return WEB_ICONS.find(x=>x.id===selectedWebIconId) || WEB_ICONS&#91;0] || null;\n}\nfunction renderWebIconGrid(){\n  const grid = document.getElementById(\"icon-grid\");\n  if(!grid) return;\n  grid.innerHTML = \"\";\n  WEB_ICONS.forEach(icon=>{\n    const tile = document.createElement(\"div\");\n    tile.className = \"icon-tile\";\n    tile.setAttribute(\"data-iconid\", icon.id);\n    tile.innerHTML = `\n      &lt;div class=\"flex items-center gap-3\">\n        &lt;div class=\"icon-preview\">&lt;img src=\"${icon.dataUrl}\" alt=\"${escapeHtml(icon.name)}\">&lt;\/div>\n        &lt;div class=\"min-w-0\">\n          &lt;div class=\"icon-tag truncate\">${escapeHtml(icon.name)}&lt;\/div>\n          &lt;div class=\"icon-sub truncate\">${escapeHtml(icon.desc)}&lt;\/div>\n        &lt;\/div>\n      &lt;\/div>\n    `;\n    tile.onclick = ()=>{ selectWebIcon(icon.id); };\n    grid.appendChild(tile);\n  });\n  selectWebIcon(selectedWebIconId, false);\n}\nfunction selectWebIcon(id, persist=true){\n  const icon = WEB_ICONS.find(x=>x.id===id) || WEB_ICONS&#91;0];\n  if(!icon) return;\n  if(persist) saveSelectedWebIcon(icon.id);\n  document.querySelectorAll(\".icon-tile\").forEach(el=>{\n    el.classList.toggle(\"active\", el.getAttribute(\"data-iconid\")===icon.id);\n  });\n  document.getElementById(\"icon-selected-preview\").src = icon.dataUrl;\n  document.getElementById(\"icon-selected-name\").textContent = icon.name;\n  document.getElementById(\"icon-selected-desc\").textContent = icon.desc;\n}\n\n\/* =========================================================\n   \u2705 Favicon\uff08\u78ba\u5b9f\u7248\uff09\uff1a\u8907\u6570rel\u3092\u5168\u90e8\u66f4\u65b0\u3059\u308b\n========================================================= *\/\nfunction applyFavicon(dataUrl){\n  const url = String(dataUrl || \"\").trim();\n  if(!url) return;\n\n  const setHref = (id, rel, type=null, sizes=null)=>{\n    let link = document.getElementById(id);\n    if(!link){\n      link = document.createElement(\"link\");\n      link.id = id;\n      link.rel = rel;\n      if(type) link.type = type;\n      if(sizes) link.sizes = sizes;\n      document.head.appendChild(link);\n    }\n    link.href = url;\n  };\n\n  setHref(\"favicon-svg\", \"icon\", \"image\/svg+xml\", null);\n  setHref(\"favicon-shortcut\", \"shortcut icon\", null, null);\n  setHref(\"favicon-any\", \"icon\", null, \"any\");\n  setHref(\"apple-touch\", \"apple-touch-icon\", null, null);\n\n  LS.set(KEY.favicon, url);\n}\nfunction resetFavicon(){\n  LS.del(KEY.favicon);\n  const base = document.getElementById(\"favicon-svg\")?.href;\n  if(base) applyFavicon(base);\n  alert(\"Favicon\u3092\u521d\u671f\u306b\u623b\u3057\u307e\u3057\u305f\uff08\u53cd\u6620\u3055\u308c\u306a\u3044\u5834\u5408\u306f Ctrl+F5\uff09\");\n}\n\nfunction applySelectedToFavicon(){\n  const icon = getSelectedWebIcon(); if(!icon) return;\n  applyFavicon(icon.dataUrl);\n  alert(\"Favicon\u306b\u9069\u7528\u3057\u307e\u3057\u305f\");\n}\nfunction applySelectedToFeed(){\n  const icon = getSelectedWebIcon(); if(!icon) return;\n  icons.feed = icon.dataUrl;\n  LS.set(KEY.icons, JSON.stringify(icons));\n  renderTimeline();\n  rebuildVRWall();\n  alert(\"Feed\u30a2\u30a4\u30b3\u30f3\u306b\u9069\u7528\u3057\u307e\u3057\u305f\");\n}\nfunction applySelectedToProfile(){\n  const icon = getSelectedWebIcon(); if(!icon) return;\n  const me = getMe();\n  if(!me) return alert(\"\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\");\n  me.profile.avatar = icon.dataUrl;\n  saveUsers();\n  syncProfileUI();\n  renderTimeline();\n  rebuildVRWall();\n  alert(\"\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u753b\u50cf\u306b\u9069\u7528\u3057\u307e\u3057\u305f\");\n}\nfunction randomPickIcon(){\n  if(!WEB_ICONS.length) return;\n  const i = Math.floor(Math.random()*WEB_ICONS.length);\n  selectWebIcon(WEB_ICONS&#91;i].id);\n}\n\n\/* =========================================================\n   \u2705 Auth\uff08\u4f1a\u54e1\u767b\u9332\/\u30ed\u30b0\u30a4\u30f3\uff09\u2014 \u4fee\u6b63\u70b9\u3042\u308a\n========================================================= *\/\nfunction normalizeEmail(s){ return String(s||\"\").trim().toLowerCase(); }\n\n\/* \u2705 \u4fee\u6b63\uff1asha256 \u304c\u4f7f\u3048\u306a\u3044\u74b0\u5883\u3067\u3082\u5fc5\u305a\u52d5\u304f\u3088\u3046\u306b\u3059\u308b *\/\nasync function sha256(text){\n  const str = String(text);\n  try{\n    if(!(window.crypto &amp;&amp; crypto.subtle &amp;&amp; window.isSecureContext)){\n      \/\/ http \/ file:\/\/ \u3060\u3068 subtle \u304c\u52d5\u304b\u306a\u3044 \u2192 fallback\n      return simpleHash(str);\n    }\n    const enc = new TextEncoder().encode(str);\n    const buf = await crypto.subtle.digest(\"SHA-256\", enc);\n    const arr = Array.from(new Uint8Array(buf));\n    return arr.map(b=>b.toString(16).padStart(2,\"0\")).join(\"\");\n  }catch(e){\n    \/\/ \u4f8b\u5916\u304c\u51fa\u3066\u3082\u767b\u9332\u3092\u6b62\u3081\u306a\u3044\n    return simpleHash(str);\n  }\n}\n\n\/* \u2705 \u8ffd\u52a0\uff1a\u7c21\u6613\u30cf\u30c3\u30b7\u30e5\uff08\u5f37\u5ea6\u306f\u4f4e\u3044\u304c \u201c\u52d5\u304b\u306a\u3044\u201d \u3092\u9632\u3050\uff09 *\/\nfunction simpleHash(str){\n  let h1 = 0xdeadbeef ^ str.length, h2 = 0x41c6ce57 ^ str.length;\n  for(let i=0;i&lt;str.length;i++){\n    const ch = str.charCodeAt(i);\n    h1 = Math.imul(h1 ^ ch, 2654435761);\n    h2 = Math.imul(h2 ^ ch, 1597334677);\n  }\n  h1 = (h1 ^ (h1>>>16)) >>> 0;\n  h2 = (h2 ^ (h2>>>16)) >>> 0;\n  return \"fh_\" + h1.toString(16).padStart(8,\"0\") + h2.toString(16).padStart(8,\"0\");\n}\n\n\/* \u30e6\u30fc\u30b6\u30fc\u914d\u5217\u306e\u8aad\u307f\u66f8\u304d *\/\nfunction loadUsers(){\n  users = JSON.parse(LS.get(KEY.users, \"&#91;]\") || \"&#91;]\");\n  if(!Array.isArray(users)) users = &#91;];\n  users = users.map(u=>{\n    if(!u || !u.email) return null;\n    if(!u.profile) u.profile = { username:\"\", bio:\"\", avatar:\"https:\/\/via.placeholder.com\/100\" };\n    if(!u.following) u.following = &#91;];\n    if(!u.blocked) u.blocked = &#91;];\n    if(!u.muted) u.muted = &#91;];\n    if(!u.createdAt) u.createdAt = Date.now();\n    if(typeof u.passHash !== \"string\") u.passHash = u.passHash ? String(u.passHash) : \"\";\n    u.email = normalizeEmail(u.email);\n    return u;\n  }).filter(Boolean);\n  LS.set(KEY.users, JSON.stringify(users));\n}\nfunction saveUsers(){ LS.set(KEY.users, JSON.stringify(users)); }\n\nfunction loadSession(){\n  const s = JSON.parse(LS.get(KEY.session, \"null\") || \"null\");\n  if(!s || !s.email) return null;\n  if(!s.remember){\n    LS.del(KEY.session);\n    return null;\n  }\n  return s;\n}\nfunction saveSession(email, remember){\n  const s = { email: normalizeEmail(email), remember: !!remember, ts: Date.now() };\n  LS.set(KEY.session, JSON.stringify(s));\n}\nfunction clearSession(){ LS.del(KEY.session); }\n\nfunction setCurrentUser(email){\n  currentUser = { email: normalizeEmail(email) };\n  LS.set(KEY.current, JSON.stringify(currentUser));\n}\nfunction clearCurrentUser(){\n  currentUser = null;\n  LS.del(KEY.current);\n}\n\nfunction ensureGuest(){\n  const guestEmail=\"guest@local\";\n  if(!users.find(u=>u.email===guestEmail)){\n    users.push({\n      email:guestEmail,\n      passHash:\"\",\n      profile:{ username:\"\u30b2\u30b9\u30c8\", bio:\"\", avatar:\"https:\/\/via.placeholder.com\/100\" },\n      following:&#91;], blocked:&#91;], muted:&#91;],\n      createdAt: Date.now()\n    });\n    saveUsers();\n  }\n  setCurrentUser(guestEmail);\n}\nfunction findUser(email){\n  const e = normalizeEmail(email);\n  return users.find(u=>normalizeEmail(u.email)===e) || null;\n}\nfunction getMe(){\n  if(!currentUser || !currentUser.email) return null;\n  return findUser(currentUser.email);\n}\n\nfunction authMsg(text, kind=\"info\"){\n  const el = document.getElementById(\"auth-msg\");\n  if(!el) return;\n  if(!text){ el.textContent=\"\"; el.className=\"text-xs muted\"; return; }\n  el.textContent = text;\n  el.className = \"text-xs \" + (kind===\"err\" ? \"text-red-700\" : kind===\"ok\" ? \"text-emerald-700\" : \"muted\");\n}\nfunction openAuth(){\n  document.getElementById(\"auth-overlay\").style.display=\"block\";\n  authMsg(\"\");\n}\nfunction closeAuth(){\n  document.getElementById(\"auth-overlay\").style.display=\"none\";\n  authMsg(\"\");\n}\nfunction setAuthTab(tab){\n  const tLogin = document.getElementById(\"tab-login\");\n  const tReg = document.getElementById(\"tab-register\");\n  const pLogin = document.getElementById(\"panel-login\");\n  const pReg = document.getElementById(\"panel-register\");\n  const onLogin = tab===\"login\";\n  tLogin.classList.toggle(\"active\", onLogin);\n  tReg.classList.toggle(\"active\", !onLogin);\n  pLogin.style.display = onLogin ? \"\" : \"none\";\n  pReg.style.display = onLogin ? \"none\" : \"\";\n  authMsg(\"\");\n}\n\nasync function doRegister(){\n  loadUsers();\n\n  const email = normalizeEmail(document.getElementById(\"reg-email\").value);\n  const pass = String(document.getElementById(\"reg-pass\").value || \"\");\n  const pass2 = String(document.getElementById(\"reg-pass2\").value || \"\");\n  const username = String(document.getElementById(\"reg-username\").value || \"\").trim();\n\n  if(!email || !\/^&#91;^\\s@]+@&#91;^\\s@]+\\.&#91;^\\s@]+$\/.test(email)){\n    authMsg(\"\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u304c\u4e0d\u6b63\u3067\u3059\", \"err\"); return;\n  }\n  if(pass.length &lt; 4){\n    authMsg(\"\u30d1\u30b9\u30ef\u30fc\u30c9\u306f4\u6587\u5b57\u4ee5\u4e0a\u306b\u3057\u3066\u304f\u3060\u3055\u3044\", \"err\"); return;\n  }\n  if(pass !== pass2){\n    authMsg(\"\u30d1\u30b9\u30ef\u30fc\u30c9\u78ba\u8a8d\u304c\u4e00\u81f4\u3057\u307e\u305b\u3093\", \"err\"); return;\n  }\n  if(username.length > 20){\n    authMsg(\"\u30e6\u30fc\u30b6\u30fc\u540d\u306f20\u6587\u5b57\u4ee5\u5185\u3067\u3059\", \"err\"); return;\n  }\n  if(findUser(email)){\n    authMsg(\"\u3053\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306f\u65e2\u306b\u767b\u9332\u6e08\u307f\u3067\u3059\", \"err\"); return;\n  }\n\n  let passHash;\n  try{\n    passHash = await sha256(pass);\n  }catch(e){\n    passHash = simpleHash(pass);\n  }\n\n  users.push({\n    email,\n    passHash,\n    profile:{\n      username: username || email.split(\"@\")&#91;0] || \"User\",\n      bio:\"\",\n      avatar: (getSelectedWebIcon() &amp;&amp; getSelectedWebIcon().dataUrl) ? getSelectedWebIcon().dataUrl : \"https:\/\/via.placeholder.com\/100\"\n    },\n    following:&#91;],\n    blocked:&#91;],\n    muted:&#91;],\n    createdAt: Date.now()\n  });\n  saveUsers();\n\n  setCurrentUser(email);\n  saveSession(email, true);\n\n  authMsg(\"\u767b\u9332\u3057\u307e\u3057\u305f\u3002\u30ed\u30b0\u30a4\u30f3\u5b8c\u4e86\uff01\", \"ok\");\n\n  document.getElementById(\"reg-email\").value=\"\";\n  document.getElementById(\"reg-pass\").value=\"\";\n  document.getElementById(\"reg-pass2\").value=\"\";\n  document.getElementById(\"reg-username\").value=\"\";\n\n  closeAuth();\n  afterAuthBootstrap();\n}\n\nasync function doLogin(){\n  loadUsers();\n\n  const email = normalizeEmail(document.getElementById(\"login-email\").value);\n  const pass = String(document.getElementById(\"login-pass\").value || \"\");\n  const remember = !!document.getElementById(\"login-remember\").checked;\n\n  if(!email){\n    authMsg(\"\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\", \"err\"); return;\n  }\n  const u = findUser(email);\n  if(!u || !u.passHash){\n    authMsg(\"\u30e6\u30fc\u30b6\u30fc\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\uff08\u4f1a\u54e1\u767b\u9332\u3057\u3066\u304f\u3060\u3055\u3044\uff09\", \"err\"); return;\n  }\n\n  const passHash = await sha256(pass);\n  if(passHash !== u.passHash){\n    authMsg(\"\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u9055\u3044\u307e\u3059\", \"err\"); return;\n  }\n\n  setCurrentUser(email);\n  if(remember) saveSession(email, true);\n  else clearSession();\n\n  authMsg(\"\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3057\u305f\", \"ok\");\n  closeAuth();\n  afterAuthBootstrap();\n}\n\nfunction doLogout(){\n  if(!confirm(\"\u30ed\u30b0\u30a2\u30a6\u30c8\u3057\u307e\u3059\u304b\uff1f\")) return;\n  stopRss(true);\n  clearCurrentUser();\n  clearSession();\n  openAuth();\n  setAuthTab(\"login\");\n  lockAppForAuth(true);\n  syncProfileUI();\n  renderTimeline();\n  rebuildVRWall();\n}\n\nfunction lockAppForAuth(locked){\n  const main = document.getElementById(\"main-content\");\n  const header = document.getElementById(\"app-header\");\n  const footer = document.querySelector(\"footer\");\n  const disable = (el)=>{\n    if(!el) return;\n    el.style.filter = locked ? \"blur(2px)\" : \"\";\n    el.style.opacity = locked ? \"0.65\" : \"\";\n    el.style.pointerEvents = locked ? \"none\" : \"\";\n    el.style.userSelect = locked ? \"none\" : \"\";\n  };\n  disable(main); disable(header); disable(footer);\n}\n\nfunction restoreAuthState(){\n  const s = loadSession();\n  if(s &amp;&amp; s.email &amp;&amp; findUser(s.email)){\n    setCurrentUser(s.email);\n    return true;\n  }\n  const cur = JSON.parse(LS.get(KEY.current, \"null\") || \"null\");\n  if(cur &amp;&amp; cur.email &amp;&amp; findUser(cur.email)){\n    setCurrentUser(cur.email);\n    return true;\n  }\n  clearCurrentUser();\n  return false;\n}\n\n\/* ---------- Posts ---------- *\/\nfunction loadPosts(){ posts = JSON.parse(LS.get(KEY.posts, \"&#91;]\") || \"&#91;]\"); if(!Array.isArray(posts)) posts=&#91;]; }\nfunction savePosts(){ LS.set(KEY.posts, JSON.stringify(posts)); }\nfunction countMyPosts(){\n  const me=getMe(); if(!me) return 0;\n  return posts.filter(p=>p.authorEmail===me.email &amp;&amp; p.type===\"user\").length;\n}\n\n\/* ---------- Appearance ---------- *\/\nfunction applyHeaderBg(dataUrl){ const h=document.getElementById(\"app-header\"); if(h) h.style.backgroundImage=`url('${dataUrl}')`; LS.set(KEY.headerBg, dataUrl); }\nfunction resetHeaderBg(){ const h=document.getElementById(\"app-header\"); if(h) h.style.backgroundImage=\"\"; LS.del(KEY.headerBg); }\nfunction loadAppearance(){\n  const defaultFeed = (WEB_ICONS.find(x=>x.id===\"eye_squ\") || WEB_ICONS&#91;0] || null);\n  const DEFAULT_ICONS = { feed: defaultFeed ? defaultFeed.dataUrl : \"https:\/\/via.placeholder.com\/40\" };\n\n  const ico = JSON.parse(LS.get(KEY.icons, \"null\") || \"null\");\n  if(ico &amp;&amp; typeof ico===\"object\") icons = { ...DEFAULT_ICONS, ...ico };\n  else icons = { ...DEFAULT_ICONS };\n\n  const hb = LS.get(KEY.headerBg, null); if(hb) applyHeaderBg(hb);\n  const fav = LS.get(KEY.favicon, null); if(fav) applyFavicon(fav); else applyFavicon(document.getElementById(\"favicon-svg\")?.href);\n\n  const me = getMe();\n  document.getElementById(\"header-avatar\").src = (me &amp;&amp; me.profile &amp;&amp; me.profile.avatar) ? me.profile.avatar : (getSelectedWebIcon()?.dataUrl || \"https:\/\/via.placeholder.com\/80\");\n}\n\n\/* ---------- Profile UI ---------- *\/\nfunction syncProfileUI(){\n  const me = getMe();\n  const isLoggedIn = !!me;\n\n  document.getElementById(\"header-email\").textContent = me ? me.email : \"\";\n  document.getElementById(\"header-username\").textContent = me ? (me.profile.username||\"\u672a\u8a2d\u5b9a\") : \"\u672a\u30ed\u30b0\u30a4\u30f3\";\n  document.getElementById(\"header-avatar\").src = me ? (me.profile.avatar||\"https:\/\/via.placeholder.com\/80\") : (getSelectedWebIcon()?.dataUrl || \"https:\/\/via.placeholder.com\/80\");\n\n  document.getElementById(\"profile-avatar\").src = me ? (me.profile.avatar||\"https:\/\/via.placeholder.com\/100\") : (getSelectedWebIcon()?.dataUrl || \"https:\/\/via.placeholder.com\/100\");\n  document.getElementById(\"inp-username\").value = me ? (me.profile.username||\"\") : \"\";\n  document.getElementById(\"inp-bio\").value = me ? (me.profile.bio||\"\") : \"\";\n  document.getElementById(\"preview-username\").textContent = me ? (me.profile.username||\"\u672a\u8a2d\u5b9a\") : \"\u672a\u30ed\u30b0\u30a4\u30f3\";\n  document.getElementById(\"preview-bio\").textContent = me ? (me.profile.bio||\"\u307e\u3060\u81ea\u5df1\u7d39\u4ecb\u304c\u3042\u308a\u307e\u305b\u3093\") : \"\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\";\n\n  document.getElementById(\"btn-logout\").style.display = isLoggedIn ? \"\" : \"none\";\n}\nfunction saveProfile(){\n  const me=getMe();\n  if(!me) return alert(\"\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\");\n  const name=(document.getElementById(\"inp-username\").value||\"\").trim();\n  const bio=(document.getElementById(\"inp-bio\").value||\"\").trim();\n  if(name.length>20) return alert(\"\u30e6\u30fc\u30b6\u30fc\u540d\u306f20\u6587\u5b57\u4ee5\u5185\u3067\u3059\");\n  me.profile.username=name||me.profile.username||\"User\";\n  me.profile.bio=bio;\n  saveUsers();\n  syncProfileUI();\n  syncStats();\n  renderTimeline();\n  rebuildVRWall();\n  alert(\"\u4fdd\u5b58\u3057\u307e\u3057\u305f\");\n}\nfunction syncStats(){\n  const me=getMe();\n  if(!me){\n    document.getElementById(\"stat-followers\").textContent=\"0\";\n    document.getElementById(\"stat-following\").textContent=\"0\";\n    document.getElementById(\"stat-posts\").textContent=\"0\";\n    return;\n  }\n  const followers=users.filter(u=>(u.following||&#91;]).includes(me.email)).length;\n  document.getElementById(\"stat-followers\").textContent=String(followers);\n  document.getElementById(\"stat-following\").textContent=String((me.following||&#91;]).length);\n  document.getElementById(\"stat-posts\").textContent=String(countMyPosts());\n}\n\n\/* ---------- Post URL ---------- *\/\nfunction postUrl(postId){\n  const base = location.href.split(\"#\")&#91;0];\n  return `${base}#post=${encodeURIComponent(postId)}`;\n}\nfunction setLatestPostUrl(postId){\n  const u = postUrl(postId);\n  LS.set(KEY.latest, u);\n  document.getElementById(\"latest-post-url\").textContent = u;\n}\nfunction loadLatestPostUrl(){\n  const u = LS.get(KEY.latest, \"\");\n  document.getElementById(\"latest-post-url\").textContent = u || \"(\u307e\u3060\u6295\u7a3f\u3057\u3066\u3044\u307e\u305b\u3093)\";\n}\nfunction highlightFromHash(scroll=true){\n  const h = location.hash || \"\";\n  const m = h.match(\/post=(&#91;^&amp;]+)\/);\n  if(!m) return;\n  const pid = decodeURIComponent(m&#91;1]);\n  const el = document.getElementById(\"post-\"+pid);\n  if(!el) return;\n  el.style.outline=\"3px solid rgba(91,95,230,.35)\";\n  el.style.boxShadow=\"0 0 0 6px rgba(91,95,230,.10)\";\n  if(scroll) el.scrollIntoView({behavior:\"smooth\", block:\"center\"});\n  setTimeout(()=>{ el.style.outline=\"\"; el.style.boxShadow=\"\"; }, 2200);\n}\n\n\/* ---------- Media attach ---------- *\/\nfunction refreshMediaPreview(){\n  const wrap=document.getElementById(\"media-preview\");\n  wrap.innerHTML=\"\";\n  if(pendingMedia.length===0) return;\n  pendingMedia.forEach((m,idx)=>{\n    const card=document.createElement(\"div\");\n    card.className=\"glass p-3 border border-slate-200\";\n    const top=document.createElement(\"div\");\n    top.className=\"flex items-center justify-between mb-2\";\n    top.innerHTML=`&lt;div class=\"text-xs font-semibold\">${escapeHtml(m.name)} &lt;span class=\"muted\">(${bytesToMB(m.size)}MB)&lt;\/span>&lt;\/div>\n                   &lt;button class=\"pill px-3 py-1 text-xs hover:bg-slate-50\">\u524a\u9664&lt;\/button>`;\n    top.querySelector(\"button\").onclick=()=>{ pendingMedia.splice(idx,1); refreshMediaPreview(); };\n    const body=document.createElement(\"div\");\n    if((m.type||\"\").startsWith(\"image\/\")) body.innerHTML=`&lt;img src=\"${m.dataUrl}\" class=\"w-full rounded-xl border border-slate-200 object-cover\" style=\"max-height:180px\">`;\n    else body.innerHTML=`&lt;video src=\"${m.dataUrl}\" controls class=\"w-full rounded-xl border border-slate-200\" style=\"max-height:180px\">&lt;\/video>`;\n    card.appendChild(top); card.appendChild(body); wrap.appendChild(card);\n  });\n}\nfunction clearMedia(){ pendingMedia=&#91;]; refreshMediaPreview(); }\n\n\/* =========================================================\n   RSS\uff08ON\/OFF\uff09\n========================================================= *\/\nfunction loadRss(){\n  let rss = JSON.parse(LS.get(KEY.rss, \"null\") || \"null\");\n  if(!rss){\n    rss = { feeds: PRESET_RSS.slice(), intervalSec: 120, enabled: false, lastRun: null };\n    LS.set(KEY.rss, JSON.stringify(rss));\n  }\n  if(!Array.isArray(rss.feeds)) rss.feeds = PRESET_RSS.slice();\n  rss.intervalSec = Math.max(10, Number(rss.intervalSec)||120);\n  rss.enabled = !!rss.enabled;\n  return rss;\n}\nfunction saveRss(rss){ LS.set(KEY.rss, JSON.stringify(rss)); }\nfunction loadSeen(){ let seen=JSON.parse(LS.get(KEY.rssSeen,\"{}\")||\"{}\"); if(!seen||typeof seen!==\"object\") seen={}; return seen; }\nfunction saveSeen(seen){ LS.set(KEY.rssSeen, JSON.stringify(seen)); }\n\nfunction setRssIndicator(running){\n  document.getElementById(\"rss-dot\").className = \"status-dot \" + (running ? \"on\":\"off\");\n  document.getElementById(\"rss-status-text\").textContent = running ? \"\u52d5\u4f5c\u4e2d\":\"\u505c\u6b62\u4e2d\";\n}\nfunction setToggleUI(enabled){\n  const t = document.getElementById(\"rss-toggle\");\n  t.classList.toggle(\"on\", !!enabled);\n  t.setAttribute(\"aria-checked\", enabled ? \"true\":\"false\");\n}\nfunction rssUI(){\n  const rss=loadRss();\n  document.getElementById(\"rss-interval\").value=rss.intervalSec||120;\n  const wrap=document.getElementById(\"rss-list\");\n  wrap.innerHTML=\"\";\n  (rss.feeds||&#91;]).forEach((u,i)=>{\n    const row=document.createElement(\"div\");\n    row.className=\"glass p-3 border border-slate-200 flex items-center gap-2\";\n    row.innerHTML=`&lt;div class=\"text-&#91;11px] flex-1 truncate\" title=\"${escapeHtml(u)}\">${escapeHtml(u)}&lt;\/div>\n                   &lt;button class=\"pill px-3 py-2 text-xs hover:bg-slate-50\" title=\"\u524a\u9664\">&lt;i class=\"fas fa-trash\">&lt;\/i>&lt;\/button>`;\n    row.querySelector(\"button\").onclick=()=>{\n      if(!confirm(\"\u3053\u306eRSS\u3092\u524a\u9664\u3057\u307e\u3059\u304b\uff1f\")) return;\n      const r=loadRss(); r.feeds.splice(i,1); saveRss(r); rssUI();\n      addLog(\"\u524a\u9664: \"+u,\"info\");\n    };\n    wrap.appendChild(row);\n  });\n  setToggleUI(rss.enabled);\n  setRssIndicator(!!rssTimer);\n}\nfunction setRssEnabled(enabled){\n  const rss = loadRss();\n  rss.enabled = !!enabled;\n  saveRss(rss);\n  setToggleUI(rss.enabled);\n  if(rss.enabled) startRss();\n  else stopRss(true);\n}\nfunction addRssFeed(){\n  const inp=document.getElementById(\"rss-url\");\n  const url=(inp.value||\"\").trim();\n  if(!\/^https?:\\\/\\\/.+\/i.test(url)) return addLog(\"\u6b63\u3057\u3044RSS URL\u3092\u5165\u308c\u3066\u304f\u3060\u3055\u3044\",\"err\");\n  const rss=loadRss();\n  if(rss.feeds.includes(url)) return addLog(\"\u3059\u3067\u306b\u767b\u9332\u6e08\u307f\u3067\u3059\",\"err\");\n  rss.feeds.push(url); saveRss(rss); inp.value=\"\";\n  rssUI();\n  addLog(\"\u8ffd\u52a0: \"+url,\"ok\");\n}\nfunction setRssInterval(){\n  const v=Number(document.getElementById(\"rss-interval\").value)||120;\n  if(v&lt;10) return alert(\"10\u79d2\u4ee5\u4e0a\u306b\u3057\u3066\u304f\u3060\u3055\u3044\");\n  const rss=loadRss(); rss.intervalSec=v; saveRss(rss);\n  addLog(\"\u9593\u9694\u8a2d\u5b9a: \"+v+\"\u79d2\",\"ok\");\n  if(rssTimer){ startRss(); }\n}\nfunction clearSeen(){\n  LS.del(KEY.rssSeen);\n  addLog(\"\u65e2\u8aad\u30ea\u30bb\u30c3\u30c8: ts_rss_seen \u3092\u524a\u9664\",\"ok\");\n}\n\n\/* RSS fetch *\/\nasync function fetchTextDirect(url){\n  const res = await fetch(url, { cache:\"no-store\" });\n  if(!res.ok) throw new Error(\"Direct HTTP \"+res.status);\n  return await res.text();\n}\nasync function fetchTextViaAllOriginsRaw(url){\n  const endpoint = \"https:\/\/api.allorigins.win\/raw?url=\"+encodeURIComponent(url);\n  const res=await fetch(endpoint,{cache:\"no-store\"});\n  if(!res.ok) throw new Error(\"AllOrigins HTTP \"+res.status);\n  return await res.text();\n}\nasync function fetchTextViaThingProxy(url){\n  const endpoint = \"https:\/\/thingproxy.freeboard.io\/fetch\/\"+url;\n  const res=await fetch(endpoint,{cache:\"no-store\"});\n  if(!res.ok) throw new Error(\"ThingProxy HTTP \"+res.status);\n  return await res.text();\n}\nasync function fetchJsonViaRss2Json(url){\n  const endpoint=\"https:\/\/api.rss2json.com\/v1\/api.json?rss_url=\"+encodeURIComponent(url);\n  const res=await fetch(endpoint,{cache:\"no-store\"});\n  if(!res.ok) throw new Error(\"rss2json HTTP \"+res.status);\n  const j=await res.json();\n  if(j &amp;&amp; j.status &amp;&amp; j.status!==\"ok\") throw new Error(\"rss2json status=\"+j.status+\" msg=\"+(j.message||\"\"));\n  return j;\n}\nfunction parseXmlToItems(xmlText){\n  const dp=new DOMParser();\n  const doc=dp.parseFromString(xmlText,\"text\/xml\");\n  if(doc.querySelector(\"parsererror\")) throw new Error(\"XML parse error\");\n  const nodes = Array.from(doc.querySelectorAll(\"item, entry\")).slice(0, 20);\n  const pickText = (n, sels) => {\n    for(const s of sels){\n      const el = n.querySelector(s);\n      if(el &amp;&amp; el.textContent) return el.textContent.trim();\n    }\n    return \"\";\n  };\n  return nodes.map(n=>{\n    const title = pickText(n, &#91;\"title\"]);\n    let link = \"\";\n    const linkEl = n.querySelector(\"link\");\n    if(linkEl){\n      link = (linkEl.getAttribute(\"href\")||\"\").trim() || (linkEl.textContent||\"\").trim();\n    }\n    if(!link){ link = pickText(n, &#91;\"id\"]); }\n    const pub = pickText(n, &#91;\"pubDate\",\"published\",\"updated\",\"dc\\\\:date\",\"date\"]);\n    return { title: title || \"(no title)\", link, pubDate: pub };\n  }).filter(x=>x.title || x.link);\n}\nfunction normalizeItems(items){\n  return (items||&#91;]).map(it=>{\n    const title=(it.title||\"\").trim();\n    const link=(it.link||\"\").trim();\n    const pubDate=(it.pubDate||it.pub||\"\").trim();\n    return { title:title||\"(no title)\", link, pubDate };\n  }).filter(it=>it.title || it.link);\n}\nasync function fetchFeedItems(feedUrl, debug=false){\n  const url = feedUrl.trim();\n  let lastErr = null;\n  try{\n    const xml = await fetchTextDirect(url);\n    const items = normalizeItems(parseXmlToItems(xml));\n    if(items.length){ if(debug) addLog(`\u53d6\u5f97\u6210\u529f(Direct): ${items.length}\u4ef6`,\"ok\"); return items; }\n  }catch(e){ lastErr=e; if(debug) addLog(\"Direct\u5931\u6557: \"+(e.message||e),\"err\"); }\n  try{\n    const xml = await fetchTextViaAllOriginsRaw(url);\n    const items = normalizeItems(parseXmlToItems(xml));\n    if(debug) addLog(`\u53d6\u5f97\u6210\u529f(AllOrigins): ${items.length}\u4ef6`,\"ok\");\n    return items;\n  }catch(e){ lastErr=e; if(debug) addLog(\"AllOrigins\u5931\u6557: \"+(e.message||e),\"err\"); }\n  try{\n    const xml = await fetchTextViaThingProxy(url);\n    const items = normalizeItems(parseXmlToItems(xml));\n    if(debug) addLog(`\u53d6\u5f97\u6210\u529f(ThingProxy): ${items.length}\u4ef6`,\"ok\");\n    return items;\n  }catch(e){ lastErr=e; if(debug) addLog(\"ThingProxy\u5931\u6557: \"+(e.message||e),\"err\"); }\n  try{\n    const j = await fetchJsonViaRss2Json(url);\n    const items = normalizeItems((j.items||&#91;]).map(x=>({ title:x.title, link:x.link, pubDate:x.pubDate })));\n    if(debug) addLog(`\u53d6\u5f97\u6210\u529f(rss2json): ${items.length}\u4ef6`,\"ok\");\n    return items;\n  }catch(e){ lastErr=e; if(debug) addLog(\"rss2json\u5931\u6557: \"+(e.message||e),\"err\"); }\n  throw lastErr || new Error(\"RSS fetch failed\");\n}\nfunction startRss(){\n  stopRss(false);\n  const rss = loadRss();\n  if(!rss.enabled){\n    rss.enabled = true; saveRss(rss); setToggleUI(true);\n  }\n  setRssIndicator(true);\n  addLog(\"RSS\u81ea\u52d5\u6295\u7a3f: ON\uff08\u5b9a\u671f\u53d6\u5f97\u958b\u59cb\uff09\",\"ok\");\n  fetchAllFeedsOnce(true);\n  rssTimer = setInterval(()=>{ fetchAllFeedsOnce(false); }, (rss.intervalSec||120)*1000);\n}\nfunction stopRss(writeState=true){\n  if(rssTimer){ clearInterval(rssTimer); rssTimer=null; }\n  setRssIndicator(false);\n  if(writeState){\n    const rss = loadRss();\n    rss.enabled = false;\n    saveRss(rss);\n    setToggleUI(false);\n    addLog(\"RSS\u81ea\u52d5\u6295\u7a3f: OFF\uff08\u505c\u6b62\uff09\",\"info\");\n  }\n}\nfunction createPost({text, type=\"user\", link=\"\", media=&#91;], authorEmail=null}){\n  const me=getMe();\n  if(type===\"user\" &amp;&amp; !me){\n    alert(\"\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\");\n    openAuth();\n    return null;\n  }\n  const post={\n    id: uid(),\n    text:(text||\"\").trim(),\n    type,\n    link,\n    media,\n    time: nowJp(),\n    authorEmail: authorEmail || (me ? me.email : \"anonymous@local\")\n  };\n  posts.unshift(post);\n  savePosts();\n  setLatestPostUrl(post.id);\n  syncStats();\n  renderTimeline();\n  return post;\n}\nasync function fetchAllFeedsOnce(verbose=false){\n  const rss = loadRss();\n  if(!rss.feeds || rss.feeds.length===0){\n    addLog(\"RSS\u304c\u672a\u767b\u9332\u3067\u3059\",\"err\");\n    return;\n  }\n  if(verbose) addLog(`\u53d6\u5f97\u958b\u59cb: feeds=${rss.feeds.length}`,\"info\");\n  const seen = loadSeen();\n  let newCount = 0;\n  for(const feedUrl of rss.feeds){\n    try{\n      const items = await fetchFeedItems(feedUrl, verbose);\n      for(const it of items.slice(0, 10)){\n        const link = safeUrl(it.link) || \"\";\n        const key = link || (feedUrl + \"::\" + (it.title||\"\"));\n        if(!key) continue;\n        if(seen&#91;key]) continue;\n        seen&#91;key]=true;\n        createPost({\n          text: it.title || \"(no title)\",\n          type: \"feed\",\n          link: link || \"\",\n          media: &#91;],\n          authorEmail: \"feed@local\"\n        });\n        newCount++;\n        if(newCount >= 20) break;\n      }\n    }catch(e){\n      addLog(`\u5931\u6557: ${feedUrl} \/ ${(e.message||e)}`,\"err\");\n    }\n    if(newCount >= 20) break;\n  }\n  saveSeen(seen);\n  if(verbose || newCount>0){\n    addLog(`\u53d6\u5f97\u5b8c\u4e86: \u65b0\u898f ${newCount}\u4ef6` , newCount ? \"ok\" : \"info\");\n  }\n  const rr = loadRss();\n  rr.lastRun = Date.now();\n  saveRss(rr);\n}\nasync function rssTestOne(){\n  const url=(document.getElementById(\"rss-url\").value||\"\").trim();\n  const target=url || (loadRss().feeds&#91;0]||\"\");\n  if(!target) return alert(\"\u30c6\u30b9\u30c8\u3059\u308bURL\u304c\u3042\u308a\u307e\u305b\u3093\");\n  addLog(\"URL\u691c\u67fb: \"+target,\"info\");\n  try{\n    const items = await fetchFeedItems(target, true);\n    addLog(`\u691c\u67fbOK: items=${items.length}\uff08\u5148\u982d: ${items&#91;0]?items&#91;0].title:\"\u306a\u3057\"}\uff09`, items.length? \"ok\":\"info\");\n  }catch(e){\n    addLog(\"\u691c\u67fb\u5931\u6557: \"+(e.message||e),\"err\");\n    alert(\"RSS\u691c\u67fb\u3067\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u30ed\u30b0\u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002\");\n  }\n}\n\n\/* =========================================================\n   Timeline rendering\n========================================================= *\/\nfunction badgeForType(p){\n  if(p.type===\"feed\") return `&lt;span class=\"badge\">&lt;i class=\"fas fa-rss\">&lt;\/i>FEED&lt;\/span>`;\n  return `&lt;span class=\"badge\">&lt;i class=\"fas fa-user\">&lt;\/i>\u30e6\u30fc\u30b6\u30fc&lt;\/span>`;\n}\nfunction iconForPost(p){\n  if(p.type===\"feed\") return icons.feed;\n  const u=users.find(x=>x.email===p.authorEmail);\n  return (u &amp;&amp; u.profile &amp;&amp; u.profile.avatar) ? u.profile.avatar : (getSelectedWebIcon()?.dataUrl || \"https:\/\/via.placeholder.com\/40\");\n}\nfunction usernameForPost(p){\n  if(p.type===\"feed\") return \"Feed\";\n  const u=users.find(x=>x.email===p.authorEmail);\n  return (u &amp;&amp; u.profile &amp;&amp; u.profile.username) ? u.profile.username : \"Unknown\";\n}\nfunction visibleInHome(p){\n  if(homeMode!==\"home\") return true;\n  const me=getMe();\n  if(!me){ return true; }\n  if((me.blocked||&#91;]).includes(p.authorEmail)) return false;\n  if((me.muted||&#91;]).includes(p.authorEmail)) return false;\n  if(p.type===\"feed\") return true;\n  if(p.authorEmail===me.email) return true;\n  return (me.following||&#91;]).includes(p.authorEmail);\n}\nfunction filterPosts(){\n  let arr=posts.slice();\n  arr=arr.filter(p=>visibleInHome(p));\n  arr=arr.filter(p=>{\n    if(currentFilter===\"all\") return true;\n    if(currentFilter===\"user\") return p.type===\"user\";\n    if(currentFilter===\"feed\") return p.type===\"feed\";\n    return true;\n  });\n  if(searchQuery.trim()){\n    const q=searchQuery.trim().toLowerCase();\n    arr=arr.filter(p=>((p.text||\"\")+\" \"+usernameForPost(p)).toLowerCase().includes(q));\n  }\n  return arr;\n}\nasync function copyArticleUrl(pid){\n  const p=posts.find(x=>String(x.id)===String(pid));\n  const u=p?safeUrl(p.link):\"\";\n  if(!u) return alert(\"\u8a18\u4e8bURL\u304c\u3042\u308a\u307e\u305b\u3093\");\n  await copyText(u);\n  alert(\"\u8a18\u4e8bURL\u3092\u30b3\u30d4\u30fc\u3057\u307e\u3057\u305f\");\n}\nfunction openArticleUrl(pid){\n  const p=posts.find(x=>String(x.id)===String(pid));\n  const u=p?safeUrl(p.link):\"\";\n  if(!u) return alert(\"\u8a18\u4e8bURL\u304c\u3042\u308a\u307e\u305b\u3093\");\n  openPopup(u);\n}\nfunction renderTimeline(){\n  const tl=document.getElementById(\"timeline\");\n  const emp=document.getElementById(\"empty\");\n  const cnt=document.getElementById(\"post-count\");\n  const arr=filterPosts();\n  cnt.textContent=`(${arr.length}\u4ef6)`;\n  if(arr.length===0){ tl.innerHTML=\"\"; emp.style.display=\"\"; return; }\n  emp.style.display=\"none\";\n  tl.innerHTML = arr.map(p=>{\n    const pid=String(p.id);\n    const myPostUrl=postUrl(pid);\n    const articleUrl=safeUrl(p.link);\n    const mainText = articleUrl\n      ? `&lt;a href=\"${escapeHtml(articleUrl)}\" target=\"_blank\" rel=\"noopener\" class=\"text-sky-600 underline\">${escapeHtml(p.text)}&lt;\/a>`\n      : escapeHtml(p.text);\n    const articleRow = articleUrl ? `\n      &lt;div class=\"mt-3 glass p-3 border border-slate-200\">\n        &lt;div class=\"flex items-center justify-between gap-2\">\n          &lt;div class=\"text-xs font-semibold flex items-center gap-2\">\n            &lt;i class=\"fas fa-link text-amber-600\">&lt;\/i> \u8a18\u4e8bURL\n          &lt;\/div>\n          &lt;div class=\"flex items-center gap-2\">\n            &lt;button class=\"pill px-3 py-2 text-xs hover:bg-slate-50\" onclick=\"copyArticleUrl('${escapeHtml(pid)}')\">\n              &lt;i class=\"fas fa-copy mr-2\">&lt;\/i>\u30b3\u30d4\u30fc\n            &lt;\/button>\n            &lt;button class=\"pill px-3 py-2 text-xs hover:bg-slate-50\" onclick=\"openArticleUrl('${escapeHtml(pid)}')\">\n              &lt;i class=\"fas fa-arrow-up-right-from-square mr-2\">&lt;\/i>\u958b\u304f\n            &lt;\/button>\n          &lt;\/div>\n        &lt;\/div>\n        &lt;div class=\"mt-2 text-xs muted break-all\">${escapeHtml(articleUrl)}&lt;\/div>\n      &lt;\/div>\n    ` : \"\";\n    const mediaHtml=(p.media &amp;&amp; p.media.length) ? `\n      &lt;div class=\"grid grid-cols-2 md:grid-cols-3 gap-3 mt-3\">\n        ${p.media.map(m=>{\n          if((m.type||\"\").startsWith(\"image\/\")) return `&lt;img src=\"${m.dataUrl}\" class=\"w-full rounded-xl border border-slate-200 object-cover\" style=\"max-height:220px\">`;\n          return `&lt;video src=\"${m.dataUrl}\" controls class=\"w-full rounded-xl border border-slate-200\" style=\"max-height:220px\">&lt;\/video>`;\n        }).join(\"\")}\n      &lt;\/div>` : \"\";\n    return `\n      &lt;article class=\"timeline-post p-6\" id=\"post-${escapeHtml(pid)}\" data-postid=\"${escapeHtml(pid)}\">\n        &lt;div class=\"flex items-start justify-between gap-3\">\n          &lt;div class=\"flex items-center gap-3\">\n            &lt;img src=\"${escapeHtml(iconForPost(p))}\" class=\"mini-avatar\" onerror=\"this.src='https:\/\/via.placeholder.com\/40'\">\n            &lt;div>\n              &lt;div class=\"flex items-center gap-2\">\n                &lt;div class=\"font-semibold\">${escapeHtml(usernameForPost(p))}&lt;\/div>\n                ${badgeForType(p)}\n              &lt;\/div>\n              &lt;div class=\"text-xs muted\">${escapeHtml(p.time)}&lt;\/div>\n            &lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n        &lt;div class=\"mt-3 text-sm leading-relaxed\">${mainText}&lt;\/div>\n        ${articleRow}\n        ${mediaHtml}\n        &lt;div class=\"mt-3 text-xs muted break-all\">\n          &lt;i class=\"fas fa-link mr-2 text-indigo-600\">&lt;\/i>\u6295\u7a3fURL: ${escapeHtml(myPostUrl)}\n        &lt;\/div>\n      &lt;\/article>\n    `;\n  }).join(\"\");\n  highlightFromHash(false);\n}\n\n\/* ---------- Export\/Import\/Clear ---------- *\/\nfunction deleteAllPosts(){\n  if(!confirm(\"\u5168\u6295\u7a3f\u3092\u524a\u9664\u3057\u307e\u3059\u304b\uff1f\")) return;\n  posts=&#91;]; savePosts(); syncStats(); renderTimeline(); rebuildVRWall();\n}\nfunction exportData(){\n  const payload = {\n    users, posts, icons,\n    headerBg: LS.get(KEY.headerBg, null),\n    favicon: LS.get(KEY.favicon, null),\n    rss: JSON.parse(LS.get(KEY.rss, \"null\") || \"null\"),\n    session: JSON.parse(LS.get(KEY.session, \"null\") || \"null\"),\n    current: JSON.parse(LS.get(KEY.current, \"null\") || \"null\"),\n    webIconSelected: JSON.parse(LS.get(KEY.webIconSelected, \"null\") || \"null\")\n  };\n  const blob=new Blob(&#91;JSON.stringify(payload,null,2)],{type:\"application\/json\"});\n  const url=URL.createObjectURL(blob);\n  const a=document.createElement(\"a\");\n  a.href=url; a.download=\"tsumugi_backup.json\"; a.click();\n  URL.revokeObjectURL(url);\n}\nfunction importData(){\n  const input=document.createElement(\"input\");\n  input.type=\"file\"; input.accept=\"application\/json\";\n  input.onchange=()=>{\n    const f=input.files &amp;&amp; input.files&#91;0]; if(!f) return;\n    const r=new FileReader();\n    r.onload=()=>{\n      try{\n        const obj=JSON.parse(String(r.result||\"{}\"));\n        if(obj.users){ users=obj.users; saveUsers(); }\n        if(obj.posts){ posts=obj.posts; savePosts(); }\n        if(obj.icons){ icons=obj.icons; LS.set(KEY.icons, JSON.stringify(icons)); }\n        if(obj.headerBg){ applyHeaderBg(obj.headerBg); }\n        if(obj.favicon){ applyFavicon(obj.favicon); }\n        if(obj.rss){ LS.set(KEY.rss, JSON.stringify(obj.rss)); }\n        if(obj.session){ LS.set(KEY.session, JSON.stringify(obj.session)); }\n        if(obj.current){ LS.set(KEY.current, JSON.stringify(obj.current)); }\n        if(obj.webIconSelected){ LS.set(KEY.webIconSelected, JSON.stringify(obj.webIconSelected)); }\n        alert(\"\u8aad\u307f\u8fbc\u307f\u5b8c\u4e86\");\n        init();\n      }catch(e){\n        alert(\"\u8aad\u307f\u8fbc\u307f\u5931\u6557: \"+(e.message||e));\n      }\n    };\n    r.readAsText(f);\n  };\n  input.click();\n}\nfunction clearAllCache(){\n  if(!confirm(\"Tsumugi\u306e\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u3092\u5168\u6d88\u53bb\u3057\u307e\u3059\u3002\u3088\u308d\u3057\u3044\u3067\u3059\u304b\uff1f\")) return;\n  Object.keys(localStorage).forEach(k=>{ if(k.startsWith(\"ts_\")) localStorage.removeItem(k); });\n  alert(\"\u5168\u6d88\u53bb\u3057\u307e\u3057\u305f\u3002\u518d\u8aad\u307f\u8fbc\u307f\u3057\u307e\u3059\u3002\");\n  location.reload();\n}\n\n\/* =========================================================\n   VR\u8a66\u9a13\n========================================================= *\/\nfunction openVR(){\n  document.getElementById(\"vr-overlay\").style.display=\"block\";\n  rebuildVRWall();\n}\nfunction closeVR(){\n  document.getElementById(\"vr-overlay\").style.display=\"none\";\n}\nfunction rebuildVRWall(){\n  const wall = document.getElementById(\"vr-wall\");\n  if(!wall) return;\n  wall.innerHTML=\"\";\n  const arr = filterPosts().slice(0, 10);\n  const startX = -3.6, startY = 2.2, gapX = 2.5, gapY = 1.35, cols = 3;\n  arr.forEach((p, idx)=>{\n    const c = idx % cols;\n    const r = Math.floor(idx \/ cols);\n    const x = startX + c*gapX;\n    const y = startY - r*gapY;\n    const z = -4.0;\n    const title = (p.text||\"\").slice(0, 46);\n    const link = safeUrl(p.link);\n    const line2 = link ? \"URL: \"+link.slice(0, 40) : \"\";\n    const text = `${usernameForPost(p)}\\n${title}${line2?(\"\\n\"+line2):\"\"}`;\n    const card = document.createElement(\"a-entity\");\n    card.setAttribute(\"position\", `${x} ${y} ${z}`);\n    const plane = document.createElement(\"a-plane\");\n    plane.setAttribute(\"width\",\"2.3\");\n    plane.setAttribute(\"height\",\"1.2\");\n    plane.setAttribute(\"color\",\"#111827\");\n    plane.setAttribute(\"opacity\",\"0.88\");\n    plane.setAttribute(\"material\",\"side:double\");\n    card.appendChild(plane);\n    const txt = document.createElement(\"a-text\");\n    txt.setAttribute(\"value\", text);\n    txt.setAttribute(\"color\",\"#e5e7eb\");\n    txt.setAttribute(\"width\",\"2.2\");\n    txt.setAttribute(\"position\",\"-1.05 0.42 0.01\");\n    txt.setAttribute(\"baseline\",\"top\");\n    txt.setAttribute(\"wrap-count\",\"28\");\n    card.appendChild(txt);\n    if(link){\n      card.addEventListener(\"click\", ()=>{\n        closeVR();\n        openPopup(link);\n      });\n    }\n    wall.appendChild(card);\n  });\n}\n\n\/* =========================================================\n   Events \/ Init\n========================================================= *\/\nfunction wireEvents(){\n  document.getElementById(\"btn-vr\").onclick = ()=>openVR();\n  document.getElementById(\"btn-clear-all\").onclick = ()=>clearAllCache();\n\n  document.getElementById(\"btn-auth-open\").onclick = ()=>openAuth();\n  document.getElementById(\"btn-logout\").onclick = ()=>doLogout();\n\n  \/\/ Auth overlay\n  document.getElementById(\"btn-auth-close\").onclick = ()=>closeAuth();\n  document.getElementById(\"tab-login\").onclick = ()=>setAuthTab(\"login\");\n  document.getElementById(\"tab-register\").onclick = ()=>setAuthTab(\"register\");\n  document.getElementById(\"btn-register\").onclick = ()=>doRegister();\n  document.getElementById(\"btn-login\").onclick = ()=>doLogin();\n  document.getElementById(\"btn-guest\").onclick = ()=>{\n    loadUsers();\n    ensureGuest();\n    saveSession(\"guest@local\", true);\n    closeAuth();\n    afterAuthBootstrap();\n  };\n\n  \/\/ VR\n  document.getElementById(\"btn-vr-exit\").onclick = ()=>closeVR();\n  document.getElementById(\"btn-vr-enter\").onclick = ()=>{\n    rebuildVRWall();\n    alert(\"VR\u8a66\u9a13: \u30de\u30a6\u30b9\u3067\u8996\u70b9\u3001WASD\u79fb\u52d5\uff08\u8a18\u4e8b\u30ab\u30fc\u30c9\u306f\u30af\u30ea\u30c3\u30af\u3067\u8a18\u4e8b\u3092\u958b\u304d\u307e\u3059\uff09\");\n  };\n\n  \/\/ profile\n  document.getElementById(\"btn-save-profile\").onclick = ()=>saveProfile();\n  document.getElementById(\"btn-change-profile\").onclick = ()=>document.getElementById(\"file-profile\").click();\n  document.getElementById(\"file-profile\").onchange = (e)=>{\n    const me=getMe();\n    if(!me) return alert(\"\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\");\n    const f=e.target.files &amp;&amp; e.target.files&#91;0]; if(!f) return;\n    const r=new FileReader();\n    r.onload=()=>{\n      me.profile.avatar=String(r.result||\"\");\n      saveUsers();\n      syncProfileUI();\n      renderTimeline();\n      rebuildVRWall();\n    };\n    r.readAsDataURL(f);\n  };\n\n  \/\/ headerbg \/ favicon \/ feed icon\uff08\u30d5\u30a1\u30a4\u30eb\uff09\n  document.getElementById(\"btn-change-headerbg\").onclick = ()=>document.getElementById(\"file-headerbg\").click();\n  document.getElementById(\"file-headerbg\").onchange = (e)=>{\n    const f=e.target.files &amp;&amp; e.target.files&#91;0]; if(!f) return;\n    const r=new FileReader();\n    r.onload=()=>applyHeaderBg(String(r.result||\"\"));\n    r.readAsDataURL(f);\n  };\n  document.getElementById(\"btn-reset-headerbg\").onclick = ()=>resetHeaderBg();\n\n  document.getElementById(\"btn-change-appicon\").onclick = ()=>document.getElementById(\"file-appicon\").click();\n  document.getElementById(\"file-appicon\").onchange = (e)=>{\n    const f=e.target.files &amp;&amp; e.target.files&#91;0]; if(!f) return;\n    const r=new FileReader();\n    r.onload=()=>applyFavicon(String(r.result||\"\"));\n    r.readAsDataURL(f);\n  };\n  document.getElementById(\"btn-reset-appicon\").onclick = ()=>resetFavicon();\n\n  document.getElementById(\"btn-change-feedicon\").onclick = ()=>document.getElementById(\"file-feedicon\").click();\n  document.getElementById(\"file-feedicon\").onchange = (e)=>{\n    const f=e.target.files &amp;&amp; e.target.files&#91;0]; if(!f) return;\n    const r=new FileReader();\n    r.onload=()=>{\n      icons.feed=String(r.result||icons.feed);\n      LS.set(KEY.icons, JSON.stringify(icons));\n      renderTimeline();\n      rebuildVRWall();\n    };\n    r.readAsDataURL(f);\n  };\n\n  \/\/ WEB\u30a2\u30a4\u30b3\u30f3\u9069\u7528\n  document.getElementById(\"btn-apply-favicon\").onclick = ()=>applySelectedToFavicon();\n  document.getElementById(\"btn-apply-feed\").onclick = ()=>applySelectedToFeed();\n  document.getElementById(\"btn-apply-profile\").onclick = ()=>applySelectedToProfile();\n  document.getElementById(\"btn-icon-random\").onclick = ()=>randomPickIcon();\n\n  \/\/ RSS toggle\n  const toggle = document.getElementById(\"rss-toggle\");\n  const toggleFlip = ()=>{\n    const rss = loadRss();\n    setRssEnabled(!rss.enabled);\n    rssUI();\n  };\n  toggle.onclick = toggleFlip;\n  toggle.onkeydown = (e)=>{ if(e.key===\"Enter\" || e.key===\" \"){ e.preventDefault(); toggleFlip(); } };\n\n  \/\/ RSS buttons\n  document.getElementById(\"btn-add-rss\").onclick = ()=>addRssFeed();\n  document.getElementById(\"btn-fetch-rss\").onclick = ()=>fetchAllFeedsOnce(true);\n  document.getElementById(\"btn-set-rss-interval\").onclick = ()=>setRssInterval();\n  document.getElementById(\"btn-rss-clear-seen\").onclick = ()=>clearSeen();\n  document.getElementById(\"btn-rss-test-one\").onclick = ()=>rssTestOne();\n\n  \/\/ posting\n  const postText=document.getElementById(\"post-text\");\n  postText.addEventListener(\"input\", ()=>{ document.getElementById(\"char-count\").textContent=`(${postText.value.length}\/600)`; });\n  postText.addEventListener(\"keydown\", (e)=>{ if(e.ctrlKey &amp;&amp; e.key===\"Enter\"){ document.getElementById(\"btn-post\").click(); } });\n\n  document.getElementById(\"btn-post\").onclick = ()=>{\n    const t=(postText.value||\"\").trim();\n    if(!t &amp;&amp; pendingMedia.length===0) return alert(\"\u672c\u6587\u304b\u6dfb\u4ed8\u304c\u5fc5\u8981\u3067\u3059\");\n    const p = createPost({ text:t||\"(\u6dfb\u4ed8)\", type:\"user\", media: pendingMedia.slice() });\n    if(!p) return;\n    postText.value=\"\"; document.getElementById(\"char-count\").textContent=\"(0\/600)\";\n    clearMedia();\n  };\n  document.getElementById(\"btn-delete-all-posts\").onclick = ()=>deleteAllPosts();\n\n  \/\/ media\n  document.getElementById(\"btn-add-media\").onclick = ()=>document.getElementById(\"file-media\").click();\n  document.getElementById(\"file-media\").onchange = async (e)=>{\n    const me=getMe();\n    if(!me){ alert(\"\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\"); openAuth(); e.target.value=\"\"; return; }\n    const files = Array.from(e.target.files||&#91;]);\n    if(!files.length) return;\n    for(const f of files){\n      const dataUrl = await new Promise((res,rej)=>{\n        const r=new FileReader();\n        r.onload=()=>res(String(r.result||\"\"));\n        r.onerror=()=>rej(new Error(\"file read error\"));\n        r.readAsDataURL(f);\n      });\n      pendingMedia.push({ name:f.name, type:f.type||\"application\/octet-stream\", size:f.size||0, dataUrl });\n      if(pendingMedia.length>9){ pendingMedia=pendingMedia.slice(0,9); break; }\n    }\n    refreshMediaPreview();\n    e.target.value=\"\";\n  };\n  document.getElementById(\"btn-clear-media\").onclick = ()=>clearMedia();\n\n  \/\/ latest post url\n  document.getElementById(\"btn-copy-post-url\").onclick = async ()=>{\n    const u=document.getElementById(\"latest-post-url\").textContent.trim();\n    if(!u || u.startsWith(\"(\")) return alert(\"\u307e\u3060\u6295\u7a3fURL\u304c\u3042\u308a\u307e\u305b\u3093\");\n    await copyText(u); alert(\"\u6295\u7a3fURL\u3092\u30b3\u30d4\u30fc\u3057\u307e\u3057\u305f\");\n  };\n  document.getElementById(\"btn-open-post-url\").onclick = ()=>{\n    const u=document.getElementById(\"latest-post-url\").textContent.trim();\n    if(!u || u.startsWith(\"(\")) return alert(\"\u307e\u3060\u6295\u7a3fURL\u304c\u3042\u308a\u307e\u305b\u3093\");\n    location.href = u;\n  };\n\n  \/\/ timeline filter\/search\n  document.querySelectorAll(\"&#91;data-filter]\").forEach(btn=>{\n    btn.onclick = ()=>{\n      currentFilter = btn.getAttribute(\"data-filter\") || \"all\";\n      document.querySelectorAll(\"&#91;data-filter]\").forEach(b=>{\n        b.classList.remove(\"btn-primary\"); b.classList.add(\"pill\",\"hover:bg-slate-50\");\n      });\n      btn.classList.remove(\"pill\",\"hover:bg-slate-50\");\n      btn.classList.add(\"btn-primary\");\n      renderTimeline();\n      rebuildVRWall();\n    };\n  });\n  document.getElementById(\"search\").addEventListener(\"input\",(e)=>{\n    searchQuery = e.target.value || \"\";\n    renderTimeline();\n    rebuildVRWall();\n  });\n  document.getElementById(\"btn-refresh\").onclick = ()=>{ renderTimeline(); rebuildVRWall(); };\n  document.getElementById(\"btn-export\").onclick = ()=>exportData();\n  document.getElementById(\"btn-import\").onclick = ()=>importData();\n\n  \/\/ home\/explore\n  document.getElementById(\"btn-home\").onclick = ()=>{ homeMode=\"home\"; renderTimeline(); rebuildVRWall(); };\n  document.getElementById(\"btn-explore\").onclick = ()=>{ homeMode=\"explore\"; renderTimeline(); rebuildVRWall(); };\n}\n\n\/* \u30ed\u30b0\u30a4\u30f3\u5f8c\u306bUI\u3092\u518d\u540c\u671f\u3059\u308b *\/\nfunction afterAuthBootstrap(){\n  lockAppForAuth(false);\n  loadAppearance();\n  syncProfileUI();\n  syncStats();\n  renderTimeline();\n  loadLatestPostUrl();\n  rssUI();\n  rebuildVRWall();\n\n  renderWebIconGrid();\n\n  const rss = loadRss();\n  if(rss.enabled){\n    startRss();\n  }else{\n    stopRss(false);\n    setToggleUI(false);\n    setRssIndicator(false);\n  }\n}\n\n\/* \u8d77\u52d5 *\/\nfunction init(){\n  buildWebIconPack();\n  loadSelectedWebIcon();\n\n  loadUsers();\n  loadPosts();\n\n  const loggedIn = restoreAuthState();\n\n  loadAppearance();\n  renderWebIconGrid();\n\n  syncProfileUI();\n  syncStats();\n  renderTimeline();\n  loadLatestPostUrl();\n  rssUI();\n  highlightFromHash(false);\n  rebuildVRWall();\n\n  if(!loggedIn){\n    lockAppForAuth(true);\n    openAuth();\n    setAuthTab(\"login\");\n  }else{\n    lockAppForAuth(false);\n    const rss = loadRss();\n    if(rss.enabled) startRss();\n    else{\n      stopRss(false);\n      setToggleUI(false);\n      setRssIndicator(false);\n    }\n  }\n}\n\n\/* boot *\/\nwireEvents();\ninit();\nwindow.addEventListener(\"hashchange\", ()=>highlightFromHash(true));\n&lt;\/script>\n\n&lt;\/body>\n&lt;\/html><\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[120,6],"tags":[160],"class_list":["post-26357","post","type-post","status-publish","format-standard","hentry","category-rss","category-web","tag-rss"],"aioseo_notices":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/26357","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=26357"}],"version-history":[{"count":1,"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/26357\/revisions"}],"predecessor-version":[{"id":26358,"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/26357\/revisions\/26358"}],"wp:attachment":[{"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=26357"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=26357"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=26357"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}