diff --git a/src/components/Player.vue b/src/components/Player.vue
index e99d488..7392068 100644
--- a/src/components/Player.vue
+++ b/src/components/Player.vue
@@ -10,9 +10,9 @@
           'no-vote': player.isVoteless,
           you: session.sessionId && player.id && player.id === session.playerId,
           'vote-yes': session.votes[index],
-          'vote-lock': voteLocked
+          'vote-lock': voteLocked,
         },
-        player.role.team
+        player.role.team,
       ]"
     >
       <div class="shroud" @click="toggleStatus()"></div>
@@ -121,7 +121,7 @@
             @click="changePronouns"
             v-if="
               !session.isSpectator ||
-                (session.isSpectator && player.id === session.playerId)
+              (session.isSpectator && player.id === session.playerId)
             "
           >
             <font-awesome-icon icon="venus-mars" />Change Pronouns
@@ -162,9 +162,7 @@
             :class="{ disabled: player.id && player.id !== session.playerId }"
           >
             <font-awesome-icon icon="chair" />
-            <template v-if="!player.id">
-              Claim seat
-            </template>
+            <template v-if="!player.id"> Claim seat </template>
             <template v-else-if="player.id === session.playerId">
               Vacate seat
             </template>
@@ -188,10 +186,12 @@
             backgroundImage: `url(${
               reminder.image && grimoire.isImageOptIn
                 ? reminder.image
-                : require('../assets/icons/' +
-                    (reminder.imageAlt || reminder.role) +
-                    '.png')
-            })`
+                : require(
+                    '../assets/icons/' +
+                      (reminder.imageAlt || reminder.role) +
+                      '.png',
+                  )
+            })`,
           }"
         ></span>
         <span class="text">{{ reminder.name }}</span>
@@ -210,22 +210,22 @@ import { mapGetters, mapState } from "vuex";
 
 export default {
   components: {
-    Token
+    Token,
   },
   props: {
     player: {
       type: Object,
-      required: true
-    }
+      required: true,
+    },
   },
   computed: {
     ...mapState("players", ["players"]),
     ...mapState(["grimoire", "session"]),
     ...mapGetters({ nightOrder: "players/nightOrder" }),
-    index: function() {
+    index: function () {
       return this.players.indexOf(this.player);
     },
-    voteLocked: function() {
+    voteLocked: function () {
       const session = this.session;
       const players = this.players.length;
       if (!session.nomination) return false;
@@ -233,7 +233,7 @@ export default {
         (this.index - 1 + players - session.nomination[1]) % players;
       return indexAdjusted < session.lockedVote - 1;
     },
-    zoom: function() {
+    zoom: function () {
       const unit = window.innerWidth > window.innerHeight ? "vh" : "vw";
       if (this.players.length < 7) {
         return { width: 18 + this.grimoire.zoom + unit };
@@ -244,12 +244,12 @@ export default {
       } else {
         return { width: 12 + this.grimoire.zoom + unit };
       }
-    }
+    },
   },
   data() {
     return {
       isMenuOpen: false,
-      isSwap: false
+      isSwap: false,
     };
   },
   methods: {
@@ -305,7 +305,7 @@ export default {
       this.$store.commit("players/update", {
         player: this.player,
         property,
-        value
+        value,
       });
       if (closeMenu) {
         this.isMenuOpen = false;
@@ -342,10 +342,10 @@ export default {
       if (!this.voteLocked) return;
       this.$store.commit("session/voteSync", [
         this.index,
-        !this.session.votes[this.index]
+        !this.session.votes[this.index],
       ]);
-    }
-  }
+    },
+  },
 };
 </script>
 
@@ -869,7 +869,10 @@ li.move:not(.from) .player .overlay svg.move {
     width: 100%;
     position: absolute;
     top: 15%;
-    text-shadow: 0 1px 1px #f6dfbd, 0 -1px 1px #f6dfbd, 1px 0 1px #f6dfbd,
+    text-shadow:
+      0 1px 1px #f6dfbd,
+      0 -1px 1px #f6dfbd,
+      1px 0 1px #f6dfbd,
       -1px 0 1px #f6dfbd;
   }
 
diff --git a/src/components/TownSquare.vue b/src/components/TownSquare.vue
index 45249a1..b3c551f 100644
--- a/src/components/TownSquare.vue
+++ b/src/components/TownSquare.vue
@@ -5,7 +5,7 @@
     :class="{
       public: grimoire.isPublic,
       spectator: session.isSpectator,
-      vote: session.nomination
+      vote: session.nomination,
     }"
   >
     <ul class="circle" :class="['size-' + players.length]">
@@ -18,7 +18,7 @@
           from: Math.max(swap, move, nominate) === index,
           swap: swap > -1,
           move: move > -1,
-          nominate: nominate > -1
+          nominate: nominate > -1,
         }"
       ></Player>
     </ul>
@@ -29,12 +29,20 @@
       ref="bluffs"
       :class="{ closed: !isBluffsOpen }"
     >
-      <h3>
+      <h5>
         <span v-if="session.isSpectator">Other characters</span>
         <span v-else>恶魔的伪装角色</span>
-        <font-awesome-icon icon="times-circle" @click.stop="toggleBluffs" />
-        <font-awesome-icon icon="plus-circle" @click.stop="toggleBluffs" />
-      </h3>
+        <font-awesome-icon
+          icon="times-circle"
+          v-if="isBluffsOpen"
+          @click.stop="toggleBluffs"
+        />
+        <font-awesome-icon
+          icon="plus-circle"
+          v-if="!isBluffsOpen"
+          @click.stop="toggleBluffs"
+        />
+      </h5>
       <ul>
         <li
           v-for="index in bluffSize"
@@ -47,11 +55,19 @@
     </div>
 
     <div class="fabled" :class="{ closed: !isFabledOpen }" v-if="fabled.length">
-      <h3>
+      <h5>
         <span>传奇角色</span>
-        <font-awesome-icon icon="times-circle" @click.stop="toggleFabled" />
-        <font-awesome-icon icon="plus-circle" @click.stop="toggleFabled" />
-      </h3>
+        <font-awesome-icon
+          icon="times-circle"
+          v-if="isFabledOpen"
+          @click.stop="toggleFabled"
+        />
+        <font-awesome-icon
+          icon="plus-circle"
+          v-if="!isFabledOpen"
+          @click.stop="toggleFabled"
+        />
+      </h5>
       <ul>
         <li
           v-for="(role, index) in fabled"
@@ -98,12 +114,12 @@ export default {
     Player,
     Token,
     RoleModal,
-    ReminderModal
+    ReminderModal,
   },
   computed: {
     ...mapGetters({ nightOrder: "players/nightOrder" }),
     ...mapState(["grimoire", "roles", "session"]),
-    ...mapState("players", ["players", "bluffs", "fabled"])
+    ...mapState("players", ["players", "bluffs", "fabled"]),
   },
   data() {
     return {
@@ -113,7 +129,7 @@ export default {
       move: -1,
       nominate: -1,
       isBluffsOpen: true,
-      isFabledOpen: true
+      isFabledOpen: true,
     };
   },
   methods: {
@@ -155,7 +171,7 @@ export default {
       if (this.session.isSpectator || this.session.lockedVote) return;
       if (
         confirm(
-          `Do you really want to remove ${this.players[playerIndex].name}?`
+          `Do you really want to remove ${this.players[playerIndex].name}?`,
         )
       ) {
         const { nomination } = this.session;
@@ -170,7 +186,7 @@ export default {
             // update nomination array if removed player has lower index
             this.$store.commit("session/setNomination", [
               nomination[0] > playerIndex ? nomination[0] - 1 : nomination[0],
-              nomination[1] > playerIndex ? nomination[1] - 1 : nomination[1]
+              nomination[1] > playerIndex ? nomination[1] - 1 : nomination[1],
             ]);
           }
         }
@@ -186,7 +202,7 @@ export default {
         if (this.session.nomination) {
           // update nomination if one of the involved players is swapped
           const swapTo = this.players.indexOf(to);
-          const updatedNomination = this.session.nomination.map(nom => {
+          const updatedNomination = this.session.nomination.map((nom) => {
             if (nom === this.swap) return swapTo;
             if (nom === swapTo) return this.swap;
             return nom;
@@ -200,7 +216,7 @@ export default {
         }
         this.$store.commit("players/swap", [
           this.swap,
-          this.players.indexOf(to)
+          this.players.indexOf(to),
         ]);
         this.cancel();
       }
@@ -214,7 +230,7 @@ export default {
         if (this.session.nomination) {
           // update nomination if it is affected by the move
           const moveTo = this.players.indexOf(to);
-          const updatedNomination = this.session.nomination.map(nom => {
+          const updatedNomination = this.session.nomination.map((nom) => {
             if (nom === this.move) return moveTo;
             if (nom > this.move && nom <= moveTo) return nom - 1;
             if (nom < this.move && nom >= moveTo) return nom + 1;
@@ -229,7 +245,7 @@ export default {
         }
         this.$store.commit("players/move", [
           this.move,
-          this.players.indexOf(to)
+          this.players.indexOf(to),
         ]);
         this.cancel();
       }
@@ -251,8 +267,8 @@ export default {
       this.move = -1;
       this.swap = -1;
       this.nominate = -1;
-    }
-  }
+    },
+  },
 };
 </script>
 
@@ -272,8 +288,8 @@ export default {
 
 .circle {
   padding: 0;
-  width: 100%;
-  height: 100%;
+  width: 100vw;
+  height: min(100vw, 100vh);
   list-style: none;
   margin: 0;
 
@@ -459,8 +475,8 @@ export default {
     align-items: center;
     justify-content: center;
     li {
-      width: 14vh;
-      height: 14vh;
+      width: max(min(8vh, 8vw), 50px);
+      height: max(min(8vh, 8vw), 50px);
       margin: 0 0.5%;
       display: inline-block;
       transition: all 250ms;
diff --git a/src/components/modals/Modal.vue b/src/components/modals/Modal.vue
index d1bd5e6..301e041 100644
--- a/src/components/modals/Modal.vue
+++ b/src/components/modals/Modal.vue
@@ -31,16 +31,16 @@
 
 <script>
 export default {
-  data: function() {
+  data: function () {
     return {
-      isMaximized: false
+      isMaximized: false,
     };
   },
   methods: {
     close() {
       this.$emit("close");
-    }
-  }
+    },
+  },
 };
 </script>
 
diff --git a/src/components/modals/ReminderModal.vue b/src/components/modals/ReminderModal.vue
index b781b7b..d1802ce 100644
--- a/src/components/modals/ReminderModal.vue
+++ b/src/components/modals/ReminderModal.vue
@@ -18,10 +18,12 @@
             backgroundImage: `url(${
               reminder.image && grimoire.isImageOptIn
                 ? reminder.image
-                : require('../../assets/icons/' +
-                    (reminder.imageAlt || reminder.role) +
-                    '.png')
-            })`
+                : require(
+                    '../../assets/icons/' +
+                      (reminder.imageAlt || reminder.role) +
+                      '.png',
+                  )
+            })`,
           }"
         ></span>
         <span class="text">{{ reminder.name }}</span>
@@ -39,12 +41,14 @@ import { mapMutations, mapState } from "vuex";
  * @param role The role for which the reminder should be generated
  * @return {function(*): {image: string|string[]|string|*, role: *, name: *, imageAlt: string|*}}
  */
-const mapReminder = ({ id, image, imageAlt }) => name => ({
-  role: id,
-  image,
-  imageAlt,
-  name
-});
+const mapReminder =
+  ({ id, image, imageAlt }) =>
+  (name) => ({
+    role: id,
+    image,
+    imageAlt,
+    name,
+  });
 
 export default {
   components: { Modal },
@@ -53,31 +57,31 @@ export default {
     availableReminders() {
       let reminders = [];
       const { players, bluffs } = this.$store.state.players;
-      this.$store.state.roles.forEach(role => {
+      this.$store.state.roles.forEach((role) => {
         // add reminders from player roles
-        if (players.some(p => p.role.id === role.id)) {
+        if (players.some((p) => p.role.id === role.id)) {
           reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
         }
         // add reminders from bluff/other roles
-        else if (bluffs.some(bluff => bluff.id === role.id)) {
+        else if (bluffs.some((bluff) => bluff.id === role.id)) {
           reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
         }
         // add global reminders
         if (role.remindersGlobal && role.remindersGlobal.length) {
           reminders = [
             ...reminders,
-            ...role.remindersGlobal.map(mapReminder(role))
+            ...role.remindersGlobal.map(mapReminder(role)),
           ];
         }
       });
       // add fabled reminders
-      this.$store.state.players.fabled.forEach(role => {
+      this.$store.state.players.fabled.forEach((role) => {
         reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
       });
 
       // add out of script traveler reminders
-      this.$store.state.otherTravelers.forEach(role => {
-        if (players.some(p => p.role.id === role.id)) {
+      this.$store.state.otherTravelers.forEach((role) => {
+        if (players.some((p) => p.role.id === role.id)) {
           reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
         }
       });
@@ -88,7 +92,7 @@ export default {
       return reminders;
     },
     ...mapState(["modals", "grimoire"]),
-    ...mapState("players", ["players"])
+    ...mapState("players", ["players"]),
   },
   methods: {
     addReminder(reminder) {
@@ -104,12 +108,12 @@ export default {
       this.$store.commit("players/update", {
         player,
         property: "reminders",
-        value
+        value,
       });
       this.$store.commit("toggleModal", "reminder");
     },
-    ...mapMutations(["toggleModal"])
-  }
+    ...mapMutations(["toggleModal"]),
+  },
 };
 </script>
 
@@ -117,8 +121,8 @@ export default {
 ul.reminders .reminder {
   background: url("../../assets/reminder.png") center center;
   background-size: 100%;
-  width: 14vh;
-  height: 14vh;
+  width: max(min(14vh, 14vw), 50px);
+  height: max(min(14vh, 14vw), 50px);
   max-width: 100px;
   max-height: 100px;
   display: flex;
@@ -145,12 +149,12 @@ ul.reminders .reminder {
 
   .text {
     color: black;
-    font-size: 65%;
+    font-size: 0.9em;
     font-weight: bold;
     text-align: center;
     top: 28%;
     width: 80%;
-    line-height: 1;
+    line-height: 0.9em;
   }
 
   &:hover {
diff --git a/src/components/modals/RoleModal.vue b/src/components/modals/RoleModal.vue
index eadde8c..6f2c8be 100644
--- a/src/components/modals/RoleModal.vue
+++ b/src/components/modals/RoleModal.vue
@@ -60,12 +60,12 @@ export default {
     availableRoles() {
       const availableRoles = [];
       const players = this.$store.state.players.players;
-      this.$store.state.roles.forEach(role => {
+      this.$store.state.roles.forEach((role) => {
         // don't show bluff roles that are already assigned to players
         if (
           this.playerIndex >= 0 ||
           (this.playerIndex < 0 &&
-            !players.some(player => player.role.id === role.id))
+            !players.some((player) => player.role.id === role.id))
         ) {
           availableRoles.push(role);
         }
@@ -75,11 +75,11 @@ export default {
     },
     ...mapState(["modals", "roles", "session"]),
     ...mapState("players", ["players"]),
-    ...mapState(["otherTravelers"])
+    ...mapState(["otherTravelers"]),
   },
   data() {
     return {
-      tab: "editionRoles"
+      tab: "editionRoles",
     };
   },
   methods: {
@@ -88,7 +88,7 @@ export default {
         // assign to bluff slot (index < 0)
         this.$store.commit("players/setBluff", {
           index: this.playerIndex * -1 - 1,
-          role
+          role,
         });
       } else {
         if (this.session.isSpectator && role.team === "traveler") return;
@@ -97,7 +97,7 @@ export default {
         this.$store.commit("players/update", {
           player,
           property: "role",
-          value: role
+          value: role,
         });
       }
       this.tab = "editionRoles";
@@ -107,8 +107,8 @@ export default {
       this.tab = "editionRoles";
       this.toggleModal("role");
     },
-    ...mapMutations(["toggleModal"])
-  }
+    ...mapMutations(["toggleModal"]),
+  },
 };
 </script>
 
@@ -117,24 +117,34 @@ export default {
 
 ul.tokens li {
   border-radius: 50%;
-  width: 6vw;
+  width: max(6vw, 60px);
   margin: 1%;
   transition: transform 500ms ease;
 
   &.townsfolk {
-    box-shadow: 0 0 10px $townsfolk, 0 0 10px #004cff;
+    box-shadow:
+      0 0 10px $townsfolk,
+      0 0 10px #004cff;
   }
   &.outsider {
-    box-shadow: 0 0 10px $outsider, 0 0 10px $outsider;
+    box-shadow:
+      0 0 10px $outsider,
+      0 0 10px $outsider;
   }
   &.minion {
-    box-shadow: 0 0 10px $minion, 0 0 10px $minion;
+    box-shadow:
+      0 0 10px $minion,
+      0 0 10px $minion;
   }
   &.demon {
-    box-shadow: 0 0 10px $demon, 0 0 10px $demon;
+    box-shadow:
+      0 0 10px $demon,
+      0 0 10px $demon;
   }
   &.traveler {
-    box-shadow: 0 0 10px $traveler, 0 0 10px $traveler;
+    box-shadow:
+      0 0 10px $traveler,
+      0 0 10px $traveler;
   }
   &:hover {
     transform: scale(1.2);