@arcblock/did-connect-service 4.1.43 → 4.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/_generated/asset-bytes.js +1 -1
  2. package/dist/_generated/asset-manifest.js +3 -3
  3. package/dist/assets/{admin-core.d42d34ce.js → admin-core.6e3d6ce9.js} +22 -19
  4. package/dist/assets/{admin-extra.da2c9785.js → admin-extra.82aff2c7.js} +1 -1
  5. package/dist/assets/{admin.d99b416e.css → admin.bd6c819b.css} +21 -26
  6. package/dist/handlers/auth-handler.d.ts.map +1 -1
  7. package/dist/handlers/auth-handler.js +2 -0
  8. package/dist/handlers/auth-handler.js.map +1 -1
  9. package/dist/handlers/did-connect-handler.d.ts.map +1 -1
  10. package/dist/handlers/did-connect-handler.js +2 -0
  11. package/dist/handlers/did-connect-handler.js.map +1 -1
  12. package/dist/handlers/email-login-handler.d.ts.map +1 -1
  13. package/dist/handlers/email-login-handler.js +1 -0
  14. package/dist/handlers/email-login-handler.js.map +1 -1
  15. package/dist/handlers/oauth-handler.d.ts.map +1 -1
  16. package/dist/handlers/oauth-handler.js +4 -0
  17. package/dist/handlers/oauth-handler.js.map +1 -1
  18. package/dist/handlers/passkey-handler.d.ts.map +1 -1
  19. package/dist/handlers/passkey-handler.js +6 -2
  20. package/dist/handlers/passkey-handler.js.map +1 -1
  21. package/dist/handlers/team-handler.d.ts.map +1 -1
  22. package/dist/handlers/team-handler.js +1 -0
  23. package/dist/handlers/team-handler.js.map +1 -1
  24. package/dist/pages/admin/i18n.d.ts.map +1 -1
  25. package/dist/pages/admin/i18n.js +4 -16
  26. package/dist/pages/admin/i18n.js.map +1 -1
  27. package/dist/pages/admin/index.js +30 -30
  28. package/dist/pages/admin/index.js.map +1 -1
  29. package/dist/pages/admin/scripts/utils.d.ts +1 -1
  30. package/dist/pages/admin/scripts/utils.d.ts.map +1 -1
  31. package/dist/pages/admin/scripts/utils.js +21 -0
  32. package/dist/pages/admin/scripts/utils.js.map +1 -1
  33. package/dist/pages/admin/styles.d.ts +1 -1
  34. package/dist/pages/admin/styles.d.ts.map +1 -1
  35. package/dist/pages/admin/styles.js +10 -29
  36. package/dist/pages/admin/styles.js.map +1 -1
  37. package/dist/pages/admin/tab-members.d.ts +1 -1
  38. package/dist/pages/admin/tab-members.d.ts.map +1 -1
  39. package/dist/pages/admin/tab-members.js +1 -1
  40. package/dist/pages/admin/tab-profile-accounts.d.ts.map +1 -1
  41. package/dist/pages/admin/tab-profile-accounts.js +1 -19
  42. package/dist/pages/admin/tab-profile-accounts.js.map +1 -1
  43. package/dist/pages/homepage.d.ts.map +1 -1
  44. package/dist/pages/homepage.js +8 -2
  45. package/dist/pages/homepage.js.map +1 -1
  46. package/dist/store/d1-store.d.ts +12 -1
  47. package/dist/store/d1-store.d.ts.map +1 -1
  48. package/dist/store/d1-store.js +69 -12
  49. package/dist/store/d1-store.js.map +1 -1
  50. package/dist/theme-utils.d.ts +18 -0
  51. package/dist/theme-utils.d.ts.map +1 -1
  52. package/dist/theme-utils.js +35 -0
  53. package/dist/theme-utils.js.map +1 -1
  54. package/dist/types.d.ts +2 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/migrations/0022_connected_account_last_login_domain.sql +6 -0
  57. package/package.json +3 -3
@@ -5,5 +5,5 @@
5
5
  * Includes shared component styles formerly from shared-styles.ts
6
6
  * (body, h1, .input, .btn, .btn-secondary, .btn-danger, .badge variants).
7
7
  */
8
- export declare const ADMIN_STYLES = "\n .hidden { display: none !important; }\n\n /* \u2500\u2500\u2500 Shared component styles (extracted from shared-styles.ts) \u2500\u2500\u2500\u2500\u2500 */\n h1 {\n font-size: 24px;\n font-weight: 600;\n margin-bottom: 8px;\n color: var(--text-white);\n letter-spacing: -0.01em;\n line-height: 1.25;\n }\n .input {\n width: 100%;\n height: 40px;\n padding: 0 14px;\n font-size: 14px;\n color: var(--text);\n background: var(--bg-input);\n border: 1px solid var(--border-strong);\n border-radius: var(--radius-sm);\n outline: none;\n margin-bottom: 0;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n }\n textarea.input { height: auto; padding: 10px 14px; }\n .input:focus { border-color: var(--blue); box-shadow: var(--shadow-focus); }\n .input::placeholder { color: var(--text-placeholder); }\n .btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 100%;\n height: 36px;\n padding: 0 16px;\n font-size: 14px;\n font-weight: 500;\n color: #fff;\n background: var(--blue);\n border: none;\n border-radius: var(--radius-sm);\n cursor: pointer;\n transition: background-color 0.15s ease;\n line-height: 1;\n }\n .btn:hover { background: var(--blue-hover); }\n .btn:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }\n .btn:focus-visible { box-shadow: var(--shadow-focus); }\n /* In-flight submit (openDialog): keep the label readable and show a spinner. */\n .btn.btn-loading { opacity: 0.8; }\n .btn.btn-loading::before {\n content: \"\";\n flex: none;\n width: 14px; height: 14px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: spin 0.6s linear infinite;\n }\n .btn svg { width: 18px; height: 18px; }\n .btn-secondary {\n background: transparent;\n border: 1px solid var(--border-strong);\n color: var(--text);\n }\n .btn-secondary:hover { background: var(--bg-hover); border-color: rgba(255,255,255,0.20); }\n .btn-danger { background: var(--red); }\n .btn-danger:hover { background: #c22a2a; }\n .badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: var(--radius-full);\n font-size: 11px;\n font-weight: 500;\n line-height: 1.45;\n white-space: nowrap;\n }\n .badge-owner { background: var(--blue-light); color: var(--blue-muted); }\n .badge-admin { background: var(--info-light); color: var(--info-text); }\n .badge-member { background: var(--green-light); color: var(--green-text); }\n .badge-guest { background: var(--yellow-light); color: var(--yellow-text); }\n .badge-verified { background: var(--green-light); color: var(--green-text); }\n .badge-unverified { background: var(--yellow-light); color: var(--yellow-text); }\n\n /* \u2500\u2500\u2500 Admin layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n body {\n display: block;\n min-height: 100vh;\n }\n\n /* \u2500\u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .admin-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 24px;\n height: 56px;\n border-bottom: 1px solid var(--border);\n background: var(--bg-surface);\n }\n .admin-header-left {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n .admin-header-left a {\n font-size: 13px;\n transition: color 0.15s ease;\n }\n .admin-header-left .brand-home {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n color: inherit;\n text-decoration: none;\n }\n .admin-header-left .brand-home:hover {\n text-decoration: none;\n }\n .admin-header-left .brand-logo {\n height: 28px;\n width: auto;\n border-radius: 4px;\n flex-shrink: 0;\n }\n .admin-header-left .app-name-link {\n color: var(--text);\n }\n .admin-header-left .brand-home:hover .app-name-link {\n color: var(--text);\n text-decoration: none;\n }\n .admin-header h1 {\n font-size: 15px;\n font-weight: 600;\n margin: 0;\n letter-spacing: -0.006em;\n }\n .admin-header-right {\n position: relative;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n /* \u2500\u2500\u2500 Toolbar buttons (ghost style) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .toolbar-wrapper {\n position: relative;\n }\n .toolbar-btn {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: var(--radius-sm);\n color: var(--text-tertiary);\n cursor: pointer;\n transition: background-color 0.15s ease, color 0.15s ease;\n -webkit-tap-highlight-color: transparent;\n }\n .toolbar-btn:hover { background: var(--bg-hover); color: var(--text); }\n .toolbar-btn:active { background: var(--bg-active); }\n .toolbar-btn svg { width: 16px; height: 16px; }\n\n /* \u2500\u2500\u2500 Popover (language picker) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .popover {\n position: absolute;\n right: 0;\n top: calc(100% + 6px);\n min-width: 160px;\n background: var(--bg-card);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n box-shadow: var(--shadow-lg);\n z-index: 1000;\n overflow: hidden;\n opacity: 0;\n transform: translateY(-4px) scale(0.96);\n pointer-events: none;\n transform-origin: top right;\n transition: opacity 0.15s ease, transform 0.15s ease;\n }\n .popover.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n .popover-header {\n padding: 8px 14px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--text-tertiary);\n border-bottom: 1px solid var(--border-subtle);\n }\n .popover-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n font-size: 13px;\n color: var(--text);\n cursor: pointer;\n transition: background 0.12s;\n -webkit-tap-highlight-color: transparent;\n }\n .popover-item:hover { background: var(--bg-hover); }\n .popover-item:active { background: var(--bg-active); }\n .popover-item.selected { color: var(--blue-muted); font-weight: 500; }\n .popover-item .check-icon { width: 14px; display: flex; align-items: center; flex-shrink: 0; }\n .popover-item .check-icon svg { width: 14px; height: 14px; }\n\n /* \u2500\u2500\u2500 Mobile: larger touch targets, full-width popover \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .admin-header { padding: 0 12px; }\n .admin-header-right { gap: 4px; }\n .toolbar-btn { width: 40px; height: 40px; }\n .popover { right: -8px; min-width: 150px; }\n .popover-item { padding: 12px 14px; font-size: 14px; }\n .user-menu-arrow { display: none; }\n .user-dropdown { right: -12px; width: 220px; }\n .dropdown-item { padding: 12px 16px; }\n }\n\n /* \u2500\u2500\u2500 User menu trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .user-menu-trigger {\n position: relative;\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n }\n .user-menu-arrow {\n font-size: 12px;\n color: var(--text-tertiary);\n line-height: 1;\n }\n\n /* \u2500\u2500\u2500 User dropdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .user-dropdown {\n position: absolute;\n right: 0;\n top: calc(100% + 8px);\n width: 240px;\n max-width: calc(100vw - 32px);\n background: var(--bg-card);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n box-shadow: var(--shadow-lg);\n z-index: 1000;\n overflow: hidden;\n }\n .dropdown-user {\n padding: 12px 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n border-bottom: 1px solid var(--border-subtle);\n }\n .dropdown-user-name {\n font-weight: 600;\n font-size: 15px;\n color: var(--text-white);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dropdown-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 16px;\n color: var(--text);\n font-size: 14px;\n cursor: pointer;\n transition: background 0.15s;\n text-decoration: none;\n }\n .dropdown-item:hover { background: var(--bg-hover); }\n .dropdown-item.danger { color: var(--red-text); }\n .dropdown-item.danger:hover { background: var(--red-light); }\n .dropdown-divider {\n height: 1px;\n background: var(--border-subtle);\n margin: 4px 0;\n }\n .dropdown-item svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--text-secondary); }\n .dropdown-item.danger svg { color: var(--red-text); }\n\n /* \u2500\u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .tabs {\n display: flex;\n gap: 0;\n border-bottom: 1px solid var(--border);\n padding: 0 24px;\n background: var(--bg-surface);\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n }\n .tabs::-webkit-scrollbar { display: none; }\n [data-tab] {\n position: relative;\n padding: 10px 16px;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-tertiary);\n cursor: pointer;\n border: none;\n background: none;\n border-bottom: 2px solid transparent;\n transition: color 0.15s ease, border-color 0.15s ease;\n }\n [data-tab]:hover { color: var(--text-secondary); }\n .tab-active {\n color: var(--text-white) !important;\n border-bottom-color: var(--text-white) !important;\n }\n .tab-panel {\n padding: 32px 40px;\n max-width: 1120px;\n margin: 0 auto;\n }\n\n /* \u2500\u2500\u2500 Section titles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .section-title {\n font-size: 20px;\n font-weight: 600;\n color: var(--text-white);\n letter-spacing: -0.02em;\n line-height: 1.25;\n margin-bottom: 4px;\n }\n .section-desc {\n font-size: 14px;\n color: var(--text-tertiary);\n margin-bottom: 32px;\n padding-bottom: 24px;\n border-bottom: 1px solid var(--border-subtle);\n line-height: 1.5;\n }\n\n /* \u2500\u2500\u2500 Profile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .profile-header {\n padding: 16px 20px;\n display: flex;\n align-items: center;\n gap: 14px;\n }\n .avatar-lg {\n width: 48px;\n height: 48px;\n font-size: 18px;\n }\n .profile-header-body {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .profile-header-name {\n font-weight: 600;\n font-size: 15px;\n color: var(--text-white);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .profile-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n border-top: 1px solid var(--border);\n }\n .profile-cell {\n padding: 12px 20px;\n }\n .profile-cell-label {\n display: block;\n font-size: 11px;\n font-weight: 500;\n color: var(--text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 4px;\n }\n .profile-cell-value {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--text);\n min-height: 24px;\n }\n .profile-cell-editable {\n margin: -2px -8px;\n }\n @media (max-width: 560px) {\n .profile-grid { grid-template-columns: 1fr; }\n }\n .profile-editable {\n flex: 1;\n min-width: 0;\n height: 30px;\n padding: 0 8px;\n font-size: 13px;\n color: var(--text);\n background: transparent;\n border: 1px solid transparent;\n border-radius: var(--radius-sm);\n outline: none;\n transition: border-color 0.15s, background 0.15s;\n }\n .profile-editable:hover {\n border-color: var(--border-strong);\n background: var(--bg-input);\n }\n .profile-editable:focus {\n border-color: var(--blue);\n background: var(--bg-input);\n box-shadow: var(--shadow-focus);\n }\n .btn-text {\n background: none;\n border: none;\n color: var(--blue);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: var(--radius-sm);\n white-space: nowrap;\n flex-shrink: 0;\n }\n .btn-text:hover { background: var(--blue-light); }\n .btn-text:disabled { opacity: 0.5; cursor: default; }\n .avatar {\n width: 48px;\n height: 48px;\n border-radius: var(--radius-full);\n background: var(--blue-light);\n color: var(--blue-muted);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 600;\n flex-shrink: 0;\n overflow: hidden;\n position: relative;\n }\n .avatar img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n .avatar-fallback {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .avatar-sm {\n width: 32px;\n height: 32px;\n font-size: 12px;\n }\n .avatar-editable { cursor: pointer; }\n .avatar-overlay {\n position: absolute;\n inset: 0;\n background: rgba(0,0,0,0.5);\n display: none;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 12px;\n border-radius: inherit;\n }\n .avatar-editable:hover .avatar-overlay { display: flex; }\n /* <did-address> Web Component theme bridge \u2014 CSS custom properties penetrate Shadow DOM */\n did-address {\n --did-text-color: var(--text-secondary);\n --did-copy-success-color: var(--green);\n font-size: 11px;\n }\n did-address:hover {\n --did-text-color: var(--text-white);\n }\n .profile-meta {\n margin: 16px 0;\n font-size: 13px;\n }\n\n /* \u2500\u2500\u2500 Settings card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .settings-card {\n background: var(--bg-card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 24px;\n }\n .settings-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--border-subtle);\n gap: 12px;\n }\n details.settings-card > summary.settings-card-header {\n list-style: none;\n }\n details.settings-card > summary.settings-card-header::before {\n content: \"\\25B6\";\n font-size: 10px;\n transition: transform 0.2s;\n margin-right: 4px;\n }\n details[open].settings-card > summary.settings-card-header::before {\n transform: rotate(90deg);\n }\n details.settings-card > summary.settings-card-header::-webkit-details-marker {\n display: none;\n }\n\n /* Icon action buttons for access/policy tables */\n .ap-actions {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n white-space: nowrap;\n }\n .ap-icon-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: 1px solid var(--border);\n border-radius: var(--radius-sm);\n background: var(--bg-card);\n color: var(--text-secondary);\n cursor: pointer;\n transition: background 0.15s, color 0.15s, border-color 0.15s;\n flex-shrink: 0;\n }\n .ap-icon-btn:hover {\n background: var(--bg-hover);\n color: var(--text-white);\n border-color: var(--border-strong);\n }\n .ap-icon-btn-danger:hover {\n background: var(--red-light);\n color: var(--red-text);\n border-color: var(--red-text);\n }\n\n .ap-lock-name {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n color: var(--fg);\n }\n .ap-lock-name svg {\n color: var(--fg-muted);\n flex-shrink: 0;\n }\n\n tr.ap-separator td {\n padding: 0;\n border-bottom: 2px solid var(--border);\n }\n\n .ap-switch {\n position: relative;\n display: inline-block;\n width: 36px;\n height: 20px;\n cursor: pointer;\n }\n .ap-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n .ap-switch-slider {\n position: absolute;\n inset: 0;\n background: var(--bg-tertiary, #ccc);\n border-radius: 20px;\n transition: background 0.2s;\n }\n .ap-switch-slider::before {\n content: \"\";\n position: absolute;\n left: 2px;\n top: 2px;\n width: 16px;\n height: 16px;\n background: #fff;\n border-radius: 50%;\n transition: transform 0.2s;\n }\n .ap-switch input:checked + .ap-switch-slider {\n background: var(--color-primary, #2563eb);\n }\n .ap-switch input:checked + .ap-switch-slider::before {\n transform: translateX(16px);\n }\n\n .settings-card-header-desc {\n font-size: 12px;\n color: var(--text-tertiary);\n font-weight: 400;\n flex: 1;\n text-align: right;\n }\n .settings-card-desc {\n font-size: 12px;\n color: var(--text-tertiary);\n padding: 8px 20px 0;\n margin: 0;\n line-height: 1.5;\n }\n .settings-card-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-white);\n }\n .settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid var(--border-subtle);\n }\n .settings-row:last-child { border-bottom: none; }\n .settings-label {\n font-size: 13px;\n font-weight: 500;\n color: var(--text);\n }\n .settings-value {\n font-size: 13px;\n color: var(--text-secondary);\n }\n\n /* \u2500\u2500\u2500 Connected Accounts chips \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .ca-section {\n padding: 12px 20px;\n }\n .ca-section + .ca-section {\n padding-top: 0;\n }\n .ca-section-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n }\n .ca-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n }\n .ca-chip {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border: 1px solid var(--border);\n border-radius: var(--radius-sm);\n background: var(--bg-elevated);\n font-size: 13px;\n color: var(--text);\n transition: border-color 0.15s, background 0.15s;\n position: relative;\n max-width: 260px;\n }\n .ca-chip-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n flex-shrink: 0;\n }\n .ca-chip-icon svg { width: 18px; height: 18px; }\n .ca-chip-body {\n display: flex;\n flex-direction: column;\n min-width: 0;\n }\n .ca-chip-name {\n font-weight: 500;\n font-size: 13px;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .ca-chip-detail {\n font-size: 11px;\n color: var(--text-secondary);\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .ca-chip-badge {\n display: inline-flex;\n font-size: 10px;\n font-weight: 500;\n padding: 1px 6px;\n border-radius: var(--radius-full);\n background: var(--blue-light);\n color: var(--blue-muted);\n white-space: nowrap;\n vertical-align: middle;\n margin-left: 4px;\n }\n .ca-chip-remove {\n display: none;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border: none;\n background: transparent;\n color: var(--text-tertiary);\n cursor: pointer;\n padding: 0;\n border-radius: var(--radius-full);\n flex-shrink: 0;\n transition: color 0.15s, background 0.15s;\n }\n .ca-chip-remove:hover { color: var(--red-text); background: var(--red-light); }\n .ca-chip:hover .ca-chip-remove { display: inline-flex; }\n\n /* \u2500\u2500\u2500 Bound account cards \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .ca-bound-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));\n gap: 12px;\n }\n .ca-bound-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--border);\n border-radius: var(--radius-sm);\n background: var(--bg-card);\n transition: border-color 0.15s;\n }\n .ca-bound-card:hover { border-color: var(--border-strong); }\n .ca-bound-top {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n }\n .ca-bound-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: var(--radius-sm);\n background: var(--bg-elevated);\n border: 1px solid var(--border);\n flex-shrink: 0;\n margin-top: 1px;\n }\n .ca-bound-icon svg { width: 22px; height: 22px; }\n .ca-bound-icon-lg {\n width: 48px;\n height: 48px;\n background: var(--blue-light);\n border-color: transparent;\n }\n .ca-bound-icon-lg svg { width: 28px; height: 28px; color: var(--blue-muted); }\n .ca-bound-top-body {\n flex: 1;\n min-width: 0;\n }\n .ca-bound-header {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .ca-bound-name {\n font-weight: 600;\n font-size: 15px;\n color: var(--text-white);\n line-height: 1.3;\n }\n .ca-bound-detail {\n font-size: 13px;\n color: var(--text-secondary);\n line-height: 1.4;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .ca-bound-field {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n }\n .ca-bound-field-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n flex-shrink: 0;\n width: 36px;\n }\n .ca-bound-field-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n color: var(--text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: 'JetBrains Mono', 'SF Mono', monospace;\n }\n .ca-bound-copy {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: none;\n background: transparent;\n color: var(--text-tertiary);\n cursor: pointer;\n padding: 0;\n border-radius: var(--radius-sm);\n flex-shrink: 0;\n transition: color 0.15s, background 0.15s;\n }\n .ca-bound-copy:hover { color: var(--text); background: var(--bg-hover); }\n .ca-bound-copy svg { width: 14px; height: 14px; }\n .ca-bound-times {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .ca-bound-time {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n }\n .ca-bound-time-label {\n flex: 0 0 auto;\n min-width: 4.5rem;\n font-size: 11px;\n font-weight: 500;\n color: var(--text-tertiary);\n letter-spacing: 0.2px;\n white-space: nowrap;\n }\n .ca-bound-time-value {\n font-size: 12px;\n color: var(--text-secondary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n .ca-bound-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n }\n\n /* \u2500\u2500\u2500 Connected Accounts: mobile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .ca-bound-grid { grid-template-columns: 1fr; gap: 8px; }\n .ca-bound-card { padding: 12px; gap: 10px; }\n .ca-bound-top { gap: 10px; }\n .ca-bound-icon { width: 32px; height: 32px; }\n .ca-bound-icon svg { width: 18px; height: 18px; }\n .ca-bound-icon-lg { width: 40px; height: 40px; }\n .ca-bound-icon-lg svg { width: 22px; height: 22px; }\n .ca-bound-name { font-size: 14px; }\n .ca-bound-detail { font-size: 12px; }\n .ca-bound-field-label { display: none; }\n .ca-bound-field-value { font-size: 11px; }\n .ca-bound-time-value { font-size: 11px; }\n .ca-section { padding: 10px 16px; }\n .ca-chips { gap: 6px; }\n .ca-chip { padding: 5px 10px; font-size: 12px; }\n }\n .ca-chip-unbound {\n border-style: dashed;\n color: var(--text-tertiary);\n cursor: pointer;\n }\n .ca-chip-unbound:hover {\n border-color: var(--border-strong);\n background: var(--bg-hover);\n color: var(--text);\n }\n .ca-chip-add-icon {\n font-size: 14px;\n font-weight: 300;\n line-height: 1;\n color: var(--text-tertiary);\n }\n .ca-chip-unbound:hover .ca-chip-add-icon { color: var(--text-secondary); }\n .ca-passkey-add {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px dashed var(--border);\n border-radius: var(--radius-sm);\n background: transparent;\n font-size: 13px;\n color: var(--text-tertiary);\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s, color 0.15s;\n }\n .ca-passkey-add:hover {\n border-color: var(--border-strong);\n background: var(--bg-hover);\n color: var(--text);\n }\n\n /* \u2500\u2500\u2500 Forms \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .form-group {\n margin-bottom: 16px;\n }\n .form-label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 600;\n color: var(--text-secondary);\n margin-bottom: 4px;\n }\n .form-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-top: 24px;\n }\n .form-actions .btn { width: auto; }\n .text-muted { color: var(--text-secondary); }\n\n /* \u2500\u2500\u2500 Tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .table {\n width: 100%;\n border-collapse: collapse;\n }\n .table th, .table td {\n padding: 12px 16px;\n text-align: left;\n border-bottom: 1px solid var(--border-subtle);\n font-size: 14px;\n vertical-align: middle;\n }\n .table th {\n color: var(--text-tertiary);\n font-weight: 500;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n background: transparent;\n border-bottom-color: var(--border);\n }\n .table th.sortable {\n cursor: pointer;\n user-select: none;\n transition: color 0.1s ease;\n }\n .table th.sortable:hover { color: var(--text-secondary); }\n .table th.sort-active { color: var(--text-white); }\n .table th .sort-arrow {\n display: inline-block;\n margin-left: 4px;\n font-size: 10px;\n opacity: 0.25;\n transition: opacity 0.1s ease;\n }\n .table th.sortable:hover .sort-arrow { opacity: 0.6; }\n .table th .sort-arrow-active { opacity: 1; }\n .table tbody tr {\n transition: background-color 0.1s ease;\n }\n .table tbody tr:hover { background: var(--bg-hover); }\n\n /* Action cell \u2014 hosts \u22EF trigger + dropdown menu */\n .action-cell {\n position: relative;\n width: 40px;\n text-align: center;\n }\n\n /* Member identity cell */\n .member-identity {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n .member-name {\n font-size: 14px;\n font-weight: 500;\n color: var(--text);\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .member-email {\n font-size: 12px;\n color: var(--text-secondary);\n line-height: 1.3;\n margin-top: 1px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .cell-date {\n font-size: 13px;\n color: var(--text-tertiary);\n font-variant-numeric: tabular-nums;\n }\n\n /* \u2500\u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .tab-toolbar {\n display: flex;\n gap: 8px;\n margin-bottom: 20px;\n align-items: center;\n }\n .search-wrapper {\n position: relative;\n width: 280px;\n }\n .search-icon {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-tertiary);\n width: 16px;\n height: 16px;\n pointer-events: none;\n }\n .search-bar {\n width: 100%;\n height: 36px;\n padding: 0 12px 0 34px;\n background: transparent;\n border: 1px solid var(--border-strong);\n border-radius: var(--radius-sm);\n font-size: 14px;\n color: var(--text);\n outline: none;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n }\n .search-bar::placeholder { color: var(--text-tertiary); }\n .search-bar:focus { border-color: var(--border-focus); box-shadow: 0 0 0 2px rgba(255,255,255,0.05); }\n .select {\n height: 36px;\n padding: 0 12px;\n font-size: 13px;\n color: var(--text);\n background: transparent;\n border: 1px solid var(--border-strong);\n border-radius: var(--radius-sm);\n outline: none;\n cursor: pointer;\n transition: border-color 0.15s ease;\n }\n .select:focus { border-color: var(--border-focus); }\n .btn-sm { height: 36px; padding: 0 12px; font-size: 13px; width: auto; }\n .btn-xs { height: 28px; padding: 0 10px; font-size: 12px; width: auto; }\n\n /* \u2500\u2500\u2500 OAuth provider picker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .provider-picker {\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: 8px;\n }\n .provider-card {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 10px 6px 8px;\n border: 1px solid var(--border);\n border-radius: var(--radius-sm);\n background: var(--bg-elevated);\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n position: relative;\n min-width: 0;\n }\n .provider-card:hover:not(.added) { border-color: var(--border-strong); background: var(--bg-hover); }\n .provider-card.selected { border-color: var(--blue); background: rgba(108,71,255,0.08); }\n .provider-card.added { opacity: 0.45; cursor: default; }\n .provider-icon { display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; }\n .provider-icon svg { width: 20px; height: 20px; }\n .provider-name { font-size: 11px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }\n .provider-card.selected .provider-name { color: var(--text); }\n .provider-added-badge {\n position: absolute;\n top: -6px; right: -6px;\n font-size: 9px;\n font-weight: 600;\n padding: 1px 4px;\n border-radius: 4px;\n background: var(--bg-card);\n border: 1px solid var(--border);\n color: var(--text-tertiary);\n line-height: 1.4;\n white-space: nowrap;\n }\n\n /* \u2500\u2500\u2500 Pagination \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .pagination {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 16px;\n font-size: 13px;\n color: var(--text-tertiary);\n }\n .pagination-btns {\n display: flex;\n gap: 4px;\n }\n .page-btn {\n min-width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n color: var(--text-secondary);\n border-radius: var(--radius-sm);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n transition: background-color 0.1s ease;\n }\n .page-btn:hover { background: var(--bg-hover); color: var(--text); }\n .page-btn.active { background: var(--bg-active); color: var(--text-white); }\n .page-btn:disabled { opacity: 0.3; cursor: not-allowed; }\n\n /* \u2500\u2500\u2500 Empty state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n text-align: center;\n }\n .empty-state-icon {\n width: 48px;\n height: 48px;\n border-radius: var(--radius);\n background: rgba(255,255,255,0.06);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--text-tertiary);\n margin-bottom: 16px;\n }\n .empty-state-title {\n font-size: 15px;\n font-weight: 500;\n color: var(--text);\n margin-bottom: 4px;\n }\n .empty-state-desc {\n font-size: 13px;\n color: var(--text-secondary);\n max-width: 320px;\n line-height: 1.5;\n }\n\n /* \u2500\u2500\u2500 Dialogs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n dialog {\n background: var(--bg-elevated);\n border: 1px solid var(--border-strong);\n border-radius: var(--radius-lg);\n color: var(--text);\n padding: 0;\n margin: auto;\n max-width: 520px;\n width: 90%;\n box-shadow: var(--shadow-lg);\n max-height: calc(100vh - 32px);\n max-height: calc(100dvh - 32px);\n overflow: hidden;\n animation: modalIn 0.2s ease;\n }\n dialog[open] {\n display: flex;\n flex-direction: column;\n }\n dialog::backdrop { background: rgba(0,0,0,0.7); }\n .dialog-content {\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior: contain;\n padding: 24px;\n }\n .dialog-content h3 {\n font-size: 16px;\n font-weight: 600;\n color: var(--text-white);\n margin-bottom: 4px;\n letter-spacing: -0.01em;\n }\n .dialog-desc {\n font-size: 14px;\n color: var(--text-secondary);\n margin-bottom: 20px;\n line-height: 1.5;\n }\n .dialog-actions {\n display: flex;\n flex: 0 0 auto;\n justify-content: flex-end;\n gap: 8px;\n padding: 16px 24px;\n border-top: 1px solid var(--border);\n }\n .dialog-actions .btn { width: auto; }\n\n dialog.merge-confirm-dialog { max-width: 560px; }\n .merge-method-list { display: grid; gap: 10px; margin-top: 16px; }\n .merge-method-list .btn,\n .merge-method-btn {\n width: 100%;\n justify-content: flex-start;\n }\n .merge-method-btn .ca-chip-icon,\n .merge-method-btn svg {\n width: 18px;\n height: 18px;\n flex-shrink: 0;\n }\n .ca-merge-prompt {\n margin: 0 20px 12px;\n padding: 12px 14px;\n border: 1px solid var(--border-strong);\n border-radius: var(--radius-sm);\n background: var(--blue-light);\n }\n .ca-merge-prompt-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-white);\n margin: 0 0 4px;\n }\n .ca-merge-prompt-body {\n font-size: 13px;\n color: var(--text-secondary);\n line-height: 1.45;\n margin: 0 0 10px;\n }\n .ca-merge-prompt-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n }\n .ca-merge-prompt .btn { width: auto; min-width: 96px; }\n .merge-email-fields { display: grid; gap: 10px; margin-top: 12px; text-align: left; }\n .merge-confirm-section { margin: 14px 0 0; }\n .merge-confirm-section h4 {\n font-size: 12px;\n font-weight: 600;\n color: var(--text-white);\n margin: 0 0 6px;\n }\n .merge-confirm-list {\n margin: 0;\n padding-left: 18px;\n font-size: 13px;\n color: var(--text);\n line-height: 1.55;\n }\n .merge-confirm-note {\n font-size: 13px;\n color: var(--text);\n line-height: 1.55;\n margin: 0;\n }\n .merge-confirm-did {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-size: 12px;\n word-break: break-all;\n }\n .merge-confirm-keep-other {\n font-size: 13px;\n color: var(--text-secondary);\n margin: 0 0 4px;\n line-height: 1.5;\n }\n .merge-confirm-irreversible {\n color: var(--red-text);\n font-weight: 600;\n font-size: 13px;\n margin: 16px 0 0;\n }\n .merge-confirm-too-large {\n color: var(--red-text);\n font-size: 13px;\n margin: 8px 0 0;\n line-height: 1.5;\n }\n\n /* \u2500\u2500\u2500 Wallet bind QR (mirrors login-page .qr-view) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .wallet-qr-view {\n margin: 16px auto;\n display: flex;\n justify-content: center;\n min-height: 212px;\n align-items: center;\n }\n .wallet-qr-placeholder {\n width: 212px;\n height: 212px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius);\n background: var(--bg-elevated);\n }\n .wallet-qr-spinner {\n display: inline-block;\n width: 32px; height: 32px;\n border: 3px solid var(--border-strong);\n border-top-color: var(--blue-muted);\n border-radius: 50%;\n animation: spin 0.6s linear infinite;\n }\n @keyframes spin { to { transform: rotate(360deg); } }\n .wallet-deep-link-btn {\n margin-top: 4px;\n width: 212px;\n }\n .wallet-bind-status {\n margin-top: 12px;\n font-size: 13px;\n min-height: 18px;\n color: var(--text-secondary);\n margin-bottom: 0;\n }\n .wallet-bind-error {\n color: var(--red-text);\n padding: 10px 12px;\n background: rgba(220,53,69,0.08);\n border: 1px solid rgba(220,53,69,0.25);\n border-radius: var(--radius-sm);\n margin-top: 8px;\n }\n\n @keyframes modalIn {\n from { opacity: 0; transform: scale(0.98); }\n to { opacity: 1; transform: scale(1); }\n }\n\n /* \u2500\u2500\u2500 Action menus \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .action-trigger {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm);\n border: none;\n background: transparent;\n color: var(--text-tertiary);\n cursor: pointer;\n font-size: 16px;\n transition: background-color 0.15s ease, color 0.15s ease;\n flex-shrink: 0;\n }\n .action-trigger:hover { background: var(--bg-hover); color: var(--text-secondary); }\n .action-menu {\n display: none;\n position: absolute;\n right: 8px;\n top: 100%;\n z-index: 50;\n min-width: 180px;\n padding: 4px;\n background: var(--bg-elevated);\n border: 1px solid var(--border-strong);\n border-radius: var(--radius);\n box-shadow: var(--shadow-md);\n }\n .action-menu.open { display: block; }\n .action-menu-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: var(--radius-xs);\n font-size: 13px;\n color: var(--text);\n cursor: pointer;\n border: none;\n background: none;\n width: 100%;\n text-align: left;\n transition: background-color 0.1s ease;\n }\n .action-menu-item:hover { background: var(--bg-hover); }\n .action-menu-item.danger { color: var(--red-text); }\n .action-menu-item.danger:hover { background: var(--red-light); }\n .action-menu-sep {\n height: 1px;\n background: var(--border);\n margin: 4px 0;\n }\n\n /* \u2500\u2500\u2500 Status badges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .badge-active { background: var(--green-light); color: var(--green-text); }\n .badge-pending { background: var(--yellow-light); color: var(--yellow-text); }\n .badge-blocked { background: var(--red-light); color: var(--red-text); }\n .badge-expired { background: rgba(255,255,255,0.06); color: var(--text-tertiary); }\n .badge-neutral { background: rgba(255,255,255,0.06); color: var(--text-secondary); }\n\n /* \u2500\u2500\u2500 Toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .toast-container {\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 100;\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n .toast {\n padding: 10px 16px;\n border-radius: var(--radius);\n font-size: 13px;\n font-weight: 500;\n color: var(--text);\n background: var(--bg-elevated);\n border: 1px solid var(--border-strong);\n box-shadow: var(--shadow-md);\n animation: toastIn 0.2s ease;\n }\n .toast.success { border-left: 3px solid var(--green); }\n .toast.error { background: var(--red); color: #fff; border-color: var(--red); }\n @keyframes toastIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n /* \u2500\u2500\u2500 Toggle switch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .toggle-switch {\n position: relative;\n display: inline-block;\n width: 40px;\n height: 22px;\n cursor: pointer;\n }\n .toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }\n .toggle-slider {\n position: absolute;\n inset: 0;\n background: var(--bg-hover);\n border-radius: 11px;\n transition: background-color 0.2s ease;\n }\n .toggle-slider::before {\n content: \"\";\n position: absolute;\n left: 2px;\n top: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--text-secondary);\n transition: transform 0.2s ease, background-color 0.2s ease;\n }\n .toggle-switch input:checked + .toggle-slider { background: var(--blue); }\n .toggle-switch input:checked + .toggle-slider::before {\n transform: translateX(18px);\n background: #fff;\n }\n\n /* \u2500\u2500\u2500 Range slider \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .range-slider {\n -webkit-appearance: none;\n width: 100%;\n height: 6px;\n border-radius: 3px;\n background: var(--bg-hover);\n outline: none;\n margin: 8px 0;\n }\n .range-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--blue);\n cursor: pointer;\n }\n .range-slider::-moz-range-thumb {\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--blue);\n cursor: pointer;\n border: none;\n }\n\n /* \u2500\u2500\u2500 Branding: Section layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .br-section {\n margin-bottom: 56px;\n }\n .br-section-header {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n margin-bottom: 20px;\n }\n .br-section-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--text);\n letter-spacing: -0.01em;\n }\n .br-section-category {\n font-size: 10px;\n font-weight: 600;\n color: var(--text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n .br-two-col {\n display: grid;\n grid-template-columns: 1fr 2fr;\n gap: 32px;\n }\n .br-col-desc {\n font-size: 13px;\n color: var(--text-secondary);\n line-height: 1.6;\n }\n .br-field-hint {\n font-size: 11px;\n color: var(--text-tertiary);\n margin-top: 4px;\n margin-bottom: 12px;\n }\n /* Info icon tooltip */\n .info-tip {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: rgba(255,255,255,0.08);\n color: var(--text-tertiary);\n font-size: 10px;\n font-weight: 600;\n cursor: help;\n flex-shrink: 0;\n }\n .info-tip::after {\n content: attr(data-tip);\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n background: var(--bg-card);\n border: 1px solid var(--border-strong);\n color: var(--text-secondary);\n font-size: 12px;\n font-weight: 400;\n padding: 8px 14px;\n border-radius: var(--radius);\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n z-index: 10;\n box-shadow: var(--shadow-md);\n min-width: 220px;\n max-width: 360px;\n white-space: normal;\n line-height: 1.5;\n text-align: left;\n }\n .info-tip:hover::after { opacity: 1; }\n .br-panel {\n background: var(--bg-elevated);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-lg);\n padding: 20px 24px;\n }\n .accent-dot {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n cursor: pointer;\n border: 2px solid transparent;\n transition: transform 0.1s ease, border-color 0.15s ease;\n }\n .accent-dot:hover { transform: scale(1.15); }\n .accent-dot.selected { border-color: var(--text-white); }\n\n /* \u2500\u2500\u2500 Branding: Language toggles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .lang-toggles {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n }\n .lang-toggle {\n position: relative;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n background: var(--bg-elevated);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius);\n font-size: 13px;\n font-weight: 500;\n color: var(--text-secondary);\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;\n }\n .lang-toggle:hover { border-color: var(--border-strong); color: var(--text); }\n .lang-toggle.active {\n border-color: var(--border-strong);\n color: var(--text-white);\n background: rgba(255,255,255,0.04);\n }\n .lang-toggle.active::after {\n content: \"\u2713\";\n position: absolute;\n top: -1px;\n right: -1px;\n width: 16px;\n height: 16px;\n background: var(--blue);\n border-radius: 0 var(--radius) 0 var(--radius);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n color: #fff;\n line-height: 1;\n }\n\n /* \u2500\u2500\u2500 Branding: Logo grid (redesigned) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .logo-grid {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n }\n .logo-item {\n position: relative;\n background: var(--bg-elevated);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-lg);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n padding: 16px;\n transition: border-color 0.15s ease;\n min-height: 170px;\n }\n .logo-item:hover { border-color: rgba(255,255,255,0.15); }\n .logo-item-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: auto;\n }\n .logo-item-icon {\n color: var(--text-tertiary);\n font-size: 20px;\n line-height: 1;\n }\n .logo-item-spec {\n font-size: 10px;\n font-weight: 600;\n color: var(--text-tertiary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n }\n .logo-preview {\n width: 100%;\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n }\n .logo-preview img {\n max-width: 100%;\n max-height: 80px;\n object-fit: contain;\n }\n .logo-item-footer {\n margin-top: 12px;\n }\n .logo-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-white);\n margin-bottom: 2px;\n }\n .logo-subtitle {\n font-size: 12px;\n color: var(--text-tertiary);\n }\n .logo-placeholder {\n font-size: 24px;\n color: var(--text-tertiary);\n opacity: 0.3;\n }\n /* Hover overlay for upload \u2014 glassmorphism */\n .logo-hover-upload {\n position: absolute;\n inset: 0;\n background: rgba(20,20,30,0.65);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n opacity: 0;\n transition: opacity 0.2s ease;\n cursor: pointer;\n border-radius: inherit;\n }\n .logo-item:hover .logo-hover-upload { opacity: 1; }\n .logo-hover-icon {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(255,255,255,0.1);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--text-white);\n margin-bottom: 2px;\n }\n .logo-hover-icon svg { width: 18px; height: 18px; }\n .logo-hover-text {\n font-size: 12px;\n font-weight: 500;\n color: var(--text-secondary);\n letter-spacing: 0.02em;\n }\n /* Uploaded state: replace/discard shown on hover via overlay */\n .logo-uploaded-overlay {\n position: absolute;\n inset: 0;\n background: rgba(20,20,30,0.65);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n opacity: 0;\n transition: opacity 0.2s ease;\n border-radius: inherit;\n z-index: 2;\n }\n .logo-item:hover .logo-uploaded-overlay { opacity: 1; }\n .logo-overlay-btn {\n font-size: 12px;\n font-weight: 600;\n padding: 6px 14px;\n border-radius: var(--radius-sm);\n cursor: pointer;\n border: 1px solid;\n transition: background 0.15s ease;\n }\n .logo-overlay-btn-replace { color: var(--blue); border-color: var(--blue); background: transparent; }\n .logo-overlay-btn-replace:hover { background: rgba(99,102,241,0.12); }\n .logo-overlay-btn-discard { color: var(--red-text); border-color: rgba(224,46,46,0.4); background: transparent; }\n .logo-overlay-btn-discard:hover { background: rgba(224,46,46,0.1); }\n .btn-text { background: none; border: none; color: var(--blue); font-size: 11px; font-weight: 600; cursor: pointer; padding: 0; }\n .btn-text:hover { text-decoration: underline; }\n .btn-text-danger { color: var(--red-text); }\n\n /* \u2500\u2500\u2500 Branding: per-field source badge (did#249) \u2500\u2500\u2500 */\n .br-src-slot:empty { display: none; }\n .br-src-row { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }\n .br-src-row:empty { display: none; }\n .br-src {\n display: inline-block;\n font-size: 10px;\n font-weight: 600;\n line-height: 1.5;\n padding: 1px 6px;\n margin-left: 6px;\n border-radius: 999px;\n border: 1px solid var(--border-subtle);\n color: var(--text-tertiary);\n white-space: nowrap;\n vertical-align: middle;\n }\n .br-src-row .br-src { margin-left: 0; }\n .br-src-instance { color: var(--blue); border-color: var(--blue-muted); }\n .br-src-package { color: var(--green-text); border-color: var(--green-light); }\n .br-src-note { font-style: italic; }\n .br-restore { margin-left: 8px; font-size: 11px; vertical-align: middle; }\n\n /* \u2500\u2500\u2500 Branding: Sticky save bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .br-save-bar {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 40;\n background: rgba(28,28,32,0.85);\n backdrop-filter: blur(12px);\n border-top: 1px solid var(--border-subtle);\n padding: 16px 40px;\n display: none;\n }\n .br-save-bar.visible { display: block; }\n .br-save-bar-inner {\n max-width: 1120px;\n margin: 0 auto;\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n }\n .br-save-hint {\n font-size: 12px;\n color: var(--text-tertiary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .br-save-actions {\n display: flex;\n gap: 8px;\n }\n .br-save-actions .btn { width: auto; }\n\n /* \u2500\u2500\u2500 Branding: Accent color swatches \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .accent-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\n gap: 10px;\n }\n .accent-swatch {\n position: relative;\n height: 64px;\n border-radius: var(--radius);\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n padding: 6px 8px;\n cursor: pointer;\n transition: transform 0.1s ease, box-shadow 0.15s ease;\n border: 2px solid transparent;\n }\n .accent-swatch:hover { transform: scale(1.02); }\n .accent-swatch.selected { border-color: var(--text-white); }\n .accent-swatch .accent-check {\n position: absolute;\n top: 4px;\n right: 4px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: rgba(0,0,0,0.5);\n display: none;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 12px;\n }\n .accent-swatch.selected .accent-check { display: flex; }\n .accent-label { font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.8; }\n .accent-hex { font-size: 10px; font-weight: 700; }\n\n /* \u2500\u2500\u2500 Branding tab: Language grid \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .lang-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n gap: 4px 16px;\n }\n .lang-item {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n padding: 4px 0;\n cursor: pointer;\n }\n\n /* \u2500\u2500\u2500 Crop dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n dialog.dialog-wide { max-width: 660px; }\n .crop-container {\n width: 100%;\n height: 400px;\n background: var(--bg);\n border-radius: var(--radius-sm);\n overflow: hidden;\n }\n .crop-toolbar {\n display: flex;\n justify-content: center;\n gap: 6px;\n padding: 12px 0 4px;\n }\n .crop-tool-btn {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--bg-input);\n border: 1px solid var(--border-strong);\n border-radius: var(--radius-sm);\n color: var(--text-secondary);\n cursor: pointer;\n transition: background-color 0.15s ease, color 0.15s ease;\n }\n .crop-tool-btn:hover { background: var(--bg-hover); color: var(--text); }\n .crop-tool-btn svg { width: 18px; height: 18px; }\n\n /* Crop dialog progress */\n .crop-progress {\n width: 120px;\n height: 4px;\n background: var(--bg-hover);\n border-radius: 2px;\n overflow: hidden;\n }\n .crop-progress-bar {\n width: 30%;\n height: 100%;\n background: var(--blue);\n border-radius: 2px;\n animation: cropProgress 1.5s ease infinite;\n }\n @keyframes cropProgress {\n 0% { width: 10%; margin-left: 0; }\n 50% { width: 40%; margin-left: 30%; }\n 100% { width: 10%; margin-left: 90%; }\n }\n\n /* cropperjs dark theme overrides */\n .cropper-view-box { outline-color: var(--blue) !important; }\n .cropper-line { background-color: var(--blue) !important; }\n .cropper-point { background-color: var(--blue) !important; width: 8px !important; height: 8px !important; }\n .cropper-modal { background-color: rgba(0,0,0,0.6) !important; }\n .cropper-dashed { border-color: rgba(255,255,255,0.3) !important; }\n\n /* \u2500\u2500\u2500 Branding: Mobile responsive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 768px) {\n .br-two-col { grid-template-columns: 1fr; gap: 16px; }\n .logo-grid { grid-template-columns: repeat(2, 1fr); }\n .br-save-bar { padding: 12px 16px; }\n .br-save-bar-inner { flex-direction: column; gap: 12px; text-align: center; }\n .br-save-actions { justify-content: center; }\n .tab-panel { padding: 20px 16px; }\n .accent-grid { grid-template-columns: repeat(3, 1fr); }\n .br-panel > div[style*=\"grid-template-columns: 1fr 1fr\"] { grid-template-columns: 1fr !important; }\n }\n @media (max-width: 480px) {\n .logo-grid { grid-template-columns: 1fr; }\n .accent-grid { grid-template-columns: repeat(2, 1fr); }\n }\n\n /* \u2500\u2500\u2500 Appearance tab: Color input \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .color-input {\n width: 40px;\n height: 28px;\n padding: 0;\n border: 1px solid var(--border-strong);\n border-radius: 4px;\n cursor: pointer;\n background: none;\n }\n\n /* \u2500\u2500\u2500 Auto theme: follow system preference \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (prefers-color-scheme: light) {\n [data-theme=\"auto\"] {\n --bg-root: #f8f9fa;\n --bg-surface: #ffffff;\n --bg-card: #ffffff;\n --bg-elevated: #f0f1f3;\n --bg-hover: #e9eaec;\n --bg-active: #dddee1;\n --bg-input: rgba(0,0,0,0.04);\n --border: rgba(0,0,0,0.10);\n --border-subtle: rgba(0,0,0,0.06);\n --border-strong: rgba(0,0,0,0.15);\n --border-focus: rgba(0,0,0,0.25);\n --text: #1a1a1a;\n --text-secondary: #6b7280;\n --text-tertiary: #9ca3af;\n --text-placeholder: #c0c5ce;\n --text-white: #1a1a1a;\n --shadow-sm: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px -1px rgba(0,0,0,0.06);\n --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.06);\n --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.12), 0 4px 6px -4px rgba(0,0,0,0.08);\n --shadow-focus: 0 0 0 1px rgba(0,0,0,0.08), 0 0 0 3px rgba(108,71,255,0.2);\n color-scheme: light;\n }\n }\n\n /* \u2500\u2500\u2500 Skeleton loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n .skel {\n display: block;\n background: linear-gradient(90deg, var(--bg-hover) 25%, var(--bg-active, rgba(255,255,255,0.06)) 50%, var(--bg-hover) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.4s infinite;\n border-radius: 4px;\n }\n .skel-circle { border-radius: 50%; }\n .skel-row td { padding: 12px 16px; }\n\n /* \u2500\u2500\u2500 Responsive: column visibility helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n /* col-hide-lg: hide on tablet (\u2264900px) */\n @media (max-width: 900px) {\n .col-hide-lg { display: none; }\n }\n /* col-hide-md: hide on small tablet (\u2264768px) */\n @media (max-width: 768px) {\n .col-hide-md { display: none; }\n }\n /* col-hide-sm: hide on mobile (\u2264640px) */\n @media (max-width: 640px) {\n .col-hide-sm { display: none; }\n }\n\n /* \u2500\u2500\u2500 Mobile sidebar drawer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .sidebar-toggle { display: none; }\n .sidebar-overlay {\n display: none;\n position: fixed;\n inset: 0;\n background: rgba(0,0,0,0.5);\n z-index: 200;\n opacity: 0;\n transition: opacity 0.25s ease;\n }\n .sidebar-overlay-visible {\n display: block;\n opacity: 1;\n }\n .sidebar {\n display: none;\n position: fixed;\n top: 0;\n left: 0;\n bottom: 0;\n width: 280px;\n max-width: 80vw;\n background: var(--bg-surface);\n border-right: 1px solid var(--border);\n z-index: 210;\n transform: translateX(-100%);\n transition: transform 0.25s ease;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n }\n .sidebar-open { transform: translateX(0); }\n .sidebar-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--border);\n }\n .sidebar-header h2 {\n font-size: 15px;\n font-weight: 600;\n color: var(--text-white);\n margin: 0;\n }\n .sidebar-nav {\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n .sidebar-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border: none;\n background: transparent;\n border-radius: var(--radius-sm);\n font-size: 14px;\n font-weight: 500;\n color: var(--text-secondary);\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n text-align: left;\n width: 100%;\n -webkit-tap-highlight-color: transparent;\n }\n .sidebar-item:hover { background: var(--bg-hover); color: var(--text); }\n .sidebar-item:active { background: var(--bg-active); }\n .sidebar-item svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--text-tertiary); transition: color 0.15s; }\n .sidebar-item:hover svg { color: var(--text-secondary); }\n .sidebar-item-active {\n background: var(--bg-hover);\n color: var(--text-white);\n }\n .sidebar-item-active svg { color: var(--text-white); }\n\n /* \u2500\u2500\u2500 Responsive: show sidebar toggle, hide tabs on mobile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .sidebar-toggle { display: flex; }\n .sidebar { display: block; }\n .tabs { display: none; }\n }\n\n /* \u2500\u2500\u2500 Responsive: toolbar / filter bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .tab-toolbar {\n flex-wrap: wrap;\n gap: 6px;\n }\n .search-wrapper {\n width: 100%;\n min-width: 0;\n }\n .tab-toolbar .select {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n padding: 0 8px;\n height: 34px;\n }\n .tab-toolbar .btn-sm {\n font-size: 12px;\n padding: 0 10px;\n height: 34px;\n }\n }\n\n /* \u2500\u2500\u2500 Responsive: header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .admin-header-left {\n gap: 6px;\n min-width: 0;\n overflow: hidden;\n }\n .admin-header-left .brand-home {\n flex-shrink: 1;\n }\n .admin-header-left .app-name-link {\n max-width: 80px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex-shrink: 1;\n }\n .admin-header h1 {\n font-size: 13px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .header-dot { display: none; }\n }\n\n /* \u2500\u2500\u2500 Responsive: table compact \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .table th, .table td {\n padding: 10px 8px;\n font-size: 13px;\n }\n .table th {\n font-size: 10px;\n }\n .member-identity {\n gap: 8px;\n }\n .member-name {\n font-size: 13px;\n max-width: 120px;\n }\n .member-email {\n font-size: 11px;\n max-width: 100px;\n }\n .action-cell { width: 32px; }\n }\n\n /* \u2500\u2500\u2500 Responsive: pagination \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .pagination {\n flex-direction: column;\n gap: 8px;\n align-items: center;\n font-size: 12px;\n }\n .pagination-info {\n font-size: 12px;\n }\n .page-btn {\n min-width: 28px;\n height: 28px;\n font-size: 12px;\n }\n }\n\n /* \u2500\u2500\u2500 Responsive: section titles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .section-title { font-size: 17px; }\n .section-desc { font-size: 13px; margin-bottom: 20px; padding-bottom: 16px; }\n }\n\n /* \u2500\u2500\u2500 Responsive: settings card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n @media (max-width: 640px) {\n .settings-card { border-radius: var(--radius-sm); }\n .settings-card-header {\n gap: 8px;\n padding: 12px 14px;\n }\n .settings-card-header-desc { text-align: left; }\n .settings-row {\n gap: 6px;\n padding: 12px 14px;\n }\n .settings-row.settings-row-stack {\n flex-direction: column;\n align-items: flex-start;\n }\n }\n\n /* \u2500\u2500\u2500 Responsive: table horizontal scroll on very small screens \u2500\u2500\u2500 */\n @media (max-width: 480px) {\n .settings-card {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .settings-card .table {\n min-width: 360px;\n }\n }\n\n /* \u2500\u2500\u2500 Settings: OAuth card grid \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .st-oauth-grid {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding: 8px 20px 16px;\n }\n .st-oauth-card {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border: 1px solid var(--border);\n border-radius: var(--radius-md);\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n min-width: 110px;\n }\n .st-oauth-card:hover {\n border-color: var(--border-strong);\n background: var(--bg-hover);\n }\n .st-oauth-card-icon {\n display: inline-flex;\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n }\n .st-oauth-card-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--text);\n }\n .st-oauth-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n flex-shrink: 0;\n margin-left: auto;\n }\n .st-oauth-dot-on { background: var(--green-text); }\n .st-oauth-dot-off { background: var(--text-tertiary); }\n\n /* \u2500\u2500\u2500 Settings: dual panel (login methods + email) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .st-dual-panel {\n display: flex;\n gap: 16px;\n }\n @media (max-width: 640px) {\n .st-dual-panel {\n flex-direction: column;\n }\n }\n\n /* \u2500\u2500\u2500 Settings: email hint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .st-hint {\n font-size: 11px;\n color: var(--yellow-text, #92400e);\n font-weight: 400;\n margin-left: 4px;\n }\n\n /* \u2500\u2500\u2500 Federation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n .fed-status-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: var(--radius-full);\n }\n .fed-status-badge.master {\n background: var(--blue-light);\n color: var(--blue-muted);\n }\n .fed-status-badge.member {\n background: var(--green-light);\n color: var(--green-text);\n }\n .fed-star {\n color: var(--yellow);\n font-size: 14px;\n }\n .fed-site-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 20px;\n border-bottom: 1px solid var(--border);\n }\n .fed-site-row:last-child { border-bottom: none; }\n .fed-site-row { flex-wrap: wrap; }\n .fed-site-name { flex: 1 1 0%; min-width: 120px; font-weight: 500; font-size: 14px; color: var(--text); display: flex; align-items: center; gap: 6px; overflow: hidden; flex-wrap: nowrap; }\n .fed-site-name > span { flex-shrink: 0; }\n .fed-site-name > span.fed-site-label { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .fed-site-url { flex: 0 0 500px; min-width: 0; font-size: 13px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .fed-site-url a { color: var(--text-secondary); text-decoration: none; }\n .fed-site-url a:hover { color: var(--blue); text-decoration: underline; }\n .fed-site-role { width: 80px; font-size: 12px; color: var(--text-tertiary); }\n .fed-site-actions { display: flex; align-items: center; gap: 6px; justify-content: flex-end; min-width: 64px; flex-shrink: 0; }\n @media (max-width: 768px) {\n .fed-site-row { flex-wrap: wrap; }\n .fed-site-name { flex: 1 1 100%; }\n .fed-site-url { flex: 1 1 auto; }\n }\n .fed-delegation-status { display: flex; align-items: center; gap: 8px; padding: 12px 20px; font-size: 13px; color: var(--text-secondary); }\n .fed-delegation-status .checkmark { color: var(--green); font-weight: 600; }\n .fed-init-buttons { display: flex; gap: 12px; justify-content: center; padding: 8px 20px 16px; }\n .fed-init-buttons .btn { width: auto; min-width: 160px; }\n";
8
+ export declare const ADMIN_STYLES: string;
9
9
  //# sourceMappingURL=styles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/styles.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,YAAY,6+0EAy1ExB,CAAC"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/styles.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAIH,eAAO,MAAM,YAAY,QAm0ErB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { INSTANCE_PALETTE_CSS } from "../../theme-utils.js";
1
2
  /**
2
3
  * Admin SPA CSS — header, tabs, tables, forms, dialogs, action menus.
3
4
  * Design inspired by Clerk, Linear, and Vercel dark-theme dashboards.
@@ -5,7 +6,9 @@
5
6
  * Includes shared component styles formerly from shared-styles.ts
6
7
  * (body, h1, .input, .btn, .btn-secondary, .btn-danger, .badge variants).
7
8
  */
8
- export const ADMIN_STYLES = /* css */ `
9
+ // PURE-wrapped: interpolates INSTANCE_PALETTE_CSS, so without it esbuild keeps
10
+ // this (externalized, admin.css) string in the worker — see package CLAUDE.md §3.
11
+ export const ADMIN_STYLES = /* @__PURE__ */ (() => /* css */ `
9
12
  .hidden { display: none !important; }
10
13
 
11
14
  /* ─── Shared component styles (extracted from shared-styles.ts) ───── */
@@ -886,8 +889,10 @@ export const ADMIN_STYLES = /* css */ `
886
889
  font-size: 12px;
887
890
  color: var(--text-secondary);
888
891
  font-variant-numeric: tabular-nums;
889
- white-space: nowrap;
892
+ min-width: 0;
893
+ overflow-wrap: anywhere;
890
894
  }
895
+ .settings-row .ca-bound-times { margin-top: 6px; }
891
896
  .ca-bound-actions {
892
897
  display: flex;
893
898
  align-items: center;
@@ -2021,32 +2026,8 @@ export const ADMIN_STYLES = /* css */ `
2021
2026
  background: none;
2022
2027
  }
2023
2028
 
2024
- /* ─── Auto theme: follow system preference ───────────────────────── */
2025
- @media (prefers-color-scheme: light) {
2026
- [data-theme="auto"] {
2027
- --bg-root: #f8f9fa;
2028
- --bg-surface: #ffffff;
2029
- --bg-card: #ffffff;
2030
- --bg-elevated: #f0f1f3;
2031
- --bg-hover: #e9eaec;
2032
- --bg-active: #dddee1;
2033
- --bg-input: rgba(0,0,0,0.04);
2034
- --border: rgba(0,0,0,0.10);
2035
- --border-subtle: rgba(0,0,0,0.06);
2036
- --border-strong: rgba(0,0,0,0.15);
2037
- --border-focus: rgba(0,0,0,0.25);
2038
- --text: #1a1a1a;
2039
- --text-secondary: #6b7280;
2040
- --text-tertiary: #9ca3af;
2041
- --text-placeholder: #c0c5ce;
2042
- --text-white: #1a1a1a;
2043
- --shadow-sm: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px -1px rgba(0,0,0,0.06);
2044
- --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.06);
2045
- --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.12), 0 4px 6px -4px rgba(0,0,0,0.08);
2046
- --shadow-focus: 0 0 0 1px rgba(0,0,0,0.08), 0 0 0 3px rgba(108,71,255,0.2);
2047
- color-scheme: light;
2048
- }
2049
- }
2029
+ /* ─── Instance palette (page layers follow the site theme) ────────── */
2030
+ ${INSTANCE_PALETTE_CSS}
2050
2031
 
2051
2032
  /* ─── Skeleton loading ──────────────────────────────────────────────── */
2052
2033
  @keyframes shimmer {
@@ -2398,5 +2379,5 @@ export const ADMIN_STYLES = /* css */ `
2398
2379
  .fed-delegation-status .checkmark { color: var(--green); font-weight: 600; }
2399
2380
  .fed-init-buttons { display: flex; gap: 12px; justify-content: center; padding: 8px 20px 16px; }
2400
2381
  .fed-init-buttons .btn { width: auto; min-width: 160px; }
2401
- `;
2382
+ `)();
2402
2383
  //# sourceMappingURL=styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/pages/admin/styles.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAy1ErC,CAAC"}
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/pages/admin/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;;;;GAMG;AAEH,+EAA+E;AAC/E,kFAAkF;AAClF,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAm+D3D,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgWrB,CAAC,EAAE,CAAC"}
@@ -3,6 +3,6 @@
3
3
  */
4
4
  type TFn = (key: string, params?: Record<string, string>) => string;
5
5
  export declare function membersTabHtml(t: TFn): string;
6
- export declare const MEMBERS_TAB_SCRIPT = "\nvar membersPage = 1;\nvar membersPageSize = 20;\nvar memberSearchTimeout = null;\nvar currentMembersList = [];\nvar selectedMember = null;\nvar __membersAggregated = false;\n// Sort state: empty string = default (server decides + self-first on client).\n// Three-state click cycle: (none) \u2192 desc \u2192 asc \u2192 (none).\n// Persisted in location.search as sortBy/sortOrder so it survives refresh + share.\nvar membersSortBy = \"\";\nvar membersSortOrder = \"\";\nvar MEMBERS_SORT_VALID = [\"name\", \"role\", \"status\", \"source\", \"joined\"];\n\n/** Populate module sort state from the current URL query string. Idempotent. */\nfunction readMembersSortFromURL() {\n membersSortBy = \"\";\n membersSortOrder = \"\";\n var params = new URLSearchParams(location.search);\n var by = params.get(\"sortBy\");\n var order = params.get(\"sortOrder\");\n if (by && MEMBERS_SORT_VALID.indexOf(by) >= 0) {\n membersSortBy = by;\n membersSortOrder = (order === \"asc\" || order === \"desc\") ? order : \"desc\";\n }\n}\n\n/** Write current sort state back to location.search without reloading. */\nfunction writeMembersSortToURL() {\n var params = new URLSearchParams(location.search);\n if (membersSortBy) {\n params.set(\"sortBy\", membersSortBy);\n params.set(\"sortOrder\", membersSortOrder || \"desc\");\n } else {\n params.delete(\"sortBy\");\n params.delete(\"sortOrder\");\n }\n var qs = params.toString();\n var newUrl = location.pathname + (qs ? \"?\" + qs : \"\") + location.hash;\n history.replaceState(null, \"\", newUrl);\n}\n\n/** Cycle sort state for a column key and reload. */\nfunction toggleSort(key) {\n if (membersSortBy !== key) {\n membersSortBy = key;\n membersSortOrder = \"desc\";\n } else if (membersSortOrder === \"desc\") {\n membersSortOrder = \"asc\";\n } else {\n // Third click \u2014 back to default.\n membersSortBy = \"\";\n membersSortOrder = \"\";\n }\n writeMembersSortToURL();\n loadMembers(1);\n}\n\n/** Render a <th> with click-to-sort behavior. */\nfunction sortableTh(key, label, extraClass) {\n var isActive = membersSortBy === key;\n var arrow;\n if (isActive) {\n arrow = membersSortOrder === \"asc\"\n ? '<span class=\"sort-arrow sort-arrow-active\">\\u2191</span>'\n : '<span class=\"sort-arrow sort-arrow-active\">\\u2193</span>';\n } else {\n arrow = '<span class=\"sort-arrow\">\\u2195</span>';\n }\n var cls = \"sortable\" + (isActive ? \" sort-active\" : \"\") + (extraClass ? \" \" + extraClass : \"\");\n return '<th class=\"' + cls + '\" onclick=\"toggleSort(\\'' + key + '\\')\">' + label + arrow + '</th>';\n}\n\nfunction providerLabel(sp) {\n if (!sp) return \"\\u2014\";\n var key = \"members.providers.\" + sp;\n var val = __t(key);\n return val !== key ? val : sp;\n}\n\nfunction debouncedMemberSearch() {\n clearTimeout(memberSearchTimeout);\n memberSearchTimeout = setTimeout(function() { loadMembers(1); }, 300);\n}\n\n// Load registered instances for the filter dropdown (system admin only)\nvar __instanceNames = {};\nvar __instanceFilterInitialized = false;\nasync function initInstanceFilter() {\n if (__instanceFilterInitialized) return;\n try {\n // Best-effort: this endpoint is system-admin-only. Instance-scoped owners/admins\n // get 403 here, which is expected \u2014 fail silently (no error toast).\n var data = await api(\"GET\", \"/../admin/instances\", undefined, { silent: true });\n if (data.ok && data.instances && data.instances.length > 0) {\n __instanceFilterInitialized = true;\n var sel = document.getElementById(\"members-instance-filter\");\n data.instances.forEach(function(inst) {\n __instanceNames[inst.instanceDid] = inst.appName || inst.instanceDid.substring(0, 16);\n var opt = document.createElement(\"option\");\n opt.value = inst.instanceDid;\n opt.textContent = __instanceNames[inst.instanceDid];\n sel.appendChild(opt);\n });\n sel.style.display = \"\";\n }\n } catch(e) { /* not admin or no instances */ }\n}\n\n// Also support specific instance filter via query param\n// When ?instance=z1xxx, team handler needs to use that instanceDid\n\nasync function loadMembers(page) {\n membersPage = page || 1;\n var instanceFilter = document.getElementById(\"members-instance-filter\").value;\n var qs = \"?page=\" + membersPage + \"&pageSize=\" + membersPageSize;\n // System view (\"\") \u2192 use defaultInstanceDid so we query membership, not global users\n if (instanceFilter === \"\" && __pageData.instanceDid) {\n qs += \"&instance=\" + encodeURIComponent(__pageData.instanceDid);\n } else if (instanceFilter) {\n qs += \"&instance=\" + encodeURIComponent(instanceFilter);\n }\n var search = document.getElementById(\"members-search\").value.trim();\n if (search) qs += \"&search=\" + encodeURIComponent(search);\n var role = document.getElementById(\"members-role-filter\").value;\n if (role) qs += \"&role=\" + encodeURIComponent(role);\n var status = document.getElementById(\"members-status-filter\").value;\n if (status !== \"\") qs += \"&approved=\" + status;\n var source = document.getElementById(\"members-source-filter\").value;\n if (source) qs += \"&sourceProvider=\" + encodeURIComponent(source);\n if (membersSortBy) {\n qs += \"&sortBy=\" + encodeURIComponent(membersSortBy);\n qs += \"&sortOrder=\" + encodeURIComponent(membersSortOrder || \"desc\");\n }\n\n document.getElementById(\"members-table-wrap\").innerHTML = skeletonTable(8, 6);\n document.getElementById(\"members-pagination\").innerHTML = \"\";\n var data = await api(\"GET\", \"/members\" + qs);\n if (!data.ok) {\n document.getElementById(\"members-table-wrap\").innerHTML = '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 8v4m0 4h.01\"/></svg></div><div class=\"empty-state-title\">' + __t(\"members.loadFailed\") + '</div></div>';\n return;\n }\n __membersAggregated = !!data.aggregated;\n // Only pin the caller to the top when using the default (server) order.\n // Under explicit sort, honor the user's intent without re-shuffling.\n if (membersSortBy) {\n currentMembersList = data.users || [];\n } else {\n currentMembersList = (data.users || []).slice().sort(function(a, b) {\n if (a.did === __caller.did) return -1;\n if (b.did === __caller.did) return 1;\n return (a.createdAt || 0) - (b.createdAt || 0);\n });\n }\n renderMembersTable(currentMembersList, data.paging);\n updateTransferSection(data.users);\n}\n\nfunction renderMembersTable(users, paging) {\n var wrap = document.getElementById(\"members-table-wrap\");\n // Aggregated (cross-instance \"All Instances\") view can't tell which instance\n // a role change should apply to, so per-row actions are disabled there \u2014 see\n // canOperate below. Surface why, instead of silently hiding the buttons.\n var aggregatedHint = __membersAggregated ? '<p class=\"text-muted\" style=\"font-size:13px;margin-bottom:12px;\">' + __t(\"members.aggregatedHint\") + '</p>' : '';\n if (!users || users.length === 0) {\n wrap.innerHTML = aggregatedHint + '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/></svg></div><div class=\"empty-state-title\">' + __t(\"members.noMembers\") + '</div><div class=\"empty-state-desc\">' + __t(\"members.noMembersHint\") + '</div></div>';\n document.getElementById(\"members-pagination\").innerHTML = \"\";\n return;\n }\n\n var instanceSel = document.getElementById(\"members-instance-filter\");\n var instanceVisible = instanceSel && instanceSel.style.display !== 'none';\n var showInstance = instanceVisible && instanceSel.value === \"all\";\n var html = aggregatedHint + '<table class=\"table\"><thead><tr>';\n html += sortableTh(\"name\", __t(\"members.col.member\"));\n html += sortableTh(\"role\", __t(\"members.col.role\"));\n if (showInstance) html += '<th>' + __t(\"members.col.instance\") + '</th>';\n html += sortableTh(\"source\", __t(\"members.col.source\"), \"col-hide-sm\");\n html += sortableTh(\"status\", __t(\"members.col.status\"), \"col-hide-sm\");\n html += sortableTh(\"joined\", __t(\"members.col.joined\"), \"col-hide-md\");\n // Registered/Last Login From columns removed \u2014 domain info not meaningful in Worker context\n html += '<th></th></tr></thead><tbody>';\n users.forEach(function(u) {\n var userDid = u.did || u.user_did || '';\n var av = renderAvatar(u.avatar, u.fullName, \"sm\");\n var name = u.fullName ? escapeHtml(u.fullName) : '<span class=\"text-muted\">(unnamed)</span>';\n var did = '<did-address did=\"' + escapeHtml(userDid) + '\" compact></did-address>';\n var roleBadge = '<span class=\"badge badge-' + (u.role || \"guest\") + '\">' + __t(\"common.\" + (u.role || \"guest\")) + '</span>';\n var statusBadge = u.approved !== undefined ? (u.approved ? '<span class=\"badge badge-active\">' + __t(\"common.active\") + '</span>' : '<span class=\"badge badge-blocked\">' + __t(\"common.blocked\") + '</span>') : '<span class=\"text-muted\">\u2014</span>';\n var joined = (u.createdAt || u.joined_at) ? '<span class=\"cell-date\">' + escapeHtml(relativeTime(u.createdAt || u.joined_at)) + '</span>' : '<span class=\"text-muted\">\u2014</span>';\n html += '<tr style=\"cursor:pointer\" onclick=\"openMemberDetail(\\'' + escapeHtml(userDid) + '\\')\">';\n html += '<td><div class=\"member-identity\">' + av + '<div><div class=\"member-name\">' + name + '</div><div class=\"member-email\">' + did + '</div></div></div></td>';\n html += '<td>' + roleBadge + '</td>';\n if (showInstance) {\n if (__membersAggregated && u.instance_count > 1) {\n html += '<td><a href=\"javascript:void(0)\" onclick=\"event.stopPropagation();openMemberDetail(\\'' + escapeHtml(userDid) + '\\')\" style=\"color:var(--blue);font-size:14px;text-decoration:none\">' + u.instance_count + ' ' + __t(\"members.instances\") + '</a></td>';\n } else {\n var instDid = (__membersAggregated && u.instances && u.instances.length === 1) ? u.instances[0].instance_did : (u.instance_did || '');\n var instName = __instanceNames[instDid] || '';\n if (!instName) instName = instDid ? __t(\"members.currentInstance\") : '\u2014';\n html += '<td><div>' + escapeHtml(instName);\n if (instDid) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\" onclick=\"event.stopPropagation()\"><did-address did=\"' + escapeHtml(instDid) + '\" compact></did-address></div>';\n html += '</div></td>';\n }\n }\n var sourceBadge = '<span class=\"text-muted\">' + escapeHtml(providerLabel(u.sourceProvider)) + '</span>';\n html += '<td class=\"col-hide-sm\">' + sourceBadge + '</td>';\n html += '<td class=\"col-hide-sm\">' + statusBadge + '</td>';\n html += '<td class=\"col-hide-md\">' + joined + '</td>';\n // Actions: only for current instance members, not cross-instance\n var canOperate = !instanceVisible || (instanceVisible && instanceSel.value === '');\n // In aggregated mode, only allow actions on users whose instance matches current system\n if (__membersAggregated) canOperate = false;\n html += '<td class=\"action-cell\">' + (canOperate ? renderMemberActions(u) : '') + '</td>';\n html += '</tr>';\n });\n html += '</tbody></table>';\n wrap.innerHTML = html;\n\n renderPagination(\"members-pagination\", paging, loadMembers);\n}\n\nfunction renderMemberActions(u) {\n var isOwner = __caller.role === \"owner\";\n var isAdmin = __caller.role === \"admin\";\n var isSelf = u.did === __caller.did;\n var ROLE_LEVEL = { owner: 3, admin: 2, member: 1, guest: 0 };\n var callerLevel = ROLE_LEVEL[__caller.role] || 0;\n var targetLevel = ROLE_LEVEL[u.role] || 0;\n // Can only act on members with lower privilege\n var canManage = !isSelf && targetLevel < callerLevel;\n var items = [];\n\n // Change role: can manage lower-level members\n if (canManage) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();openChangeRoleForDid(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.changeRole\") + '</button>');\n }\n if (canManage && u.approved) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();blockMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.block\") + '</button>');\n }\n if (canManage && !u.approved) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();unblockMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.unblock\") + '</button>');\n }\n if (items.length > 0 && canManage) {\n items.push('<div class=\"action-menu-sep\"></div>');\n }\n if (canManage) {\n items.push('<button class=\"action-menu-item danger\" onclick=\"event.stopPropagation();removeMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.remove\") + '</button>');\n }\n\n if (items.length === 0) return \"\";\n return '<button class=\"action-trigger\" onclick=\"event.stopPropagation();toggleActionMenu(this)\">\u22EF</button><div class=\"action-menu\">' + items.join(\"\") + '</div>';\n}\n\nfunction toggleActionMenu(trigger) {\n var menu = trigger.nextElementSibling;\n var wasOpen = menu.classList.contains(\"open\");\n // Close all menus first\n document.querySelectorAll(\".action-menu.open\").forEach(function(m) { m.classList.remove(\"open\"); });\n if (!wasOpen) menu.classList.toggle(\"open\");\n}\n\nasync function openMemberDetail(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n if (!u) return;\n selectedMember = u;\n\n // Fetch full member info (with connectedAccounts) from API.\n // Falls back to in-memory list data if the request fails.\n var detail = u;\n try {\n var resp = await api(\"GET\", \"/members/\" + encodeURIComponent(did));\n if (resp && resp.ok && resp.user) {\n // Merge: in-memory list has fields the API may omit (instances, passkeyCount in some paths).\n detail = Object.assign({}, u, resp.user);\n selectedMember = detail;\n }\n } catch (e) {}\n\n var body = document.getElementById(\"member-detail-body\");\n var html = '<div class=\"profile-header\">' + renderAvatar(detail.avatar, detail.fullName) + '<div>';\n html += '<div style=\"font-weight:500;font-size:16px;color:var(--text-white)\">' + escapeHtml(detail.fullName || \"(unnamed)\") + '</div>';\n html += '<did-address did=\"' + escapeHtml(detail.did) + '\" compact></did-address>';\n html += '</div></div>';\n\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailRole\") + '</span><span class=\"settings-value\"><span class=\"badge badge-' + (detail.role || \"guest\") + '\">' + __t(\"common.\" + (detail.role || \"guest\")) + '</span></span></div>';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailStatus\") + '</span><span class=\"settings-value\">' + (detail.approved ? '<span class=\"badge badge-active\">' + __t(\"common.active\") + '</span>' : '<span class=\"badge badge-blocked\">' + __t(\"common.blocked\") + '</span>') + '</span></div>';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailSource\") + '</span><span class=\"settings-value\">' + escapeHtml(providerLabel(detail.sourceProvider)) + '</span></div>';\n if (detail.email) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailEmail\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.email) + '</span></div>';\n if (detail.inviterName) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailInvitedBy\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.inviterName) + '</span></div>';\n if (detail.createdAt) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailJoined\") + '</span><span class=\"settings-value\">' + escapeHtml(absoluteTime(detail.createdAt)) + '</span></div>';\n if (detail.lastLoginAt) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailLastLogin\") + '</span><span class=\"settings-value\">' + escapeHtml(absoluteTime(detail.lastLoginAt)) + '</span></div>';\n if (detail.sourceDomain) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailRegisteredFrom\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.sourceDomain) + '</span></div>';\n if (detail.lastLoginDomain) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailLastLoginFrom\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.lastLoginDomain) + '</span></div>';\n if (detail.passkeyCount !== undefined) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailPasskeys\") + '</span><span class=\"settings-value\">' + detail.passkeyCount + '</span></div>';\n html += '</div>';\n\n // Connected Accounts (read-only)\n if (detail.connectedAccounts && detail.connectedAccounts.length > 0) {\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-card-header\"><span>' + __t(\"members.detailConnectedAccounts\") + ' (' + detail.connectedAccounts.length + ')</span></div>';\n html += '<div style=\"max-height:240px;overflow-y:auto\">';\n detail.connectedAccounts.forEach(function(a) {\n var label = providerLabel(a.provider);\n var info = '';\n if (a.userInfo && a.userInfo.name) info = a.userInfo.name;\n else if (a.userInfo && a.userInfo.email) info = a.userInfo.email;\n else if (a.id) info = a.id;\n html += '<div class=\"settings-row\" style=\"gap:12px;align-items:flex-start\">';\n html += '<div style=\"flex:1;min-width:0\">';\n html += '<div style=\"font-size:14px;font-weight:500;color:var(--text)\">' + escapeHtml(label);\n if (a.isMain) html += ' <span class=\"badge badge-active\" style=\"font-size:10px;padding:1px 6px;\">' + __t(\"profile.mainAccount\") + '</span>';\n html += '</div>';\n if (info) html += '<div style=\"font-size:12px;color:var(--text-secondary);margin-top:2px\">' + escapeHtml(info) + '</div>';\n if (a.did) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\"><did-address did=\"' + escapeHtml(a.did) + '\" compact></did-address></div>';\n html += '</div>';\n if (a.lastLoginAt) html += '<div style=\"font-size:12px;color:var(--text-secondary);flex-shrink:0\">' + escapeHtml(relativeTime(a.lastLoginAt)) + '</div>';\n html += '</div>';\n });\n html += '</div></div>';\n }\n\n // Instance memberships (aggregated mode)\n if (detail.instances && detail.instances.length > 0) {\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-card-header\"><span>' + __t(\"members.instanceMemberships\") + ' (' + detail.instances.length + ')</span></div>';\n html += '<div style=\"max-height:240px;overflow-y:auto\">';\n detail.instances.forEach(function(inst) {\n var instName = __instanceNames[inst.instance_did] || '';\n var instDid = inst.instance_did || '';\n html += '<div class=\"settings-row\" style=\"gap:12px\">';\n html += '<div style=\"flex:1;min-width:0\"><div style=\"font-size:14px;font-weight:500;color:var(--text)\">' + escapeHtml(instName || __t(\"members.currentInstance\")) + '</div>';\n if (instDid) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\"><did-address did=\"' + escapeHtml(instDid) + '\" compact></did-address></div>';\n html += '</div>';\n html += '<div style=\"display:flex;align-items:center;gap:8px;flex-shrink:0\"><span class=\"badge badge-' + inst.role + '\">' + __t(\"common.\" + inst.role) + '</span>';\n if (inst.joined_at) html += '<span style=\"font-size:12px;color:var(--text-secondary)\">' + escapeHtml(relativeTime(inst.joined_at)) + '</span>';\n html += '</div></div>';\n });\n html += '</div></div>';\n }\n\n body.innerHTML = html;\n\n // Change role: mirror list's canOperate + renderMemberActions logic exactly.\n // Use post-merge detail so role/did reflect the latest API response.\n var changeRoleBtn = document.getElementById(\"member-detail-change-role\");\n var instanceSel = document.getElementById(\"members-instance-filter\");\n var instanceVisible = instanceSel && instanceSel.style.display !== 'none';\n var canOperate = !instanceVisible || (instanceVisible && instanceSel.value === '');\n if (__membersAggregated) canOperate = false;\n var isSelf = detail.did === __caller.did;\n var ROLE_LEVEL = { owner: 3, admin: 2, member: 1, guest: 0 };\n var callerLevel = ROLE_LEVEL[__caller.role] || 0;\n var targetLevel = ROLE_LEVEL[detail.role] || 0;\n var canChangeRole = canOperate && !isSelf && targetLevel < callerLevel;\n if (canChangeRole) {\n changeRoleBtn.classList.remove(\"hidden\");\n } else {\n changeRoleBtn.classList.add(\"hidden\");\n }\n\n showDialog(\"member-detail-dialog\");\n}\n\nfunction openChangeRoleForDid(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n if (!u) return;\n selectedMember = u;\n openChangeRoleDialog();\n}\n\nfunction openChangeRoleDialog() {\n if (!selectedMember) return;\n document.getElementById(\"change-role-title\").textContent = __t(\"members.changeRole\");\n document.getElementById(\"change-role-subtitle\").textContent = __t(\"members.changeRoleFor\", { name: selectedMember.fullName || truncateDid(selectedMember.did) });\n // Show/hide role options based on caller's level (only owner can assign admin)\n var radios = document.querySelectorAll('input[name=\"new-role\"]');\n radios.forEach(function(r) {\n r.checked = r.value === selectedMember.role;\n var label = r.closest('label');\n if (label) {\n if (r.value === 'admin' && __caller.role !== 'owner') label.style.display = 'none';\n else label.style.display = '';\n }\n });\n closeDialog(\"member-detail-dialog\");\n showDialog(\"change-role-dialog\");\n}\n\nasync function saveRoleChange() {\n if (!selectedMember) return;\n var selected = document.querySelector('input[name=\"new-role\"]:checked');\n if (!selected) return;\n var newRole = selected.value;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(selectedMember.did) + \"/role\", { role: newRole });\n if (data.ok) {\n showToast(__t(\"members.roleUpdated\"), \"success\");\n closeDialog(\"change-role-dialog\");\n loadMembers(membersPage);\n }\n}\n\n// name is looked up (not passed via onclick) so a member's self-set fullName\n// never has to be embedded as a JS-string literal inside an inline onclick=\n// attribute \u2014 HTML entity-escaping alone can't fully neutralize that context,\n// since the browser decodes entities before parsing onclick as script.\nasync function blockMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.blockTitle\"), message: __t(\"members.blockMsg\", { name: name }), confirmLabel: __t(\"members.blockBtn\"), danger: true });\n if (!ok) return;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(did) + \"/approval\", { approved: false });\n if (data.ok) { showToast(__t(\"members.blockSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nasync function unblockMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.unblockTitle\"), message: __t(\"members.unblockMsg\", { name: name }), confirmLabel: __t(\"members.unblock\") });\n if (!ok) return;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(did) + \"/approval\", { approved: true });\n if (data.ok) { showToast(__t(\"members.unblockSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nasync function removeMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.removeTitle\"), message: __t(\"members.removeMsg\", { name: name }), confirmLabel: __t(\"members.remove\"), danger: true });\n if (!ok) return;\n var data = await api(\"DELETE\", \"/members/\" + encodeURIComponent(did));\n if (data.ok) { showToast(__t(\"members.removeSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nfunction updateTransferSection(users) {\n var section = document.getElementById(\"ownership-transfer-section\");\n if (__caller.role !== \"owner\") {\n section.classList.add(\"hidden\");\n return;\n }\n section.classList.remove(\"hidden\");\n var select = document.getElementById(\"transfer-target\");\n var html = '<option value=\"\">' + __t(\"members.transferSelect\") + '</option>';\n users.forEach(function(u) {\n if (u.did !== __caller.did && u.approved && u.role !== \"owner\") {\n html += '<option value=\"' + escapeHtml(u.did) + '\">' + escapeHtml(u.fullName || truncateDid(u.did)) + ' (' + u.role + ')</option>';\n }\n });\n select.innerHTML = html;\n}\n\nasync function handleTransferOwnership() {\n var target = document.getElementById(\"transfer-target\").value;\n if (!target) { showToast(__t(\"members.selectMemberFirst\"), \"error\"); return; }\n var targetUser = currentMembersList.find(function(u) { return u.did === target; });\n var name = targetUser ? (targetUser.fullName || truncateDid(target)) : truncateDid(target);\n var ok = await confirmDialog({\n title: __t(\"members.transferTitle\"),\n message: __t(\"members.transferMsg\", { name: name }),\n confirmLabel: __t(\"members.transferConfirm\"),\n danger: true,\n });\n if (!ok) return;\n var data = await api(\"POST\", \"/transfer-ownership\", { targetDid: target });\n if (data.ok) {\n showToast(__t(\"members.transferSuccess\"), \"success\");\n setTimeout(function() { location.reload(); }, 1000);\n }\n}\n\n// Button-driven mutations must not double-submit (submitOnce \u2014 utils.ts).\nsaveRoleChange = submitOnce(saveRoleChange);\nblockMember = submitOnce(blockMember);\nunblockMember = submitOnce(unblockMember);\nremoveMember = submitOnce(removeMember);\nhandleTransferOwnership = submitOnce(handleTransferOwnership);\n\n// Close action menus on outside click\ndocument.addEventListener(\"click\", function() {\n document.querySelectorAll(\".action-menu.open\").forEach(function(m) { m.classList.remove(\"open\"); });\n});\n\nregisterTab(\"members\", function() {\n initInstanceFilter().then(function() {\n // Auto-select instance from URL query (e.g., ?instance=z1xxx)\n var urlInstance = new URLSearchParams(window.location.search).get(\"instance\");\n if (urlInstance) {\n var sel = document.getElementById(\"members-instance-filter\");\n if (sel) sel.value = urlInstance;\n }\n // Restore sort state from URL (sortBy / sortOrder) on tab enter.\n readMembersSortFromURL();\n loadMembers(1);\n });\n});\n";
6
+ export declare const MEMBERS_TAB_SCRIPT = "\nvar membersPage = 1;\nvar membersPageSize = 20;\nvar memberSearchTimeout = null;\nvar currentMembersList = [];\nvar selectedMember = null;\nvar __membersAggregated = false;\n// Sort state: empty string = default (server decides + self-first on client).\n// Three-state click cycle: (none) \u2192 desc \u2192 asc \u2192 (none).\n// Persisted in location.search as sortBy/sortOrder so it survives refresh + share.\nvar membersSortBy = \"\";\nvar membersSortOrder = \"\";\nvar MEMBERS_SORT_VALID = [\"name\", \"role\", \"status\", \"source\", \"joined\"];\n\n/** Populate module sort state from the current URL query string. Idempotent. */\nfunction readMembersSortFromURL() {\n membersSortBy = \"\";\n membersSortOrder = \"\";\n var params = new URLSearchParams(location.search);\n var by = params.get(\"sortBy\");\n var order = params.get(\"sortOrder\");\n if (by && MEMBERS_SORT_VALID.indexOf(by) >= 0) {\n membersSortBy = by;\n membersSortOrder = (order === \"asc\" || order === \"desc\") ? order : \"desc\";\n }\n}\n\n/** Write current sort state back to location.search without reloading. */\nfunction writeMembersSortToURL() {\n var params = new URLSearchParams(location.search);\n if (membersSortBy) {\n params.set(\"sortBy\", membersSortBy);\n params.set(\"sortOrder\", membersSortOrder || \"desc\");\n } else {\n params.delete(\"sortBy\");\n params.delete(\"sortOrder\");\n }\n var qs = params.toString();\n var newUrl = location.pathname + (qs ? \"?\" + qs : \"\") + location.hash;\n history.replaceState(null, \"\", newUrl);\n}\n\n/** Cycle sort state for a column key and reload. */\nfunction toggleSort(key) {\n if (membersSortBy !== key) {\n membersSortBy = key;\n membersSortOrder = \"desc\";\n } else if (membersSortOrder === \"desc\") {\n membersSortOrder = \"asc\";\n } else {\n // Third click \u2014 back to default.\n membersSortBy = \"\";\n membersSortOrder = \"\";\n }\n writeMembersSortToURL();\n loadMembers(1);\n}\n\n/** Render a <th> with click-to-sort behavior. */\nfunction sortableTh(key, label, extraClass) {\n var isActive = membersSortBy === key;\n var arrow;\n if (isActive) {\n arrow = membersSortOrder === \"asc\"\n ? '<span class=\"sort-arrow sort-arrow-active\">\\u2191</span>'\n : '<span class=\"sort-arrow sort-arrow-active\">\\u2193</span>';\n } else {\n arrow = '<span class=\"sort-arrow\">\\u2195</span>';\n }\n var cls = \"sortable\" + (isActive ? \" sort-active\" : \"\") + (extraClass ? \" \" + extraClass : \"\");\n return '<th class=\"' + cls + '\" onclick=\"toggleSort(\\'' + key + '\\')\">' + label + arrow + '</th>';\n}\n\nfunction providerLabel(sp) {\n if (!sp) return \"\\u2014\";\n var key = \"members.providers.\" + sp;\n var val = __t(key);\n return val !== key ? val : sp;\n}\n\nfunction debouncedMemberSearch() {\n clearTimeout(memberSearchTimeout);\n memberSearchTimeout = setTimeout(function() { loadMembers(1); }, 300);\n}\n\n// Load registered instances for the filter dropdown (system admin only)\nvar __instanceNames = {};\nvar __instanceFilterInitialized = false;\nasync function initInstanceFilter() {\n if (__instanceFilterInitialized) return;\n try {\n // Best-effort: this endpoint is system-admin-only. Instance-scoped owners/admins\n // get 403 here, which is expected \u2014 fail silently (no error toast).\n var data = await api(\"GET\", \"/../admin/instances\", undefined, { silent: true });\n if (data.ok && data.instances && data.instances.length > 0) {\n __instanceFilterInitialized = true;\n var sel = document.getElementById(\"members-instance-filter\");\n data.instances.forEach(function(inst) {\n __instanceNames[inst.instanceDid] = inst.appName || inst.instanceDid.substring(0, 16);\n var opt = document.createElement(\"option\");\n opt.value = inst.instanceDid;\n opt.textContent = __instanceNames[inst.instanceDid];\n sel.appendChild(opt);\n });\n sel.style.display = \"\";\n }\n } catch(e) { /* not admin or no instances */ }\n}\n\n// Also support specific instance filter via query param\n// When ?instance=z1xxx, team handler needs to use that instanceDid\n\nasync function loadMembers(page) {\n membersPage = page || 1;\n var instanceFilter = document.getElementById(\"members-instance-filter\").value;\n var qs = \"?page=\" + membersPage + \"&pageSize=\" + membersPageSize;\n // System view (\"\") \u2192 use defaultInstanceDid so we query membership, not global users\n if (instanceFilter === \"\" && __pageData.instanceDid) {\n qs += \"&instance=\" + encodeURIComponent(__pageData.instanceDid);\n } else if (instanceFilter) {\n qs += \"&instance=\" + encodeURIComponent(instanceFilter);\n }\n var search = document.getElementById(\"members-search\").value.trim();\n if (search) qs += \"&search=\" + encodeURIComponent(search);\n var role = document.getElementById(\"members-role-filter\").value;\n if (role) qs += \"&role=\" + encodeURIComponent(role);\n var status = document.getElementById(\"members-status-filter\").value;\n if (status !== \"\") qs += \"&approved=\" + status;\n var source = document.getElementById(\"members-source-filter\").value;\n if (source) qs += \"&sourceProvider=\" + encodeURIComponent(source);\n if (membersSortBy) {\n qs += \"&sortBy=\" + encodeURIComponent(membersSortBy);\n qs += \"&sortOrder=\" + encodeURIComponent(membersSortOrder || \"desc\");\n }\n\n document.getElementById(\"members-table-wrap\").innerHTML = skeletonTable(8, 6);\n document.getElementById(\"members-pagination\").innerHTML = \"\";\n var data = await api(\"GET\", \"/members\" + qs);\n if (!data.ok) {\n document.getElementById(\"members-table-wrap\").innerHTML = '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 8v4m0 4h.01\"/></svg></div><div class=\"empty-state-title\">' + __t(\"members.loadFailed\") + '</div></div>';\n return;\n }\n __membersAggregated = !!data.aggregated;\n // Only pin the caller to the top when using the default (server) order.\n // Under explicit sort, honor the user's intent without re-shuffling.\n if (membersSortBy) {\n currentMembersList = data.users || [];\n } else {\n currentMembersList = (data.users || []).slice().sort(function(a, b) {\n if (a.did === __caller.did) return -1;\n if (b.did === __caller.did) return 1;\n return (a.createdAt || 0) - (b.createdAt || 0);\n });\n }\n renderMembersTable(currentMembersList, data.paging);\n updateTransferSection(data.users);\n}\n\nfunction renderMembersTable(users, paging) {\n var wrap = document.getElementById(\"members-table-wrap\");\n // Aggregated (cross-instance \"All Instances\") view can't tell which instance\n // a role change should apply to, so per-row actions are disabled there \u2014 see\n // canOperate below. Surface why, instead of silently hiding the buttons.\n var aggregatedHint = __membersAggregated ? '<p class=\"text-muted\" style=\"font-size:13px;margin-bottom:12px;\">' + __t(\"members.aggregatedHint\") + '</p>' : '';\n if (!users || users.length === 0) {\n wrap.innerHTML = aggregatedHint + '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/></svg></div><div class=\"empty-state-title\">' + __t(\"members.noMembers\") + '</div><div class=\"empty-state-desc\">' + __t(\"members.noMembersHint\") + '</div></div>';\n document.getElementById(\"members-pagination\").innerHTML = \"\";\n return;\n }\n\n var instanceSel = document.getElementById(\"members-instance-filter\");\n var instanceVisible = instanceSel && instanceSel.style.display !== 'none';\n var showInstance = instanceVisible && instanceSel.value === \"all\";\n var html = aggregatedHint + '<table class=\"table\"><thead><tr>';\n html += sortableTh(\"name\", __t(\"members.col.member\"));\n html += sortableTh(\"role\", __t(\"members.col.role\"));\n if (showInstance) html += '<th>' + __t(\"members.col.instance\") + '</th>';\n html += sortableTh(\"source\", __t(\"members.col.source\"), \"col-hide-sm\");\n html += sortableTh(\"status\", __t(\"members.col.status\"), \"col-hide-sm\");\n html += sortableTh(\"joined\", __t(\"members.col.joined\"), \"col-hide-md\");\n // Registered/Last Login From columns removed \u2014 domain info not meaningful in Worker context\n html += '<th></th></tr></thead><tbody>';\n users.forEach(function(u) {\n var userDid = u.did || u.user_did || '';\n var av = renderAvatar(u.avatar, u.fullName, \"sm\");\n var name = u.fullName ? escapeHtml(u.fullName) : '<span class=\"text-muted\">(unnamed)</span>';\n var did = '<did-address did=\"' + escapeHtml(userDid) + '\" compact></did-address>';\n var roleBadge = '<span class=\"badge badge-' + (u.role || \"guest\") + '\">' + __t(\"common.\" + (u.role || \"guest\")) + '</span>';\n var statusBadge = u.approved !== undefined ? (u.approved ? '<span class=\"badge badge-active\">' + __t(\"common.active\") + '</span>' : '<span class=\"badge badge-blocked\">' + __t(\"common.blocked\") + '</span>') : '<span class=\"text-muted\">\u2014</span>';\n var joined = (u.createdAt || u.joined_at) ? '<span class=\"cell-date\">' + escapeHtml(relativeTime(u.createdAt || u.joined_at)) + '</span>' : '<span class=\"text-muted\">\u2014</span>';\n html += '<tr style=\"cursor:pointer\" onclick=\"openMemberDetail(\\'' + escapeHtml(userDid) + '\\')\">';\n html += '<td><div class=\"member-identity\">' + av + '<div><div class=\"member-name\">' + name + '</div><div class=\"member-email\">' + did + '</div></div></div></td>';\n html += '<td>' + roleBadge + '</td>';\n if (showInstance) {\n if (__membersAggregated && u.instance_count > 1) {\n html += '<td><a href=\"javascript:void(0)\" onclick=\"event.stopPropagation();openMemberDetail(\\'' + escapeHtml(userDid) + '\\')\" style=\"color:var(--blue);font-size:14px;text-decoration:none\">' + u.instance_count + ' ' + __t(\"members.instances\") + '</a></td>';\n } else {\n var instDid = (__membersAggregated && u.instances && u.instances.length === 1) ? u.instances[0].instance_did : (u.instance_did || '');\n var instName = __instanceNames[instDid] || '';\n if (!instName) instName = instDid ? __t(\"members.currentInstance\") : '\u2014';\n html += '<td><div>' + escapeHtml(instName);\n if (instDid) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\" onclick=\"event.stopPropagation()\"><did-address did=\"' + escapeHtml(instDid) + '\" compact></did-address></div>';\n html += '</div></td>';\n }\n }\n var sourceBadge = '<span class=\"text-muted\">' + escapeHtml(providerLabel(u.sourceProvider)) + '</span>';\n html += '<td class=\"col-hide-sm\">' + sourceBadge + '</td>';\n html += '<td class=\"col-hide-sm\">' + statusBadge + '</td>';\n html += '<td class=\"col-hide-md\">' + joined + '</td>';\n // Actions: only for current instance members, not cross-instance\n var canOperate = !instanceVisible || (instanceVisible && instanceSel.value === '');\n // In aggregated mode, only allow actions on users whose instance matches current system\n if (__membersAggregated) canOperate = false;\n html += '<td class=\"action-cell\">' + (canOperate ? renderMemberActions(u) : '') + '</td>';\n html += '</tr>';\n });\n html += '</tbody></table>';\n wrap.innerHTML = html;\n\n renderPagination(\"members-pagination\", paging, loadMembers);\n}\n\nfunction renderMemberActions(u) {\n var isOwner = __caller.role === \"owner\";\n var isAdmin = __caller.role === \"admin\";\n var isSelf = u.did === __caller.did;\n var ROLE_LEVEL = { owner: 3, admin: 2, member: 1, guest: 0 };\n var callerLevel = ROLE_LEVEL[__caller.role] || 0;\n var targetLevel = ROLE_LEVEL[u.role] || 0;\n // Can only act on members with lower privilege\n var canManage = !isSelf && targetLevel < callerLevel;\n var items = [];\n\n // Change role: can manage lower-level members\n if (canManage) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();openChangeRoleForDid(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.changeRole\") + '</button>');\n }\n if (canManage && u.approved) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();blockMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.block\") + '</button>');\n }\n if (canManage && !u.approved) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();unblockMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.unblock\") + '</button>');\n }\n if (items.length > 0 && canManage) {\n items.push('<div class=\"action-menu-sep\"></div>');\n }\n if (canManage) {\n items.push('<button class=\"action-menu-item danger\" onclick=\"event.stopPropagation();removeMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.remove\") + '</button>');\n }\n\n if (items.length === 0) return \"\";\n return '<button class=\"action-trigger\" onclick=\"event.stopPropagation();toggleActionMenu(this)\">\u22EF</button><div class=\"action-menu\">' + items.join(\"\") + '</div>';\n}\n\nfunction toggleActionMenu(trigger) {\n var menu = trigger.nextElementSibling;\n var wasOpen = menu.classList.contains(\"open\");\n // Close all menus first\n document.querySelectorAll(\".action-menu.open\").forEach(function(m) { m.classList.remove(\"open\"); });\n if (!wasOpen) menu.classList.toggle(\"open\");\n}\n\nasync function openMemberDetail(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n if (!u) return;\n selectedMember = u;\n\n // Fetch full member info (with connectedAccounts) from API.\n // Falls back to in-memory list data if the request fails.\n var detail = u;\n try {\n var resp = await api(\"GET\", \"/members/\" + encodeURIComponent(did));\n if (resp && resp.ok && resp.user) {\n // Merge: in-memory list has fields the API may omit (instances, passkeyCount in some paths).\n detail = Object.assign({}, u, resp.user);\n selectedMember = detail;\n }\n } catch (e) {}\n\n var body = document.getElementById(\"member-detail-body\");\n var html = '<div class=\"profile-header\">' + renderAvatar(detail.avatar, detail.fullName) + '<div>';\n html += '<div style=\"font-weight:500;font-size:16px;color:var(--text-white)\">' + escapeHtml(detail.fullName || \"(unnamed)\") + '</div>';\n html += '<did-address did=\"' + escapeHtml(detail.did) + '\" compact></did-address>';\n html += '</div></div>';\n\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailRole\") + '</span><span class=\"settings-value\"><span class=\"badge badge-' + (detail.role || \"guest\") + '\">' + __t(\"common.\" + (detail.role || \"guest\")) + '</span></span></div>';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailStatus\") + '</span><span class=\"settings-value\">' + (detail.approved ? '<span class=\"badge badge-active\">' + __t(\"common.active\") + '</span>' : '<span class=\"badge badge-blocked\">' + __t(\"common.blocked\") + '</span>') + '</span></div>';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailSource\") + '</span><span class=\"settings-value\">' + escapeHtml(providerLabel(detail.sourceProvider)) + '</span></div>';\n if (detail.email) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailEmail\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.email) + '</span></div>';\n if (detail.inviterName) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailInvitedBy\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.inviterName) + '</span></div>';\n if (detail.createdAt) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailJoined\") + '</span><span class=\"settings-value\">' + escapeHtml(absoluteTime(detail.createdAt)) + '</span></div>';\n if (detail.lastLoginAt) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailLastLogin\") + '</span><span class=\"settings-value\">' + escapeHtml(absoluteTime(detail.lastLoginAt)) + '</span></div>';\n if (detail.sourceDomain) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailRegisteredFrom\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.sourceDomain) + '</span></div>';\n if (detail.lastLoginDomain) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailLastLoginFrom\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.lastLoginDomain) + '</span></div>';\n if (detail.passkeyCount !== undefined) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailPasskeys\") + '</span><span class=\"settings-value\">' + detail.passkeyCount + '</span></div>';\n html += '</div>';\n\n // Connected Accounts (read-only)\n if (detail.connectedAccounts && detail.connectedAccounts.length > 0) {\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-card-header\"><span>' + __t(\"members.detailConnectedAccounts\") + ' (' + detail.connectedAccounts.length + ')</span></div>';\n html += '<div style=\"max-height:240px;overflow-y:auto\">';\n detail.connectedAccounts.forEach(function(a) {\n var label = providerLabel(a.provider);\n var info = '';\n if (a.userInfo && a.userInfo.name) info = a.userInfo.name;\n else if (a.userInfo && a.userInfo.email) info = a.userInfo.email;\n else if (a.id) info = a.id;\n html += '<div class=\"settings-row\" style=\"gap:12px;align-items:flex-start\">';\n html += '<div style=\"flex:1;min-width:0\">';\n html += '<div style=\"font-size:14px;font-weight:500;color:var(--text)\">' + escapeHtml(label);\n if (a.isMain) html += ' <span class=\"badge badge-active\" style=\"font-size:10px;padding:1px 6px;\">' + __t(\"profile.mainAccount\") + '</span>';\n html += '</div>';\n if (info) html += '<div style=\"font-size:12px;color:var(--text-secondary);margin-top:2px\">' + escapeHtml(info) + '</div>';\n if (a.did) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\"><did-address did=\"' + escapeHtml(a.did) + '\" compact></did-address></div>';\n html += renderAccountTimeRows(a);\n html += '</div>';\n html += '</div>';\n });\n html += '</div></div>';\n }\n\n // Instance memberships (aggregated mode)\n if (detail.instances && detail.instances.length > 0) {\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-card-header\"><span>' + __t(\"members.instanceMemberships\") + ' (' + detail.instances.length + ')</span></div>';\n html += '<div style=\"max-height:240px;overflow-y:auto\">';\n detail.instances.forEach(function(inst) {\n var instName = __instanceNames[inst.instance_did] || '';\n var instDid = inst.instance_did || '';\n html += '<div class=\"settings-row\" style=\"gap:12px\">';\n html += '<div style=\"flex:1;min-width:0\"><div style=\"font-size:14px;font-weight:500;color:var(--text)\">' + escapeHtml(instName || __t(\"members.currentInstance\")) + '</div>';\n if (instDid) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\"><did-address did=\"' + escapeHtml(instDid) + '\" compact></did-address></div>';\n html += '</div>';\n html += '<div style=\"display:flex;align-items:center;gap:8px;flex-shrink:0\"><span class=\"badge badge-' + inst.role + '\">' + __t(\"common.\" + inst.role) + '</span>';\n if (inst.joined_at) html += '<span style=\"font-size:12px;color:var(--text-secondary)\">' + escapeHtml(relativeTime(inst.joined_at)) + '</span>';\n html += '</div></div>';\n });\n html += '</div></div>';\n }\n\n body.innerHTML = html;\n\n // Change role: mirror list's canOperate + renderMemberActions logic exactly.\n // Use post-merge detail so role/did reflect the latest API response.\n var changeRoleBtn = document.getElementById(\"member-detail-change-role\");\n var instanceSel = document.getElementById(\"members-instance-filter\");\n var instanceVisible = instanceSel && instanceSel.style.display !== 'none';\n var canOperate = !instanceVisible || (instanceVisible && instanceSel.value === '');\n if (__membersAggregated) canOperate = false;\n var isSelf = detail.did === __caller.did;\n var ROLE_LEVEL = { owner: 3, admin: 2, member: 1, guest: 0 };\n var callerLevel = ROLE_LEVEL[__caller.role] || 0;\n var targetLevel = ROLE_LEVEL[detail.role] || 0;\n var canChangeRole = canOperate && !isSelf && targetLevel < callerLevel;\n if (canChangeRole) {\n changeRoleBtn.classList.remove(\"hidden\");\n } else {\n changeRoleBtn.classList.add(\"hidden\");\n }\n\n showDialog(\"member-detail-dialog\");\n}\n\nfunction openChangeRoleForDid(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n if (!u) return;\n selectedMember = u;\n openChangeRoleDialog();\n}\n\nfunction openChangeRoleDialog() {\n if (!selectedMember) return;\n document.getElementById(\"change-role-title\").textContent = __t(\"members.changeRole\");\n document.getElementById(\"change-role-subtitle\").textContent = __t(\"members.changeRoleFor\", { name: selectedMember.fullName || truncateDid(selectedMember.did) });\n // Show/hide role options based on caller's level (only owner can assign admin)\n var radios = document.querySelectorAll('input[name=\"new-role\"]');\n radios.forEach(function(r) {\n r.checked = r.value === selectedMember.role;\n var label = r.closest('label');\n if (label) {\n if (r.value === 'admin' && __caller.role !== 'owner') label.style.display = 'none';\n else label.style.display = '';\n }\n });\n closeDialog(\"member-detail-dialog\");\n showDialog(\"change-role-dialog\");\n}\n\nasync function saveRoleChange() {\n if (!selectedMember) return;\n var selected = document.querySelector('input[name=\"new-role\"]:checked');\n if (!selected) return;\n var newRole = selected.value;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(selectedMember.did) + \"/role\", { role: newRole });\n if (data.ok) {\n showToast(__t(\"members.roleUpdated\"), \"success\");\n closeDialog(\"change-role-dialog\");\n loadMembers(membersPage);\n }\n}\n\n// name is looked up (not passed via onclick) so a member's self-set fullName\n// never has to be embedded as a JS-string literal inside an inline onclick=\n// attribute \u2014 HTML entity-escaping alone can't fully neutralize that context,\n// since the browser decodes entities before parsing onclick as script.\nasync function blockMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.blockTitle\"), message: __t(\"members.blockMsg\", { name: name }), confirmLabel: __t(\"members.blockBtn\"), danger: true });\n if (!ok) return;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(did) + \"/approval\", { approved: false });\n if (data.ok) { showToast(__t(\"members.blockSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nasync function unblockMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.unblockTitle\"), message: __t(\"members.unblockMsg\", { name: name }), confirmLabel: __t(\"members.unblock\") });\n if (!ok) return;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(did) + \"/approval\", { approved: true });\n if (data.ok) { showToast(__t(\"members.unblockSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nasync function removeMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.removeTitle\"), message: __t(\"members.removeMsg\", { name: name }), confirmLabel: __t(\"members.remove\"), danger: true });\n if (!ok) return;\n var data = await api(\"DELETE\", \"/members/\" + encodeURIComponent(did));\n if (data.ok) { showToast(__t(\"members.removeSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nfunction updateTransferSection(users) {\n var section = document.getElementById(\"ownership-transfer-section\");\n if (__caller.role !== \"owner\") {\n section.classList.add(\"hidden\");\n return;\n }\n section.classList.remove(\"hidden\");\n var select = document.getElementById(\"transfer-target\");\n var html = '<option value=\"\">' + __t(\"members.transferSelect\") + '</option>';\n users.forEach(function(u) {\n if (u.did !== __caller.did && u.approved && u.role !== \"owner\") {\n html += '<option value=\"' + escapeHtml(u.did) + '\">' + escapeHtml(u.fullName || truncateDid(u.did)) + ' (' + u.role + ')</option>';\n }\n });\n select.innerHTML = html;\n}\n\nasync function handleTransferOwnership() {\n var target = document.getElementById(\"transfer-target\").value;\n if (!target) { showToast(__t(\"members.selectMemberFirst\"), \"error\"); return; }\n var targetUser = currentMembersList.find(function(u) { return u.did === target; });\n var name = targetUser ? (targetUser.fullName || truncateDid(target)) : truncateDid(target);\n var ok = await confirmDialog({\n title: __t(\"members.transferTitle\"),\n message: __t(\"members.transferMsg\", { name: name }),\n confirmLabel: __t(\"members.transferConfirm\"),\n danger: true,\n });\n if (!ok) return;\n var data = await api(\"POST\", \"/transfer-ownership\", { targetDid: target });\n if (data.ok) {\n showToast(__t(\"members.transferSuccess\"), \"success\");\n setTimeout(function() { location.reload(); }, 1000);\n }\n}\n\n// Button-driven mutations must not double-submit (submitOnce \u2014 utils.ts).\nsaveRoleChange = submitOnce(saveRoleChange);\nblockMember = submitOnce(blockMember);\nunblockMember = submitOnce(unblockMember);\nremoveMember = submitOnce(removeMember);\nhandleTransferOwnership = submitOnce(handleTransferOwnership);\n\n// Close action menus on outside click\ndocument.addEventListener(\"click\", function() {\n document.querySelectorAll(\".action-menu.open\").forEach(function(m) { m.classList.remove(\"open\"); });\n});\n\nregisterTab(\"members\", function() {\n initInstanceFilter().then(function() {\n // Auto-select instance from URL query (e.g., ?instance=z1xxx)\n var urlInstance = new URLSearchParams(window.location.search).get(\"instance\");\n if (urlInstance) {\n var sel = document.getElementById(\"members-instance-filter\");\n if (sel) sel.value = urlInstance;\n }\n // Restore sort state from URL (sortBy / sortOrder) on tab enter.\n readMembersSortFromURL();\n loadMembers(1);\n });\n});\n";
7
7
  export {};
8
8
  //# sourceMappingURL=tab-members.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tab-members.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-members.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAEpE,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAiG7C;AAED,eAAO,MAAM,kBAAkB,ki2BA+e9B,CAAC"}
1
+ {"version":3,"file":"tab-members.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-members.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAEpE,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAiG7C;AAED,eAAO,MAAM,kBAAkB,w61BA+e9B,CAAC"}
@@ -415,8 +415,8 @@ async function openMemberDetail(did) {
415
415
  html += '</div>';
416
416
  if (info) html += '<div style="font-size:12px;color:var(--text-secondary);margin-top:2px">' + escapeHtml(info) + '</div>';
417
417
  if (a.did) html += '<div style="font-size:11px;color:var(--text-secondary);margin-top:2px"><did-address did="' + escapeHtml(a.did) + '" compact></did-address></div>';
418
+ html += renderAccountTimeRows(a);
418
419
  html += '</div>';
419
- if (a.lastLoginAt) html += '<div style="font-size:12px;color:var(--text-secondary);flex-shrink:0">' + escapeHtml(relativeTime(a.lastLoginAt)) + '</div>';
420
420
  html += '</div>';
421
421
  });
422
422
  html += '</div></div>';
@@ -1 +1 @@
1
- {"version":3,"file":"tab-profile-accounts.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-profile-accounts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAqBpE,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAUxD;AAID,eAAO,MAAM,yBAAyB,QAkjClC,CAAC"}
1
+ {"version":3,"file":"tab-profile-accounts.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-profile-accounts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAqBpE,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAUxD;AAID,eAAO,MAAM,yBAAyB,QAgiClC,CAAC"}
@@ -242,30 +242,12 @@ function renderBoundCard(a) {
242
242
  html += '</div>';
243
243
  }
244
244
 
245
- html += renderAccountTimes(a);
245
+ html += renderAccountTimeRows(a);
246
246
 
247
247
  html += '</div>';
248
248
  return html;
249
249
  }
250
250
 
251
- function renderAccountTimes(a) {
252
- var rows = "";
253
- if (a.createdAt) {
254
- rows += '<div class="ca-bound-time">';
255
- rows += '<span class="ca-bound-time-label">' + __t("profile.createdAt") + '</span>';
256
- rows += '<span class="ca-bound-time-value">' + escapeHtml(absoluteTime(a.createdAt)) + '</span>';
257
- rows += '</div>';
258
- }
259
- if (a.lastLoginAt) {
260
- rows += '<div class="ca-bound-time">';
261
- rows += '<span class="ca-bound-time-label">' + __t("profile.lastUsed") + '</span>';
262
- rows += '<span class="ca-bound-time-value">' + escapeHtml(absoluteTime(a.lastLoginAt)) + '</span>';
263
- rows += '</div>';
264
- }
265
- if (!rows) return "";
266
- return '<div class="ca-bound-times">' + rows + '</div>';
267
- }
268
-
269
251
  function getPasskeyName(a) {
270
252
  return (a.userInfo && a.userInfo.name) ? a.userInfo.name : "Passkey";
271
253
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tab-profile-accounts.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-profile-accounts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAIvE,uDAAuD;AACvD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC;AAE/D,4DAA4D;AAC5D,SAAS,gBAAgB;IACvB,MAAM,GAAG,GAA2B;QAClC,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,eAAe;QACvB,aAAa,EAAE,eAAe;QAC9B,KAAK,EAAE,UAAU;KAClB,CAAC;IACF,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,CAAM;IAC9C,OAAO,UAAU,CAAC;;;0CAGsB,CAAC,CAAC,2BAA2B,CAAC;;;;;CAKvE,CAAC;AACF,CAAC;AAED,mFAAmF;AACnF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,eAAe,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;;;;;wBAKjD,gBAAgB,EAAE;0BAChB,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC;EAC7D,uBAAuB,CAAC,QAAQ,EAAE;EAClC,0BAA0B,CAAC,QAAQ,EAAE;EACrC,2BAA2B,CAAC,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyiCvC,CAAC,EAAE,CAAC"}
1
+ {"version":3,"file":"tab-profile-accounts.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-profile-accounts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAIvE,uDAAuD;AACvD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC;AAE/D,4DAA4D;AAC5D,SAAS,gBAAgB;IACvB,MAAM,GAAG,GAA2B;QAClC,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,eAAe;QACvB,aAAa,EAAE,eAAe;QAC9B,KAAK,EAAE,UAAU;KAClB,CAAC;IACF,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,CAAM;IAC9C,OAAO,UAAU,CAAC;;;0CAGsB,CAAC,CAAC,2BAA2B,CAAC;;;;;CAKvE,CAAC;AACF,CAAC;AAED,mFAAmF;AACnF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,eAAe,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;;;;;wBAKjD,gBAAgB,EAAE;0BAChB,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC;EAC7D,uBAAuB,CAAC,QAAQ,EAAE;EAClC,0BAA0B,CAAC,QAAQ,EAAE;EACrC,2BAA2B,CAAC,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuhCvC,CAAC,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"homepage.d.ts","sourceRoot":"","sources":["../../src/pages/homepage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA2GD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGzB,CAAC;AA+MF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CA+J5D"}
1
+ {"version":3,"file":"homepage.d.ts","sourceRoot":"","sources":["../../src/pages/homepage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA2GD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGzB,CAAC;AA+MF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAoK5D"}
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { assetUrl } from "../asset-registry.js";
8
8
  import { resolvePageLocale } from "../locale.js";
9
+ import { INSTANCE_PALETTE_CSS, THEME_HEAD_PIN_JS } from "../theme-utils.js";
9
10
  // ─── i18n ────────────────────────────────────────────────────────────────
10
11
  const EN = {
11
12
  portal: "Service Portal",
@@ -303,7 +304,9 @@ function escapeForScript(s) {
303
304
  }
304
305
  export function buildHomepageHTML(data) {
305
306
  const { locale, theme, appName, appDescription, appLogo, version } = data;
306
- const dataThemeAttr = theme === "light" ? ' data-theme="light"' : theme === "auto" ? ' data-theme="auto"' : "";
307
+ // Fallback only: theme/init.js resolves the real mode before paint (shared
308
+ // web-mode / instance prefer / OS) and THEME_HEAD_PIN_JS pins it.
309
+ const dataThemeAttr = theme === "light" ? ' data-theme="light"' : theme === "dark" ? ' data-theme="dark"' : "";
307
310
  const { code, htmlLang } = resolvePageLocale(locale);
308
311
  const description = appDescription || t("heroDesc", code);
309
312
  const cards = [
@@ -340,8 +343,11 @@ export function buildHomepageHTML(data) {
340
343
  <meta charset="utf-8" />
341
344
  <meta name="viewport" content="width=device-width, initial-scale=1" />
342
345
  <title>${escapeHTML(appName)} — ${escapeHTML(t("portal", code))}</title>
346
+ <link rel="stylesheet" href="/.well-known/service/theme/styles.css" />
347
+ <script src="/.well-known/service/theme/init.js"></script>
348
+ <script>${THEME_HEAD_PIN_JS}</script>
343
349
  <link rel="stylesheet" href="${assetUrl("design.css")}" />
344
- <style>${HOMEPAGE_STYLES}</style>
350
+ <style>${HOMEPAGE_STYLES}${INSTANCE_PALETTE_CSS}</style>
345
351
  <script src="${assetUrl("header.js")}" defer></script>
346
352
  </head>
347
353
  <body>
@@ -1 +1 @@
1
- {"version":3,"file":"homepage.js","sourceRoot":"","sources":["../../src/pages/homepage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"homepage.js","sourceRoot":"","sources":["../../src/pages/homepage.ts"],"names":[],"mappings":"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"}