diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46de744e5..24aede302 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,7 @@ You should also include the user name that made the change.
 =======
 - Server: Add rate limit to i/notifications @tamaina
 - Client: Improve files page of control panel @syuilo
+- Client: Show warning in control panel when there is an unresolved abuse report @syuilo
 - Improve player detection in URL preview @mei23
 - Add Badge Image to Push Notification #8012 @tamaina
 
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 19e751587..28990f81b 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -850,6 +850,12 @@ rateLimitExceeded: "レート制限を超えました"
 cropImage: "画像のクロップ"
 cropImageAsk: "画像をクロップしますか?"
 file: "ファイル"
+recentNHours: "直近{n}時間"
+recentNDays: "直近{n}日"
+noEmailServerWarning: "メールサーバーの設定がされていません。"
+thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。"
+recommended: "推奨"
+check: "チェック"
 
 _emailUnavailable:
   used: "既に使用されています"
diff --git a/packages/backend/src/models/repositories/instance.ts b/packages/backend/src/models/repositories/instance.ts
index 4594d8634..e9ee18ea3 100644
--- a/packages/backend/src/models/repositories/instance.ts
+++ b/packages/backend/src/models/repositories/instance.ts
@@ -1,11 +1,13 @@
 import { db } from '@/db/postgre.js';
 import { Instance } from '@/models/entities/instance.js';
 import { Packed } from '@/misc/schema.js';
+import { fetchMeta } from '@/misc/fetch-meta.js';
 
 export const InstanceRepository = db.getRepository(Instance).extend({
 	async pack(
 		instance: Instance,
 	): Promise<Packed<'FederationInstance'>> {
+		const meta = await fetchMeta();
 		return {
 			id: instance.id,
 			caughtAt: instance.caughtAt.toISOString(),
@@ -18,6 +20,7 @@ export const InstanceRepository = db.getRepository(Instance).extend({
 			lastCommunicatedAt: instance.lastCommunicatedAt.toISOString(),
 			isNotResponding: instance.isNotResponding,
 			isSuspended: instance.isSuspended,
+			isBlocked: meta.blockedHosts.includes(instance.host),
 			softwareName: instance.softwareName,
 			softwareVersion: instance.softwareVersion,
 			openRegistrations: instance.openRegistrations,
diff --git a/packages/backend/src/models/schema/federation-instance.ts b/packages/backend/src/models/schema/federation-instance.ts
index 9f27aab98..3efff6ca9 100644
--- a/packages/backend/src/models/schema/federation-instance.ts
+++ b/packages/backend/src/models/schema/federation-instance.ts
@@ -52,6 +52,10 @@ export const packedFederationInstanceSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
+		isBlocked: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
 		softwareName: {
 			type: 'string',
 			optional: false, nullable: true,
diff --git a/packages/client/src/components/abuse-report.vue b/packages/client/src/components/abuse-report.vue
index 86eccd627..e8ab6f600 100644
--- a/packages/client/src/components/abuse-report.vue
+++ b/packages/client/src/components/abuse-report.vue
@@ -79,7 +79,8 @@ function resolve() {
 			align-items: center;
 			padding: 14px;
 			border-radius: 8px;
-			background-image: linear-gradient(45deg, rgb(255 196 0 / 15%) 16.67%, transparent 16.67%, transparent 50%, rgb(255 196 0 / 15%) 50%, rgb(255 196 0 / 15%) 66.67%, transparent 66.67%, transparent 100%);
+			--c: rgb(255 196 0 / 15%);
+			background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
 			background-size: 16px 16px;
 
 			> .avatar {
diff --git a/packages/client/src/components/instance-info.vue b/packages/client/src/components/instance-info.vue
new file mode 100644
index 000000000..e55c1d821
--- /dev/null
+++ b/packages/client/src/components/instance-info.vue
@@ -0,0 +1,96 @@
+<template>
+<div :class="[$style.root, { yellow: instance.isNotResponding, red: instance.isBlocked, gray: instance.isSuspended }]">
+	<img v-if="instance.iconUrl" class="icon" :src="instance.iconUrl" alt=""/>
+	<div class="body">
+		<span class="host">{{ instance.host }}</span>
+		<span class="sub">{{ instance.softwareName || '?' }} {{ instance.softwareVersion }}</span>
+	</div>
+	<MkMiniChart v-if="chart" class="chart" :src="chart.requests.received"/>
+</div>
+</template>
+
+<script lang="ts" setup>
+import * as misskey from 'misskey-js';
+import MkMiniChart from '@/components/mini-chart.vue';
+import * as os from '@/os';
+
+const props = defineProps<{
+	instance: misskey.entities.Instance;
+}>();
+
+const chart = $ref(null);
+
+os.api('charts/instance', { host: props.instance.host, limit: 16, span: 'hour' }).then(res => {
+	chart = res;
+});
+</script>
+
+<style lang="scss" module>
+.root {
+	$bodyTitleHieght: 18px;
+	$bodyInfoHieght: 16px;
+
+	display: flex;
+	align-items: center;
+	padding: 16px;
+	background: var(--panel);
+	border-radius: 8px;
+
+	> :global(.icon) {
+		display: block;
+		width: ($bodyTitleHieght + $bodyInfoHieght);
+		height: ($bodyTitleHieght + $bodyInfoHieght);
+		object-fit: cover;
+		border-radius: 4px;
+		margin-right: 8px;
+	}
+
+	> :global(.body) {
+		flex: 1;
+		overflow: hidden;
+		font-size: 0.9em;
+		color: var(--fg);
+		padding-right: 8px;
+
+		> :global(.host) {
+			display: block;
+			width: 100%;
+			white-space: nowrap;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			line-height: $bodyTitleHieght;
+		}
+
+		> :global(.sub) {
+			font-size: 75%;
+			opacity: 0.7;
+			line-height: $bodyInfoHieght;
+			white-space: nowrap;
+			overflow: hidden;
+			text-overflow: ellipsis;
+		}
+	}
+
+	> :global(.chart) {
+		height: 30px;
+	}
+
+	&:global(.yellow) {
+		--c: rgb(255 196 0 / 15%);
+		background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
+		background-size: 16px 16px;
+	}
+
+	&:global(.red) {
+		--c: rgb(255 0 0 / 15%);
+		background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
+		background-size: 16px 16px;
+	}
+
+	&:global(.gray) {
+		--c: var(--bg);
+		background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
+		background-size: 16px 16px;
+	}
+}
+</style>
diff --git a/packages/client/src/pages/admin/email-settings.vue b/packages/client/src/pages/admin/email-settings.vue
index 5487c5f33..c0ff94fad 100644
--- a/packages/client/src/pages/admin/email-settings.vue
+++ b/packages/client/src/pages/admin/email-settings.vue
@@ -5,7 +5,7 @@
 		<FormSuspense :p="init">
 			<div class="_formRoot">
 				<FormSwitch v-model="enableEmail" class="_formBlock">
-					<template #label>{{ i18n.ts.enableEmail }}</template>
+					<template #label>{{ i18n.ts.enableEmail }} ({{ i18n.ts.recommended }})</template>
 					<template #caption>{{ i18n.ts.emailConfigInfo }}</template>
 				</FormSwitch>
 
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index 5db91101d..b91330e1b 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -7,8 +7,10 @@
 					<img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
 				</div>
 
+				<MkInfo v-if="thereIsUnresolvedAbuseReport" warn class="info">{{ $ts.thereIsUnresolvedAbuseReportWarning }} <MkA to="/admin/abuses" class="_link">{{ $ts.check }}</MkA></MkInfo>
 				<MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
 				<MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ $ts.configure }}</MkA></MkInfo>
+				<MkInfo v-if="noEmailServer" warn class="info">{{ $ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
 
 				<MkSuperMenu :def="menuDef" :grid="initialPage == null"></MkSuperMenu>
 			</div>
@@ -58,6 +60,15 @@ let el = $ref(null);
 let pageProps = $ref({});
 let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
 let noBotProtection = !instance.enableHcaptcha && !instance.enableRecaptcha;
+let noEmailServer = !instance.enableEmail;
+let thereIsUnresolvedAbuseReport = $ref(false);
+
+os.api('admin/abuse-user-reports', {
+	state: 'unresolved',
+	limit: 1,
+}).then(reports => {
+	if (reports.length > 0) thereIsUnresolvedAbuseReport = true;
+});
 
 const NARROW_THRESHOLD = 600;
 const ro = new ResizeObserver((entries, observer) => {
diff --git a/packages/client/src/pages/federation.vue b/packages/client/src/pages/federation.vue
index eda46b9aa..38d42f2be 100644
--- a/packages/client/src/pages/federation.vue
+++ b/packages/client/src/pages/federation.vue
@@ -41,51 +41,8 @@
 
 			<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
 				<div class="dqokceoi">
-					<MkA v-for="instance in items" :key="instance.id" class="instance" :to="`/instance-info/${instance.host}`">
-						<div class="host"><img :src="instance.faviconUrl">{{ instance.host }}</div>
-						<div class="table">
-							<div class="cell">
-								<div class="key">{{ $ts.registeredAt }}</div>
-								<div class="value"><MkTime :time="instance.caughtAt"/></div>
-							</div>
-							<div class="cell">
-								<div class="key">{{ $ts.software }}</div>
-								<div class="value">{{ instance.softwareName || `(${$ts.unknown})` }}</div>
-							</div>
-							<div class="cell">
-								<div class="key">{{ $ts.version }}</div>
-								<div class="value">{{ instance.softwareVersion || `(${$ts.unknown})` }}</div>
-							</div>
-							<div class="cell">
-								<div class="key">{{ $ts.users }}</div>
-								<div class="value">{{ instance.usersCount }}</div>
-							</div>
-							<div class="cell">
-								<div class="key">{{ $ts.notes }}</div>
-								<div class="value">{{ instance.notesCount }}</div>
-							</div>
-							<div class="cell">
-								<div class="key">{{ $ts.sent }}</div>
-								<div class="value"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
-							</div>
-							<div class="cell">
-								<div class="key">{{ $ts.received }}</div>
-								<div class="value"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
-							</div>
-						</div>
-						<div class="footer">
-							<span class="status" :class="getStatus(instance)">{{ getStatus(instance) }}</span>
-							<span class="pubSub">
-								<span v-if="instance.followersCount > 0" class="sub"><i class="fas fa-caret-down icon"></i>Sub</span>
-								<span v-else class="sub"><i class="fas fa-caret-down icon"></i>-</span>
-								<span v-if="instance.followingCount > 0" class="pub"><i class="fas fa-caret-up icon"></i>Pub</span>
-								<span v-else class="pub"><i class="fas fa-caret-up icon"></i>-</span>
-							</span>
-							<span class="right">
-								<span class="latestStatus">{{ instance.latestStatus || '-' }}</span>
-								<span class="lastCommunicatedAt"><MkTime :time="instance.lastCommunicatedAt"/></span>
-							</span>
-						</div>
+					<MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Last communicated: ${new Date(instance.lastCommunicatedAt).toLocaleString()}\nStatus: ${getStatus(instance)}`" class="instance" :to="`/instance-info/${instance.host}`" :behavior="'window'">
+						<MkInstanceInfo :instance="instance"/>
 					</MkA>
 				</div>
 			</MkPagination>
@@ -100,6 +57,7 @@ import MkButton from '@/components/ui/button.vue';
 import MkInput from '@/components/form/input.vue';
 import MkSelect from '@/components/form/select.vue';
 import MkPagination from '@/components/ui/pagination.vue';
+import MkInstanceInfo from '@/components/instance-info.vue';
 import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os';
 import { i18n } from '@/i18n';
@@ -127,9 +85,10 @@ const pagination = {
 };
 
 function getStatus(instance) {
-	if (instance.isSuspended) return 'suspended';
-	if (instance.isNotResponding) return 'error';
-	return 'alive';
+	if (instance.isSuspended) return 'Suspended';
+	if (instance.isBlocked) return 'Blocked';
+	if (instance.isNotResponding) return 'Error';
+	return 'Alive';
 }
 
 const headerActions = $computed(() => []);
@@ -156,86 +115,8 @@ definePageMetadata({
 	grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
 	grid-gap: 12px;
 
-	> .instance {
-		padding: 16px;
-		background: var(--panel);
-		border-radius: 8px;
-
-		&:hover {
-			text-decoration: none;
-		}
-
-		> .host {
-			font-weight: bold;
-			white-space: nowrap;
-			overflow: hidden;
-			text-overflow: ellipsis;
-
-			> img {
-				width: 18px;
-				height: 18px;
-				margin-right: 6px;
-				vertical-align: middle;
-			}
-		}
-
-		> .table {
-			display: grid;
-			grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
-			grid-gap: 6px;
-			margin: 6px 0;
-			font-size: 70%;
-
-			> .cell {
-				> .key, > .value {
-					white-space: nowrap;
-					overflow: hidden;
-					text-overflow: ellipsis;
-				}
-
-				> .key {
-					opacity: 0.7;
-				}
-
-				> .value {
-				}
-			}
-		}
-
-		> .footer {
-			display: flex;
-			align-items: center;
-			font-size: 0.9em;
-
-			> .status {
-				&.suspended {
-					opacity: 0.5;
-				}
-
-				&.error {
-					color: var(--error);
-				}
-
-				&.alive {
-					color: var(--success);
-				}
-			}
-
-			> .pubSub {
-				margin-left: 8px;
-			}
-
-			> .right {
-				margin-left: auto;
-
-				> .latestStatus {
-					border: solid 1px var(--divider);
-					border-radius: 4px;
-					margin: 0 8px;
-					padding: 0 4px;
-				}
-			}
-		}
+	> .instance:hover {
+		text-decoration: none;
 	}
 }
 </style>
diff --git a/packages/client/src/pages/instance-info.vue b/packages/client/src/pages/instance-info.vue
index 231b1dfd1..11e361857 100644
--- a/packages/client/src/pages/instance-info.vue
+++ b/packages/client/src/pages/instance-info.vue
@@ -1,8 +1,8 @@
 <template>
 <MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
-	<MkSpacer :content-max="600" :margin-min="16" :margin-max="32">
-		<div v-if="instance" class="_formRoot">
+	<MkSpacer v-if="instance" :content-max="600" :margin-min="16" :margin-max="32">
+		<div v-if="tab === 'overview'" class="_formRoot">
 			<div class="fnfelxur">
 				<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/>
 			</div>
@@ -64,37 +64,6 @@
 				</MkKeyValue>
 			</FormSection>
 
-			<FormSection>
-				<template #label>{{ $ts.statistics }}</template>
-				<div class="cmhjzshl">
-					<div class="selects">
-						<MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
-							<option value="instance-requests">{{ $ts._instanceCharts.requests }}</option>
-							<option value="instance-users">{{ $ts._instanceCharts.users }}</option>
-							<option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option>
-							<option value="instance-notes">{{ $ts._instanceCharts.notes }}</option>
-							<option value="instance-notes-total">{{ $ts._instanceCharts.notesTotal }}</option>
-							<option value="instance-ff">{{ $ts._instanceCharts.ff }}</option>
-							<option value="instance-ff-total">{{ $ts._instanceCharts.ffTotal }}</option>
-							<option value="instance-drive-usage">{{ $ts._instanceCharts.cacheSize }}</option>
-							<option value="instance-drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option>
-							<option value="instance-drive-files">{{ $ts._instanceCharts.files }}</option>
-							<option value="instance-drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option>
-						</MkSelect>
-						<MkSelect v-model="chartSpan" style="margin: 0;">
-							<option value="hour">{{ $ts.perHour }}</option>
-							<option value="day">{{ $ts.perDay }}</option>
-						</MkSelect>
-					</div>
-					<div class="chart">
-						<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :args="{ host: host }" :detailed="true"></MkChart>
-					</div>
-				</div>
-			</FormSection>
-
-			<MkObjectView tall :value="instance">
-			</MkObjectView>
-
 			<FormSection>
 				<template #label>Well-known resources</template>
 				<FormLink :to="`https://${host}/.well-known/host-meta`" external style="margin-bottom: 8px;">host-meta</FormLink>
@@ -104,6 +73,35 @@
 				<FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink>
 			</FormSection>
 		</div>
+		<div v-if="tab === 'chart'" class="_formRoot">
+			<div class="cmhjzshl">
+				<div class="selects">
+					<MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
+						<option value="instance-requests">{{ $ts._instanceCharts.requests }}</option>
+						<option value="instance-users">{{ $ts._instanceCharts.users }}</option>
+						<option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option>
+						<option value="instance-notes">{{ $ts._instanceCharts.notes }}</option>
+						<option value="instance-notes-total">{{ $ts._instanceCharts.notesTotal }}</option>
+						<option value="instance-ff">{{ $ts._instanceCharts.ff }}</option>
+						<option value="instance-ff-total">{{ $ts._instanceCharts.ffTotal }}</option>
+						<option value="instance-drive-usage">{{ $ts._instanceCharts.cacheSize }}</option>
+						<option value="instance-drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option>
+						<option value="instance-drive-files">{{ $ts._instanceCharts.files }}</option>
+						<option value="instance-drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option>
+					</MkSelect>
+				</div>
+				<div class="charts">
+					<div class="label">{{ i18n.t('recentNHours', { n: 90 }) }}</div>
+					<MkChart class="chart" :src="chartSrc" span="hour" :limit="90" :args="{ host: host }" :detailed="true"></MkChart>
+					<div class="label">{{ i18n.t('recentNDays', { n: 90 }) }}</div>
+					<MkChart class="chart" :src="chartSrc" span="day" :limit="90" :args="{ host: host }" :detailed="true"></MkChart>
+				</div>
+			</div>
+		</div>
+		<div v-if="tab === 'raw'" class="_formRoot">
+			<MkObjectView tall :value="instance">
+			</MkObjectView>
+		</div>
 	</MkSpacer>
 </MkStickyContainer>
 </template>
@@ -125,17 +123,18 @@ import number from '@/filters/number';
 import bytes from '@/filters/bytes';
 import { iAmModerator } from '@/account';
 import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	host: string;
 }>();
 
+let tab = $ref('overview');
 let meta = $ref<misskey.entities.DetailedInstanceMetadata | null>(null);
 let instance = $ref<misskey.entities.Instance | null>(null);
 let suspended = $ref(false);
 let isBlocked = $ref(false);
 let chartSrc = $ref('instance-requests');
-let chartSpan = $ref('hour');
 
 async function fetch() {
 	if (iAmModerator) {
@@ -184,11 +183,26 @@ const headerActions = $computed(() => [{
 	},
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = $computed(() => [{
+	active: tab === 'overview',
+	title: i18n.ts.overview,
+	icon: 'fas fa-info-circle',
+	onClick: () => { tab = 'overview'; },
+}, {
+	active: tab === 'chart',
+	title: i18n.ts.charts,
+	icon: 'fas fa-chart-simple',
+	onClick: () => { tab = 'chart'; },
+}, {
+	active: tab === 'raw',
+	title: 'Raw data',
+	icon: 'fas fa-code',
+	onClick: () => { tab = 'raw'; },
+}]);
 
 definePageMetadata({
 	title: props.host,
-	icon: 'fas fa-info-circle',
+	icon: 'fas fa-server',
 	bg: 'var(--bg)',
 });
 </script>
@@ -208,5 +222,12 @@ definePageMetadata({
 		display: flex;
 		margin: 0 0 16px 0;
 	}
+
+	> .charts {
+		> .label {
+			margin-bottom: 12px;
+			font-weight: bold;
+		}
+	}
 }
 </style>
diff --git a/packages/client/src/scripts/i18n.ts b/packages/client/src/scripts/i18n.ts
index 3fe88e551..54184386d 100644
--- a/packages/client/src/scripts/i18n.ts
+++ b/packages/client/src/scripts/i18n.ts
@@ -11,13 +11,13 @@ export class I18n<T extends Record<string, any>> {
 
 	// string にしているのは、ドット区切りでのパス指定を許可するため
 	// なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
-	public t(key: string, args?: Record<string, string>): string {
+	public t(key: string, args?: Record<string, string | number>): string {
 		try {
 			let str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string;
 
 			if (args) {
 				for (const [k, v] of Object.entries(args)) {
-					str = str.replace(`{${k}}`, v);
+					str = str.replace(`{${k}}`, v.toString());
 				}
 			}
 			return str;