diff --git a/apps/web/src/shared/assets/mascot/whale-pat.gif b/apps/web/src/shared/assets/mascot/whale-pat.gif
new file mode 100644
index 0000000..1c2b234
Binary files /dev/null and b/apps/web/src/shared/assets/mascot/whale-pat.gif differ
diff --git a/apps/web/src/shared/assets/mascot/whale.webp b/apps/web/src/shared/assets/mascot/whale.webp
new file mode 100644
index 0000000..ed4f91c
Binary files /dev/null and b/apps/web/src/shared/assets/mascot/whale.webp differ
diff --git a/apps/web/src/shared/components/Mascot.vue b/apps/web/src/shared/components/Mascot.vue
new file mode 100644
index 0000000..e113635
--- /dev/null
+++ b/apps/web/src/shared/components/Mascot.vue
@@ -0,0 +1,316 @@
+
+
+
+
+
+
+
+
![]()
+
![]()
+
+
+
+
+
+
+
diff --git a/apps/web/src/shared/layout/default.vue b/apps/web/src/shared/layout/default.vue
index 71ff7f4..2863a3f 100644
--- a/apps/web/src/shared/layout/default.vue
+++ b/apps/web/src/shared/layout/default.vue
@@ -5,12 +5,16 @@ import Login from '../components/Login.vue'
import Signup from '../components/Signup.vue'
import AchievementToast from '../components/AchievementToast.vue'
import LessonUpdateNotice from '../components/LessonUpdateNotice.vue'
+import Mascot from '../components/Mascot.vue'
+import { useBreakpoints } from 'shared/composables/breakpoints'
import { useAchievementStore } from 'shared/store/achievement'
import { useUserStore } from 'shared/store/user'
const achievementStore = useAchievementStore()
const userStore = useUserStore()
const route = useRoute()
+// 看板娘只在电脑上出现:手机屏幕窄,底下还有题目页的操作栏,一个看板娘就把内容挡掉一大块
+const { isDesktop } = useBreakpoints()
// 拉取才是主通道:WebSocket 不是常驻连接(只在问题页且有提交监听时建连),
// 所以任何页面、任何时刻解锁的成就都靠这里补上
@@ -43,6 +47,7 @@ watch(
+