diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue
index c4d0eb85dd..1959271f5d 100644
--- a/packages/client/src/components/chart.vue
+++ b/packages/client/src/components/chart.vue
@@ -170,10 +170,10 @@ export default defineComponent({
 					aspectRatio: props.aspectRatio || 2.5,
 					layout: {
 						padding: {
-							left: 16,
-							right: 16,
-							top: 16,
-							bottom: 8,
+							left: 0,
+							right: 0,
+							top: 0,
+							bottom: 0,
 						},
 					},
 					scales: {
diff --git a/packages/client/src/components/form/section.vue b/packages/client/src/components/form/section.vue
index ab9fbe5fcd..c6e34ef1cc 100644
--- a/packages/client/src/components/form/section.vue
+++ b/packages/client/src/components/form/section.vue
@@ -1,5 +1,5 @@
 <template>
-<div v-size="{ max: [500] }" v-sticky-container class="vrtktovh _formBlock">
+<div class="vrtktovh _formBlock">
 	<div class="label"><slot name="label"></slot></div>
 	<div class="main _formRoot">
 		<slot></slot>
@@ -12,10 +12,8 @@
 
 <style lang="scss" scoped>
 .vrtktovh {
-	margin: 0;
 	border-top: solid 0.5px var(--divider);
 	border-bottom: solid 0.5px var(--divider);
-	padding: 24px 0;
 
 	& + .vrtktovh {
 		border-top: none;
@@ -31,7 +29,7 @@
 
 	> .label {
 		font-weight: bold;
-		padding: 0 0 16px 0;
+		margin: 1.5em 0 16px 0;
 
 		&:empty {
 			display: none;
@@ -39,6 +37,7 @@
 	}
 
 	> .main {
+		margin: 1.5em 0;
 	}
 }
 </style>
diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue
index ac3284e7da..f8a07b4caa 100644
--- a/packages/client/src/components/form/switch.vue
+++ b/packages/client/src/components/form/switch.vue
@@ -111,7 +111,7 @@ export default defineComponent({
 	}
 
 	> .label {
-		margin-left: 16px;
+		margin-left: 12px;
 		margin-top: 2px;
 		display: block;
 		transition: inherit;
diff --git a/packages/client/src/components/global/spacer.vue b/packages/client/src/components/global/spacer.vue
index e2f1d1aec7..8a1d7a4e8a 100644
--- a/packages/client/src/components/global/spacer.vue
+++ b/packages/client/src/components/global/spacer.vue
@@ -40,7 +40,7 @@ export default defineComponent({
 				return;
 			}
 
-			if (rect.width > props.contentMax || rect.width > 500) {
+			if (rect.width > props.contentMax || (rect.width > 360 && window.innerWidth > 400)) {
 				margin.value = props.marginMax;
 			} else {
 				margin.value = props.marginMin;
diff --git a/packages/client/src/components/instance-stats.vue b/packages/client/src/components/instance-stats.vue
index bc62998a4a..409c3a49ca 100644
--- a/packages/client/src/components/instance-stats.vue
+++ b/packages/client/src/components/instance-stats.vue
@@ -1,5 +1,5 @@
 <template>
-<div class="zbcjwnqg" style="margin-top: -8px;">
+<div class="zbcjwnqg">
 	<div class="selects" style="display: flex;">
 		<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;">
 			<optgroup :label="$ts.federation">
@@ -29,16 +29,16 @@
 			<option value="day">{{ $ts.perDay }}</option>
 		</MkSelect>
 	</div>
-	<MkChart :src="chartSrc" :span="chartSpan" :limit="chartLimit" :detailed="detailed"></MkChart>
+	<div class="chart">
+		<MkChart :src="chartSrc" :span="chartSpan" :limit="chartLimit" :detailed="detailed"></MkChart>
+	</div>
 </div>
 </template>
 
 <script lang="ts">
-import { defineComponent, onMounted, ref, watch } from 'vue';
+import { defineComponent, ref } from 'vue';
 import MkSelect from '@/components/form/select.vue';
 import MkChart from '@/components/chart.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
 
 export default defineComponent({
 	components: {
@@ -74,7 +74,10 @@ export default defineComponent({
 <style lang="scss" scoped>
 .zbcjwnqg {
 	> .selects {
-		padding: 8px 16px 0 16px;
+	}
+
+	> .chart {
+		padding: 8px 0 0 0;
 	}
 }
 </style>
diff --git a/packages/client/src/components/key-value.vue b/packages/client/src/components/key-value.vue
index 6a9a948ce9..da98abd77c 100644
--- a/packages/client/src/components/key-value.vue
+++ b/packages/client/src/components/key-value.vue
@@ -1,5 +1,5 @@
 <template>
-<div class="alqyeyti">
+<div class="alqyeyti" :class="{ oneline }">
 	<div class="key">
 		<slot name="key"></slot>
 	</div>
@@ -22,6 +22,11 @@ export default defineComponent({
 			required: false,
 			default: null,
 		},
+		oneline: {
+			type: Boolean,
+			required: false,
+			default: false,
+		},
 	},
 
 	setup(props) {
@@ -39,10 +44,30 @@ export default defineComponent({
 
 <style lang="scss" scoped>
 .alqyeyti {
+	> .key, > .value {
+		white-space: nowrap;
+		overflow: hidden;
+		text-overflow: ellipsis;
+	}
+
 	> .key {
 		font-size: 0.85em;
 		padding: 0 0 0.25em 0;
 		opacity: 0.75;
 	}
+
+	&.oneline {
+		display: flex;
+
+		> .key {
+			width: 30%;
+			font-size: 1em;
+			padding: 0 8px 0 0;
+		}
+
+		> .value {
+			width: 70%;
+		}
+	}
 }
 </style>
diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue
index 618e569839..e9d17feec1 100644
--- a/packages/client/src/pages/about.vue
+++ b/packages/client/src/pages/about.vue
@@ -93,7 +93,8 @@ export default defineComponent({
 		return {
 			[symbols.PAGE_INFO]: {
 				title: this.$ts.instanceInfo,
-				icon: 'fas fa-info-circle'
+				icon: 'fas fa-info-circle',
+				bg: 'var(--bg)',
 			},
 			host,
 			version,
diff --git a/packages/client/src/pages/admin/instance.vue b/packages/client/src/pages/admin/instance.vue
deleted file mode 100644
index 51fcb8675a..0000000000
--- a/packages/client/src/pages/admin/instance.vue
+++ /dev/null
@@ -1,291 +0,0 @@
-<template>
-<XModalWindow ref="dialog"
-	:width="520"
-	:height="500"
-	@close="$refs.dialog.close()"
-	@closed="$emit('closed')"
->
-	<template #header>{{ instance.host }}</template>
-	<div class="mk-instance-info">
-		<div class="_table section">
-			<div class="_row">
-				<div class="_cell">
-					<div class="_label">{{ $ts.software }}</div>
-					<div class="_data">{{ instance.softwareName || '?' }}</div>
-				</div>
-				<div class="_cell">
-					<div class="_label">{{ $ts.version }}</div>
-					<div class="_data">{{ instance.softwareVersion || '?' }}</div>
-				</div>
-			</div>
-		</div>
-		<div class="_table data section">
-			<div class="_row">
-				<div class="_cell">
-					<div class="_label">{{ $ts.registeredAt }}</div>
-					<div class="_data">{{ new Date(instance.caughtAt).toLocaleString() }} (<MkTime :time="instance.caughtAt"/>)</div>
-				</div>
-			</div>
-			<div class="_row">
-				<div class="_cell">
-					<div class="_label">{{ $ts.following }}</div>
-					<button class="_data _textButton" @click="showFollowing()">{{ number(instance.followingCount) }}</button>
-				</div>
-				<div class="_cell">
-					<div class="_label">{{ $ts.followers }}</div>
-					<button class="_data _textButton" @click="showFollowers()">{{ number(instance.followersCount) }}</button>
-				</div>
-			</div>
-			<div class="_row">
-				<div class="_cell">
-					<div class="_label">{{ $ts.users }}</div>
-					<button class="_data _textButton" @click="showUsers()">{{ number(instance.usersCount) }}</button>
-				</div>
-				<div class="_cell">
-					<div class="_label">{{ $ts.notes }}</div>
-					<div class="_data">{{ number(instance.notesCount) }}</div>
-				</div>
-			</div>
-			<div class="_row">
-				<div class="_cell">
-					<div class="_label">{{ $ts.files }}</div>
-					<div class="_data">{{ number(instance.driveFiles) }}</div>
-				</div>
-				<div class="_cell">
-					<div class="_label">{{ $ts.storageUsage }}</div>
-					<div class="_data">{{ bytes(instance.driveUsage) }}</div>
-				</div>
-			</div>
-			<div class="_row">
-				<div class="_cell">
-					<div class="_label">{{ $ts.latestRequestSentAt }}</div>
-					<div class="_data"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
-				</div>
-				<div class="_cell">
-					<div class="_label">{{ $ts.latestStatus }}</div>
-					<div class="_data">{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</div>
-				</div>
-			</div>
-			<div class="_row">
-				<div class="_cell">
-					<div class="_label">{{ $ts.latestRequestReceivedAt }}</div>
-					<div class="_data"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
-				</div>
-			</div>
-		</div>
-		<div class="chart">
-			<div class="header">
-				<span class="label">{{ $ts.charts }}</span>
-				<div class="selects">
-					<MkSelect v-model="chartSrc" style="margin: 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>
-			<div class="chart">
-				<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :detailed="true"></MkChart>
-			</div>
-		</div>
-		<div class="operations section">
-			<span class="label">{{ $ts.operations }}</span>
-			<MkSwitch v-model="isSuspended" class="switch">{{ $ts.stopActivityDelivery }}</MkSwitch>
-			<MkSwitch :model-value="isBlocked" class="switch" @update:modelValue="changeBlock">{{ $ts.blockThisInstance }}</MkSwitch>
-			<details>
-				<summary>{{ $ts.deleteAllFiles }}</summary>
-				<MkButton style="margin: 0.5em 0 0.5em 0;" @click="deleteAllFiles()"><i class="fas fa-trash-alt"></i> {{ $ts.deleteAllFiles }}</MkButton>
-			</details>
-			<details>
-				<summary>{{ $ts.removeAllFollowing }}</summary>
-				<MkButton style="margin: 0.5em 0 0.5em 0;" @click="removeAllFollowing()"><i class="fas fa-minus-circle"></i> {{ $ts.removeAllFollowing }}</MkButton>
-				<MkInfo warn>{{ $t('removeAllFollowingDescription', { host: instance.host }) }}</MkInfo>
-			</details>
-		</div>
-		<details class="metadata section">
-			<summary class="label">{{ $ts.metadata }}</summary>
-			<pre><code>{{ JSON.stringify(instance, null, 2) }}</code></pre>
-		</details>
-	</div>
-</XModalWindow>
-</template>
-
-<script lang="ts">
-import { defineComponent, markRaw } from 'vue';
-import XModalWindow from '@/components/ui/modal-window.vue';
-import MkSelect from '@/components/form/select.vue';
-import MkButton from '@/components/ui/button.vue';
-import MkSwitch from '@/components/form/switch.vue';
-import MkInfo from '@/components/ui/info.vue';
-import MkChart from '@/components/chart.vue';
-import bytes from '@/filters/bytes';
-import number from '@/filters/number';
-import * as os from '@/os';
-
-export default defineComponent({
-	components: {
-		XModalWindow,
-		MkSelect,
-		MkButton,
-		MkSwitch,
-		MkInfo,
-		MkChart,
-	},
-
-	props: {
-		instance: {
-			type: Object,
-			required: true
-		}
-	},
-
-	emits: ['closed'],
-
-	data() {
-		return {
-			isSuspended: this.instance.isSuspended,
-			chartSrc: 'requests',
-			chartSpan: 'hour',
-		};
-	},
-
-	computed: {
-		meta() {
-			return this.$instance;
-		},
-
-		isBlocked() {
-			return this.meta && this.meta.blockedHosts && this.meta.blockedHosts.includes(this.instance.host);
-		}
-	},
-
-	watch: {
-		isSuspended() {
-			os.api('admin/federation/update-instance', {
-				host: this.instance.host,
-				isSuspended: this.isSuspended
-			});
-		},
-	},
-
-	methods: {
-		changeBlock(e) {
-			os.api('admin/update-meta', {
-				blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host)
-			});
-		},
-
-		removeAllFollowing() {
-			os.apiWithDialog('admin/federation/remove-all-following', {
-				host: this.instance.host
-			});
-		},
-
-		deleteAllFiles() {
-			os.apiWithDialog('admin/federation/delete-all-files', {
-				host: this.instance.host
-			});
-		},
-
-		showFollowing() {
-			// TODO: ページ遷移
-		},
-
-		showFollowers() {
-			// TODO: ページ遷移
-		},
-
-		showUsers() {
-			// TODO: ページ遷移
-		},
-
-		bytes,
-
-		number
-	}
-});
-</script>
-
-<style lang="scss" scoped>
-.mk-instance-info {
-	overflow: auto;
-
-	> .section {
-		padding: 16px 32px;
-
-		@media (max-width: 500px) {
-			padding: 8px 16px;
-		}
-
-		&:not(:first-child) {
-			border-top: solid 0.5px var(--divider);
-		}
-	}
-
-	> .chart {
-		border-top: solid 0.5px var(--divider);
-		padding: 16px 0 12px 0;
-
-		> .header {
-			padding: 0 32px;
-
-			@media (max-width: 500px) {
-				padding: 0 16px;
-			}
-
-			> .label {
-				font-size: 80%;
-				opacity: 0.7;
-			}
-
-			> .selects {
-				display: flex;
-			}
-		}
-
-		> .chart {
-			padding: 0 16px;
-
-			@media (max-width: 500px) {
-				padding: 0;
-			}
-		}
-	}
-
-	> .operations {
-		> .label {
-			font-size: 80%;
-			opacity: 0.7;
-		}
-
-		> .switch {
-			margin: 16px 0;
-		}
-	}
-
-	> .metadata {
-		> .label {
-			font-size: 80%;
-			opacity: 0.7;
-		}
-
-		> pre > code {
-			display: block;
-			max-height: 200px;
-			overflow: auto;
-		}
-	}
-}
-</style>
diff --git a/packages/client/src/pages/admin/metrics.vue b/packages/client/src/pages/admin/metrics.vue
index f566061ceb..1de297fd93 100644
--- a/packages/client/src/pages/admin/metrics.vue
+++ b/packages/client/src/pages/admin/metrics.vue
@@ -76,7 +76,6 @@ import MkwFederation from '../../widgets/federation.vue';
 import { version, url } from '@/config';
 import bytes from '@/filters/bytes';
 import number from '@/filters/number';
-import MkInstanceInfo from './instance.vue';
 
 Chart.register(
   ArcElement,
diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue
index 3dcfce01e5..564a63fda0 100644
--- a/packages/client/src/pages/admin/overview.vue
+++ b/packages/client/src/pages/admin/overview.vue
@@ -19,7 +19,7 @@
 
 	<MkContainer :foldable="true" class="charts">
 		<template #header><i class="fas fa-chart-bar"></i>{{ $ts.charts }}</template>
-		<div style="padding-top: 12px;">
+		<div style="padding: 12px;">
 			<MkInstanceStats :chart-limit="500" :detailed="true"/>
 		</div>
 	</MkContainer>
@@ -77,7 +77,6 @@ import MkQueueChart from '@/components/queue-chart.vue';
 import { version, url } from '@/config';
 import bytes from '@/filters/bytes';
 import number from '@/filters/number';
-import MkInstanceInfo from './instance.vue';
 import XMetrics from './metrics.vue';
 import * as os from '@/os';
 import { stream } from '@/stream';
@@ -159,9 +158,7 @@ export default defineComponent({
 					host: q
 				});
 			}
-			os.popup(MkInstanceInfo, {
-				instance: instance
-			}, {}, 'closed');
+			// TODO
 		},
 
 		bytes,
diff --git a/packages/client/src/pages/instance-info.vue b/packages/client/src/pages/instance-info.vue
index 85096d991a..d6d72f6601 100644
--- a/packages/client/src/pages/instance-info.vue
+++ b/packages/client/src/pages/instance-info.vue
@@ -1,70 +1,71 @@
 <template>
-<FormBase>
-	<FormGroup v-if="instance">
-		<template #label>{{ instance.host }}</template>
-		<FormGroup>
-			<div class="_debobigegoItem">
-				<div class="_debobigegoPanel fnfelxur">
-					<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/>
-				</div>
-			</div>
-			<FormKeyValueView>
-				<template #key>Name</template>
-				<template #value><span class="_monospace">{{ instance.name || `(${$ts.unknown})` }}</span></template>
-			</FormKeyValueView>
-		</FormGroup>
+<MkSpacer :content-max="600" :margin-min="16" :margin-max="32">
+	<div v-if="instance" class="_formRoot">
+		<div class="fnfelxur">
+			<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/>
+		</div>
+		<MkKeyValue :copy="host" oneline style="margin: 1em 0;">
+			<template #key>Host</template>
+			<template #value><span class="_monospace"><MkLink :url="`https://${host}`">{{ host }}</MkLink></span></template>
+		</MkKeyValue>
+		<MkKeyValue oneline style="margin: 1em 0;">
+			<template #key>Name</template>
+			<template #value>{{ instance.name || `(${$ts.unknown})` }}</template>
+		</MkKeyValue>
+		<MkKeyValue>
+			<template #key>{{ $ts.description }}</template>
+			<template #value>{{ instance.description }}</template>
+		</MkKeyValue>
+		<MkKeyValue oneline style="margin: 1em 0;">
+			<template #key>{{ $ts.software }}</template>
+			<template #value><span class="_monospace">{{ instance.softwareName || `(${$ts.unknown})` }} / {{ instance.softwareVersion || `(${$ts.unknown})` }}</span></template>
+		</MkKeyValue>
+		<MkKeyValue oneline style="margin: 1em 0;">
+			<template #key>{{ $ts.administrator }}</template>
+			<template #value>{{ instance.maintainerName || `(${$ts.unknown})` }} ({{ instance.maintainerEmail || `(${$ts.unknown})` }})</template>
+		</MkKeyValue>
 
-		<FormButton v-if="$i.isAdmin || $i.isModerator" primary @click="info">{{ $ts.settings }}</FormButton>
+		<FormSection v-if="iAmModerator">
+			<template #label>Moderation</template>
+			<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.stopActivityDelivery }}</FormSwitch>
+			<FormSwitch :model-value="isBlocked" class="switch" @update:modelValue="changeBlock">{{ $ts.blockThisInstance }}</FormSwitch>
+		</FormSection>
 
-		<FormTextarea readonly :value="instance.description">
-			<span>{{ $ts.description }}</span>
-		</FormTextarea>
-
-		<FormGroup>
-			<FormKeyValueView>
-				<template #key>{{ $ts.software }}</template>
-				<template #value><span class="_monospace">{{ instance.softwareName || `(${$ts.unknown})` }}</span></template>
-			</FormKeyValueView>
-			<FormKeyValueView>
-				<template #key>{{ $ts.version }}</template>
-				<template #value><span class="_monospace">{{ instance.softwareVersion || `(${$ts.unknown})` }}</span></template>
-			</FormKeyValueView>
-		</FormGroup>
-		<FormGroup>
-			<FormKeyValueView>
-				<template #key>{{ $ts.administrator }}</template>
-				<template #value><span class="_monospace">{{ instance.maintainerName || `(${$ts.unknown})` }}</span></template>
-			</FormKeyValueView>
-			<FormKeyValueView>
-				<template #key>{{ $ts.contact }}</template>
-				<template #value><span class="_monospace">{{ instance.maintainerEmail || `(${$ts.unknown})` }}</span></template>
-			</FormKeyValueView>
-		</FormGroup>
-		<FormGroup>
-			<FormKeyValueView>
+		<FormSection>
+			<MkKeyValue oneline style="margin: 1em 0;">
+				<template #key>{{ $ts.registeredAt }}</template>
+				<template #value><MkTime mode="detail" :time="instance.caughtAt"/></template>
+			</MkKeyValue>
+			<MkKeyValue oneline style="margin: 1em 0;">
+				<template #key>{{ $ts.updatedAt }}</template>
+				<template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template>
+			</MkKeyValue>
+			<MkKeyValue oneline style="margin: 1em 0;">
 				<template #key>{{ $ts.latestRequestSentAt }}</template>
 				<template #value><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></template>
-			</FormKeyValueView>
-			<FormKeyValueView>
+			</MkKeyValue>
+			<MkKeyValue oneline style="margin: 1em 0;">
 				<template #key>{{ $ts.latestStatus }}</template>
 				<template #value>{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</template>
-			</FormKeyValueView>
-			<FormKeyValueView>
+			</MkKeyValue>
+			<MkKeyValue oneline style="margin: 1em 0;">
 				<template #key>{{ $ts.latestRequestReceivedAt }}</template>
 				<template #value><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></template>
-			</FormKeyValueView>
-		</FormGroup>
-		<FormGroup>
-			<FormKeyValueView>
+			</MkKeyValue>
+		</FormSection>
+	
+		<FormSection>
+			<MkKeyValue oneline style="margin: 1em 0;">
 				<template #key>Open Registrations</template>
 				<template #value>{{ instance.openRegistrations ? $ts.yes : $ts.no }}</template>
-			</FormKeyValueView>
-		</FormGroup>
-		<div class="_debobigegoItem">
-			<div class="_debobigegoLabel">{{ $ts.statistics }}</div>
-			<div class="_debobigegoPanel cmhjzshl">
+			</MkKeyValue>
+		</FormSection>
+
+		<FormSection>
+			<template #label>{{ $ts.statistics }}</template>
+			<div class="cmhjzshl">
 				<div class="selects">
-					<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;">
+					<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>
@@ -83,86 +84,56 @@
 					</MkSelect>
 				</div>
 				<div class="chart">
-					<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :detailed="true"></MkChart>
+					<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :args="{ host: host }" :detailed="true"></MkChart>
 				</div>
 			</div>
-		</div>
-		<FormGroup>
-			<FormKeyValueView>
-				<template #key>{{ $ts.registeredAt }}</template>
-				<template #value><MkTime mode="detail" :time="instance.caughtAt"/></template>
-			</FormKeyValueView>
-			<FormKeyValueView>
-				<template #key>{{ $ts.updatedAt }}</template>
-				<template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template>
-			</FormKeyValueView>
-		</FormGroup>
+		</FormSection>
+
 		<FormObjectView tall :value="instance">
 			<span>Raw</span>
 		</FormObjectView>
-		<FormGroup>
+
+		<FormSection>
 			<template #label>Well-known resources</template>
-			<FormLink :to="`https://${host}/.well-known/host-meta`" external>host-meta</FormLink>
-			<FormLink :to="`https://${host}/.well-known/host-meta.json`" external>host-meta.json</FormLink>
-			<FormLink :to="`https://${host}/.well-known/nodeinfo`" external>nodeinfo</FormLink>
-			<FormLink :to="`https://${host}/robots.txt`" external>robots.txt</FormLink>
-			<FormLink :to="`https://${host}/manifest.json`" external>manifest.json</FormLink>
-		</FormGroup>
-		<FormSuspense v-slot="{ result: dns }" :p="dnsPromiseFactory">
-			<FormGroup>
-				<template #label>DNS</template>
-				<FormKeyValueView v-for="record in dns.a" :key="record">
-					<template #key>A</template>
-					<template #value><span class="_monospace">{{ record }}</span></template>
-				</FormKeyValueView>
-				<FormKeyValueView v-for="record in dns.aaaa" :key="record">
-					<template #key>AAAA</template>
-					<template #value><span class="_monospace">{{ record }}</span></template>
-				</FormKeyValueView>
-				<FormKeyValueView v-for="record in dns.cname" :key="record">
-					<template #key>CNAME</template>
-					<template #value><span class="_monospace">{{ record }}</span></template>
-				</FormKeyValueView>
-				<FormKeyValueView v-for="record in dns.txt">
-					<template #key>TXT</template>
-					<template #value><span class="_monospace">{{ record[0] }}</span></template>
-				</FormKeyValueView>
-			</FormGroup>
-		</FormSuspense>
-	</FormGroup>
-</FormBase>
+			<FormLink :to="`https://${host}/.well-known/host-meta`" external style="margin-bottom: 8px;">host-meta</FormLink>
+			<FormLink :to="`https://${host}/.well-known/host-meta.json`" external style="margin-bottom: 8px;">host-meta.json</FormLink>
+			<FormLink :to="`https://${host}/.well-known/nodeinfo`" external style="margin-bottom: 8px;">nodeinfo</FormLink>
+			<FormLink :to="`https://${host}/robots.txt`" external style="margin-bottom: 8px;">robots.txt</FormLink>
+			<FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink>
+		</FormSection>
+	</div>
+</MkSpacer>
 </template>
 
 <script lang="ts">
 import { defineAsyncComponent, defineComponent } from 'vue';
 import MkChart from '@/components/chart.vue';
 import FormObjectView from '@/components/debobigego/object-view.vue';
-import FormTextarea from '@/components/debobigego/textarea.vue';
-import FormLink from '@/components/debobigego/link.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormTextarea from '@/components/form/textarea.vue';
+import FormLink from '@/components/form/link.vue';
+import MkLink from '@/components/link.vue';
+import FormSection from '@/components/form/section.vue';
+import FormButton from '@/components/ui/button.vue';
+import MkKeyValue from '@/components/key-value.vue';
 import MkSelect from '@/components/form/select.vue';
+import FormSwitch from '@/components/form/switch.vue';
 import * as os from '@/os';
 import number from '@/filters/number';
 import bytes from '@/filters/bytes';
 import * as symbols from '@/symbols';
-import MkInstanceInfo from '@/pages/admin/instance.vue';
 
 export default defineComponent({
 	components: {
-		FormBase,
 		FormTextarea,
 		FormObjectView,
 		FormButton,
 		FormLink,
-		FormGroup,
-		FormKeyValueView,
-		FormSuspense,
+		FormSection,
+		FormSwitch,
+		MkKeyValue,
 		MkSelect,
 		MkChart,
+		MkLink,
 	},
 
 	props: {
@@ -175,8 +146,9 @@ export default defineComponent({
 	data() {
 		return {
 			[symbols.PAGE_INFO]: {
-				title: this.$ts.instanceInfo,
+				title: this.host,
 				icon: 'fas fa-info-circle',
+				bg: 'var(--bg)',
 				actions: [{
 					text: `https://${this.host}`,
 					icon: 'fas fa-external-link-alt',
@@ -186,14 +158,22 @@ export default defineComponent({
 				}],
 			},
 			instance: null,
-			dnsPromiseFactory: () => os.api('federation/dns', {
-				host: this.host
-			}),
+			suspended: false,
 			chartSrc: 'instance-requests',
 			chartSpan: 'hour',
 		}
 	},
 
+	computed: {
+		iAmModerator(): boolean {
+			return this.$i && (this.$i.isAdmin || this.$i.isModerator);
+		},
+
+		isBlocked() {
+			return this.instance && this.$instance && this.$instance.blockedHosts && this.$instance.blockedHosts.includes(this.instance.host);
+		}
+	},
+
 	mounted() {
 		this.fetch();
 	},
@@ -206,24 +186,30 @@ export default defineComponent({
 			this.instance = await os.api('federation/show-instance', {
 				host: this.host
 			});
+			this.suspended = this.instance.isSuspended;
 		},
 
-		info() {
-			os.popup(MkInstanceInfo, {
-				instance: this.instance
-			}, {}, 'closed');
-		}
+		changeBlock(e) {
+			os.api('admin/update-meta', {
+				blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host)
+			});
+		},
+
+		async toggleSuspend(v) {
+			await os.api('admin/federation/update-instance', {
+				host: this.instance.host,
+				isSuspended: this.suspended
+			});
+		},
 	}
 });
 </script>
 
 <style lang="scss" scoped>
 .fnfelxur {
-	padding: 16px;
-
 	> .icon {
 		display: block;
-		margin: auto;
+		margin: 0;
 		height: 64px;
 		border-radius: 8px;
 	}
@@ -232,7 +218,7 @@ export default defineComponent({
 .cmhjzshl {
 	> .selects {
 		display: flex;
-		padding: 16px;
+		margin: 0 0 16px 0;
 	}
 }
 </style>
diff --git a/packages/client/src/pages/user-ap-info.vue b/packages/client/src/pages/user-ap-info.vue
deleted file mode 100644
index 0027381f53..0000000000
--- a/packages/client/src/pages/user-ap-info.vue
+++ /dev/null
@@ -1,124 +0,0 @@
-<template>
-<FormBase>
-	<FormSuspense v-slot="{ result: ap }" :p="apPromiseFactory">
-		<FormGroup>
-			<template #label>ActivityPub</template>
-			<FormKeyValueView>
-				<template #key>Type</template>
-				<template #value><span class="_monospace">{{ ap.type }}</span></template>
-			</FormKeyValueView>
-			<FormKeyValueView>
-				<template #key>URI</template>
-				<template #value><span class="_monospace">{{ ap.id }}</span></template>
-			</FormKeyValueView>
-			<FormKeyValueView>
-				<template #key>URL</template>
-				<template #value><span class="_monospace">{{ ap.url }}</span></template>
-			</FormKeyValueView>
-			<FormGroup>
-				<FormKeyValueView>
-					<template #key>Inbox</template>
-					<template #value><span class="_monospace">{{ ap.inbox }}</span></template>
-				</FormKeyValueView>
-				<FormKeyValueView>
-					<template #key>Shared Inbox</template>
-					<template #value><span class="_monospace">{{ ap.sharedInbox || ap.endpoints.sharedInbox }}</span></template>
-				</FormKeyValueView>
-				<FormKeyValueView>
-					<template #key>Outbox</template>
-					<template #value><span class="_monospace">{{ ap.outbox }}</span></template>
-				</FormKeyValueView>
-			</FormGroup>
-			<FormTextarea readonly tall code pre :value="ap.publicKey.publicKeyPem">
-				<span>Public Key</span>
-			</FormTextarea>
-			<FormKeyValueView>
-				<template #key>Discoverable</template>
-				<template #value>{{ ap.discoverable ? $ts.yes : $ts.no }}</template>
-			</FormKeyValueView>
-			<FormKeyValueView>
-				<template #key>ManuallyApprovesFollowers</template>
-				<template #value>{{ ap.manuallyApprovesFollowers ? $ts.yes : $ts.no }}</template>
-			</FormKeyValueView>
-			<FormObjectView tall :value="ap">
-				<span>Raw</span>
-			</FormObjectView>
-			<FormGroup>
-				<FormLink :to="`https://${user.host}/.well-known/webfinger?resource=acct:${user.username}`" external>WebFinger</FormLink>
-			</FormGroup>
-			<FormLink v-if="user.host" :to="`/instance-info/${user.host}`">{{ $ts.instanceInfo }}<template #suffix>{{ user.host }}</template></FormLink>
-			<FormKeyValueView v-else>
-				<template #key>{{ $ts.instanceInfo }}</template>
-				<template #value>(Local user)</template>
-			</FormKeyValueView>
-		</FormGroup>
-	</FormSuspense>
-</FormBase>
-</template>
-
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
-import FormObjectView from '@/components/debobigego/object-view.vue';
-import FormTextarea from '@/components/debobigego/textarea.vue';
-import FormLink from '@/components/debobigego/link.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
-import * as os from '@/os';
-import number from '@/filters/number';
-import bytes from '@/filters/bytes';
-import * as symbols from '@/symbols';
-import { url } from '@/config';
-
-export default defineComponent({
-	components: {
-		FormBase,
-		FormTextarea,
-		FormObjectView,
-		FormButton,
-		FormLink,
-		FormGroup,
-		FormKeyValueView,
-		FormSuspense,
-	},
-
-	props: {
-		userId: {
-			type: String,
-			required: true
-		}
-	},
-
-	data() {
-		return {
-			[symbols.PAGE_INFO]: {
-				title: this.$ts.userInfo,
-				icon: 'fas fa-info-circle'
-			},
-			user: null,
-			apPromiseFactory: null,
-		}
-	},
-
-	mounted() {
-		this.fetch();
-	},
-
-	methods: {
-		number,
-		bytes,
-
-		async fetch() {
-			this.user = await os.api('users/show', {
-				userId: this.userId
-			});
-
-			this.apPromiseFactory = () => os.api('ap/get', {
-				uri: this.user.uri || `${url}/users/${this.user.id}`
-			});
-		}
-	}
-});
-</script>
diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue
index 0fd208a64a..e3c10c6df9 100644
--- a/packages/client/src/pages/user-info.vue
+++ b/packages/client/src/pages/user-info.vue
@@ -1,70 +1,76 @@
 <template>
-<FormBase>
+<MkSpacer :content-max="500" :margin-min="16" :margin-max="32">
 	<FormSuspense :p="init">
-		<div class="_debobigegoItem aeakzknw">
-			<MkAvatar class="avatar" :user="user" :show-indicator="true"/>
+		<div class="_formRoot">
+			<div class="_formBlock aeakzknw">
+				<MkAvatar class="avatar" :user="user" :show-indicator="true"/>
+			</div>
+
+			<FormLink :to="userPage(user)">Profile</FormLink>
+
+			<div class="_formBlock">
+				<MkKeyValue :copy="acct(user)" oneline style="margin: 1em 0;">
+					<template #key>Acct</template>
+					<template #value><span class="_monospace">{{ acct(user) }}</span></template>
+				</MkKeyValue>
+
+				<MkKeyValue :copy="user.id" oneline style="margin: 1em 0;">
+					<template #key>ID</template>
+					<template #value><span class="_monospace">{{ user.id }}</span></template>
+				</MkKeyValue>
+			</div>
+
+			<FormSection v-if="iAmModerator">
+				<template #label>Moderation</template>
+				<FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" class="_formBlock" @update:modelValue="toggleModerator">{{ $ts.moderator }}</FormSwitch>
+				<FormSwitch v-model="silenced" class="_formBlock" @update:modelValue="toggleSilence">{{ $ts.silence }}</FormSwitch>
+				<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.suspend }}</FormSwitch>
+				<FormButton v-if="user.host == null && iAmModerator" class="_formBlock" @click="resetPassword"><i class="fas fa-key"></i> {{ $ts.resetPassword }}</FormButton>
+			</FormSection>
+
+			<FormSection>
+				<template #label>ActivityPub</template>
+
+				<div class="_formBlock">
+					<MkKeyValue v-if="user.host" oneline style="margin: 1em 0;">
+						<template #key>{{ $ts.instanceInfo }}</template>
+						<template #value><MkA :to="`/instance-info/${user.host}`" class="_link">{{ user.host }} <i class="fas fa-angle-right"></i></MkA></template>
+					</MkKeyValue>
+					<MkKeyValue v-else oneline style="margin: 1em 0;">
+						<template #key>{{ $ts.instanceInfo }}</template>
+						<template #value>(Local user)</template>
+					</MkKeyValue>
+					<MkKeyValue oneline style="margin: 1em 0;">
+						<template #key>{{ $ts.updatedAt }}</template>
+						<template #value><MkTime v-if="user.lastFetchedAt" mode="detail" :time="user.lastFetchedAt"/><span v-else>N/A</span></template>
+					</MkKeyValue>
+					<MkKeyValue v-if="ap" oneline style="margin: 1em 0;">
+						<template #key>Type</template>
+						<template #value><span class="_monospace">{{ ap.type }}</span></template>
+					</MkKeyValue>
+				</div>
+
+				<FormButton v-if="user.host != null" class="_formBlock" @click="updateRemoteUser"><i class="fas fa-sync"></i> {{ $ts.updateRemoteUser }}</FormButton>
+			</FormSection>
+
+			<FormObjectView tall :value="user">
+				<span>Raw</span>
+			</FormObjectView>
 		</div>
-
-		<FormLink :to="userPage(user)">Profile</FormLink>
-
-		<FormGroup>
-			<FormKeyValueView>
-				<template #key>Acct</template>
-				<template #value><span class="_monospace">{{ acct(user) }}</span></template>
-			</FormKeyValueView>
-
-			<FormKeyValueView>
-				<template #key>ID</template>
-				<template #value><span class="_monospace">{{ user.id }}</span></template>
-			</FormKeyValueView>
-		</FormGroup>
-
-		<FormGroup v-if="iAmModerator">
-			<FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" @update:modelValue="toggleModerator">{{ $ts.moderator }}</FormSwitch>
-			<FormSwitch v-model="silenced" @update:modelValue="toggleSilence">{{ $ts.silence }}</FormSwitch>
-			<FormSwitch v-model="suspended" @update:modelValue="toggleSuspend">{{ $ts.suspend }}</FormSwitch>
-		</FormGroup>
-
-		<FormGroup>
-			<FormButton v-if="user.host != null" @click="updateRemoteUser"><i class="fas fa-sync"></i> {{ $ts.updateRemoteUser }}</FormButton>
-			<FormButton v-if="user.host == null && iAmModerator" @click="resetPassword"><i class="fas fa-key"></i> {{ $ts.resetPassword }}</FormButton>
-		</FormGroup>
-
-		<FormGroup>
-			<FormLink :to="`/user-ap-info/${user.id}`">ActivityPub</FormLink>
-
-			<FormLink v-if="user.host" :to="`/instance-info/${user.host}`">{{ $ts.instanceInfo }}<template #suffix>{{ user.host }}</template></FormLink>
-			<FormKeyValueView v-else>
-				<template #key>{{ $ts.instanceInfo }}</template>
-				<template #value>(Local user)</template>
-			</FormKeyValueView>
-		</FormGroup>
-
-		<FormGroup>
-			<FormKeyValueView>
-				<template #key>{{ $ts.updatedAt }}</template>
-				<template #value><MkTime v-if="user.lastFetchedAt" mode="detail" :time="user.lastFetchedAt"/><span v-else>N/A</span></template>
-			</FormKeyValueView>
-		</FormGroup>
-
-		<FormObjectView tall :value="user">
-			<span>Raw</span>
-		</FormObjectView>
 	</FormSuspense>
-</FormBase>
+</MkSpacer>
 </template>
 
 <script lang="ts">
 import { computed, defineAsyncComponent, defineComponent } from 'vue';
 import FormObjectView from '@/components/debobigego/object-view.vue';
-import FormTextarea from '@/components/debobigego/textarea.vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormLink from '@/components/debobigego/link.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormTextarea from '@/components/form/textarea.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormLink from '@/components/form/link.vue';
+import FormSection from '@/components/form/section.vue';
+import FormButton from '@/components/ui/button.vue';
+import MkKeyValue from '@/components/key-value.vue';
+import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os';
 import number from '@/filters/number';
 import bytes from '@/filters/bytes';
@@ -74,14 +80,13 @@ import { userPage, acct } from '@/filters/user';
 
 export default defineComponent({
 	components: {
-		FormBase,
+		FormSection,
 		FormTextarea,
 		FormSwitch,
 		FormObjectView,
 		FormButton,
 		FormLink,
-		FormGroup,
-		FormKeyValueView,
+		MkKeyValue,
 		FormSuspense,
 	},
 
@@ -97,6 +102,7 @@ export default defineComponent({
 			[symbols.PAGE_INFO]: computed(() => ({
 				title: this.user ? acct(this.user) : this.$ts.userInfo,
 				icon: 'fas fa-info-circle',
+				bg: 'var(--bg)',
 				actions: this.user ? [this.user.url ? {
 					text: this.user.url,
 					icon: 'fas fa-external-link-alt',
@@ -108,6 +114,7 @@ export default defineComponent({
 			init: null,
 			user: null,
 			info: null,
+			ap: null,
 			moderator: false,
 			silenced: false,
 			suspended: false,
@@ -126,6 +133,13 @@ export default defineComponent({
 				this.init = this.createFetcher();
 			},
 			immediate: true
+		},
+		user() {
+			os.api('ap/get', {
+				uri: this.user.uri || `${url}/users/${this.user.id}`
+			}).then(res => {
+				this.ap = res;
+			});
 		}
 	},
 
@@ -234,7 +248,6 @@ export default defineComponent({
 .aeakzknw {
 	> .avatar {
 		display: block;
-		margin: 0 auto;
 		width: 64px;
 		height: 64px;
 	}
diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts
index 9b4dd162f3..2a07ff37cd 100644
--- a/packages/client/src/router.ts
+++ b/packages/client/src/router.ts
@@ -73,7 +73,6 @@ const defaultRoutes = [
 	{ path: '/notes/:note', name: 'note', component: page('note'), props: route => ({ noteId: route.params.note }) },
 	{ path: '/tags/:tag', component: page('tag'), props: route => ({ tag: route.params.tag }) },
 	{ path: '/user-info/:user', component: page('user-info'), props: route => ({ userId: route.params.user }) },
-	{ path: '/user-ap-info/:user', component: page('user-ap-info'), props: route => ({ userId: route.params.user }) },
 	{ path: '/instance-info/:host', component: page('instance-info'), props: route => ({ host: route.params.host }) },
 	{ path: '/games/reversi', component: page('reversi/index') },
 	{ path: '/games/reversi/:gameId', component: page('reversi/game'), props: route => ({ gameId: route.params.gameId }) },