From 1aa83d83f95fa1ba3a41f5838417c669ad7b8049 Mon Sep 17 00:00:00 2001 From: fretuks Date: Sun, 27 Sep 2026 21:35:05 +0200 Subject: [PATCH] Refactor dashboard.js: streamline state management, improve layouts, optimize role and command handlers. --- public/app.js | 13 +- public/concierge.png | Bin 0 -> 144791 bytes public/concierge.svg | 24 + public/dashboard.html | 684 +++++----- public/dashboard.js | 2113 +++++++++++++++---------------- public/icons.png | Bin 0 -> 24124 bytes public/icons.svg | 17 + public/index.html | 81 +- public/mark.png | Bin 0 -> 8303 bytes public/mark.svg | 1 + public/styles.css | 2800 ++++++++++++++++++++++++++--------------- 11 files changed, 3378 insertions(+), 2355 deletions(-) create mode 100644 public/concierge.png create mode 100644 public/concierge.svg create mode 100644 public/icons.png create mode 100644 public/icons.svg create mode 100644 public/mark.png create mode 100644 public/mark.svg diff --git a/public/app.js b/public/app.js index b4083f3..c5911d5 100644 --- a/public/app.js +++ b/public/app.js @@ -121,7 +121,7 @@ const initIndexPage = async () => { try { const data = await fetchJson('/api/me'); setCsrfToken(data.csrfToken); - sessionStatus.textContent = `Signed in as ${formatUserDisplay(data.user)}.`; + sessionStatus.textContent = formatUserDisplay(data.user); loginButton.hidden = true; dashboardButton.hidden = false; logoutButton.hidden = false; @@ -130,26 +130,27 @@ const initIndexPage = async () => { banner.style.display = 'none'; } } catch (error) { - sessionStatus.textContent = 'You are not signed in yet.'; + sessionStatus.textContent = ''; loginButton.hidden = false; dashboardButton.hidden = true; logoutButton.hidden = true; if (banner) { - banner.textContent = 'Log in with Discord to access the dashboard.'; - banner.style.display = 'block'; + const sessionExpired = error.status === 401 || error.status === 403; + banner.textContent = sessionExpired ? '' : 'Unable to check your session. Try again.'; + banner.style.display = sessionExpired ? 'none' : 'block'; } } logoutButton?.addEventListener('click', async () => { try { - await fetch('/auth/logout', { + await fetchJson('/auth/logout', { method: 'POST', credentials: 'include', headers: { ...(csrfToken ? {'X-CSRF-Token': csrfToken} : {}), }, }); - sessionStatus.textContent = 'Logged out successfully.'; + sessionStatus.textContent = ''; loginButton.hidden = false; dashboardButton.hidden = true; logoutButton.hidden = true; diff --git a/public/concierge.png b/public/concierge.png new file mode 100644 index 0000000000000000000000000000000000000000..4abd6cd13c1b24271a828870745f9416b0a1a0c9 GIT binary patch literal 144791 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 + + + + + + + + + + + + + + + + + + + + + +THE STEWARD + diff --git a/public/dashboard.html b/public/dashboard.html index bbd46c4..f3addf8 100644 --- a/public/dashboard.html +++ b/public/dashboard.html @@ -1,287 +1,407 @@ - - - - Discord Bot Dashboard - - - -
- - - - -
- -
- - -
-
-
- -
-

Select a guild

-
- - -
-
-
-
- - -
-
- -
- - - -
- -
-
-
-

Admin access

-

Control who can edit bot configuration and permissions.

-
-
- -
- - Admin roles - ? - -
-
-
- - Admin user IDs - ? - -
- - -
-
-
-
- - - - - - - - -
-
+ + + + + Overview · The Steward + + + + + +
+ +
+
+ + +
+
+ +
+

Overview

+
+ SSelect a server +
+
+
+
+
+ Bot status +
+ – +
+
+
+ +
+ – +
+
+
+ Admin roles +
+ – +
+
+
+ Restricted commands +
+ – +
+
+ + + + + + + +
+
+
+
+ + + + + + diff --git a/public/dashboard.js b/public/dashboard.js index 49c4042..11a2d2a 100644 --- a/public/dashboard.js +++ b/public/dashboard.js @@ -1,1076 +1,1083 @@ +const api = window.dashboardApi; +const $ = (id) => document.getElementById(id); const state = { - guilds: [], - filteredGuilds: [], - selectedGuildId: null, - roles: [], - permissions: null, - commands: [], - botGuildIds: new Set(), - botInstalled: false, - dirty: false, - originalPermissions: null, - selectedWarningUser: null, + guilds: [], + botGuildIds: new Set(), + botStatusKnown: false, + selectedGuildId: null, + roles: [], + commands: [], + baseCommands: [], + permissions: null, + originalPermissions: null, + botInstalled: false, + dirty: false, + loading: false, + saving: false, + tab: "settings", + selectedWarningUser: null, + warningBusy: false, + searchVersion: 0, }; - -const elements = { - errorBanner: document.getElementById('errorBanner'), - userDisplay: document.getElementById('userDisplay'), - sessionExpiry: document.getElementById('sessionExpiry'), - logoutButton: document.getElementById('logoutButton'), - guildList: document.getElementById('guildList'), - guildListSkeleton: document.getElementById('guildListSkeleton'), - guildEmpty: document.getElementById('guildEmpty'), - guildSearch: document.getElementById('guildSearch'), - guildHeaderIcon: document.getElementById('guildHeaderIcon'), - guildHeaderName: document.getElementById('guildHeaderName'), - guildStatusBadge: document.getElementById('guildStatusBadge'), - guildStatusNote: document.getElementById('guildStatusNote'), - inviteButton: document.getElementById('inviteButton'), - refreshBotButton: document.getElementById('refreshBotButton'), - tabs: document.querySelectorAll('.tab'), - settingsPanel: document.getElementById('settingsPanel'), - permissionsPanel: document.getElementById('permissionsPanel'), - warningsPanel: document.getElementById('warningsPanel'), - adminRolesPicker: document.getElementById('adminRolesPicker'), - adminUserInput: document.getElementById('adminUserInput'), - addAdminUserButton: document.getElementById('addAdminUserButton'), - adminUserChips: document.getElementById('adminUserChips'), - newCommandInput: document.getElementById('newCommandInput'), - addCommandButton: document.getElementById('addCommandButton'), - commandsList: document.getElementById('commandsList'), - commandsEmpty: document.getElementById('commandsEmpty'), - clearAllCommands: document.getElementById('clearAllCommands'), - copyFromGuild: document.getElementById('copyFromGuild'), - savePermissionsButton: document.getElementById('savePermissionsButton'), - discardChangesButton: document.getElementById('discardChangesButton'), - saveBar: document.getElementById('saveBar'), - botInactiveCallout: document.getElementById('botInactiveCallout'), - warningsUserInput: document.getElementById('warningsUserInput'), - warningsUserSearch: document.getElementById('warningsUserSearch'), - warningsUserResults: document.getElementById('warningsUserResults'), - warningsSelectedUser: document.getElementById('warningsSelectedUser'), - fetchWarningsButton: document.getElementById('fetchWarningsButton'), - clearWarningsButton: document.getElementById('clearWarningsButton'), - warningsList: document.getElementById('warningsList'), - warningsEmpty: document.getElementById('warningsEmpty'), - warningsSpinner: document.getElementById('warningsSpinner'), - newWarningUserInput: document.getElementById('newWarningUserInput'), - warningReasonInput: document.getElementById('warningReasonInput'), - warningCharCount: document.getElementById('warningCharCount'), - addWarningButton: document.getElementById('addWarningButton'), - warningStatus: document.getElementById('warningStatus'), - contentSkeleton: document.getElementById('contentSkeleton'), - copyModal: document.getElementById('copyModal'), - copyGuildSelect: document.getElementById('copyGuildSelect'), - cancelCopyButton: document.getElementById('cancelCopyButton'), - confirmCopyButton: document.getElementById('confirmCopyButton'), +const pages = { + settings: "Overview", + permissions: "Permissions", + warnings: "Warnings", }; - -const dashboardApi = window.dashboardApi; -const fetchDashboardJson = dashboardApi.fetchJson; - -const COMMAND_NAME_REGEX = /^[a-z0-9_-]{1,32}$/i; - -const clonePermissions = (permissions) => JSON.parse(JSON.stringify(permissions || {})); - -const normalizePermissions = (permissions = {}) => ({ - adminRoleIds: Array.isArray(permissions.adminRoleIds) ? permissions.adminRoleIds : [], - adminUserIds: Array.isArray(permissions.adminUserIds) ? permissions.adminUserIds : [], - commandPermissions: permissions.commandPermissions || {}, +const clone = (value) => JSON.parse(JSON.stringify(value)); +const node = (tag, className, text) => { + const element = document.createElement(tag); + if (className) element.className = className; + if (text !== undefined) element.textContent = text; + return element; +}; +const normalizeEntry = (entry = {}) => ({ + allowedRoleIds: Array.isArray(entry.allowedRoleIds) + ? [...entry.allowedRoleIds] + : [], + allowedUserIds: Array.isArray(entry.allowedUserIds) + ? [...entry.allowedUserIds] + : [], + restricted: Boolean( + entry.allowedRoleIds?.length || entry.allowedUserIds?.length, + ), }); - -const setLoadingGuilds = (isLoading) => { - elements.guildList.hidden = isLoading; - elements.guildListSkeleton.hidden = !isLoading; -}; - -const setLoadingDetails = (isLoading) => { - elements.contentSkeleton.hidden = !isLoading; - if (isLoading) { - elements.settingsPanel.hidden = true; - elements.permissionsPanel.hidden = true; - elements.warningsPanel.hidden = true; - return; - } - const activeTab = document.querySelector('.tab.active')?.dataset.tab || 'settings'; - elements.settingsPanel.hidden = activeTab !== 'settings'; - elements.permissionsPanel.hidden = activeTab !== 'permissions'; - elements.warningsPanel.hidden = activeTab !== 'warnings'; -}; - -const setDirty = (isDirty) => { - state.dirty = isDirty; - elements.saveBar.hidden = !isDirty; -}; - -const parseIdList = (value) => { - if (!value) { - return []; - } - return Array.from( - new Set( - value - .split(/[\s,\n]+/) - .map((entry) => entry.trim()) - .filter(Boolean), - ), - ); -}; - +const normalizePermissions = (permissions = {}) => ({ + adminRoleIds: [...(permissions.adminRoleIds || [])], + adminUserIds: [...(permissions.adminUserIds || [])], + commandPermissions: Object.fromEntries( + Object.entries(permissions.commandPermissions || {}).map( + ([name, entry]) => [name, normalizeEntry(entry)], + ), + ), +}); +const canonicalEntry = (entry = {}) => + JSON.stringify({ + roles: [...(entry.allowedRoleIds || [])].sort(), + users: [...(entry.allowedUserIds || [])].sort(), + restricted: Boolean(entry.restricted), + }); +const commandChanged = (name) => + canonicalEntry(state.permissions?.commandPermissions[name]) !== + canonicalEntry(state.originalPermissions?.commandPermissions[name]) || + !state.baseCommands.includes(name); +const permissionSignature = (permissions) => + JSON.stringify({ + roles: [...(permissions?.adminRoleIds || [])].sort(), + users: [...(permissions?.adminUserIds || [])].sort(), + commands: state.commands + .map((name) => [ + name, + canonicalEntry(permissions?.commandPermissions[name]), + ]) + .sort(), + }); const setError = (error) => { - if (!error) { - dashboardApi.clearErrorBanner(elements.errorBanner); - return; - } - dashboardApi.setErrorBanner(elements.errorBanner, error.message || 'Something went wrong.', error.payload || error); + if (!error) return api.clearErrorBanner($("errorBanner")); + api.setErrorBanner( + $("errorBanner"), + error.message || "Please try again.", + error.payload || { message: error.message }, + ); }; - -const handleFetchError = (error, fallback) => { - if (error?.code === 'BOT_NOT_IN_GUILD') { - return; - } - setError(error || {message: fallback}); -}; - -const updateGuildHeader = (guild) => { - if (!guild) { - elements.guildHeaderName.textContent = 'Select a guild'; - elements.guildHeaderIcon.src = ''; - elements.guildStatusBadge.textContent = ''; - elements.guildStatusNote.textContent = ''; - return; - } - elements.guildHeaderName.textContent = guild.name; - if (guild.icon) { - elements.guildHeaderIcon.src = `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png`; - } else { - elements.guildHeaderIcon.removeAttribute('src'); - } - if (state.botInstalled) { - elements.guildStatusBadge.textContent = 'Bot added'; - elements.guildStatusBadge.className = 'status-badge status-badge--ok'; - elements.guildStatusNote.textContent = 'Settings are active.'; - } else { - elements.guildStatusBadge.textContent = 'Not added'; - elements.guildStatusBadge.className = 'status-badge status-badge--warn'; - elements.guildStatusNote.textContent = 'The bot isn’t in this server yet. Invite it to configure settings.'; - } -}; - -const renderGuilds = () => { - elements.guildList.innerHTML = ''; - if (state.filteredGuilds.length === 0) { - elements.guildEmpty.hidden = false; - return; - } - elements.guildEmpty.hidden = true; - state.filteredGuilds.forEach((guild) => { - const item = document.createElement('button'); - item.type = 'button'; - item.className = `guild-item${state.selectedGuildId === guild.id ? ' active' : ''}`; - item.dataset.guildId = guild.id; - const info = document.createElement('div'); - info.className = 'guild-item__info'; - const icon = document.createElement('img'); - if (guild.icon) { - icon.src = `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png`; - icon.alt = `${guild.name} icon`; - } else { - icon.alt = ''; - } - const name = document.createElement('span'); - name.textContent = guild.name; - info.appendChild(icon); - info.appendChild(name); - - const badge = document.createElement('span'); - const isBotInGuild = state.botGuildIds.size === 0 ? null : state.botGuildIds.has(guild.id); - badge.className = `status-pill ${isBotInGuild ? 'status-pill--ok' : 'status-pill--warn'}`; - const dot = document.createElement('span'); - dot.className = 'status-pill__dot'; - const text = document.createElement('span'); - text.textContent = isBotInGuild ? 'Bot added' : 'Not added'; - badge.appendChild(dot); - badge.appendChild(text); - - item.appendChild(info); - item.appendChild(badge); - item.addEventListener('click', () => selectGuild(guild.id)); - elements.guildList.appendChild(item); +const parseIds = (value) => [...new Set(value.split(/[\s,]+/).filter(Boolean))]; +const validIds = (ids) => ids.every((id) => /^\d{17,20}$/.test(id)); +const readIds = (input) => { + const ids = parseIds(input.value.trim()); + if (!validIds(ids)) { + setError({ + message: + "Use Discord user IDs with 17–20 digits, separated by spaces or commas.", }); + input.focus(); + return []; + } + setError(null); + return ids; }; - -const renderRolePicker = (container, roles, selectedIds, onChange, options = {}) => { - container.innerHTML = ''; - const wrapper = document.createElement('div'); - wrapper.className = `multi-picker${options.disabled ? ' multi-picker--disabled' : ''}`; - - const search = document.createElement('input'); - search.type = 'text'; - search.className = 'multi-picker__search'; - search.placeholder = 'Search roles'; - const list = document.createElement('div'); - list.className = 'multi-picker__list'; - const chips = document.createElement('div'); - chips.className = 'chip-list'; - - const renderChips = () => { - chips.innerHTML = ''; - if (selectedIds.length === 0) { - const empty = document.createElement('span'); - empty.className = 'muted'; - empty.textContent = options.emptyMessage || 'No roles selected.'; - chips.appendChild(empty); - return; - } - selectedIds.forEach((roleId) => { - const role = roles.find((entry) => entry.id === roleId); - if (!role) { - return; - } - const chip = document.createElement('span'); - chip.className = 'chip'; - chip.textContent = role.name; - const removeButton = document.createElement('button'); - removeButton.type = 'button'; - removeButton.textContent = '✕'; - removeButton.addEventListener('click', () => { - const next = selectedIds.filter((id) => id !== roleId); - selectedIds.splice(0, selectedIds.length, ...next); - onChange([...selectedIds]); - renderList(search.value); - renderChips(); - }); - chip.appendChild(removeButton); - chips.appendChild(chip); - }); - }; - - const renderList = (query = '') => { - list.innerHTML = ''; - const filteredRoles = roles - .filter((role) => role.name.toLowerCase().includes(query.toLowerCase())) - .sort((a, b) => b.position - a.position); - if (filteredRoles.length === 0) { - const empty = document.createElement('span'); - empty.className = 'muted'; - empty.textContent = 'No matching roles.'; - list.appendChild(empty); - return; - } - filteredRoles.forEach((role) => { - const label = document.createElement('label'); - const checkbox = document.createElement('input'); - checkbox.type = 'checkbox'; - checkbox.checked = selectedIds.includes(role.id); - checkbox.disabled = options.disabled; - checkbox.addEventListener('change', () => { - if (checkbox.checked) { - if (!selectedIds.includes(role.id)) { - selectedIds.push(role.id); - } - } else { - const next = selectedIds.filter((id) => id !== role.id); - selectedIds.splice(0, selectedIds.length, ...next); - } - onChange([...selectedIds]); - renderChips(); - }); - label.appendChild(checkbox); - label.appendChild(document.createTextNode(role.name)); - list.appendChild(label); - }); - }; - - search.addEventListener('input', () => renderList(search.value)); - - renderList(); - renderChips(); - - wrapper.appendChild(search); - wrapper.appendChild(list); - wrapper.appendChild(chips); - container.appendChild(wrapper); -}; - -const renderAdminUsers = () => { - elements.adminUserChips.innerHTML = ''; - const adminUserIds = state.permissions?.adminUserIds || []; - if (adminUserIds.length === 0) { - const empty = document.createElement('span'); - empty.className = 'muted'; - empty.textContent = 'No admin users added.'; - elements.adminUserChips.appendChild(empty); - return; - } - - adminUserIds.forEach((userId) => { - const chip = document.createElement('span'); - chip.className = 'chip'; - chip.textContent = userId; - const removeButton = document.createElement('button'); - removeButton.type = 'button'; - removeButton.textContent = '✕'; - removeButton.addEventListener('click', () => { - state.permissions.adminUserIds = adminUserIds.filter((id) => id !== userId); - setDirty(true); - renderAdminUsers(); - }); - chip.appendChild(removeButton); - elements.adminUserChips.appendChild(chip); - }); -}; - -const formatMemberDisplay = (member) => { - const user = member?.user || {}; - const displayName = member?.nick || user.global_name || user.username || 'Unknown user'; - const tag = user.discriminator && user.discriminator !== '0' ? `#${user.discriminator}` : ''; - return `${displayName}${tag}`; -}; - -const setSelectedWarningUser = (member) => { - state.selectedWarningUser = member || null; - if (!member) { - elements.warningsSelectedUser.textContent = 'No user selected.'; - return; - } - elements.warningsSelectedUser.textContent = `${formatMemberDisplay(member)} (${member.id})`; - elements.warningsUserInput.value = member.id; - elements.newWarningUserInput.value = member.id; -}; - -const setManualWarningUser = (userId) => { - state.selectedWarningUser = null; - if (!userId) { - elements.warningsSelectedUser.textContent = 'No user selected.'; - return; - } - elements.warningsSelectedUser.textContent = `Using manual ID: ${userId}`; -}; - -const resetWarningSelection = () => { - setSelectedWarningUser(null); - elements.warningsUserResults.innerHTML = ''; - elements.warningsUserSearch.value = ''; - elements.warningsUserInput.value = ''; - elements.newWarningUserInput.value = ''; - elements.warningsList.innerHTML = ''; - elements.warningsEmpty.hidden = true; -}; - -const ensureCommandEntry = (commandName) => { - if (!state.permissions.commandPermissions[commandName]) { - state.permissions.commandPermissions[commandName] = { - allowedRoleIds: [], - allowedUserIds: [], - restricted: false, - }; - } - const entry = state.permissions.commandPermissions[commandName]; - entry.allowedRoleIds = Array.isArray(entry.allowedRoleIds) ? entry.allowedRoleIds : []; - entry.allowedUserIds = Array.isArray(entry.allowedUserIds) ? entry.allowedUserIds : []; - if (typeof entry.restricted !== 'boolean') { - entry.restricted = entry.allowedRoleIds.length > 0 || entry.allowedUserIds.length > 0; - } -}; - -const renderCommands = () => { - elements.commandsList.innerHTML = ''; - const commands = [...new Set(state.commands)].sort(); - if (commands.length === 0) { - elements.commandsEmpty.hidden = false; - return; - } - elements.commandsEmpty.hidden = true; - - commands.forEach((commandName) => { - ensureCommandEntry(commandName); - const commandConfig = state.permissions.commandPermissions[commandName]; - const isRestricted = Boolean(commandConfig.restricted); - - const card = document.createElement('div'); - card.className = `command-card${state.botInstalled ? '' : ' command-card--disabled'}`; - const header = document.createElement('div'); - header.className = 'command-card__header'; - - const title = document.createElement('strong'); - title.textContent = `/${commandName}`; - - const toggleLabel = document.createElement('label'); - toggleLabel.className = 'toggle'; - const toggle = document.createElement('input'); - toggle.type = 'checkbox'; - toggle.checked = isRestricted; - toggleLabel.appendChild(toggle); - toggleLabel.appendChild(document.createTextNode('Restricted')); - - header.appendChild(title); - header.appendChild(toggleLabel); - - const body = document.createElement('div'); - body.className = 'command-card__body'; - - const roleSection = document.createElement('div'); - const roleLabel = document.createElement('p'); - roleLabel.className = 'muted'; - roleLabel.textContent = 'Allowed roles'; - const rolePicker = document.createElement('div'); - roleSection.appendChild(roleLabel); - roleSection.appendChild(rolePicker); - - const userSection = document.createElement('div'); - const userLabel = document.createElement('p'); - userLabel.className = 'muted'; - userLabel.textContent = 'Allowed users'; - const userInput = document.createElement('input'); - userInput.type = 'text'; - userInput.placeholder = 'Paste user IDs'; - const addUserButton = document.createElement('button'); - addUserButton.type = 'button'; - addUserButton.className = 'button'; - addUserButton.textContent = 'Add'; - const userControls = document.createElement('div'); - userControls.className = 'input-row'; - userControls.appendChild(userInput); - userControls.appendChild(addUserButton); - const chipList = document.createElement('div'); - chipList.className = 'chip-list'; - - const renderUserChips = () => { - chipList.innerHTML = ''; - if (commandConfig.allowedUserIds.length === 0) { - const empty = document.createElement('span'); - empty.className = 'muted'; - empty.textContent = 'No users added.'; - chipList.appendChild(empty); - return; - } - commandConfig.allowedUserIds.forEach((userId) => { - const chip = document.createElement('span'); - chip.className = 'chip'; - chip.textContent = userId; - const remove = document.createElement('button'); - remove.type = 'button'; - remove.textContent = '✕'; - remove.addEventListener('click', () => { - commandConfig.allowedUserIds = commandConfig.allowedUserIds.filter((id) => id !== userId); - setDirty(true); - renderUserChips(); - }); - chip.appendChild(remove); - chipList.appendChild(chip); - }); - }; - - const addUsers = () => { - const values = parseIdList(userInput.value); - if (values.length === 0) { - return; - } - const merged = Array.from(new Set([...commandConfig.allowedUserIds, ...values])); - commandConfig.allowedUserIds = merged; - setDirty(true); - userInput.value = ''; - renderUserChips(); - }; - - addUserButton.addEventListener('click', addUsers); - userInput.addEventListener('keydown', (event) => { - if (event.key === 'Enter') { - event.preventDefault(); - addUsers(); - } - }); - - userSection.appendChild(userLabel); - userSection.appendChild(userControls); - userSection.appendChild(chipList); - renderUserChips(); - - const actions = document.createElement('div'); - const clearButton = document.createElement('button'); - clearButton.type = 'button'; - clearButton.className = 'button button--ghost'; - clearButton.textContent = 'Clear'; - clearButton.addEventListener('click', () => { - commandConfig.allowedRoleIds = []; - commandConfig.allowedUserIds = []; - commandConfig.restricted = false; - setDirty(true); - renderCommands(); - }); - actions.appendChild(clearButton); - - body.appendChild(roleSection); - body.appendChild(userSection); - body.appendChild(actions); - - toggle.addEventListener('change', () => { - setDirty(true); - commandConfig.restricted = toggle.checked; - if (!toggle.checked) { - commandConfig.allowedRoleIds = []; - commandConfig.allowedUserIds = []; - } - renderCommands(); - }); - - const controlsDisabled = !state.botInstalled || !isRestricted; - userInput.disabled = controlsDisabled; - addUserButton.disabled = controlsDisabled; - - renderRolePicker( - rolePicker, - state.roles, - commandConfig.allowedRoleIds, - (selected) => { - commandConfig.allowedRoleIds = selected; - setDirty(true); - }, - { - disabled: !state.botInstalled || !isRestricted, - emptyMessage: isRestricted ? 'Select roles to allow.' : 'Allow all roles.', - }, - ); - - card.appendChild(header); - card.appendChild(body); - elements.commandsList.appendChild(card); - }); -}; - -const renderPanels = () => { - renderRolePicker( - elements.adminRolesPicker, - state.roles, - state.permissions.adminRoleIds, - (selected) => { - state.permissions.adminRoleIds = selected; - setDirty(true); - }, - { - disabled: !state.botInstalled, - emptyMessage: state.botInstalled ? 'Select roles to grant admin access.' : 'Invite bot to load roles.', - }, +const updateSummary = () => { + const ready = Boolean(state.permissions); + const total = state.commands.length; + const restricted = state.commands.filter( + (name) => state.permissions?.commandPermissions[name]?.restricted, + ).length; + const changed = state.commands.filter(commandChanged).length; + $("navCommandCount").textContent = ready ? total : "–"; + $("overviewCommandCount").textContent = ready ? total : "–"; + $("overviewRoleCount").textContent = ready + ? state.permissions.adminRoleIds.length + : "–"; + $("overviewRestrictedCount").textContent = ready ? restricted : "–"; + $("permissionTotalCount").textContent = total; + $("permissionRestrictedCount").textContent = restricted; + $("permissionOpenCount").textContent = total - restricted; + $("permissionChangedCount").textContent = changed; + document + .querySelectorAll(".command-card") + .forEach((card) => + card.classList.toggle( + "command-card--changed", + commandChanged(card.dataset.command), + ), ); - renderAdminUsers(); - renderCommands(); - - if (!state.botInstalled) { - elements.settingsPanel.classList.add('panel--disabled'); - elements.permissionsPanel.classList.add('panel--disabled'); - elements.botInactiveCallout.hidden = false; - } else { - elements.settingsPanel.classList.remove('panel--disabled'); - elements.permissionsPanel.classList.remove('panel--disabled'); - elements.botInactiveCallout.hidden = true; - } }; - -const updateBotStatusControls = () => { - if (state.botInstalled) { - elements.inviteButton.hidden = true; - elements.refreshBotButton.hidden = true; - } else { - elements.inviteButton.hidden = false; - elements.refreshBotButton.hidden = false; - } +const updateDirty = () => { + state.dirty = Boolean( + state.permissions && + state.originalPermissions && + (permissionSignature(state.permissions) !== + permissionSignature(state.originalPermissions) || + state.commands.some((name) => !state.baseCommands.includes(name))), + ); + $("saveBar").hidden = !state.dirty; + updateSummary(); }; - -const loadSession = async () => { - const data = await fetchDashboardJson('/api/me'); - elements.userDisplay.textContent = dashboardApi.formatUserDisplay(data.user); - dashboardApi.setCsrfToken(data.csrfToken); - if (data.expiresAt) { - const expires = new Date(data.expiresAt); - elements.sessionExpiry.textContent = `Session expires ${expires.toLocaleString()}`; - } +const syncVisibility = () => { + const ready = Boolean(state.permissions) && !state.loading; + ["settings", "permissions", "warnings"].forEach((name) => { + $(`${name}Panel`).hidden = !ready || state.tab !== name; + }); + $("overviewStats").hidden = state.tab !== "settings"; + $("contentSkeleton").hidden = !state.loading; + $("workspaceEmpty").hidden = Boolean(state.selectedGuildId) || state.loading; + $("botInactiveCallout").hidden = + !state.selectedGuildId || state.botInstalled || !state.botStatusKnown; + $("inviteButton").hidden = $("botInactiveCallout").hidden; + $("saveBar").hidden = !state.dirty; + $("savePermissionsButton").disabled = state.saving || state.loading; + $("discardChangesButton").disabled = state.saving || state.loading; }; - -const loadGuilds = async () => { - setLoadingGuilds(true); - try { - const data = await fetchDashboardJson('/api/guilds'); - state.guilds = data.guilds || []; - state.filteredGuilds = [...state.guilds]; - if (state.guilds.length > 0 && !state.selectedGuildId) { - state.selectedGuildId = state.guilds[0].id; - } - renderGuilds(); - } finally { - setLoadingGuilds(false); - } +const switchTab = (name) => { + if (!pages[name]) return; + state.tab = name; + const label = pages[name]; + $("currentPage").textContent = label; + $("pageTitle").textContent = label; + document.title = `${label} · The Steward`; + document.querySelectorAll(".tab").forEach((tab) => { + tab.classList.toggle("active", tab.dataset.tab === name); + if (tab.dataset.tab === name) tab.setAttribute("aria-current", "page"); + else tab.removeAttribute("aria-current"); + }); + syncVisibility(); +}; +const initials = (name) => + name + .trim() + .split(/\s+/) + .slice(0, 2) + .map((word) => word[0]) + .join("") + .toUpperCase(); +const updateGuildHeader = () => { + const guild = state.guilds.find( + (entry) => entry.id === state.selectedGuildId, + ); + $("guildHeaderName").textContent = guild?.name || "Select a server"; + $("guildHeaderFallback").textContent = initials(guild?.name || "Steward"); + $("guildHeaderIcon").hidden = !guild?.icon; + $("guildHeaderFallback").hidden = Boolean(guild?.icon); + if (guild?.icon) + $("guildHeaderIcon").src = + `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png`; + else $("guildHeaderIcon").removeAttribute("src"); + const known = guild && state.botStatusKnown; + $("overviewBotStatus").textContent = !guild + ? "–" + : !known + ? "Unknown" + : state.botInstalled + ? "Added" + : "Not added"; + $("overviewBotStatus").classList.toggle( + "is-connected", + Boolean(known && state.botInstalled), + ); + $("refreshBotButton").hidden = !guild; +}; +const renderGuilds = () => { + const query = $("guildSearch").value.trim().toLowerCase(); + const guilds = state.guilds.filter((guild) => + guild.name.toLowerCase().includes(query), + ); + $("guildCount").textContent = state.guilds.length.toString().padStart(2, "0"); + $("guildList").replaceChildren(); + $("guildEmpty").hidden = guilds.length > 0; + $("guildEmpty").textContent = query + ? "No matching servers." + : "No servers available. Manage Server permission required."; + guilds.forEach((guild) => { + const button = node( + "button", + `guild-item${guild.id === state.selectedGuildId ? " active" : ""}`, + ); + button.type = "button"; + button.disabled = state.loading || state.saving; + button.setAttribute( + "aria-pressed", + String(guild.id === state.selectedGuildId), + ); + const info = node("span", "guild-item__info"); + const fallback = node("span", "guild-avatar", initials(guild.name)); + if (guild.icon) { + const img = node("img"); + img.src = `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png`; + img.alt = ""; + img.addEventListener("error", () => img.replaceWith(fallback), { + once: true, + }); + info.append(img); + } else info.append(fallback); + info.append(node("span", "guild-item__name", guild.name)); + const installed = state.botGuildIds.has(guild.id); + const status = !state.botStatusKnown + ? "Status unavailable" + : installed + ? "Bot added" + : "Not invited"; + button.title = `${guild.name} · ${status}`; + const badge = node( + "span", + `status-pill${installed ? " status-pill--ok" : ""}`, + ); + badge.setAttribute("aria-label", status); + badge.append(node("span", "status-pill__dot")); + button.append(info, badge); + button.addEventListener("click", () => selectGuild(guild.id)); + $("guildList").append(button); + }); +}; +const renderChips = (container, ids, nameForId, onRemove, disabled = false) => { + container.replaceChildren(); + container.hidden = !ids.length; + ids.forEach((id) => { + const chip = node("span", "chip", nameForId(id)); + const remove = node("button", "", "×"); + remove.type = "button"; + remove.disabled = disabled; + remove.setAttribute("aria-label", `Remove ${nameForId(id)}`); + remove.addEventListener("click", () => onRemove(id)); + chip.append(remove); + container.append(chip); + }); +}; +const renderRolePicker = (container, ids, onChange, label) => { + container.replaceChildren(); + const wrapper = node("div", "multi-picker"); + const search = node("input", "multi-picker__search"); + search.type = "search"; + search.placeholder = "Find a role…"; + search.setAttribute("aria-label", `Search ${label}`); + search.disabled = !state.botInstalled; + const list = node("div", "multi-picker__list"); + const chips = node("div", "chip-list"); + const refreshChips = () => { + renderChips( + chips, + ids, + (id) => + state.roles.find((role) => role.id === id)?.name || + `Unavailable role · ${id}`, + (id) => { + ids.splice(ids.indexOf(id), 1); + onChange(); + refreshList(); + refreshChips(); + }, + !state.botInstalled, + ); + }; + const refreshList = () => { + list.replaceChildren(); + const roles = state.roles + .filter((role) => + role.name.toLowerCase().includes(search.value.toLowerCase()), + ) + .sort((a, b) => b.position - a.position); + if (!roles.length) + list.append( + node( + "span", + "field-hint", + state.botInstalled + ? "No matching roles." + : "Server roles are unavailable.", + ), + ); + roles.forEach((role) => { + const row = node("label"); + const checkbox = node("input"); + checkbox.type = "checkbox"; + checkbox.checked = ids.includes(role.id); + checkbox.disabled = !state.botInstalled; + checkbox.addEventListener("change", () => { + if (checkbox.checked) ids.push(role.id); + else ids.splice(ids.indexOf(role.id), 1); + onChange(); + refreshChips(); + }); + const dot = node("span", "role-dot"); + if ( + Number.isInteger(role.color) && + role.color > 0 && + role.color <= 0xffffff + ) + dot.style.setProperty( + "--role-color", + `#${role.color.toString(16).padStart(6, "0")}`, + ); + row.append(checkbox, dot, document.createTextNode(role.name)); + list.append(row); + }); + }; + search.addEventListener("input", refreshList); + wrapper.append(search, list, chips); + container.append(wrapper); + refreshList(); + refreshChips(); +}; +const ensureCommands = () => { + state.commands = [ + ...new Set([ + ...state.commands, + ...Object.keys(state.permissions.commandPermissions), + ]), + ].sort(); + const entries = state.permissions.commandPermissions; + state.permissions.commandPermissions = Object.fromEntries( + state.commands.map((name) => [ + name, + Object.hasOwn(entries, name) ? entries[name] : normalizeEntry(), + ]), + ); +}; +const renderAdminUsers = () => + renderChips( + $("adminUserChips"), + state.permissions.adminUserIds, + (id) => id, + (id) => { + state.permissions.adminUserIds = state.permissions.adminUserIds.filter( + (entry) => entry !== id, + ); + updateDirty(); + renderAdminUsers(); + }, + !state.botInstalled, + ); +const renderCommands = () => { + $("commandsList").replaceChildren(); + if (!state.permissions) return; + const query = $("commandSearch") + .value.trim() + .toLowerCase() + .replace(/^\//, ""); + const filter = $("commandFilter").value; + const names = state.commands.filter( + (name) => + name.toLowerCase().includes(query) && + (filter === "all" || + (filter === "restricted" && + state.permissions.commandPermissions[name].restricted) || + (filter === "open" && + !state.permissions.commandPermissions[name].restricted) || + (filter === "changed" && commandChanged(name))), + ); + $("commandsEmpty").hidden = names.length > 0; + names.forEach((name) => { + const config = state.permissions.commandPermissions[name]; + const card = node( + "article", + `command-card${commandChanged(name) ? " command-card--changed" : ""}`, + ); + card.dataset.command = name; + const header = node("div", "command-card__header"); + const title = node("div", "command-card__title"); + title.append(node("strong", "", `/${name}`)); + const toggleLabel = node("label", "toggle"); + const toggle = node("input"); + toggle.type = "checkbox"; + toggle.checked = config.restricted; + toggle.disabled = !state.botInstalled; + toggle.setAttribute("aria-label", `Restrict /${name}`); + toggle.id = `restrict-${name}`; + toggleLabel.append(document.createTextNode("Restricted"), toggle); + header.append(title, toggleLabel); + card.append(header); + toggle.addEventListener("change", () => { + config.restricted = toggle.checked; + if (!toggle.checked) { + config.allowedRoleIds = []; + config.allowedUserIds = []; + } + updateDirty(); + renderCommands(); + document + .getElementById(`restrict-${name}`) + ?.focus({ preventScroll: true }); + }); + if (config.restricted) { + const body = node("div", "command-card__body"); + const roles = node("div", "field"); + roles.append(node("span", "", "Allowed roles")); + const picker = node("div"); + roles.append(picker); + const note = node("p", "command-card__note"); + const updateNote = () => { + note.hidden = Boolean( + config.allowedRoleIds.length || config.allowedUserIds.length, + ); + note.textContent = "Select at least one role or member."; + }; + renderRolePicker( + picker, + config.allowedRoleIds, + () => { + updateDirty(); + updateNote(); + }, + `roles for /${name}`, + ); + const users = node("div", "field"); + const label = node("label", "", "Allowed members"); + const input = node("input"); + input.type = "text"; + input.placeholder = "Paste Discord user IDs"; + input.id = `users-${name}`; + input.disabled = !state.botInstalled; + label.htmlFor = input.id; + const add = node("button", "button", "Add"); + add.type = "button"; + add.disabled = !state.botInstalled; + const row = node("div", "input-row"); + row.append(input, add); + const chips = node("div", "chip-list"); + const refreshUsers = () => + renderChips( + chips, + config.allowedUserIds, + (id) => id, + (id) => { + config.allowedUserIds = config.allowedUserIds.filter( + (entry) => entry !== id, + ); + updateDirty(); + refreshUsers(); + updateNote(); + }, + !state.botInstalled, + ); + const addUsers = () => { + const ids = readIds(input); + if (!ids.length) return; + config.allowedUserIds = [ + ...new Set([...config.allowedUserIds, ...ids]), + ]; + input.value = ""; + updateDirty(); + refreshUsers(); + updateNote(); + }; + add.addEventListener("click", addUsers); + input.addEventListener("keydown", (event) => { + if (event.key === "Enter") addUsers(); + }); + users.append(label, row, chips); + refreshUsers(); + updateNote(); + body.append(roles, users, note); + card.append(body); + } + $("commandsList").append(card); + }); + updateSummary(); +}; +const renderPanels = () => { + if (!state.permissions) return; + renderRolePicker( + $("adminRolesPicker"), + state.permissions.adminRoleIds, + updateDirty, + "admin roles", + ); + renderAdminUsers(); + renderCommands(); + updateSummary(); + [ + "adminUserInput", + "addAdminUserButton", + "newCommandInput", + "addCommandButton", + "clearAllCommands", + "copyFromGuild", + ].forEach((id) => { + $(id).disabled = !state.botInstalled; + }); + $("copyFromGuild").disabled = !state.botInstalled || state.guilds.length < 2; + $("warningsUserSearch").disabled = !state.botInstalled; + syncVisibility(); }; - const loadBotGuilds = async () => { - try { - const data = await fetchDashboardJson('/api/bot/guilds'); - state.botGuildIds = new Set(data.guildIds || []); - } catch (error) { - state.botGuildIds = new Set(); - handleFetchError(error, 'Failed to fetch bot status.'); - } + try { + const data = await api.fetchJson("/api/bot/guilds"); + state.botGuildIds = new Set(data.guildIds || []); + state.botStatusKnown = true; + } catch (error) { + state.botStatusKnown = false; + setError(error); + } }; - -const updateBotInstalled = () => { - state.botInstalled = state.selectedGuildId ? state.botGuildIds.has(state.selectedGuildId) : false; +const resetWarningHistory = () => { + $("warningsList").replaceChildren(); + $("warningsTotalCount").textContent = "–"; + $("warningsLatestDate").textContent = "–"; + $("warningsEmpty").hidden = false; + $("warningsEmpty").textContent = "Select a member to view warnings."; +}; +const warningTarget = () => $("warningsUserInput").value.trim(); +const setWarningTarget = (member = null) => { + state.selectedWarningUser = member; + if (member) $("warningsUserInput").value = member.id; + const id = warningTarget(); + $("warningsSelectedUser").textContent = member + ? member.nick || + member.user?.global_name || + member.user?.username || + member.id + : id || "No member selected"; + $("warningsSelectedCard").hidden = !id; + $("warningsSelectedHint").textContent = member ? id : ""; + $("warningReasonInput").value = ""; + $("warningStatus").textContent = ""; + updateWarningCount(); + resetWarningHistory(); + document + .querySelectorAll(".member-item") + .forEach((button) => + button.classList.toggle("active", button.dataset.userId === id), + ); +}; +const resetWarningSelection = () => { + state.searchVersion += 1; + $("warningsUserInput").value = ""; + $("warningsUserSearch").value = ""; + $("warningsUserResults").replaceChildren(); + $("warningsSearchStatus").textContent = ""; + setWarningTarget(); }; - const loadGuildDetails = async () => { - if (!state.selectedGuildId) { - return; - } - setLoadingDetails(true); - setError(null); - setDirty(false); - resetWarningSelection(); - - const guild = state.guilds.find((entry) => entry.id === state.selectedGuildId); - updateBotInstalled(); - updateGuildHeader(guild); - updateBotStatusControls(); - - try { - const [permissionsData, commandsData] = await Promise.all([ - fetchDashboardJson(`/api/guilds/${state.selectedGuildId}/permissions`), - fetchDashboardJson(`/api/guilds/${state.selectedGuildId}/commands`), - ]); - - state.permissions = normalizePermissions(permissionsData.permissions || {}); - state.permissions.commandPermissions = state.permissions.commandPermissions || {}; - state.commands = commandsData.commands || []; - - Object.keys(state.permissions.commandPermissions).forEach((commandName) => { - if (!state.commands.includes(commandName)) { - state.commands.push(commandName); - } - }); - - if (state.botInstalled) { - try { - const rolesData = await fetchDashboardJson(`/api/guilds/${state.selectedGuildId}/roles`); - state.roles = rolesData.roles || []; - } catch (error) { - if (error?.code === 'BOT_NOT_IN_GUILD') { - state.botInstalled = false; - state.roles = []; - } else { - handleFetchError(error, 'Failed to fetch roles.'); - } - } - } else { - state.roles = []; - } - - updateGuildHeader(guild); - updateBotStatusControls(); - state.originalPermissions = clonePermissions(state.permissions); - renderPanels(); - const activeTab = document.querySelector('.tab.active')?.dataset.tab || 'settings'; - switchTab(activeTab); - } catch (error) { - handleFetchError(error, 'Failed to load guild data.'); - } finally { - setLoadingDetails(false); + if (!state.selectedGuildId) { + syncVisibility(); + return; + } + state.loading = true; + state.permissions = null; + state.originalPermissions = null; + state.commands = []; + state.roles = []; + state.dirty = false; + state.botInstalled = + state.botGuildIds.has(state.selectedGuildId) && state.botStatusKnown; + resetWarningSelection(); + updateGuildHeader(); + updateSummary(); + syncVisibility(); + renderGuilds(); + try { + const [permissions, commands] = await Promise.all([ + api.fetchJson(`/api/guilds/${state.selectedGuildId}/permissions`), + api.fetchJson(`/api/guilds/${state.selectedGuildId}/commands`), + ]); + if (state.botInstalled) { + try { + state.roles = + (await api.fetchJson(`/api/guilds/${state.selectedGuildId}/roles`)) + .roles || []; + } catch (error) { + if (error.code === "BOT_NOT_IN_GUILD") { + state.botInstalled = false; + state.botGuildIds.delete(state.selectedGuildId); + } else setError(error); + } } + state.permissions = normalizePermissions(permissions.permissions || {}); + state.commands = commands.commands || []; + ensureCommands(); + state.baseCommands = [...state.commands]; + state.originalPermissions = clone(state.permissions); + renderPanels(); + updateGuildHeader(); + } catch (error) { + setError(error); + } finally { + state.loading = false; + syncVisibility(); + renderGuilds(); + } }; - -const selectGuild = async (guildId) => { - if (state.dirty) { - const proceed = window.confirm('You have unsaved changes. Discard them and switch guilds?'); - if (!proceed) { - return; - } +const selectGuild = async (id) => { + if (state.loading || state.saving || state.warningBusy) return; + if ( + state.dirty && + !window.confirm("Discard your unsaved changes and switch servers?") + ) + return; + setError(null); + state.selectedGuildId = id; + await loadGuildDetails(); +}; +const savePermissions = async () => { + if (!state.permissions || state.saving || state.loading || !state.dirty) + return; + const incomplete = state.commands.find((name) => { + const entry = state.permissions.commandPermissions[name]; + return ( + entry.restricted && + !entry.allowedRoleIds.length && + !entry.allowedUserIds.length + ); + }); + if (incomplete) { + switchTab("permissions"); + $("commandSearch").value = incomplete; + $("commandFilter").value = "all"; + renderCommands(); + setError({ + message: `Select a role or member for /${incomplete}, or turn off its restriction before saving.`, + }); + return; + } + state.saving = true; + state.loading = true; + syncVisibility(); + renderGuilds(); + $("savePermissionsButton").textContent = "Saving…"; + try { + const payload = { + adminRoleIds: state.permissions.adminRoleIds, + adminUserIds: state.permissions.adminUserIds, + commandPermissions: Object.fromEntries( + state.commands.map((name) => { + const { allowedRoleIds, allowedUserIds } = + state.permissions.commandPermissions[name]; + return [name, { allowedRoleIds, allowedUserIds }]; + }), + ), + }; + const data = await api.fetchJson( + `/api/guilds/${state.selectedGuildId}/permissions`, + { method: "PATCH", body: JSON.stringify(payload) }, + ); + state.permissions = normalizePermissions(data.permissions || payload); + ensureCommands(); + state.originalPermissions = clone(state.permissions); + state.baseCommands = [...state.commands]; + updateDirty(); + renderPanels(); + setError(null); + api.showToast("Changes saved.", "success"); + } catch (error) { + setError(error); + } finally { + state.saving = false; + state.loading = false; + $("savePermissionsButton").textContent = "Save changes"; + syncVisibility(); + renderGuilds(); + } +}; +const discardChanges = () => { + if (!state.originalPermissions || state.saving) return; + state.permissions = clone(state.originalPermissions); + state.commands = [...state.baseCommands]; + updateDirty(); + renderPanels(); + setError(null); +}; +const addAdminUser = () => { + if (!state.permissions || !state.botInstalled) return; + const ids = readIds($("adminUserInput")); + if (!ids.length) return; + state.permissions.adminUserIds = [ + ...new Set([...state.permissions.adminUserIds, ...ids]), + ]; + $("adminUserInput").value = ""; + updateDirty(); + renderAdminUsers(); +}; +const addCommand = () => { + if (!state.permissions || !state.botInstalled) return; + const name = $("newCommandInput") + .value.trim() + .toLowerCase() + .replace(/^\//, ""); + if (!/^[a-z0-9_-]{1,32}$/.test(name)) { + setError({ + message: + "Use a command name of 1–32 letters, numbers, hyphens or underscores.", + }); + return; + } + if (!state.commands.includes(name)) { + state.commands.push(name); + ensureCommands(); + } + $("newCommandInput").value = ""; + $("commandSearch").value = name; + $("commandFilter").value = "all"; + updateDirty(); + renderCommands(); + setError(null); +}; +const searchWarningMembers = async () => { + const query = $("warningsUserSearch").value.trim(); + const version = ++state.searchVersion; + const guildId = state.selectedGuildId; + $("warningsUserResults").replaceChildren(); + if (!guildId || !state.botInstalled || query.length < 2) { + $("warningsSearchStatus").textContent = query + ? "Enter at least 2 characters." + : ""; + return; + } + $("warningsSearchStatus").textContent = "Looking up members…"; + try { + const data = await api.fetchJson( + `/api/guilds/${guildId}/members?query=${encodeURIComponent(query)}`, + ); + if (version !== state.searchVersion || guildId !== state.selectedGuildId) + return; + const members = data.members || []; + $("warningsSearchStatus").textContent = members.length + ? `${members.length} member${members.length === 1 ? "" : "s"} found` + : "No matching members."; + members.forEach((member) => { + const button = node( + "button", + `member-item${warningTarget() === member.id ? " active" : ""}`, + ); + button.type = "button"; + button.dataset.userId = member.id; + const meta = node("span", "member-item__meta"); + meta.append( + node( + "span", + "member-item__name", + member.nick || + member.user?.global_name || + member.user?.username || + member.id, + ), + node("span", "member-item__id", member.id), + ); + button.append(meta); + button.addEventListener("click", () => { + if (!state.warningBusy) setWarningTarget(member); + }); + $("warningsUserResults").append(button); + }); + } catch (error) { + if (version !== state.searchVersion || guildId !== state.selectedGuildId) + return; + $("warningsSearchStatus").textContent = + "Search unavailable. Enter a user ID instead."; + setError(error); + } +}; +const renderWarnings = (warnings) => { + $("warningsList").replaceChildren(); + $("warningsEmpty").hidden = warnings.length > 0; + $("warningsEmpty").textContent = "No warnings."; + $("warningsTotalCount").textContent = warnings.length; + const dates = warnings + .filter((warning) => warning.createdAt) + .map((warning) => new Date(warning.createdAt)) + .filter((date) => !Number.isNaN(date.getTime())); + $("warningsLatestDate").textContent = dates.length + ? new Date(Math.max(...dates)).toLocaleDateString(undefined, { + day: "numeric", + month: "short", + year: "numeric", + }) + : "None"; + [...warnings].reverse().forEach((warning) => { + const item = node("li", "warning-item"); + const meta = node("div", "warning-item__meta"); + meta.append( + node("span", "", `Moderator: ${warning.moderatorId || "Unknown"}`), + node( + "span", + "", + warning.createdAt + ? new Date(warning.createdAt).toLocaleString() + : "Unknown date", + ), + ); + const copy = node("button", "button button--ghost", "Copy moderator ID"); + copy.type = "button"; + copy.disabled = !warning.moderatorId; + copy.addEventListener("click", async () => { + try { + await navigator.clipboard.writeText(warning.moderatorId); + api.showToast("Moderator ID copied."); + } catch { + api.showToast("Unable to copy the moderator ID.", "error"); + } + }); + item.append(node("p", "", warning.reason), meta, copy); + $("warningsList").append(item); + }); +}; +const updateWarningCount = () => { + $("warningCharCount").textContent = + `${$("warningReasonInput").value.length}/500`; +}; +const warningAction = async (method = "GET") => { + if (!state.selectedGuildId || state.warningBusy || state.loading) return; + const id = warningTarget(); + if (!/^\d{17,20}$/.test(id)) { + setError({ + message: + "Select a member or enter a valid Discord user ID (17–20 digits).", + }); + $("warningsUserInput").focus(); + return; + } + const reason = $("warningReasonInput").value.trim(); + if (method === "POST" && (!reason || reason.length > 500)) { + $("warningStatus").textContent = + "Please enter a reason of 1–500 characters."; + $("warningReasonInput").focus(); + return; + } + if ( + method === "DELETE" && + !window.confirm( + `Clear all warnings for member ${id}? This cannot be undone.`, + ) + ) + return; + const guildId = state.selectedGuildId; + state.warningBusy = true; + $("warningsSpinner").hidden = false; + const locked = [ + "fetchWarningsButton", + "clearWarningsButton", + "addWarningButton", + "warningsUserInput", + "warningsUserSearch", + "warningReasonInput", + ]; + const previousDisabled = locked.map((key) => $(key).disabled); + locked.forEach((key) => { + $(key).disabled = true; + }); + try { + const data = await api.fetchJson(`/api/guilds/${guildId}/warnings/${id}`, { + method, + ...(method === "POST" ? { body: JSON.stringify({ reason }) } : {}), + }); + if (guildId !== state.selectedGuildId || id !== warningTarget()) return; + renderWarnings(data.warnings || []); + setError(null); + if (method !== "GET") { + const message = + method === "POST" + ? "Warning added to the member record." + : "Warnings cleared."; + $("warningStatus").textContent = message; + api.showToast(message); + if (method === "POST") { + $("warningReasonInput").value = ""; + updateWarningCount(); + } } - state.selectedGuildId = guildId; + } catch (error) { + setError(error); + } finally { + state.warningBusy = false; + $("warningsSpinner").hidden = true; + locked.forEach((key, i) => { + $(key).disabled = previousDisabled[i]; + }); + } +}; +const refreshBotStatus = async () => { + if (state.loading || state.saving || state.warningBusy) return; + if ( + state.dirty && + !window.confirm("Discard unsaved changes and refresh this server?") + ) + return; + state.loading = true; + syncVisibility(); + renderGuilds(); + $("refreshBotButton").disabled = true; + setError(null); + await loadBotGuilds(); + await loadGuildDetails(); + $("refreshBotButton").disabled = false; +}; +const openInvite = async () => { + $("inviteButton").disabled = true; + try { + const data = await api.fetchJson( + `/api/invite-url?guildId=${state.selectedGuildId}`, + ); + if (data.inviteUrl) window.location.assign(data.inviteUrl); + } catch (error) { + setError(error); + } finally { + $("inviteButton").disabled = false; + } +}; +const openCopyModal = () => { + $("copyGuildSelect").replaceChildren(); + state.guilds + .filter((guild) => guild.id !== state.selectedGuildId) + .forEach((guild) => { + const option = node("option", "", guild.name); + option.value = guild.id; + $("copyGuildSelect").append(option); + }); + $("confirmCopyButton").disabled = !$("copyGuildSelect").options.length; + $("copyModal").showModal(); +}; +const confirmCopy = async () => { + const guildId = $("copyGuildSelect").value; + if (!guildId || !state.permissions) return; + const targetGuildId = state.selectedGuildId; + $("confirmCopyButton").disabled = true; + try { + const data = await api.fetchJson(`/api/guilds/${guildId}/permissions`); + if (state.selectedGuildId !== targetGuildId || !state.permissions) return; + state.permissions = normalizePermissions(data.permissions || {}); + ensureCommands(); + updateDirty(); + renderPanels(); + api.showToast("Permissions copied. Review before saving."); + $("copyModal").close(); + } catch (error) { + $("copyModal").close(); + setError(error); + } finally { + $("confirmCopyButton").disabled = false; + } +}; +const initDashboard = async () => { + document + .querySelectorAll(".tab") + .forEach((tab) => + tab.addEventListener("click", () => switchTab(tab.dataset.tab)), + ); + document.querySelectorAll("[data-open-tab]").forEach((button) => + button.addEventListener("click", () => { + switchTab(button.dataset.openTab); + $("mainContent").focus({ preventScroll: true }); + window.scrollTo({ top: 0, behavior: "instant" }); + }), + ); + $("guildSearch").addEventListener("input", renderGuilds); + $("commandSearch").addEventListener("input", renderCommands); + $("commandFilter").addEventListener("change", renderCommands); + $("addAdminUserButton").addEventListener("click", addAdminUser); + $("adminUserInput").addEventListener("keydown", (event) => { + if (event.key === "Enter") addAdminUser(); + }); + $("addCommandButton").addEventListener("click", addCommand); + $("newCommandInput").addEventListener("keydown", (event) => { + if (event.key === "Enter") addCommand(); + }); + $("savePermissionsButton").addEventListener("click", savePermissions); + $("discardChangesButton").addEventListener("click", discardChanges); + $("fetchWarningsButton").addEventListener("click", () => warningAction()); + $("addWarningButton").addEventListener("click", () => warningAction("POST")); + $("clearWarningsButton").addEventListener("click", () => + warningAction("DELETE"), + ); + $("warningReasonInput").addEventListener("input", updateWarningCount); + const debouncedSearch = api.debounce(searchWarningMembers, 250); + $("warningsUserSearch").addEventListener("input", () => { + state.searchVersion += 1; + debouncedSearch(); + }); + $("warningsUserInput").addEventListener("input", () => setWarningTarget()); + $("inviteButton").addEventListener("click", openInvite); + $("refreshBotButton").addEventListener("click", refreshBotStatus); + $("clearAllCommands").addEventListener("click", () => { + if (!state.permissions || !state.botInstalled) return; + if ( + !window.confirm( + "Reset all custom command restrictions? Review and save to apply this change.", + ) + ) + return; + state.commands.forEach((name) => { + state.permissions.commandPermissions[name] = normalizeEntry(); + }); + updateDirty(); + renderCommands(); + }); + $("copyFromGuild").addEventListener("click", openCopyModal); + $("cancelCopyButton").addEventListener("click", () => $("copyModal").close()); + $("confirmCopyButton").addEventListener("click", confirmCopy); + $("guildHeaderIcon").addEventListener("error", () => { + $("guildHeaderIcon").hidden = true; + $("guildHeaderFallback").hidden = false; + }); + $("logoutButton").addEventListener("click", async () => { + if (state.saving || state.warningBusy) return; + if (state.dirty && !window.confirm("Discard unsaved changes and log out?")) + return; + try { + await api.fetchJson("/auth/logout", { method: "POST" }); + state.dirty = false; + window.location.href = "/"; + } catch (error) { + setError(error); + } + }); + window.addEventListener("beforeunload", (event) => { + if (state.dirty) { + event.preventDefault(); + event.returnValue = ""; + } + }); + $("guildListSkeleton").hidden = false; + $("guildList").hidden = true; + try { + const session = await api.fetchJson("/api/me"); + api.setCsrfToken(session.csrfToken); + const displayName = + session.user?.global_name || api.formatUserDisplay(session.user); + $("userDisplay").textContent = displayName; + $("userInitial").textContent = initials(displayName); + $("sessionExpiry").textContent = "Discord account"; + if (session.expiresAt) + $("sessionExpiry").title = + `Session expires ${new Date(session.expiresAt).toLocaleString()}`; + } catch (error) { + if (error.status === 401 || error.status === 403) + window.location.replace("/"); + else setError(error); + $("guildListSkeleton").hidden = true; + return; + } + try { + const [guildData] = await Promise.all([ + api.fetchJson("/api/guilds"), + loadBotGuilds(), + ]); + state.guilds = guildData.guilds || []; + state.selectedGuildId = state.guilds[0]?.id || null; renderGuilds(); await loadGuildDetails(); + } catch (error) { + setError(error); + } finally { + $("guildListSkeleton").hidden = true; + $("guildList").hidden = false; + syncVisibility(); + } }; - -const addAdminUser = () => { - const values = parseIdList(elements.adminUserInput.value); - if (values.length === 0) { - return; - } - state.permissions.adminUserIds = Array.from(new Set([...state.permissions.adminUserIds, ...values])); - elements.adminUserInput.value = ''; - setDirty(true); - renderAdminUsers(); -}; - -const addCommand = () => { - const value = elements.newCommandInput.value.trim(); - if (!value) { - return; - } - if (!COMMAND_NAME_REGEX.test(value)) { - setError({message: 'Command name must be 1-32 characters and use letters, numbers, - or _.'}); - return; - } - if (!state.commands.includes(value)) { - state.commands.push(value); - setDirty(true); - } - elements.newCommandInput.value = ''; - renderCommands(); -}; - -const savePermissions = async () => { - if (!state.selectedGuildId) { - return; - } - - setLoadingDetails(true); - try { - const data = await fetchDashboardJson(`/api/guilds/${state.selectedGuildId}/permissions`, { - method: 'PATCH', - body: JSON.stringify(state.permissions), - }); - state.permissions = normalizePermissions(data.permissions || {}); - state.originalPermissions = clonePermissions(state.permissions); - setDirty(false); - dashboardApi.showToast('Permissions saved.', 'success'); - renderPanels(); - } catch (error) { - handleFetchError(error, 'Failed to save permissions.'); - } finally { - setLoadingDetails(false); - } -}; - -const discardChanges = () => { - if (!state.originalPermissions) { - return; - } - state.permissions = clonePermissions(state.originalPermissions); - setDirty(false); - renderPanels(); -}; - -const switchTab = (tabName) => { - elements.tabs.forEach((tab) => { - tab.classList.toggle('active', tab.dataset.tab === tabName); - }); - elements.settingsPanel.hidden = tabName !== 'settings'; - elements.permissionsPanel.hidden = tabName !== 'permissions'; - elements.warningsPanel.hidden = tabName !== 'warnings'; -}; - -const resolveWarningUserId = (value) => { - if (value && value.trim()) { - return value.trim(); - } - return state.selectedWarningUser?.id || ''; -}; - -const renderWarningMembers = (members = []) => { - elements.warningsUserResults.innerHTML = ''; - if (!members.length) { - const empty = document.createElement('span'); - empty.className = 'muted'; - empty.textContent = 'No members found.'; - elements.warningsUserResults.appendChild(empty); - return; - } - - members.forEach((member) => { - const button = document.createElement('button'); - button.type = 'button'; - button.className = `member-item${state.selectedWarningUser?.id === member.id ? ' active' : ''}`; - const meta = document.createElement('div'); - meta.className = 'member-item__meta'; - const name = document.createElement('span'); - name.className = 'member-item__name'; - name.textContent = formatMemberDisplay(member); - const id = document.createElement('span'); - id.className = 'member-item__id'; - id.textContent = member.id; - meta.appendChild(name); - meta.appendChild(id); - button.appendChild(meta); - button.addEventListener('click', () => { - setSelectedWarningUser(member); - renderWarningMembers(members); - }); - elements.warningsUserResults.appendChild(button); - }); -}; - -const searchWarningMembers = async (query) => { - if (!state.selectedGuildId || !query || query.trim().length < 2) { - elements.warningsUserResults.innerHTML = ''; - return; - } - try { - const data = await fetchDashboardJson( - `/api/guilds/${state.selectedGuildId}/members?query=${encodeURIComponent(query.trim())}`, - ); - const members = data.members || []; - renderWarningMembers(members); - } catch (error) { - handleFetchError(error, 'Failed to search members.'); - } -}; - -const fetchWarnings = async () => { - const userId = resolveWarningUserId(elements.warningsUserInput.value); - if (!userId) { - setError({message: 'Enter a user ID to fetch warnings.'}); - return; - } - elements.warningsSpinner.hidden = false; - try { - const data = await fetchDashboardJson(`/api/guilds/${state.selectedGuildId}/warnings/${userId}`); - const warnings = data.warnings || []; - elements.warningsList.innerHTML = ''; - if (warnings.length === 0) { - elements.warningsEmpty.hidden = false; - } else { - elements.warningsEmpty.hidden = true; - warnings.forEach((warning) => { - const item = document.createElement('li'); - item.className = 'warning-item'; - const text = document.createElement('p'); - text.textContent = warning.reason; - const meta = document.createElement('div'); - meta.className = 'warning-item__meta'; - const dateText = warning.createdAt ? new Date(warning.createdAt).toLocaleString() : 'Unknown date'; - const moderator = document.createElement('span'); - moderator.textContent = `Moderator: ${warning.moderatorId || 'Unknown'}`; - const copyButton = document.createElement('button'); - copyButton.type = 'button'; - copyButton.className = 'button button--ghost'; - copyButton.textContent = 'Copy moderator ID'; - copyButton.addEventListener('click', async () => { - if (!warning.moderatorId) { - return; - } - try { - await navigator.clipboard.writeText(warning.moderatorId); - dashboardApi.showToast('Moderator ID copied.', 'success'); - } catch (error) { - dashboardApi.showToast('Unable to copy moderator ID.', 'error'); - } - }); - meta.appendChild(moderator); - meta.appendChild(document.createElement('span')).textContent = dateText; - item.appendChild(text); - item.appendChild(meta); - item.appendChild(copyButton); - elements.warningsList.appendChild(item); - }); - } - setError(null); - } catch (error) { - handleFetchError(error, 'Failed to fetch warnings.'); - } finally { - elements.warningsSpinner.hidden = true; - } -}; - -const updateWarningCount = () => { - const length = elements.warningReasonInput.value.length; - elements.warningCharCount.textContent = `${length}/200`; -}; - -const addWarning = async () => { - const userId = resolveWarningUserId(elements.newWarningUserInput.value); - const reason = elements.warningReasonInput.value.trim(); - if (!userId || !reason) { - elements.warningStatus.textContent = 'User ID and reason are required.'; - return; - } - if (reason.length > 200) { - elements.warningStatus.textContent = 'Reason must be under 200 characters.'; - return; - } - - elements.warningStatus.textContent = ''; - try { - const data = await fetchDashboardJson(`/api/guilds/${state.selectedGuildId}/warnings/${userId}`, { - method: 'POST', - body: JSON.stringify({reason}), - }); - elements.warningStatus.textContent = `Warning added. Total warnings: ${data.warnings?.length || 0}`; - elements.warningReasonInput.value = ''; - updateWarningCount(); - dashboardApi.showToast('Warning saved.', 'success'); - } catch (error) { - handleFetchError(error, 'Failed to add warning.'); - } -}; - -const clearWarnings = async () => { - const userId = resolveWarningUserId(elements.warningsUserInput.value); - if (!userId) { - setError({message: 'Select a user to clear warnings.'}); - return; - } - const proceed = window.confirm('Clear all warnings for this user?'); - if (!proceed) { - return; - } - try { - await fetchDashboardJson(`/api/guilds/${state.selectedGuildId}/warnings/${userId}`, { - method: 'DELETE', - }); - elements.warningsList.innerHTML = ''; - elements.warningsEmpty.hidden = false; - elements.warningStatus.textContent = 'Warnings cleared.'; - dashboardApi.showToast('Warnings cleared.', 'success'); - } catch (error) { - handleFetchError(error, 'Failed to clear warnings.'); - } -}; - -const refreshBotStatus = async () => { - await loadBotGuilds(); - updateBotInstalled(); - const guild = state.guilds.find((entry) => entry.id === state.selectedGuildId); - updateGuildHeader(guild); - updateBotStatusControls(); - renderPanels(); -}; - -const openInvite = async () => { - try { - const data = await fetchDashboardJson(`/api/invite-url?guildId=${state.selectedGuildId}`); - if (data.inviteUrl) { - window.open(data.inviteUrl, '_blank', 'noopener,noreferrer'); - } - } catch (error) { - handleFetchError(error, 'Failed to generate invite URL.'); - } -}; - -const openCopyModal = () => { - elements.copyGuildSelect.innerHTML = ''; - state.guilds - .filter((guild) => guild.id !== state.selectedGuildId) - .forEach((guild) => { - const option = document.createElement('option'); - option.value = guild.id; - option.textContent = guild.name; - elements.copyGuildSelect.appendChild(option); - }); - elements.copyModal.hidden = false; -}; - -const closeCopyModal = () => { - elements.copyModal.hidden = true; -}; - -const confirmCopy = async () => { - const guildId = elements.copyGuildSelect.value; - if (!guildId) { - closeCopyModal(); - return; - } - try { - const data = await fetchDashboardJson(`/api/guilds/${guildId}/permissions`); - state.permissions = normalizePermissions(data.permissions || {}); - setDirty(true); - renderPanels(); - dashboardApi.showToast('Permissions copied. Review before saving.', 'success'); - } catch (error) { - handleFetchError(error, 'Failed to copy permissions.'); - } finally { - closeCopyModal(); - } -}; - -const initDashboard = async () => { - try { - await loadSession(); - await Promise.all([loadGuilds(), loadBotGuilds()]); - renderGuilds(); - if (state.selectedGuildId) { - await loadGuildDetails(); - } - } catch (error) { - setError({message: 'Please log in to access the dashboard.'}); - window.location.href = '/'; - return; - } - - elements.logoutButton.addEventListener('click', async () => { - await fetch('/auth/logout', { - method: 'POST', - credentials: 'include', - headers: { - ...(dashboardApi.getCsrfToken() ? {'X-CSRF-Token': dashboardApi.getCsrfToken()} : {}), - }, - }); - window.location.href = '/'; - }); - - elements.tabs.forEach((tab) => { - tab.addEventListener('click', () => switchTab(tab.dataset.tab)); - }); - - elements.addAdminUserButton.addEventListener('click', addAdminUser); - elements.adminUserInput.addEventListener('keydown', (event) => { - if (event.key === 'Enter') { - event.preventDefault(); - addAdminUser(); - } - }); - elements.addCommandButton.addEventListener('click', addCommand); - elements.savePermissionsButton.addEventListener('click', savePermissions); - elements.discardChangesButton.addEventListener('click', discardChanges); - elements.fetchWarningsButton.addEventListener('click', fetchWarnings); - elements.clearWarningsButton.addEventListener('click', clearWarnings); - elements.addWarningButton.addEventListener('click', addWarning); - elements.warningReasonInput.addEventListener('input', updateWarningCount); - elements.warningsUserSearch.addEventListener( - 'input', - dashboardApi.debounce((event) => { - searchWarningMembers(event.target.value); - }, 250), - ); - elements.warningsUserInput.addEventListener('input', (event) => { - setManualWarningUser(event.target.value.trim()); - }); - elements.newWarningUserInput.addEventListener('input', (event) => { - if (event.target.value.trim()) { - setManualWarningUser(event.target.value.trim()); - } - }); - elements.inviteButton.addEventListener('click', openInvite); - elements.refreshBotButton.addEventListener('click', refreshBotStatus); - elements.clearAllCommands.addEventListener('click', () => { - Object.keys(state.permissions.commandPermissions).forEach((commandName) => { - state.permissions.commandPermissions[commandName] = { - allowedRoleIds: [], - allowedUserIds: [], - restricted: false, - }; - }); - setDirty(true); - renderCommands(); - }); - elements.copyFromGuild.addEventListener('click', openCopyModal); - elements.cancelCopyButton.addEventListener('click', closeCopyModal); - elements.confirmCopyButton.addEventListener('click', confirmCopy); - updateWarningCount(); - - elements.guildSearch.addEventListener( - 'input', - dashboardApi.debounce((event) => { - const query = event.target.value.toLowerCase(); - state.filteredGuilds = state.guilds.filter((guild) => guild.name.toLowerCase().includes(query)); - renderGuilds(); - }, 200), - ); - - window.addEventListener('beforeunload', (event) => { - if (state.dirty) { - event.preventDefault(); - event.returnValue = ''; - } - }); -}; - -document.addEventListener('DOMContentLoaded', () => { - initDashboard(); -}); +document.addEventListener("DOMContentLoaded", initDashboard); diff --git a/public/icons.png b/public/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..b3687fed4f9fb6baf2b4eb87056ce28ae940823d GIT binary patch literal 24124 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 $ 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 N-c@6r3N1V2{{T7QEtUWP literal 0 HcmV?d00001 diff --git a/public/icons.svg b/public/icons.svg new file mode 100644 index 0000000..b9ce855 --- /dev/null +++ b/public/icons.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/index.html b/public/index.html index c372e8e..0ad506a 100644 --- a/public/index.html +++ b/public/index.html @@ -1,36 +1,53 @@ - - - - Discord Bot Dashboard - - - -
-
-
-
Discord Bot Dashboard
-

Manage your moderation settings

-

Configure permissions and warnings without touching JSON.

-
- -
- - - -
-

Session status

-

Checking your session...

-
- -
-
+ + + + + The Steward + + + + +
+
+ THESteward. +
+
+
+

Welcome.

+

Manage your Discord servers.

+ +
+ Checking session… +
+
- - - +
+ Brass service bell +
+
+
+ + diff --git a/public/mark.png b/public/mark.png new file mode 100644 index 0000000000000000000000000000000000000000..926e6df3759d200a5e6292ac7deb47f39d80e972 GIT binary patch literal 8303 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 diff --git a/public/styles.css b/public/styles.css index d2a9f18..c9b6790 100644 --- a/public/styles.css +++ b/public/styles.css @@ -1,1001 +1,1837 @@ :root { - color-scheme: light; - --bg: #0b1113; - --bg-accent: #122025; - --panel: #101820; - --card: #151f27; - --border: #24313b; - --text: #f4f7fb; - --muted: #9fb0bc; - --primary: #2cb9c6; - --primary-dark: #2197a2; - --success: #33d17a; - --danger: #ff6b6b; - --warning: #f4b860; - --glow: rgba(44, 185, 198, 0.25); - font-family: 'Space Grotesk', 'Satoshi', 'Segoe UI', sans-serif; + color-scheme: light; + --bg: #f7f5ef; + --panel: #fffefa; + --ink: #342a2a; + --muted: #77736b; + --line: #e5e0d5; + --wine: #432c30; + --wine-dark: #302224; + --gold: #b99a64; + --serif: Georgia, "Times New Roman", serif; + font-family: + "Segoe UI", + -apple-system, + BlinkMacSystemFont, + sans-serif; + font-size: 13px; + color: var(--ink); + background: var(--bg); + font-synthesis: none; } - * { - box-sizing: border-box; + box-sizing: border-box; } - [hidden] { - display: none !important; + display: none !important; } - body { - margin: 0; - min-height: 100vh; - background: radial-gradient(1200px 800px at 15% 10%, rgba(44, 185, 198, 0.12), transparent 60%), - radial-gradient(900px 700px at 85% 0%, rgba(244, 184, 96, 0.12), transparent 55%), - linear-gradient(180deg, #0b1113 0%, #0e1418 55%, #0b1113 100%); - color: var(--text); + margin: 0; + min-width: 320px; } - -.app { - max-width: 960px; - margin: 0 auto; - padding: 48px 24px 96px; - position: relative; -} - -.topbar { - display: flex; - justify-content: space-between; - align-items: center; - gap: 24px; - padding: 28px; - background: linear-gradient(135deg, rgba(44, 185, 198, 0.08), rgba(16, 24, 32, 0.9)); - border-radius: 22px; - border: 1px solid rgba(44, 185, 198, 0.35); - margin-bottom: 28px; - box-shadow: 0 20px 40px rgba(5, 8, 10, 0.45); - animation: rise 0.45s ease; -} - -.badge { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 6px 12px; - border-radius: 999px; - font-size: 0.85rem; - background: rgba(44, 185, 198, 0.15); - border: 1px solid rgba(44, 185, 198, 0.4); - color: #d7fbff; - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.banner { - padding: 14px 18px; - border-radius: 14px; - border: 1px solid rgba(244, 184, 96, 0.35); - background: rgba(244, 184, 96, 0.12); - margin-bottom: 20px; - color: #ffe3b0; - display: none; -} - -.muted { - color: var(--muted); -} - -a { - color: inherit; - text-decoration: none; -} - -button { - font: inherit; -} - +button, input, select, textarea { - font: inherit; + font: inherit; +} +button, +a { + touch-action: manipulation; +} +button { + cursor: pointer; + color: inherit; +} +a { + color: inherit; + text-decoration: none; +} +svg { + width: 20px; + height: 20px; + flex-shrink: 0; + fill: none; + stroke: currentColor; + stroke-width: 1.6; + stroke-linecap: round; + stroke-linejoin: round; } - -.app-shell { - max-width: 1280px; - margin: 0 auto; - padding: 24px 24px 60px; - position: relative; - animation: rise 0.45s ease; -} - -.navbar { - display: flex; - justify-content: space-between; - align-items: center; - gap: 16px; - padding: 16px 22px; - background: rgba(16, 24, 32, 0.9); - border-radius: 18px; - border: 1px solid var(--border); - margin-bottom: 22px; - box-shadow: 0 14px 30px rgba(5, 8, 10, 0.4); -} - -.navbar__brand { - display: flex; - align-items: center; - gap: 12px; -} - -.brand-mark { - width: 44px; - height: 44px; - border-radius: 14px; - background: linear-gradient(135deg, var(--primary), #58d6c7); - display: grid; - place-items: center; - font-weight: 700; - color: #031417; - text-shadow: none; -} - -.navbar__actions { - display: flex; - gap: 12px; - align-items: center; -} - -.error-banner { - display: flex; - justify-content: space-between; - align-items: center; - gap: 16px; - padding: 14px 18px; - border-radius: 12px; - border: 1px solid rgba(255, 107, 107, 0.45); - background: rgba(255, 107, 107, 0.12); - margin-bottom: 16px; -} - -.toast-container { - position: fixed; - top: 20px; - right: 24px; - display: flex; - flex-direction: column; - gap: 12px; - z-index: 50; -} - -.toast { - opacity: 0; - transform: translateY(-8px); - padding: 12px 16px; - border-radius: 12px; - background: rgba(16, 24, 32, 0.92); - border: 1px solid var(--border); - transition: all 0.2s ease; -} - -.toast--visible { - opacity: 1; - transform: translateY(0); -} - -.toast--success { - border-color: rgba(59, 165, 93, 0.5); -} - -.toast--error { - border-color: rgba(237, 66, 69, 0.5); -} - -.layout { - display: grid; - grid-template-columns: 280px 1fr; - gap: 24px; -} - -.sidebar { - background: rgba(16, 24, 32, 0.9); - border-radius: 18px; - border: 1px solid var(--border); - padding: 16px; - box-shadow: inset 0 0 0 1px rgba(44, 185, 198, 0.08); - animation: rise 0.45s ease; -} - -.sidebar__header { - display: flex; - flex-direction: column; - gap: 12px; - margin-bottom: 16px; -} - -.sidebar input[type='search'] { - padding: 10px 12px; - border-radius: 10px; - border: 1px solid var(--border); - background: #111a22; - color: var(--text); -} - -.guild-list { - display: flex; - flex-direction: column; - gap: 10px; -} - -.guild-item { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 10px 12px; - background: rgba(21, 31, 39, 0.9); - border-radius: 12px; - border: 1px solid transparent; - cursor: pointer; - transition: transform 0.15s ease, border-color 0.2s ease, background 0.2s ease; -} - -.guild-item__info { - display: flex; - align-items: center; - gap: 12px; - min-width: 0; -} - -.guild-item img { - width: 36px; - height: 36px; - border-radius: 12px; - background: var(--border); -} - -.guild-item span { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.guild-item.active { - border-color: var(--primary); -} - -.guild-item:hover { - border-color: rgba(44, 185, 198, 0.5); - transform: translateY(-1px); -} - -.status-pill { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 10px; - border-radius: 999px; - font-size: 0.75rem; - border: 1px solid var(--border); -} - -.status-pill__dot { - width: 8px; - height: 8px; - border-radius: 999px; - background: var(--muted); -} - -.status-pill--ok { - border-color: rgba(59, 165, 93, 0.5); - color: #d6f5de; -} - -.status-pill--ok .status-pill__dot { - background: var(--success); -} - -.status-pill--warn { - border-color: rgba(246, 195, 67, 0.5); - color: #ffe8b1; -} - -.status-pill--warn .status-pill__dot { - background: var(--warning); -} - -.content { - display: flex; - flex-direction: column; - gap: 16px; -} - -.guild-header { - display: flex; - justify-content: space-between; - align-items: center; - gap: 16px; - padding: 18px; - background: rgba(16, 24, 32, 0.9); - border-radius: 18px; - border: 1px solid var(--border); - box-shadow: inset 0 0 0 1px rgba(244, 184, 96, 0.06); - animation: rise 0.45s ease; -} - -.guild-header__info { - display: flex; - align-items: center; - gap: 16px; -} - -.guild-header__info img { - width: 56px; - height: 56px; - border-radius: 16px; - background: var(--border); -} - -.guild-header__status { - display: flex; - align-items: center; - gap: 10px; - margin-top: 6px; -} - -.status-badge { - padding: 4px 10px; - border-radius: 999px; - border: 1px solid var(--border); - font-size: 0.8rem; -} - -.status-badge--ok { - border-color: rgba(59, 165, 93, 0.5); - color: #d6f5de; -} - -.status-badge--warn { - border-color: rgba(246, 195, 67, 0.5); - color: #ffe8b1; -} - -.tabs { - display: inline-flex; - gap: 8px; - padding: 6px; - background: rgba(16, 24, 32, 0.9); - border-radius: 14px; - border: 1px solid var(--border); - width: fit-content; -} - -.tab { - padding: 8px 16px; - border-radius: 8px; - border: none; - background: transparent; - color: var(--muted); - cursor: pointer; - transition: all 0.2s ease; -} - -.tab.active { - background: linear-gradient(135deg, var(--primary), #58d6c7); - color: #041316; - box-shadow: 0 10px 18px rgba(44, 185, 198, 0.25); -} - -.panel { - background: rgba(16, 24, 32, 0.9); - border-radius: 18px; - border: 1px solid var(--border); - padding: 18px; - box-shadow: 0 18px 30px rgba(5, 8, 10, 0.35); - animation: rise 0.45s ease; -} - -.panel__header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - margin-bottom: 16px; -} - -.panel__header--split { - align-items: flex-start; -} - -.callout { - padding: 12px; - border-radius: 12px; - border: 1px solid rgba(244, 184, 96, 0.5); - background: rgba(244, 184, 96, 0.14); - margin-bottom: 16px; -} - -.field { - display: flex; - flex-direction: column; - gap: 8px; - margin-bottom: 16px; -} - -.field label { - font-size: 0.9rem; - color: var(--muted); -} - -.field input, -.field select, -.field textarea { - padding: 10px 12px; - border-radius: 10px; - border: 1px solid var(--border); - background: #111a22; - color: var(--text); -} - -.field textarea { - min-height: 100px; - resize: vertical; -} - -.field__meta { - display: flex; - justify-content: space-between; - align-items: center; -} - -.input-row { - display: flex; - gap: 12px; - flex-wrap: wrap; -} - -.multi-picker { - background: #111a22; - border-radius: 12px; - border: 1px solid var(--border); - padding: 12px; - display: flex; - flex-direction: column; - gap: 12px; -} - -.multi-picker__search { - padding: 8px 10px; - border-radius: 8px; - border: 1px solid var(--border); - background: #0f1820; - color: var(--text); -} - -.multi-picker__list { - max-height: 180px; - overflow-y: auto; - display: grid; - gap: 8px; -} - -.multi-picker__list label { - display: flex; - align-items: center; - gap: 8px; - font-size: 0.9rem; -} - -.chip-list { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.chip { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 6px 10px; - border-radius: 999px; - background: var(--card); - border: 1px solid var(--border); - font-size: 0.85rem; -} - -.chip button { - border: none; - background: transparent; - color: var(--muted); - cursor: pointer; -} - -.button { - padding: 10px 16px; - border-radius: 10px; - border: 1px solid var(--border); - background: rgba(21, 31, 39, 0.9); - color: var(--text); - cursor: pointer; - transition: transform 0.15s ease, border-color 0.2s ease, box-shadow 0.2s ease; -} - -.button--primary { - background: linear-gradient(135deg, var(--primary), #58d6c7); - border-color: var(--primary); - color: #041316; - box-shadow: 0 10px 18px rgba(44, 185, 198, 0.3); -} - -.button--primary:hover { - background: linear-gradient(135deg, var(--primary-dark), #3cc0b1); - transform: translateY(-1px); -} - -.button--ghost { - background: transparent; -} - -.button:hover { - border-color: rgba(44, 185, 198, 0.35); - transform: translateY(-1px); -} - -.button:focus-visible, -input:focus-visible, -select:focus-visible, -textarea:focus-visible, -.tab:focus-visible { - outline: 2px solid rgba(44, 185, 198, 0.6); - outline-offset: 2px; -} - -.button-row { - display: flex; - gap: 12px; - flex-wrap: wrap; -} - -.toolbar { - display: grid; - grid-template-columns: minmax(240px, 1fr) 220px; - gap: 14px; - margin-bottom: 18px; -} - -.toolbar__field { - margin-bottom: 0; -} - -.stats-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); - gap: 12px; - margin-bottom: 18px; -} - -.stats-grid--compact { - margin-top: 4px; - margin-bottom: 16px; -} - -.stat-card { - padding: 14px; - border-radius: 14px; - border: 1px solid var(--border); - background: linear-gradient(180deg, rgba(21, 31, 39, 0.96), rgba(17, 26, 34, 0.9)); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); -} - -.stat-card--compact { - padding: 12px; -} - -.stat-card__label { - display: block; - margin-bottom: 8px; - color: var(--muted); - font-size: 0.82rem; - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.stat-card__value { - display: block; - font-size: 1.8rem; - line-height: 1.1; - margin-bottom: 8px; -} - -.stat-card__value--small { - font-size: 1rem; -} - -.command-list { - display: flex; - flex-direction: column; - gap: 16px; -} - -.command-card { - background: rgba(21, 31, 39, 0.95); - border-radius: 16px; - border: 1px solid var(--border); - padding: 14px; - transition: border-color 0.2s ease, transform 0.2s ease; -} - -.command-card:hover { - border-color: rgba(44, 185, 198, 0.4); - transform: translateY(-1px); -} - -.command-card__header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.command-card__title { - display: flex; - flex-direction: column; - gap: 6px; -} - -.command-card__summary { - display: inline-flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; -} - -.mini-badge { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 9px; - border-radius: 999px; - border: 1px solid var(--border); - color: var(--muted); - font-size: 0.76rem; - background: rgba(11, 17, 19, 0.25); -} - -.mini-badge--accent { - border-color: rgba(44, 185, 198, 0.45); - color: #ccf7fb; -} - -.mini-badge--warn { - border-color: rgba(244, 184, 96, 0.45); - color: #ffe8b1; -} - -.mini-badge--ok { - border-color: rgba(51, 209, 122, 0.45); - color: #d6f5de; -} - -.command-card__body { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); - gap: 16px; - margin-top: 12px; -} - -.member-list { - display: flex; - flex-direction: column; - gap: 8px; - max-height: 220px; - overflow-y: auto; - padding: 10px; - border-radius: 12px; - border: 1px solid var(--border); - background: #111a22; -} - -.member-item { - display: flex; - justify-content: space-between; - align-items: center; - gap: 10px; - padding: 8px 10px; - border-radius: 10px; - border: 1px solid transparent; - background: var(--card); - cursor: pointer; -} - -.member-item__meta { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} - -.member-item__name { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.member-item__id { - font-size: 0.8rem; - color: var(--muted); -} - -.member-item.active { - border-color: var(--primary); -} - -.toggle { - display: inline-flex; - align-items: center; - gap: 8px; -} - -.toggle input { - width: 40px; - height: 20px; - appearance: none; - background: var(--border); - border-radius: 999px; - position: relative; - cursor: pointer; -} - -.toggle input::after { - content: ''; - position: absolute; - width: 16px; - height: 16px; - border-radius: 999px; - background: white; - top: 2px; - left: 2px; - transition: transform 0.2s ease; -} - -.toggle input:checked { - background: var(--primary); -} - -.toggle input:checked::after { - transform: translateX(20px); -} - -.warning-item { - background: rgba(21, 31, 39, 0.9); - border-radius: 12px; - border: 1px solid var(--border); - padding: 10px; - margin-bottom: 10px; - list-style: none; -} - -.warning-item__meta { - display: flex; - justify-content: space-between; - align-items: center; - gap: 8px; - font-size: 0.85rem; - color: var(--muted); -} - -.warnings-layout { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 16px; -} - -.warnings-pane { - background: rgba(21, 31, 39, 0.9); - border-radius: 12px; - border: 1px solid var(--border); - padding: 14px; -} - -.selected-user-card { - display: flex; - flex-direction: column; - gap: 8px; - padding: 14px; - border-radius: 14px; - border: 1px solid rgba(44, 185, 198, 0.25); - background: linear-gradient(180deg, rgba(44, 185, 198, 0.08), rgba(17, 26, 34, 0.9)); - margin-bottom: 16px; -} - -.selected-user-card__label { - color: var(--muted); - font-size: 0.78rem; - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.warning-status-pill { - display: inline-flex; - align-items: center; - padding: 6px 10px; - border-radius: 999px; - border: 1px solid var(--border); - color: var(--muted); - background: rgba(17, 26, 34, 0.8); - font-size: 0.8rem; -} - -.divider { - height: 1px; - background: var(--border); - margin: 16px 0; -} - -.save-bar { - position: sticky; - bottom: 16px; - background: rgba(16, 24, 32, 0.95); - border-radius: 18px; - border: 1px solid var(--border); - padding: 12px 16px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - box-shadow: 0 16px 30px rgba(5, 8, 10, 0.4); -} - -.save-indicator { - display: inline-flex; - align-items: center; - gap: 8px; - font-weight: 600; -} - -.save-indicator::before { - content: ''; - width: 8px; - height: 8px; - border-radius: 999px; - background: var(--warning); -} - -.empty-state { - padding: 20px; - border-radius: 12px; - border: 1px dashed var(--border); - background: #111a22; - color: var(--muted); -} - -.spinner { - width: 28px; - height: 28px; - border-radius: 999px; - border: 3px solid rgba(255, 255, 255, 0.2); - border-top-color: var(--primary); - animation: spin 1s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -.skeleton-list, -.skeleton-panel { - display: grid; - gap: 12px; -} - -.skeleton-line { - height: 14px; - border-radius: 999px; - background: linear-gradient(90deg, #16222a 25%, #1d2b34 37%, #16222a 63%); - background-size: 400% 100%; - animation: shimmer 1.4s ease infinite; -} - -@keyframes shimmer { - 0% { - background-position: 100% 0; - } - 100% { - background-position: 0 0; - } -} - -.tooltip { - display: inline-flex; - align-items: center; - justify-content: center; - width: 18px; - height: 18px; - border-radius: 999px; - background: var(--border); - color: var(--muted); - font-size: 0.75rem; - margin-left: 6px; - cursor: help; -} - -.modal { - position: fixed; - inset: 0; - background: rgba(10, 12, 18, 0.7); - display: grid; - place-items: center; - z-index: 40; - backdrop-filter: blur(6px); -} - -.modal[hidden] { - display: none; -} - -.modal__content { - background: rgba(16, 24, 32, 0.98); - border-radius: 18px; - border: 1px solid var(--border); - padding: 20px; - width: min(420px, 90vw); - display: flex; - flex-direction: column; - gap: 16px; - box-shadow: 0 24px 40px rgba(5, 8, 10, 0.6); -} - -.modal__content select { - padding: 10px 12px; - border-radius: 10px; - border: 1px solid var(--border); - background: #111a22; - color: var(--text); -} - -.command-card--disabled, -.panel--disabled, -.multi-picker--disabled { - opacity: 0.6; - pointer-events: none; -} - h1, h2, h3, -strong { - font-family: 'Fraunces', 'Space Grotesk', 'Segoe UI', sans-serif; - letter-spacing: 0.01em; +p { + margin: 0; +} +h1, +h2, +h3 { + font-family: var(--serif); + font-weight: 400; +} +h2 { + font-size: 24px; + line-height: 1.25; +} +h3 { + font-size: 22px; +} +p { + line-height: 1.7; +} +button:disabled { + opacity: 0.46; + cursor: not-allowed; +} +:focus-visible { + outline: 2px solid #a17c3e; + outline-offset: 4px; +} +::selection { + background: #ddcda9; + color: var(--wine-dark); +} +.muted, +.field-hint { + color: var(--muted); +} +.field-hint { + font-size: 12px; + line-height: 1.6; +} +.skip-link { + position: fixed; + top: -70px; + left: 16px; + z-index: 100; + padding: 12px 18px; + background: var(--panel); + border: 1px solid var(--gold); +} +.skip-link:focus { + top: 12px; +} +.app-shell { + min-height: 100vh; + display: grid; + grid-template-columns: 248px minmax(0, 1fr); +} +.sidebar { + background: var(--wine-dark); + color: #d8c9c0; + padding: 34px 20px 0; + display: flex; + flex-direction: column; + position: sticky; + top: 0; + height: 100vh; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: #75615b transparent; +} +.brand { + display: flex; + align-items: center; + gap: 12px; + width: fit-content; +} +.brand img { + width: 43px; + height: 47px; + border-radius: 3px; +} +.brand > span { + font-family: var(--serif); + font-size: 31px; + letter-spacing: -0.7px; + line-height: 1; + color: #f7efe3; +} +.brand small { + display: block; + font-family: "Segoe UI", sans-serif; + font-size: 8px; + font-weight: 400; + letter-spacing: 3.6px; + margin: 0 0 5px 2px; +} +.brand-period { + color: var(--gold); +} +.sidebar__section-label { + font-size: 9px; + letter-spacing: 1.6px; + color: #b7a295; + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 10px; + margin-bottom: 14px; +} +.sidebar__section-label > span { + font-family: var(--serif); + font-size: 11px; + letter-spacing: 0; +} +.tabs { + display: grid; + gap: 5px; +} +.tab { + position: relative; + width: 100%; + border: 1px solid transparent; + border-radius: 4px; + padding: 13px 11px; + display: flex; + align-items: center; + gap: 12px; + background: none; + color: #c9bbb1; + font-size: 12px; + text-align: left; + white-space: nowrap; +} +.tab svg { + width: 17px; + height: 17px; + color: #bfa786; +} +.tab:hover { + background: #ffffff08; +} +.tab.active { + color: #f8efdf; + background: #54403a; + border-color: #81684866; +} +.tab.active::before { + content: ""; + position: absolute; + left: -21px; + top: 10px; + bottom: 10px; + width: 3px; + background: var(--gold); +} +.nav-arrow, +.nav-count { + margin-left: auto; + color: #d7be94; +} +.nav-count { + font-size: 9px; + min-width: 19px; + padding: 3px; + border: 1px solid #b99a643d; + text-align: center; + border-radius: 3px; +} +.server-label { + margin-top: 37px; +} +.search-field { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; + border: 1px solid var(--line); + border-radius: 4px; + background: var(--panel); + padding: 0 12px; +} +.search-field svg { + color: #8f8a7d; + width: 16px; + height: 16px; +} +.search-field input { + border: 0; + padding-left: 0; + background: transparent; + width: 100%; + min-width: 0; + outline-offset: 1px; +} +.search-field:focus-within { + border-color: #b99a64; +} +.sidebar-search { + background: #ffffff04; + border-color: #a990752e; + margin-bottom: 12px; +} +.sidebar-search input { + color: #eee4d6; + font-size: 11px; +} +.sidebar-search input::placeholder { + color: #b7a497; +} +.guild-list { + display: grid; + gap: 4px; +} +.guild-item { + border: 1px solid transparent; + background: transparent; + width: 100%; + display: flex; + text-align: left; + align-items: center; + gap: 9px; + padding: 9px; + border-radius: 4px; + color: #cdbfb4; +} +.guild-item:hover, +.guild-item.active { + background: #ffffff07; + border-color: #bda58226; +} +.guild-item.active { + color: #f3e6d3; +} +.guild-item__info { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; + flex: 1; +} +.guild-item__name { + font-size: 12px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.guild-avatar { + width: 32px; + height: 32px; + display: grid; + place-items: center; + background: #e9e0d0; + color: #7c6442; + border-radius: 4px; + font-family: var(--serif); + font-size: 17px; + flex-shrink: 0; +} +.guild-item .guild-avatar { + width: 29px; + height: 29px; + font-size: 13px; + color: #d8c8ad; + background: #614d43; +} +.guild-item:nth-child(3n + 2) .guild-avatar { + background: #4a5650; + color: #d0d9c7; +} +.guild-item:nth-child(3n) .guild-avatar { + background: #514753; + color: #d6c9d9; +} +.guild-item img { + width: 29px; + height: 29px; + object-fit: cover; + border-radius: 4px; +} +.status-pill__dot { + display: block; + width: 5px; + height: 5px; + border-radius: 50%; + background: #ba9365; +} +.status-pill--ok .status-pill__dot { + background: #a7bb91; +} +.sidebar-bottom { + margin-top: auto; + padding-top: 55px; +} +.profile { + display: flex; + align-items: center; + gap: 10px; + padding: 20px 0; + border-top: 1px solid #a48c7430; +} +.profile-avatar { + display: grid; + place-items: center; + height: 31px; + width: 31px; + border-radius: 50%; + background: #675147; + color: #e9d8bf; + font-family: var(--serif); + font-size: 17px; + flex-shrink: 0; +} +.profile__text { + min-width: 0; + flex: 1; +} +.profile__text strong { + display: block; + font-size: 11px; + color: #e4d8ca; + overflow: hidden; + text-overflow: ellipsis; +} +.profile__text > span { + display: block; + font-size: 9px; + color: #b5a297; + margin-top: 4px; +} +.icon-button { + display: inline-grid; + place-items: center; + width: 32px; + height: 32px; + background: transparent; + border: 1px solid transparent; + border-radius: 4px; + padding: 6px; +} +.icon-button:hover { + border-color: currentColor; +} +.icon-button svg { + width: 17px; + height: 17px; +} +.workspace { + min-width: 0; +} +.topbar { + height: 71px; + border-bottom: 1px solid var(--line); + padding: 0 40px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 15px; + background: #fbfaf6; +} +.breadcrumb { + display: flex; + gap: 17px; + color: var(--muted); + font-size: 11px; +} +.breadcrumb > span { + color: #b1a797; +} +.breadcrumb strong { + color: #51463f; + font-weight: 500; +} +.content { + max-width: 1450px; + margin: auto; + padding: 34px 40px 0; + outline: none; +} +.page-heading { + display: flex; + justify-content: space-between; + align-items: center; + gap: 24px; + margin-bottom: 29px; +} +.page-heading h1 { + font-size: clamp(29px, 2.7vw, 40px); + line-height: 1.25; + letter-spacing: -0.9px; + margin-top: 8px; +} +.page-heading .muted { + margin-top: 8px; + font-size: 12px; +} +.guild-header { + display: flex; + align-items: center; + gap: 10px; + border: 1px solid var(--line); + background: #fffdf8; + border-radius: 5px; + padding: 11px 16px; + max-width: 280px; + min-width: 205px; +} +.guild-header img { + width: 33px; + height: 33px; + border-radius: 4px; +} +.guild-header > div { + min-width: 0; +} +.guild-header strong { + font-size: 12px; + display: block; + margin-top: 4px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.overview-stats { + margin: 23px 0 29px; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + background: var(--panel); + border: 1px solid var(--line); + border-radius: 5px; +} +.overview-stat { + padding: 20px 22px; + min-width: 0; +} +.overview-stat + .overview-stat { + border-left: 1px solid var(--line); +} +.stat-heading { + display: flex; + justify-content: space-between; + gap: 8px; + align-items: center; + color: #777064; + font-size: 11px; +} +.stat-heading svg { + color: #a98e60; + width: 17px; + height: 17px; +} +.stat-value { + font-size: 35px; + line-height: 1.3; + font-family: var(--serif); + font-weight: 400; + display: block; + margin: 11px 0 6px; + font-variant-numeric: tabular-nums; +} +.stat-value--status { + font-size: 28px; + line-height: 1.62; +} +.stat-value--status.is-connected { + color: #4c6854; +} +.text-link { + display: inline-flex; + align-items: center; + gap: 12px; + background: none; + border: none; + padding: 0; + font-size: 11px; + color: #765e3b; + text-align: left; +} +.text-link:hover { + color: var(--wine); + text-decoration: underline; + text-underline-offset: 4px; +} +.panel { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 5px; + padding: 25px; + min-width: 0; +} +.panel__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 25px; +} +.panel__header h2 { + font-size: 23px; +} +.panel__header .muted { + font-size: 11px; + margin-top: 5px; +} +.section-title { + display: flex; + align-items: center; + gap: 12px; +} +.section-icon { + display: grid; + place-items: center; + width: 35px; + height: 35px; + color: #a58a5e; + background: #f2eddf; + border: 1px solid #e7deca; + border-radius: 4px; + flex-shrink: 0; +} +.section-icon svg { + width: 18px; + height: 18px; +} +.subtle-tag { + font-size: 8px; + letter-spacing: 0.8px; + color: #8b7a5c; + border: 1px solid #e3dac6; + padding: 5px 7px; + border-radius: 3px; + white-space: nowrap; +} +.field { + display: grid; + gap: 9px; + margin-top: 24px; +} +.field > label, +.custom-command label { + font-size: 12px; + font-weight: 600; + color: #51483f; +} +.field > p { + margin-top: -4px; +} +input, +select, +textarea { + width: 100%; + border: 1px solid #e2ddd1; + border-radius: 4px; + background: #fffefa; + color: var(--ink); + min-height: 41px; + padding: 10px 12px; +} +input::placeholder, +textarea::placeholder { + color: #979184; + font-size: 11px; +} +input:disabled, +select:disabled, +textarea:disabled { + background: #f2f0e9; + cursor: not-allowed; +} +input[type="checkbox"] { + width: 15px; + height: 15px; + min-height: 0; + margin: 0; + accent-color: #70503e; + flex-shrink: 0; + cursor: pointer; +} +textarea { + resize: vertical; + line-height: 1.7; +} +.input-row { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; +} +.input-row input { + min-width: 0; + flex: 1; +} +.button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; + min-height: 39px; + padding: 9px 15px; + border: 1px solid var(--line); + background: var(--panel); + color: #635541; + border-radius: 4px; + white-space: nowrap; + font-size: 11px; + font-weight: 500; + transition: + background 0.15s, + border-color 0.15s, + transform 0.15s; +} +.button svg { + width: 15px; + height: 15px; +} +.button > span { + margin-left: 5px; +} +.button:hover:not(:disabled) { + background: #f1ecdf; + border-color: #c9b99a; +} +.button:active:not(:disabled) { + transform: translateY(1px); +} +.button--primary, +.button--dark { + background: var(--wine); + color: #fff7e8; + border-color: var(--wine); +} +.button--primary:hover:not(:disabled), +.button--dark:hover:not(:disabled) { + background: #634249; + border-color: #634249; +} +.button--soft { + background: #f0eadc; + border-color: #e6dcc7; +} +.button--ghost { + background: transparent; + border-color: transparent; +} +.button--danger { + color: #9d443b; + border-color: #e4d1c9; +} +.button-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} +.multi-picker { + border: 1px solid var(--line); + border-radius: 4px; + overflow: hidden; +} +.multi-picker__search { + border: none; + border-bottom: 1px solid var(--line); + border-radius: 0; + min-height: 36px; + background: #faf8f2; +} +.multi-picker__list { + max-height: 145px; + overflow-y: auto; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 3px; + padding: 10px; + scrollbar-width: thin; +} +.multi-picker__list label { + display: flex; + align-items: center; + gap: 9px; + padding: 7px 6px; + border-radius: 3px; + cursor: pointer; + font-size: 11px; + min-width: 0; + overflow-wrap: anywhere; +} +.multi-picker__list label:hover { + background: #f6f1e5; +} +.multi-picker__list label:has(input:checked) { + background: #f1ecdf; +} +.role-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--role-color, #a38d60); + flex-shrink: 0; +} +.multi-picker > .chip-list { + border-top: 1px solid var(--line); + padding: 10px; + background: #fcfaf5; +} +.chip-list { + display: flex; + flex-wrap: wrap; + gap: 6px; + min-width: 0; +} +.chip-list .muted { + font-size: 10px; +} +.chip { + display: inline-flex; + align-items: center; + gap: 7px; + border: 1px solid #ded4be; + border-radius: 3px; + padding: 4px 7px; + background: #f2ebdb; + font-size: 10px; + color: #715d37; + overflow-wrap: anywhere; +} +.chip button { + display: grid; + place-items: center; + background: none; + border: 0; + padding: 0 2px; + font-size: 15px; + line-height: 1; + color: #8b7752; +} +.panel-footnote { + margin: 25px -25px -25px; + padding: 13px 25px; + background: #faf8f2; + border-top: 1px solid var(--line); + display: flex; + align-items: center; + gap: 8px; + font-size: 10px; + color: #898071; + border-radius: 0 0 5px 5px; +} +.panel-footnote svg { + width: 14px; + height: 14px; +} +.stats-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + border: 1px solid var(--line); + border-radius: 4px; + background: #faf8f2; + margin-bottom: 22px; +} +.stat-card { + padding: 18px; + display: flex; + flex-direction: column; + gap: 7px; + color: #827765; + font-size: 11px; +} +.stat-card + .stat-card { + border-left: 1px solid var(--line); +} +.stat-card strong { + font-family: var(--serif); + font-size: 28px; + font-weight: 400; + color: var(--ink); +} +.stat-card .stat-value--small { + font-size: 18px; + line-height: 2; +} +.toolbar { + display: flex; + gap: 12px; + margin-bottom: 12px; +} +.toolbar .search-field { + flex: 1; +} +.toolbar select { + width: 205px; +} +.command-list { + display: grid; + gap: 12px; + margin-top: 22px; +} +.command-card { + border: 1px solid var(--line); + border-radius: 4px; + overflow: hidden; +} +.command-card--changed { + border-color: #baa06c; +} +.command-card__header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + padding: 16px 18px; + background: #fcfaf5; +} +.command-card__title { + display: flex; + align-items: center; + gap: 12px; + flex-wrap: wrap; +} +.command-card__title strong { + font-family: Consolas, monospace; + font-size: 13px; + color: #795a46; + font-weight: 500; +} +.command-card__title span { + color: #999080; + font-size: 10px; +} +.command-card__body { + padding: 18px; + border-top: 1px solid var(--line); + display: grid; + grid-template-columns: 1fr 1fr; + align-items: start; + gap: 20px; +} +.command-card__body .field { + margin: 0; +} +.command-card__body .field > label, +.command-card__body .field > span { + font-size: 11px; +} +.command-card__body .multi-picker__list { + grid-template-columns: 1fr; +} +.command-card__body .chip-list { + margin-top: 8px; +} +.command-card__body .multi-picker > .chip-list { + margin-top: 0; +} +.command-card__note { + grid-column: 1 / -1; + font-size: 11px; + color: #8d6b39; +} +.toggle { + display: flex; + align-items: center; + gap: 8px; + font-size: 10px; + color: #88775e; +} +.toggle input { + appearance: none; + width: 29px; + height: 17px; + background: #d9d4c8; + border: 0; + border-radius: 12px; + padding: 2px; + cursor: pointer; + position: relative; +} +.toggle input::before { + content: ""; + display: block; + width: 13px; + height: 13px; + border-radius: 50%; + background: #fffdf6; + box-shadow: 0 1px 3px #0002; + transition: transform 0.15s; +} +.toggle input:checked { + background: #886644; +} +.toggle input:checked::before { + transform: translateX(12px); +} +.custom-command { + display: flex; + align-items: center; + justify-content: space-between; + gap: 24px; + margin-top: 24px; + padding-top: 22px; + border-top: 1px solid var(--line); +} +.custom-command p { + margin-top: 5px; +} +.warnings-layout { + display: grid; + grid-template-columns: minmax(270px, 0.8fr) minmax(0, 1.2fr); + gap: 24px; + align-items: start; +} +.warnings-pane .panel__header { + align-items: start; +} +.warnings-pane > .muted { + font-size: 11px; + margin-top: 8px; +} +.stats-grid--compact { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} +.selected-user-card { + padding: 18px; + margin: 24px 0 18px; + border: 1px solid #e3d9c7; + background: #f5efe3; + display: grid; + gap: 8px; + border-radius: 4px; +} +.selected-user-card strong { + font-size: 12px; + overflow-wrap: anywhere; +} +.member-list { + display: grid; + gap: 5px; + margin-top: 12px; +} +.member-item { + background: #faf7ee; + border: 1px solid var(--line); + text-align: left; + padding: 12px; + border-radius: 4px; +} +.member-item:hover, +.member-item.active { + border-color: var(--gold); + background: #f0e7d4; +} +.member-item__meta { + display: grid; + gap: 4px; +} +.member-item__name { + font-size: 12px; +} +.member-item__id { + font-size: 10px; + color: #897c69; +} +#warningsList { + list-style: none; + padding: 0; + margin: 0; +} +.warning-item { + border-bottom: 1px solid var(--line); + padding: 16px 0; +} +.warning-item > p { + overflow-wrap: anywhere; +} +.warning-item__meta { + display: flex; + flex-wrap: wrap; + gap: 10px; + font-size: 10px; + color: #998973; + margin: 7px 0; +} +.warning-item .button { + min-height: 28px; + padding: 4px 0; + font-size: 10px; +} +.divider { + height: 1px; + background: var(--line); + margin: 24px 0; +} +.field__meta { + display: flex; + justify-content: space-between; + gap: 12px; + font-size: 11px; + color: #967043; +} +.field__meta .field-hint { + white-space: nowrap; +} +.empty-state { + padding: 30px 18px; + text-align: center; + font-size: 12px; + line-height: 1.8; + color: #978975; + background: #f7f3e9; + border: 1px dashed #ded3bc; + border-radius: 4px; +} +.empty-state h2 { + margin-bottom: 10px; +} +.sidebar .empty-state { + padding: 16px 10px; + background: transparent; + border-color: #776454; + font-size: 11px; + color: #c4b19a; +} +.error-banner, +.callout { + display: flex; + align-items: center; + gap: 15px; + padding: 18px 22px; + margin-bottom: 24px; + border: 1px solid #dbc29c; + background: #f5ecd9; + border-radius: 4px; + font-size: 12px; +} +.error-banner { + background: #f5e6e0; + border-color: #d8b7ac; + color: #8d4c3b; +} +.error-banner > div, +.callout > div { + flex: 1; +} +.callout > svg { + color: #987e4f; +} +.callout p, +.error-banner p { + font-size: 11px; + margin-top: 3px; +} +.save-bar { + position: sticky; + bottom: 18px; + background: #fffdf6f5; + backdrop-filter: blur(12px); + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + border: 1px solid #b9a47b; + box-shadow: 0 6px 35px #4f362020; + border-radius: 5px; + margin-top: 20px; + z-index: 10; +} +.save-bar > div:first-child { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} +.save-bar strong { + font-size: 12px; + font-weight: 500; +} +.save-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: #aa8652; +} +.toast-container { + position: fixed; + bottom: 25px; + right: 25px; + z-index: 100; + display: grid; + gap: 10px; + max-width: calc(100vw - 50px); +} +.toast { + padding: 16px 22px; + background: var(--wine); + color: #fff7e8; + border: 1px solid #a88c60; + box-shadow: 0 5px 24px #30222425; + font-size: 12px; + border-radius: 4px; + opacity: 0; + transform: translateY(8px); + transition: + opacity 0.2s, + transform 0.2s; +} +.toast--visible { + opacity: 1; + transform: none; +} +.toast--error { + background: #873f36; +} +.modal { + border: 1px solid #cbbb9b; + border-radius: 6px; + width: min(460px, calc(100vw - 36px)); + background: var(--panel); + color: var(--ink); + padding: 0; + box-shadow: 0 24px 100px #24181940; +} +.modal::backdrop { + background: #30222480; + backdrop-filter: blur(3px); +} +.modal__content { + padding: 32px; +} +.modal h2 { + margin: 14px 0 12px; + font-size: 30px; +} +.modal label { + display: block; + margin: 22px 0 8px; + font-size: 12px; +} +.modal .field-hint { + margin-top: 12px; +} +.modal .button-row { + justify-content: flex-end; + margin-top: 24px; +} +.skeleton-line { + height: 22px; + background: linear-gradient( + 90deg, + #aa927415 25%, + #aa92742b 50%, + #aa927415 75% + ); + background-size: 200% 100%; + border-radius: 3px; + animation: shimmer 1.6s infinite; + margin: 12px 0; +} +.skeleton-line:nth-child(2) { + width: 75%; +} +.skeleton-line:nth-child(3) { + width: 55%; +} +.skeleton-panel { + margin-top: 20px; + min-height: 250px; +} +.spinner { + width: 20px; + height: 20px; + border: 2px solid var(--line); + border-top-color: var(--gold); + border-radius: 50%; + animation: spin 0.7s linear infinite; + margin-top: 20px; +} +@keyframes shimmer { + to { + background-position: -200% 0; + } +} +@keyframes spin { + to { + transform: rotate(360deg); + } +} +.landing-body { + background: #f4f0e6; +} +.landing-shell { + max-width: 1370px; + padding: 0 65px; + margin: auto; +} +.landing-nav { + display: flex; + justify-content: space-between; + align-items: center; + padding: 32px 0; + border-bottom: 1px solid #dcd2bf; +} +.landing-nav .brand > span { + color: var(--wine); +} +.landing-main { + display: grid; + grid-template-columns: 1.1fr 1fr; + gap: 30px; + padding: 65px 0 60px; + align-items: center; +} +.landing-copy h1 { + font-size: clamp(50px, 5.8vw, 80px); + line-height: 1.05; + letter-spacing: -2.5px; + margin: 24px 0; +} +.landing-copy h1 em { + color: #94774c; +} +.landing-description { + max-width: 405px; + font-size: 13px; + color: #8a7c68; + line-height: 1.9; + margin-bottom: 28px; +} +.landing-copy .button { + min-height: 47px; + padding: 12px 20px; +} +.landing-session { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; + color: #928571; + font-size: 10px; + margin-top: 18px; +} +.landing-session .text-link { + font-size: 10px; +} +.landing-art { + position: relative; + background: #eae1cf; + border: 1px solid #d4c3a2; + border-radius: 180px 180px 3px 3px; + min-height: 480px; + display: flex; + align-items: center; +} +.landing-art::before { + content: ""; + position: absolute; + inset: 12px; + border: 1px solid #cdbb9666; + border-radius: inherit; +} +.landing-art img { + width: 100%; + transform: scale(1.1); +} +.banner { + display: none; + margin-top: 18px; + font-size: 11px; + color: #a18b68; +} +@media (min-width: 1600px) { + .content { + padding-top: 42px; + } + .overview-stat { + padding: 24px; + } +} +@media (max-width: 1200px) { + .app-shell { + grid-template-columns: 220px minmax(0, 1fr); + } + .sidebar { + padding-right: 14px; + padding-left: 14px; + } + .tab { + gap: 9px; + font-size: 11px; + } + .tab.active::before { + left: -15px; + } + .topbar { + padding: 0 26px; + } + .content { + padding: 28px 26px 0; + } + .panel { + padding: 21px; + } + .admin-panel .subtle-tag { + display: none; + } + .panel-footnote { + margin: 24px -21px -21px; + padding: 13px 21px; + } + .overview-stat { + padding: 18px 15px; + } + .stat-heading { + font-size: 10px; + } + .stat-heading svg { + width: 14px; + height: 14px; + } + .guild-header { + min-width: 175px; + padding: 10px; + } + .panel__header--split { + flex-wrap: wrap; + } + .section-title { + gap: 10px; + } + .section-title h2 { + font-size: 21px; + } + .section-icon { + display: none; + } + .landing-shell { + padding: 0 40px; + } + .landing-art { + min-height: 420px; + } +} +@media (max-width: 1000px) { + .overview-stats { + grid-template-columns: repeat(2, 1fr); + } + .overview-stat:nth-child(3) { + border-left: 0; + } + .overview-stat:nth-child(n + 3) { + border-top: 1px solid var(--line); + } + .stat-heading { + font-size: 11px; + } + .page-heading { + align-items: start; + } + .page-heading h1 { + font-size: 30px; + } + .guild-header { + max-width: 180px; + min-width: 155px; + margin-top: 15px; + } + .guild-header .guild-avatar, + .guild-header img { + display: none; + } + .warnings-layout { + grid-template-columns: 1fr; + } + .custom-command { + align-items: start; + flex-direction: column; + gap: 12px; + } + .custom-command .input-row { + width: 100%; + } + .command-card__body { + grid-template-columns: 1fr; + } + .landing-main { + gap: 25px; + } + .landing-art { + min-height: 370px; + } + .landing-copy h1 { + font-size: 60px; + } +} +@media (max-width: 760px) { + .app-shell { + display: block; + } + .sidebar { + position: relative; + height: auto; + overflow: visible; + padding: 20px 20px 16px; + } + .brand > span { + font-size: 26px; + } + .brand img { + width: 35px; + height: 39px; + } + .sidebar__section-label { + display: none; + } + .tabs { + display: flex; + margin-top: 22px; + gap: 4px; + } + .tab { + justify-content: center; + flex: 1; + font-size: 11px; + padding: 11px 8px; + width: auto; + } + .tab.active::before, + .nav-arrow, + .nav-count { + display: none; + } + .sidebar-search { + margin: 17px 0 8px; + } + .sidebar-search input { + min-height: 34px; + } + .guild-list { + display: flex; + overflow-x: auto; + gap: 8px; + padding-bottom: 3px; + } + .guild-item { + width: auto; + flex-shrink: 0; + max-width: 210px; + } + .sidebar-bottom { + position: absolute; + right: 20px; + top: 16px; + margin: 0; + padding: 0; + } + .profile { + padding: 0; + border: 0; + } + .profile-avatar, + .profile__text { + display: none; + } + .profile .icon-button { + color: #ccb898; + } + .topbar { + height: 53px; + padding: 0 22px; + } + .breadcrumb { + font-size: 10px; + gap: 10px; + } + .content { + padding: 25px 22px 0; + } + .page-heading { + margin-bottom: 22px; + } + .page-heading h1 { + font-size: 32px; + } + .guild-header { + display: none; + } + .overview-stats { + margin: 18px 0 23px; + } + .overview-stat { + padding: 18px; + } + .section-icon { + display: grid; + } + .section-title h2 { + font-size: 23px; + } + .save-bar { + bottom: 10px; + padding: 12px 14px; + } + .save-bar strong { + font-size: 11px; + } + .save-bar .button { + padding: 8px 11px; + } + .callout, + .error-banner { + flex-wrap: wrap; + } + .callout > div { + min-width: 200px; + } + .landing-shell { + padding: 0 25px; + } + .landing-nav { + padding: 23px 0; + } + .landing-main { + padding: 40px 0; + } + .landing-copy h1 { + font-size: 48px; + letter-spacing: -1.5px; + } + .landing-art { + min-height: 340px; + } + .landing-description { + font-size: 12px; + } +} +@media (max-width: 520px) { + .sidebar { + padding: 18px 16px 12px; + } + .tab { + font-size: 10px; + gap: 6px; + flex: 1 1 0; + flex-direction: column; + min-width: 0; + min-height: 62px; + padding: 8px 4px; + white-space: normal; + text-align: center; + } + .tab svg { + width: 14px; + height: 14px; + } + .topbar { + padding: 0 18px; + } + .content { + padding: 23px 16px 0; + } + .page-heading h1 { + font-size: 31px; + } + .overview-stat { + padding: 16px; + } + .stat-heading { + font-size: 10px; + } + .stat-value { + font-size: 31px; + } + .stat-value--status { + font-size: 25px; + } + .panel { + padding: 20px; + } + .panel__header { + margin-bottom: 20px; + } + .section-title h2 { + font-size: 23px; + } + .panel__header .muted { + font-size: 10px; + } + .section-icon { + display: none; + } + .field { + margin-top: 22px; + } + .input-row { + flex-wrap: wrap; + } + .input-row input { + flex-basis: 170px; + } + .input-row .button { + flex: 1; + } + .panel-footnote { + margin: 23px -20px -20px; + padding: 12px 20px; + font-size: 9px; + } + .stats-grid { + grid-template-columns: repeat(2, 1fr); + } + .stat-card:nth-child(3) { + border-left: 0; + } + .stat-card:nth-child(n + 3) { + border-top: 1px solid var(--line); + } + .toolbar { + flex-direction: column; + } + .toolbar select { + width: 100%; + } + .command-card__header { + padding: 14px; + } + .command-card__title { + gap: 6px; + } + .command-card__title span { + font-size: 9px; + } + .command-card__body { + padding: 14px; + } + .save-bar { + flex-wrap: wrap; + justify-content: center; + } + .save-bar > div:first-child { + justify-content: center; + } + .save-bar .button-row { + width: 100%; + justify-content: center; + } + .save-bar .button { + flex: 1; + } + .landing-main { + grid-template-columns: 1fr; + padding: 42px 0 30px; + gap: 35px; + } + .landing-copy h1 { + font-size: clamp(47px, 15.2vw, 62px); + } + .landing-description { + max-width: 330px; + } + .landing-art { + min-height: 310px; + height: 340px; + border-radius: 150px 150px 3px 3px; + } + .landing-art img { + max-height: 290px; + } +} +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation: none !important; + transition: none !important; + scroll-behavior: auto !important; + } } -@keyframes rise { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } +.sidebar .brand { + margin-bottom: 40px; } - -@media (max-width: 1080px) { - .layout { - grid-template-columns: 1fr; - } - - .warnings-layout { - grid-template-columns: 1fr; - } - - .save-bar { - position: static; - } - - .topbar { - flex-direction: column; - align-items: flex-start; - } - - .toolbar { - grid-template-columns: 1fr; - } +.content { + padding-bottom: 40px; +} +.page-heading h1 { + margin: 0; +} +.guild-header { + min-width: 0; +} +.guild-header strong { + margin: 0; +} +.overview-stats { + margin-top: 0; +} +.stat-heading { + min-height: 24px; + font-size: 12px; +} +.stat-heading .icon-button { + width: 24px; + height: 24px; + font-size: 19px; + padding: 0; +} +.stat-heading .text-link { + font: inherit; + color: inherit; +} +.stat-value--status { + line-height: 1.3; + font-size: 35px; +} +.admin-fields { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 32px; + align-items: start; +} +.admin-fields > .field { + margin-top: 0; + align-content: start; +} +#adminRolesLabel { + font-size: 12px; + font-weight: 600; + color: #51483f; +} +.warning-actions { + margin-top: 20px; +} +.selected-user-card + .warning-actions { + margin-top: 0; +} +.selected-user-card[hidden] + .warning-actions { + margin-top: 20px; +} +#warningsSearchStatus:empty, +#warningsSelectedHint:empty, +.landing-session:has(#sessionStatus:empty) { + display: none; +} +.callout > p, +.error-banner > p { + flex: 1; + margin: 0; +} +.landing-main { + min-height: calc(100vh - 116px); +} +.landing-copy h1 { + margin-top: 0; +} +@media (max-width: 1000px) { + .admin-fields { + grid-template-columns: 1fr; + gap: 24px; + } +} +@media (max-width: 760px) { + .sidebar .brand { + margin-bottom: 0; + } + .content { + padding-bottom: 28px; + } +} +@media (max-width: 520px) { + .stat-value--status { + font-size: 28px; + } + .stat-heading { + font-size: 10px; + } + .landing-main { + min-height: 0; + } }