From 9234226104764b91df9d677fb360ad538b98510c Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Mon, 10 Dec 2018 16:12:58 +0100
Subject: [PATCH] Some code cleaning + clarifying (TODO: work on variables
 names)

---
 README.md                                     |  3 +-
 app.js                                        |  3 +-
 public/images/Hexagonal_chess.svg             |  4 +-
 public/images/pieces/{LICENSE => README}      |  0
 public/images/tmp_checkered/README            |  2 +
 public/javascripts/base_rules.js              | 23 +++++--
 public/javascripts/components/game.js         | 66 ++++++++++++-------
 public/javascripts/components/rules.js        |  4 +-
 .../javascripts/components/variantSummary.js  |  1 +
 public/javascripts/index.js                   |  5 ++
 public/javascripts/utils/misc.js              |  3 +-
 public/javascripts/variants/Alice.js          | 29 ++++----
 public/javascripts/variants/Antiking.js       | 17 ++---
 public/javascripts/variants/Atomic.js         | 13 ++--
 public/javascripts/variants/Checkered.js      |  9 ++-
 public/javascripts/variants/Crazyhouse.js     |  4 +-
 public/javascripts/variants/Extinction.js     |  2 +-
 public/javascripts/variants/Grand.js          | 37 +++++++----
 public/javascripts/variants/Loser.js          | 19 ++++--
 public/javascripts/variants/Magnetic.js       |  2 +-
 public/javascripts/variants/Wildebeest.js     | 48 ++++++++------
 public/javascripts/variants/Zen.js            | 27 +++-----
 public/stylesheets/index.sass                 | 19 ++++++
 public/stylesheets/variant.sass               | 15 ++++-
 views/index.pug                               | 52 +++++++++------
 views/layout.pug                              | 28 ++++----
 views/rules/Alice.pug                         | 13 ++--
 views/rules/Antiking.pug                      | 14 ++--
 views/rules/Atomic.pug                        | 16 +++--
 views/rules/Checkered.pug                     | 24 +++++--
 views/rules/Crazyhouse.pug                    |  3 +-
 views/rules/Grand.pug                         |  6 +-
 views/rules/Magnetic.pug                      |  9 ++-
 views/rules/Switching.pug                     |  3 +-
 views/rules/Wildebeest.pug                    |  3 +-
 views/rules/Zen.pug                           | 13 ++--
 views/variant.pug                             |  7 +-
 37 files changed, 345 insertions(+), 201 deletions(-)
 rename public/images/pieces/{LICENSE => README} (100%)
 create mode 100644 public/images/tmp_checkered/README

diff --git a/README.md b/README.md
index c5cb5081..3147c1fc 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ Pieces images where found at various locations.
 ## Installation (for developers)
 
  0. Install git-fat https://github.com/jedbrown/git-fat
- 1. Rename public/javascripts/utils/socket\_url.js.dist into socket\_url.js and adjust its content.
+ 1. Rename public/javascripts/utils/socket\_url.js.dist into socket\_url.js
+    and adjust its content.
  2. git fat init && git fat pull
  3. npm i && npm start
diff --git a/app.js b/app.js
index 7a8b98f3..e32ef39e 100644
--- a/app.js
+++ b/app.js
@@ -19,7 +19,8 @@ if (app.get('env') === 'development')
 }
 else
 {
-	app.set('trust proxy', true); //http://dev.rdybarra.com/2016/06/23/Production-Logging-With-Morgan-In-Express/
+	// http://dev.rdybarra.com/2016/06/23/Production-Logging-With-Morgan-In-Express/
+	app.set('trust proxy', true);
 	// In prod, only log error responses (https://github.com/expressjs/morgan)
 	app.use(logger('combined', {
 		skip: function (req, res) { return res.statusCode < 400 }
diff --git a/public/images/Hexagonal_chess.svg b/public/images/Hexagonal_chess.svg
index fe8626c2..7bc0fb4b 100644
--- a/public/images/Hexagonal_chess.svg
+++ b/public/images/Hexagonal_chess.svg
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
-
+<!-- Online source: https://en.wikipedia.org/wiki/List_of_chess_variants#/media/File:Hexagonal_chess.svg -->
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:cc="http://creativecommons.org/ns#"
@@ -2412,4 +2412,4 @@
    </g>
   </g>
  </g>
-</svg>
\ No newline at end of file
+</svg>
diff --git a/public/images/pieces/LICENSE b/public/images/pieces/README
similarity index 100%
rename from public/images/pieces/LICENSE
rename to public/images/pieces/README
diff --git a/public/images/tmp_checkered/README b/public/images/tmp_checkered/README
new file mode 100644
index 00000000..aac1f828
--- /dev/null
+++ b/public/images/tmp_checkered/README
@@ -0,0 +1,2 @@
+Initial PNG images designed by Patrick Bernier,
+from a model found online (cannot remember where, sorry)
diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index 860495a4..5c1ef97c 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -1,3 +1,6 @@
+// (Orthodox) Chess rules are defined in ChessRules class.
+// Variants generally inherit from it, and modify some parts.
+
 class PiPo //Piece+Position
 {
 	// o: {piece[p], color[c], posX[x], posY[y]}
@@ -61,7 +64,7 @@ class ChessRules
 	{
 		this.INIT_COL_KING = {'w':-1, 'b':-1};
 		this.INIT_COL_ROOK = {'w':[-1,-1], 'b':[-1,-1]};
-		this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; //respective squares of white and black king
+		this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; //squares of white and black king
 		const fenParts = fen.split(" ");
 		const position = fenParts[0].split("/");
 		for (let i=0; i<position.length; i++)
@@ -380,7 +383,8 @@ class ChessRules
 	// What are the knight moves from square x,y ?
 	getPotentialKnightMoves(sq)
 	{
-		return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+		return this.getSlideNJumpMoves(
+			sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
 	}
 
 	// What are the bishop moves from square x,y ?
@@ -481,7 +485,8 @@ class ChessRules
 
 	canIplay(side, [x,y])
 	{
-		return ((side=='w' && this.moves.length%2==0) || (side=='b' && this.moves.length%2==1))
+		return ((side=='w' && this.moves.length%2==0)
+				|| (side=='b' && this.moves.length%2==1))
 			&& this.getColor(x,y) == side;
 	}
 
@@ -491,7 +496,7 @@ class ChessRules
 		return this.filterValid( this.getPotentialMovesFrom(sq) );
 	}
 
-	// TODO: once a promotion is filtered, the others results are same: useless computations
+	// TODO: promotions (into R,B,N,Q) should be filtered only once
 	filterValid(moves)
 	{
 		if (moves.length == 0)
@@ -510,7 +515,7 @@ class ChessRules
 		{
 			for (let j=0; j<sizeY; j++)
 			{
-				// Next condition ... != oppCol is a little HACK to work with checkered variant
+				// Next condition "!= oppCol" = harmless hack to work with checkered variant
 				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
 					Array.prototype.push.apply(potentialMoves, this.getPotentialMovesFrom([i,j]));
 			}
@@ -869,8 +874,11 @@ class ChessRules
 				const score = this.checkGameEnd();
 				eval2 = (score=="1/2" ? 0 : (score=="1-0" ? 1 : -1) * maxeval);
 			}
-			if ((color=="w" && eval2 > moves1[i].eval) || (color=="b" && eval2 < moves1[i].eval))
+			if ((color=="w" && eval2 > moves1[i].eval)
+				|| (color=="b" && eval2 < moves1[i].eval))
+			{
 				moves1[i].eval = eval2;
+			}
 			this.undo(moves1[i]);
 		}
 		moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
@@ -1137,7 +1145,8 @@ class ChessRules
 		const d = new Date();
 		const opponent = mode=="human" ? "Anonymous" : "Computer";
 		pgn += '[Variant "' + variant + '"]<br>';
-		pgn += '[Date "' + d.getFullYear() + '-' + (d.getMonth()+1) + '-' + zeroPad(d.getDate()) + '"]<br>';
+		pgn += '[Date "' + d.getFullYear() + '-' + (d.getMonth()+1) +
+			'-' + zeroPad(d.getDate()) + '"]<br>';
 		pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]<br>';
 		pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]<br>';
 		pgn += '[FenStart "' + fenStart + '"]<br>';
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index 44db7dd8..7b238f5c 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -1,13 +1,14 @@
+// Game logic on a variant page
 Vue.component('my-game', {
 	data: function() {
 		return {
 			vr: null, //object to check moves, store them, FEN..
 			mycolor: "w",
 			possibleMoves: [], //filled after each valid click/dragstart
-			choices: [], //promotion pieces, or checkered captures... (contain possible pieces)
+			choices: [], //promotion pieces, or checkered captures... (as moves)
 			start: {}, //pixels coordinates + id of starting square (click or drag)
 			selectedPiece: null, //moving piece (or clicked piece)
-			conn: null, //socket messages
+			conn: null, //socket connection
 			score: "*", //'*' means 'unfinished'
 			mode: "idle", //human, friend, computer or idle (when not playing)
 			oppid: "", //opponent ID in case of HH game
@@ -16,7 +17,7 @@ Vue.component('my-game', {
 			fenStart: "",
 			incheck: [],
 			pgnTxt: "",
-			expert: getCookie("expert") === "1" ? true : false,
+			expert: (getCookie("expert") === "1" ? true : false),
 			gameId: "", //used to limit computer moves' time
 		};
 	},
@@ -127,7 +128,6 @@ Vue.component('my-game', {
 				{
 					on: { click: this.toggleExpertMode },
 					attrs: { "aria-label": 'Toggle expert mode' },
-					style: { "padding-top": "0", "margin-top": "0" },
 					'class': {
 						"tooltip":true,
 						"topindicator": true,
@@ -273,10 +273,12 @@ Vue.component('my-game', {
 				actionArray = actionArray.concat([
 					h('button',
 						{
-							style: { "margin-left": "30px" },
 							on: { click: e => this.undo() },
 							attrs: { "aria-label": 'Undo' },
-							"class": { "small": smallScreen },
+							"class": {
+								"small": smallScreen,
+								"marginleft": true,
+							},
 						},
 						[h('i', { 'class': { "material-icons": true } }, "fast_rewind")]),
 					h('button',
@@ -295,10 +297,12 @@ Vue.component('my-game', {
 				[
 					h('button',
 						{
-							style: { "margin-left": "30px" },
 							on: { click: this.undoInGame },
 							attrs: { "aria-label": 'Undo' },
-							"class": { "small": smallScreen },
+							"class": {
+								"small": smallScreen,
+								"marginleft": true,
+							},
 						},
 						[h('i', { 'class': { "material-icons": true } }, "undo")]
 					),
@@ -334,7 +338,7 @@ Vue.component('my-game', {
 							}
 						}),
 						h('sup',
-							{style: { "padding-left":"40%"} },
+							{"class": { "reserve-count": true } },
 							[ this.vr.reserve[this.mycolor][VariantRules.RESERVE_PIECES[i]] ]
 						)
 					]));
@@ -358,21 +362,25 @@ Vue.component('my-game', {
 							}
 						}),
 						h('sup',
-							{style: { "padding-left":"40%"} },
+							{"class": { "reserve-count": true } },
 							[ this.vr.reserve[oppCol][VariantRules.RESERVE_PIECES[i]] ]
 						)
 					]));
 				}
 				let reserves = h('div',
 					{
-						'class':{'game':true},
-						style: {"margin-bottom": "20px"},
+						'class':{
+							'game': true,
+							"reserve-div": true,
+						},
 					},
 					[
 						h('div',
 							{
-								'class': { 'row': true },
-								style: {"margin-bottom": "15px"},
+								'class': {
+									'row': true,
+									"reserve-row-1": true,
+								},
 							},
 							myReservePiecesArray
 						),
@@ -605,7 +613,8 @@ Vue.component('my-game', {
 		this.myid = continuation ? localStorage.getItem("myid") : getRandString();
 		if (!continuation)
 		{
-			// HACK: play a small silent sound to allow "new game" sound later if tab not focused
+			// HACK: play a small silent sound to allow "new game" sound later
+			// if tab not focused (TODO: does it really work ?!)
 			new Audio("/sounds/silent.mp3").play().then(() => {}).catch(err => {});
 		}
 		this.conn = new WebSocket(url + "/?sid=" + this.myid + "&page=" + variant);
@@ -631,7 +640,8 @@ Vue.component('my-game', {
 			switch (data.code)
 			{
 				case "newgame": //opponent found
-					this.newGame("human", data.fen, data.color, data.oppid); //oppid: opponent socket ID
+					// oppid: opponent socket ID
+					this.newGame("human", data.fen, data.color, data.oppid);
 					break;
 				case "newmove": //..he played!
 					this.play(data.move, "animate");
@@ -717,7 +727,8 @@ Vue.component('my-game', {
 			// Prepare and trigger download link
 			let downloadAnchor = document.getElementById("download");
 			downloadAnchor.setAttribute("download", "game.pgn");
-			downloadAnchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content);
+			downloadAnchor.href = "data:text/plain;charset=utf-8," +
+				encodeURIComponent(content);
 			downloadAnchor.click();
 		},
 		endGame: function(score) {
@@ -884,7 +895,8 @@ Vue.component('my-game', {
 		},
 		playComputerMove: function() {
 			const timeStart = Date.now();
-			const nbMoves = this.vr.moves.length; //using played moves to know if search finished
+			// We use moves' count to know if search finished:
+			const nbMoves = this.vr.moves.length;
 			const gameId = this.gameId; //to know if game was reset before timer end
 			setTimeout(
 				() => {
@@ -943,7 +955,8 @@ Vue.component('my-game', {
 				const iCanPlay = this.mode!="idle"
 					&& (this.mode=="friend" || this.vr.canIplay(this.mycolor,startSquare));
 				this.possibleMoves = iCanPlay ? this.vr.getPossibleMovesFrom(startSquare) : [];
-				// Next line add moving piece just after current image (required for Crazyhouse reserve)
+				// Next line add moving piece just after current image
+				// (required for Crazyhouse reserve)
 				e.target.parentNode.insertBefore(this.selectedPiece, e.target.nextSibling);
 			}
 		},
@@ -966,16 +979,20 @@ Vue.component('my-game', {
 				return;
 			e = e || window.event;
 			// Read drop target (or parentElement, parentNode... if type == "img")
-			this.selectedPiece.style.zIndex = -3000; //HACK to find square from final coordinates
+			this.selectedPiece.style.zIndex = -3000; //HACK to find square from final coords
 			const [offsetX,offsetY] = !!e.clientX
 				? [e.clientX,e.clientY]
 				: [e.changedTouches[0].pageX, e.changedTouches[0].pageY];
 			let landing = document.elementFromPoint(offsetX, offsetY);
 			this.selectedPiece.style.zIndex = 3000;
-			while (landing.tagName == "IMG") //classList.contains(piece) fails because of mark/highlight
+			// Next condition: classList.contains(piece) fails because of marks
+			while (landing.tagName == "IMG")
 				landing = landing.parentNode;
-			if (this.start.id == landing.id) //a click: selectedPiece and possibleMoves already filled
+			if (this.start.id == landing.id)
+			{
+				// A click: selectedPiece and possibleMoves are already filled
 				return;
+			}
 			// OK: process move attempt
 			let endSquare = this.getSquareFromId(landing.id);
 			let moves = this.findMatchingMoves(endSquare);
@@ -1006,7 +1023,7 @@ Vue.component('my-game', {
 			let translation = {x:rectEnd.x-rectStart.x, y:rectEnd.y-rectStart.y};
 			let movingPiece =
 				document.querySelector("#" + this.getSquareId(move.start) + " > img.piece");
-			// HACK for animation (with positive translate, image slides "under background"...)
+			// HACK for animation (with positive translate, image slides "under background")
 			// Possible improvement: just alter squares on the piece's way...
 			squares = document.getElementsByClassName("board");
 			for (let i=0; i<squares.length; i++)
@@ -1015,7 +1032,8 @@ Vue.component('my-game', {
 				if (square.id != this.getSquareId(move.start))
 					square.style.zIndex = "-1";
 			}
-			movingPiece.style.transform = "translate(" + translation.x + "px," + translation.y + "px)";
+			movingPiece.style.transform = "translate(" + translation.x + "px," +
+				translation.y + "px)";
 			movingPiece.style.transitionDuration = "0.2s";
 			movingPiece.style.zIndex = "3000";
 			setTimeout( () => {
diff --git a/public/javascripts/components/rules.js b/public/javascripts/components/rules.js
index 718021d0..fd05d44d 100644
--- a/public/javascripts/components/rules.js
+++ b/public/javascripts/components/rules.js
@@ -1,3 +1,4 @@
+// Load rules on variant page
 Vue.component('my-rules', {
 	data: function() {
 		return { content: "" };
@@ -16,7 +17,7 @@ Vue.component('my-rules', {
 		};
 		xhr.open("GET", "/rules/" + variant, true);
 		xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest");
-		xhr.send(null); //TODO: or just xhr.send() ?
+		xhr.send();
 	},
 	methods: {
 		drawDiag: function(fen) {
@@ -51,7 +52,6 @@ Vue.component('my-rules', {
 				boardDiv += "<div class='row'>";
 				for (let j=startY; j>=0 && j<sizeY; j+=inc)
 				{
-					// NOTE: 'board' to distinguish from coords
 					boardDiv += "<div class='board board" + sizeY + " " +
 						((i+j)%2==0 ? "light-square-diag" : "dark-square-diag") + "'>";
 					if (markArray.length>0 && markArray[i][j])
diff --git a/public/javascripts/components/variantSummary.js b/public/javascripts/components/variantSummary.js
index ed9b7b64..620f810f 100644
--- a/public/javascripts/components/variantSummary.js
+++ b/public/javascripts/components/variantSummary.js
@@ -1,3 +1,4 @@
+// Show a variant summary on index
 Vue.component('my-variant-summary', {
 	props: ['vobj'],
 	template: `
diff --git a/public/javascripts/index.js b/public/javascripts/index.js
index 007c9fe2..29e48f74 100644
--- a/public/javascripts/index.js
+++ b/public/javascripts/index.js
@@ -1,3 +1,4 @@
+// Javascript for index page: mostly counters updating
 new Vue({
 	el: "#indexPage",
 	data: {
@@ -79,5 +80,9 @@ new Vue({
 			document.getElementById("modal-b4welcome").checked = false;
 			document.getElementById("modal-welcome").checked = true;
 		},
+		markAsVisited: function() {
+			setCookie('visited', '1');
+			document.getElementById('modal-welcome').checked = false;
+		},
 	},
 });
diff --git a/public/javascripts/utils/misc.js b/public/javascripts/utils/misc.js
index 89a545cf..0b68ac45 100644
--- a/public/javascripts/utils/misc.js
+++ b/public/javascripts/utils/misc.js
@@ -1,5 +1,4 @@
 // Source: https://www.quirksmode.org/js/cookies.html
-
 function setCookie(name,value)
 {
 	var date = new Date();
@@ -22,9 +21,9 @@ function getCookie(name) {
 	return null;
 }
 
+// Random (enough) string for socket and game IDs
 function getRandString()
 {
-	// Random enough (for socket and game IDs)
 	return (Date.now().toString(36) + Math.random().toString(36).substr(2, 7))
 		.toUpperCase();
 }
diff --git a/public/javascripts/variants/Alice.js b/public/javascripts/variants/Alice.js
index b286bf66..220fbd40 100644
--- a/public/javascripts/variants/Alice.js
+++ b/public/javascripts/variants/Alice.js
@@ -175,7 +175,9 @@ class AliceRules extends ChessRules
 				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) == color)
 				{
 					const mirrorSide =
-						(Object.keys(VariantRules.ALICE_CODES).includes(this.getPiece(i,j)) ? 1 : 2);
+						Object.keys(VariantRules.ALICE_CODES).includes(this.getPiece(i,j))
+							? 1
+							: 2;
 					Array.prototype.push.apply(potentialMoves,
 						this.getPotentialMovesFrom([i,j], sideBoard[mirrorSide-1]));
 				}
@@ -291,20 +293,17 @@ class AliceRules extends ChessRules
 	}
 
 	static get VALUES() {
-		return {
-			'p': 1,
-			's': 1,
-			'r': 5,
-			'u': 5,
-			'n': 3,
-			'o': 3,
-			'b': 3,
-			'c': 3,
-			'q': 9,
-			't': 9,
-			'k': 1000,
-			'l': 1000
-		};
+		return Object.assign(
+			ChessRules.VALUES,
+			{
+				's': 1,
+				'u': 5,
+				'o': 3,
+				'c': 3,
+				't': 9,
+				'l': 1000,
+			}
+		);
 	}
 
 	getNotation(move)
diff --git a/public/javascripts/variants/Antiking.js b/public/javascripts/variants/Antiking.js
index b22cbd75..014a9c89 100644
--- a/public/javascripts/variants/Antiking.js
+++ b/public/javascripts/variants/Antiking.js
@@ -1,6 +1,5 @@
 class AntikingRules extends ChessRules
 {
-	// Path to pieces
 	static getPpath(b)
 	{
 		return b[1]=='a' ? "Antiking/"+b : b;
@@ -142,17 +141,11 @@ class AntikingRules extends ChessRules
 		return color == "w" ? "0-1" : "1-0";
 	}
 
-	// Pieces values (TODO: use Object.assign() + ChessRules.VALUES ?)
 	static get VALUES() {
-		return {
-			'p': 1,
-			'r': 5,
-			'n': 3,
-			'b': 3,
-			'q': 9,
-			'k': 1000,
-			'a': 1000
-		};
+		return Object.assign(
+			ChessRules.VALUES,
+			{ 'a': 1000 }
+		);
 	}
 
 	static GenRandInitFen()
@@ -206,7 +199,7 @@ class AntikingRules extends ChessRules
 		let fen = pieces["b"].join("") + "/" + ranks23_black +
 			"/8/8/" +
 			ranks23_white + "/" + pieces["w"].join("").toUpperCase() +
-			" 1111"; //add flags
+			" 1111";
 		return fen;
 	}
 }
diff --git a/public/javascripts/variants/Atomic.js b/public/javascripts/variants/Atomic.js
index 21fbedb2..28ee1f26 100644
--- a/public/javascripts/variants/Atomic.js
+++ b/public/javascripts/variants/Atomic.js
@@ -8,7 +8,7 @@ class AtomicRules extends ChessRules
 		moves.forEach(m => {
 			if (m.vanish.length > 1 && m.appear.length <= 1) //avoid castles
 			{
-				// Explosion! TODO: drop moves which explode our king here
+				// Explosion! TODO(?): drop moves which explode our king here
 				let steps = [ [-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1] ];
 				for (let step of steps)
 				{
@@ -17,7 +17,8 @@ class AtomicRules extends ChessRules
 					if (x>=0 && x<8 && y>=0 && y<8 && this.board[x][y] != VariantRules.EMPTY
 						&& this.getPiece(x,y) != VariantRules.PAWN)
 					{
-						m.vanish.push(new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y}));
+						m.vanish.push(
+							new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y}));
 					}
 				}
 				m.end = {x:m.appear[0].x, y:m.appear[0].y};
@@ -47,8 +48,11 @@ class AtomicRules extends ChessRules
 
 	isAttacked(sq, colors)
 	{
-		if (this.getPiece(sq[0],sq[1]) == VariantRules.KING && this.isAttackedByKing(sq, colors))
+		if (this.getPiece(sq[0],sq[1]) == VariantRules.KING
+			&& this.isAttackedByKing(sq, colors))
+		{
 			return false; //king cannot take...
+		}
 		return (this.isAttackedByPawn(sq, colors)
 			|| this.isAttackedByRook(sq, colors)
 			|| this.isAttackedByKnight(sq, colors)
@@ -145,7 +149,6 @@ class AtomicRules extends ChessRules
 			return color == "w" ? "0-1" : "1-0";
 		if (!this.isAttacked(kp, [this.getOppCol(color)]))
 			return "1/2";
-		// Checkmate
-		return color == "w" ? "0-1" : "1-0";
+		return color == "w" ? "0-1" : "1-0"; //checkmate
 	}
 }
diff --git a/public/javascripts/variants/Checkered.js b/public/javascripts/variants/Checkered.js
index b9e7223b..019e1e2c 100644
--- a/public/javascripts/variants/Checkered.js
+++ b/public/javascripts/variants/Checkered.js
@@ -103,7 +103,8 @@ class CheckeredRules extends ChessRules
 
 	canIplay(side, [x,y])
 	{
-		return ((side=='w' && this.moves.length%2==0) || (side=='b' && this.moves.length%2==1))
+		return ((side=='w' && this.moves.length%2==0)
+				|| (side=='b' && this.moves.length%2==1))
 			&& [side,'c'].includes(this.getColor(x,y));
 	}
 
@@ -166,7 +167,8 @@ class CheckeredRules extends ChessRules
 		this.play(move);
 		const color = this.turn;
 		this.moves.push(move); //artifically change turn, for checkered pawns (TODO)
-		const kingAttacked = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']);
+		const kingAttacked = this.isAttacked(
+			this.kingPos[color], [this.getOppCol(color),'c']);
 		let res = kingAttacked
 			? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate!
 			: [ ];
@@ -259,7 +261,8 @@ class CheckeredRules extends ChessRules
 			{
 				// Capture
 				let startColumn = String.fromCharCode(97 + move.start.y);
-				notation = startColumn + "x" + finalSquare + "=" + move.appear[0].p.toUpperCase();
+				notation = startColumn + "x" + finalSquare +
+					"=" + move.appear[0].p.toUpperCase();
 			}
 			else //no capture
 			{
diff --git a/public/javascripts/variants/Crazyhouse.js b/public/javascripts/variants/Crazyhouse.js
index 88193edb..b297f1fb 100644
--- a/public/javascripts/variants/Crazyhouse.js
+++ b/public/javascripts/variants/Crazyhouse.js
@@ -51,7 +51,7 @@ class CrazyhouseRules extends ChessRules
 		return color + VariantRules.RESERVE_PIECES[index];
 	}
 
-	// Put an ordering on reserve pieces
+	// Ordering on reserve pieces
 	static get RESERVE_PIECES() {
 		const V = VariantRules;
 		return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
@@ -97,7 +97,7 @@ class CrazyhouseRules extends ChessRules
 		const sizeX = VariantRules.size[0];
 		if (x >= sizeX)
 		{
-			// Reserves, outside of board: x == sizeX
+			// Reserves, outside of board: x == sizeX(+1)
 			return this.getReserveMoves([x,y]);
 		}
 		// Standard moves
diff --git a/public/javascripts/variants/Extinction.js b/public/javascripts/variants/Extinction.js
index 9be4b0d7..f0ebeabb 100644
--- a/public/javascripts/variants/Extinction.js
+++ b/public/javascripts/variants/Extinction.js
@@ -123,13 +123,13 @@ class ExtinctionRules extends ChessRules
 		return this.turn == "w" ? "0-1" : "1-0";
 	}
 
-	// Very negative (resp. positive) if white (reps. black) pieces set is incomplete
 	evalPosition()
 	{
 		const color = this.turn;
 		if (Object.keys(this.material[color]).some(
 			p => { return this.material[color][p] == 0; }))
 		{
+			// Very negative (resp. positive) if white (reps. black) pieces set is incomplete
 			return (color=="w"?-1:1) * VariantRules.INFINITY;
 		}
 		return super.evalPosition();
diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js
index 844d62c7..9e1504d9 100644
--- a/public/javascripts/variants/Grand.js
+++ b/public/javascripts/variants/Grand.js
@@ -1,4 +1,5 @@
-//https://www.chessvariants.com/large.dir/freeling.html
+// NOTE: initial setup differs from the original; see
+// https://www.chessvariants.com/large.dir/freeling.html
 class GrandRules extends ChessRules
 {
 	static getPpath(b)
@@ -84,10 +85,16 @@ class GrandRules extends ChessRules
 				}
 			}
 			// Captures
-			if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+			if (y>0 && this.canTake([x,y], [x+shift,y-1])
+				&& this.board[x+shift][y-1] != V.EMPTY)
+			{
 				moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
-			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+			}
+			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
+				&& this.board[x+shift][y+1] != V.EMPTY)
+			{
 				moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
+			}
 		}
 
 		if (lastRanks.includes(x+shift))
@@ -101,10 +108,16 @@ class GrandRules extends ChessRules
 				if (this.board[x+shift][y] == V.EMPTY)
 					moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
 				// Captures
-				if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+				if (y>0 && this.canTake([x,y], [x+shift,y-1])
+					&& this.board[x+shift][y-1] != V.EMPTY)
+				{
 					moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
-				if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+				}
+				if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
+					&& this.board[x+shift][y+1] != V.EMPTY)
+				{
 					moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
+				}
 			});
 		}
 
@@ -161,14 +174,16 @@ class GrandRules extends ChessRules
 	{
 		const V = VariantRules;
 		return this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK])
-			|| this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.KNIGHT], "oneStep");
+			|| this.isAttackedBySlideNJump(
+				sq, colors, V.MARSHALL, V.steps[V.KNIGHT], "oneStep");
 	}
 
 	isAttackedByCardinal(sq, colors)
 	{
 		const V = VariantRules;
 		return this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP])
-			|| this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.KNIGHT], "oneStep");
+			|| this.isAttackedBySlideNJump(
+				sq, colors, V.CARDINAL, V.steps[V.KNIGHT], "oneStep");
 	}
 
 	updateVariables(move)
@@ -208,9 +223,9 @@ class GrandRules extends ChessRules
 	// TODO: this function could be generalized and shared better
 	static GenRandInitFen()
 	{
-		let pieces = [new Array(10), new Array(10)];
+		let pieces = { "w": new Array(10), "b": new Array(10) };
 		// Shuffle pieces on first and last rank
-		for (let c = 0; c <= 1; c++)
+		for (let c of ["w","b"])
 		{
 			let positions = _.range(10);
 
@@ -264,9 +279,9 @@ class GrandRules extends ChessRules
 			pieces[c][knight2Pos] = 'n';
 			pieces[c][rook2Pos] = 'r';
 		}
-		let fen = pieces[0].join("") +
+		let fen = pieces["b"].join("") +
 			"/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" +
-			pieces[1].join("").toUpperCase() +
+			pieces["w"].join("").toUpperCase() +
 			" 1111";
 		return fen;
 	}
diff --git a/public/javascripts/variants/Loser.js b/public/javascripts/variants/Loser.js
index 936c2590..9ae2e7fc 100644
--- a/public/javascripts/variants/Loser.js
+++ b/public/javascripts/variants/Loser.js
@@ -24,15 +24,20 @@ class LoserRules extends ChessRules
 		const lastRank = (color == "w" ? 0 : sizeX-1);
 		if (x+shift == lastRank)
 		{
-			let p = V.KING;
 			// Normal move
 			if (this.board[x+shift][y] == V.EMPTY)
-				moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
+				moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:V.KING}));
 			// Captures
-			if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
-				moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
-			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
-				moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
+			if (y>0 && this.canTake([x,y], [x+shift,y-1])
+				&& this.board[x+shift][y-1] != V.EMPTY)
+			{
+				moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:V.KING}));
+			}
+			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
+				&& this.board[x+shift][y+1] != V.EMPTY)
+			{
+				moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:V.KING}));
+			}
 		}
 
 		return moves;
@@ -112,7 +117,7 @@ class LoserRules extends ChessRules
 
 	getFlagsFen()
 	{
-		return "";
+		return "-";
 	}
 
 	checkGameEnd()
diff --git a/public/javascripts/variants/Magnetic.js b/public/javascripts/variants/Magnetic.js
index 4d83fad1..7d9d5113 100644
--- a/public/javascripts/variants/Magnetic.js
+++ b/public/javascripts/variants/Magnetic.js
@@ -174,7 +174,7 @@ class MagneticRules extends ChessRules
 			this.kingPos[oppCol] = [-1,-1];
 			this.castleFlags[oppCol] = [false,false];
 		}
-		// Did we move our (init) rooks or opponents' ones ?
+		// Did we magnetically move our (init) rooks or opponents' ones ?
 		const firstRank = (c == "w" ? 7 : 0);
 		const oppFirstRank = 7 - firstRank;
 		const oppCol = this.getOppCol(c);
diff --git a/public/javascripts/variants/Wildebeest.js b/public/javascripts/variants/Wildebeest.js
index c557a2e8..5bfa87b0 100644
--- a/public/javascripts/variants/Wildebeest.js
+++ b/public/javascripts/variants/Wildebeest.js
@@ -1,4 +1,3 @@
-//https://www.chessvariants.com/large.dir/wildebeest.html
 class WildebeestRules extends ChessRules
 {
 	static getPpath(b)
@@ -84,10 +83,16 @@ class WildebeestRules extends ChessRules
 				}
 			}
 			// Captures
-			if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+			if (y>0 && this.canTake([x,y], [x+shift,y-1])
+				&& this.board[x+shift][y-1] != V.EMPTY)
+			{
 				moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
-			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+			}
+			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
+				&& this.board[x+shift][y+1] != V.EMPTY)
+			{
 				moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
+			}
 		}
 
 		if (x+shift == lastRank)
@@ -99,10 +104,16 @@ class WildebeestRules extends ChessRules
 				if (this.board[x+shift][y] == V.EMPTY)
 					moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
 				// Captures
-				if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+				if (y>0 && this.canTake([x,y], [x+shift,y-1])
+					&& this.board[x+shift][y-1] != V.EMPTY)
+				{
 					moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
-				if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+				}
+				if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
+					&& this.board[x+shift][y+1] != V.EMPTY)
+				{
 					moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
+				}
 			});
 		}
 
@@ -136,13 +147,15 @@ class WildebeestRules extends ChessRules
 
 	getPotentialCamelMoves(sq)
 	{
-		return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.CAMEL], "oneStep");
+		return this.getSlideNJumpMoves(
+			sq, VariantRules.steps[VariantRules.CAMEL], "oneStep");
 	}
 
 	getPotentialWildebeestMoves(sq)
 	{
 		const V = VariantRules;
-		return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
+		return this.getSlideNJumpMoves(
+			sq, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
 	}
 
 	isAttacked(sq, colors)
@@ -182,9 +195,8 @@ class WildebeestRules extends ChessRules
 
 	static GenRandInitFen()
 	{
-		let pieces = [new Array(10), new Array(10)];
-		// Shuffle pieces on first and last rank
-		for (let c = 0; c <= 1; c++)
+		let pieces = { "w": new Array(10), "b": new Array(10) };
+		for (let c of ["w","b"])
 		{
 			let positions = _.range(11);
 
@@ -196,11 +208,12 @@ class WildebeestRules extends ChessRules
 			let randIndexes_tmp = _.sample(_.range(5), 2).map(i => { return 2*i+1; });
 			let bishop2Pos = positions[randIndexes_tmp[0]];
 			let camel2Pos = positions[randIndexes_tmp[1]];
-			// Remove chosen squares
-			for (let idx of randIndexes.concat(randIndexes_tmp).sort((a,b) => { return b-a; }))
+			for (let idx of randIndexes.concat(randIndexes_tmp)
+				.sort((a,b) => { return b-a; })) //largest indices first
+			{
 				positions.splice(idx, 1);
+			}
 
-			// Get random squares for knights
 			let randIndex = _.random(6);
 			let knight1Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
@@ -208,22 +221,19 @@ class WildebeestRules extends ChessRules
 			let knight2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			// Get random square for queen
 			randIndex = _.random(4);
 			let queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			// ...random square for wildebeest
+			// Random square for wildebeest
 			randIndex = _.random(3);
 			let wildebeestPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			// Rooks and king positions are now fixed, because of the ordering rook-king-rook
 			let rook1Pos = positions[0];
 			let kingPos = positions[1];
 			let rook2Pos = positions[2];
 
-			// Finally put the shuffled pieces in the board array
 			pieces[c][rook1Pos] = 'r';
 			pieces[c][knight1Pos] = 'n';
 			pieces[c][bishop1Pos] = 'b';
@@ -236,9 +246,9 @@ class WildebeestRules extends ChessRules
 			pieces[c][knight2Pos] = 'n';
 			pieces[c][rook2Pos] = 'r';
 		}
-		let fen = pieces[0].join("") +
+		let fen = pieces["b"].join("") +
 			"/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
-			pieces[1].join("").toUpperCase() +
+			pieces["w"].join("").toUpperCase() +
 			" 1111";
 		return fen;
 	}
diff --git a/public/javascripts/variants/Zen.js b/public/javascripts/variants/Zen.js
index 31dfccd1..c1f48147 100644
--- a/public/javascripts/variants/Zen.js
+++ b/public/javascripts/variants/Zen.js
@@ -6,7 +6,7 @@ class ZenRules extends ChessRules
 		return undefined;
 	}
 
-	// TODO: some duplicated code in 2 next functions
+	// TODO(?): some duplicated code in 2 next functions
 	getSlideNJumpMoves([x,y], steps, oneStep)
 	{
 		const color = this.getColor(x,y);
@@ -83,21 +83,12 @@ class ZenRules extends ChessRules
 	// Find possible captures from a square: look in every direction!
 	findCaptures(sq)
 	{
-		var moves = [];
+		let moves = [];
 
-		// PAWN
 		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.PAWN));
-
-		// ROOK
 		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.ROOK));
-
-		// KNIGHT
 		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.KNIGHT));
-
-		// BISHOP
 		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.BISHOP));
-
-		// QUEEN
 		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.QUEEN));
 
 		return moves;
@@ -149,22 +140,24 @@ class ZenRules extends ChessRules
 
 	getPotentialRookMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.ROOK]);
+		let noCaptures = this.getSlideNJumpMoves(
+			sq, VariantRules.steps[VariantRules.ROOK]);
 		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
 	getPotentialKnightMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(sq,
-			VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+		let noCaptures = this.getSlideNJumpMoves(
+			sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
 		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
 	getPotentialBishopMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.BISHOP]);
+		let noCaptures = this.getSlideNJumpMoves(
+			sq, VariantRules.steps[VariantRules.BISHOP]);
 		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
@@ -172,8 +165,8 @@ class ZenRules extends ChessRules
 	getPotentialQueenMoves(sq)
 	{
 		const V = VariantRules;
-		let noCaptures =
-			this.getSlideNJumpMoves(sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+		let noCaptures = this.getSlideNJumpMoves(
+			sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
 		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
diff --git a/public/stylesheets/index.sass b/public/stylesheets/index.sass
index 6f5fedfe..f67ed08b 100644
--- a/public/stylesheets/index.sass
+++ b/public/stylesheets/index.sass
@@ -16,6 +16,9 @@
 .card > h3.section.red
   color: #cc3300
 
+.main-title
+  font-style: italic
+
 #welcome, #help
   max-height: 100vh
   max-width: 90vw
@@ -37,6 +40,22 @@
 #welcome ul > li
   font-family: monospace
 
+.read-this
+  color: blue
+  text-decoration: underline
+  cursor: pointer
+
+.emphasis
+  font-style: italic
+  color: purple
+
+.disable-msg
+  cursor: pointer
+  color: darkred
+
+.smallfont
+  font-size: 0.8em
+
 table.list-table
   width: 300px
   margin: 0 auto
diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass
index d23e83f0..c98807f3 100644
--- a/public/stylesheets/variant.sass
+++ b/public/stylesheets/variant.sass
@@ -55,6 +55,18 @@ figure.diagram-container > .diagram
   float: right
   margin: 0 20px 10px 0
 
+.marginleft
+  margin-left: 30px
+
+.reserve-count
+  padding-left: 40%
+
+.reserve-div
+  margin-bottom: 20px
+
+.reserve-row-1
+  margin-bottom: 15px
+
 .connected
   background-color: green
 
@@ -62,7 +74,8 @@ figure.diagram-container > .diagram
   background-color: red
 
 .expert-switch
-  padding: 5px 10px
+  padding: 0 10px 5px 10px
+  margin: 0
 
 .expert-mode, button.expert-mode:hover
   background-color: #ffcc99
diff --git a/views/index.pug b/views/index.pug
index 6e8cc0b4..ba5393cc 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -7,9 +7,10 @@ block content
 	.container#indexPage
 		.row
 			.col-sm-12
-				h1.text-center(style="font-style:italic") Welcome to v[ariant] chess club !
+				h1.text-center.main-title Welcome to v[ariant] chess club !
 				h2.text-center
-					span.help(onClick="document.getElementById('modal-help').checked=true") Help ? 
+					span.help(onClick="document.getElementById('modal-help').checked=true")
+						| Help ? 
 					a(href="/demo.webm") Demo !
 				input#modal-help.modal(type="checkbox")
 				div(role="dialog")
@@ -17,9 +18,13 @@ block content
 						label.modal-close(for="modal-help")
 						h3.blue.section Tips
 						p.section
-							span.conditional-jump On a variant page, read the rules by clicking on the title "&lt;Variant&gt; rules". 
-							span.conditional-jump Try playing against a human: click the leftmost "new game" button :) 
-							| ...then, while waiting you can play against a (rather weak) bot or a friend.
+							span.conditional-jump
+								| On a variant page, read the rules by clicking on the title
+								| "&lt;Variant&gt; rules". 
+							span.conditional-jump
+								| Try playing against a human: click the leftmost "new game" button :) 
+							| ...then, while waiting you can play against a (rather weak) bot
+							| or a friend.
 						// TODO? On the index page, try typing the first letters of a variant.
 						h3.blue.section Comments
 						p.section.
@@ -27,20 +32,21 @@ block content
 							Games are untimed, and played anonymously. #[br]
 							No chat, to rather focus on the moves :)
 						h3.red.section Bug report
-						p.section.
-							If you find a bug in a game, please follow this procedure: #[br]
-							1. stop playing: click on the resign button; #[br]
-							2. click on the PGN to download it; #[br]
-							3. send an email to
-							#[a(href="mailto:contact@vchess.club?subject=[vchess.club] bug report") contact@vchess.club]
-							with relevant comments and the PGN attached. Thank you!
+						p.section
+							| If you find a bug in a game, please follow this procedure: #[br]
+							| 1. stop playing: click on the resign button; #[br]
+							| 2. click on the PGN to download it; #[br]
+							| 3. send an email to 
+							a(href="mailto:contact@vchess.club?subject=[vchess.club] bug report")
+								| contact@vchess.club 
+							| with relevant comments and the PGN attached. Thank you!
 				input#modal-b4welcome.modal(type="checkbox")
 				div(role="dialog")
 					#b4welcome.card.text-center
 						label.modal-close(for="modal-b4welcome")
 						h3.blue.section First visit?
 						p Please 
-							span(style="color:blue;text-decoration:underline;cursor:pointer" @click="showWelcomeMsg") read this
+							span.read-this(@click="showWelcomeMsg") read this
 							span &nbsp;before playing &#9786;
 				input#modal-welcome.modal(type="checkbox")
 				div(role="dialog")
@@ -55,7 +61,7 @@ block content
 								As suggested by the picture, a variant setup generally
 								looks more or less like a chessboard with regular pieces
 								(otherwise it's no longer a variant but a whole new game!).
-							p(style="font-style:italic;color:purple") However...
+							p.emphasis However...
 							p Each variant has its own new rules, which can involve
 							table.list-table
 								tbody
@@ -71,21 +77,27 @@ block content
 										td ...and so on
 						.section
 							p.
-								Example: imagine that a capture is an atomic explosion, wiping all adjacent squares
-								&ndash; except the pawns, which as cockroaches can resist this kind of event.
+								Example: imagine that a capture is an atomic explosion, wiping all
+								adjacent squares &ndash; except the pawns, which as cockroaches can
+								resist this kind of event.
 							p Also state a goal: make the opponent's king explode.
 							p &rarr; Congrats, you defined Atomic chess! (Playable here)
 						.section
-							p(style="font-style:italic;color:purple") OK, this all sounds interesting, but why would that be fun?
+							p.emphasis OK, this all sounds interesting, but why would that be fun?
 							p.
 								Because all games here start with a random setup: no more boring
 								openings memorization, you have to rely on your chess skills only :)
 							p Moreover, I claim that the chosen variants here are fun to play :P
+							-
+								var wikipediaUrl = "https://en.wikipedia.org/wiki/" +
+									"List_of_chess_variants#/media/File:Hexagonal_chess.svg";
 							p.
 								For informations about hundreds (if not thousands!) of variants, you
-								can visit the excellent #[a(href="https://www.chessvariants.com/") chessvariants] website.
-						p(style="cursor:pointer;color:darkred" onClick="setCookie('visited','1');document.getElementById('modal-welcome').checked=false") Click here to not show this message next time
-						p(style="font-size:0.8em") Image credit: #[a(href="https://en.wikipedia.org/wiki/List_of_chess_variants#/media/File:Hexagonal_chess.svg") Wikpedia]
+									can visit the excellent
+									#[a(href="https://www.chessvariants.com/") chessvariants] website.
+						p.disable-msg(@click="markAsVisited")
+							| Click here to not show this message next time
+						p.smallfont Image credit: #[a(href=wikipediaUrl) Wikipedia]
 		.row
 			my-variant-summary(
 				v-for="(v,idx) in sortedCounts",
diff --git a/views/layout.pug b/views/layout.pug
index 10b1ae9d..0416766d 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -4,18 +4,22 @@ html(lang="fr")
 	head
 		meta(charset="UTF-8")
 		title vchess - #{title}
-		meta(name="viewport", content="width=device-width, initial-scale=1")
-		meta(name="msapplication-config", content="/images/favicon/browserconfig.xml")
-		meta(name="theme-color", content="#ffffff")
-		link(rel="stylesheet", href="//cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.0/mini-default.min.css")
-		link(rel="stylesheet", href="//fonts.googleapis.com/css?family=Open+Sans:400,700")
-		link(rel="apple-touch-icon", sizes="180x180", href="/images/favicon/apple-touch-icon.png")
-		link(rel="icon", type="image/png", sizes="32x32", href="/images/favicon/favicon-32x32.png")
-		link(rel="icon", type="image/png", sizes="16x16", href="/images/favicon/favicon-16x16.png")
-		link(rel="manifest", href="/images/favicon/manifest.json")
-		link(rel="mask-icon", href="/images/favicon/safari-pinned-tab.svg", color="#5bbad5")
-		link(rel="shortcut icon", href="/images/favicon/favicon.ico")
-		link(rel="stylesheet", href="/stylesheets/layout.css")
+		meta(name="viewport" content="width=device-width, initial-scale=1")
+		meta(name="msapplication-config" content="/images/favicon/browserconfig.xml")
+		meta(name="theme-color" content="#ffffff")
+		link(rel="stylesheet"
+			href="//cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.0/mini-default.min.css")
+		link(rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400,700")
+		link(rel="apple-touch-icon" sizes="180x180"
+			href="/images/favicon/apple-touch-icon.png")
+		link(rel="icon" type="image/png" sizes="32x32"
+			href="/images/favicon/favicon-32x32.png")
+		link(rel="icon" type="image/png" sizes="16x16"
+			href="/images/favicon/favicon-16x16.png")
+		link(rel="manifest" href="/images/favicon/manifest.json")
+		link(rel="mask-icon" href="/images/favicon/safari-pinned-tab.svg" color="#5bbad5")
+		link(rel="shortcut icon" href="/images/favicon/favicon.ico")
+		link(rel="stylesheet" href="/stylesheets/layout.css")
 		block css
 
 	body
diff --git a/views/rules/Alice.pug b/views/rules/Alice.pug
index e23d97a7..7c5fca41 100644
--- a/views/rules/Alice.pug
+++ b/views/rules/Alice.pug
@@ -1,5 +1,6 @@
 p.boxed
-	| Every move played ends up on another board (the "other side of the mirror"). So there are two boards. All pieces start on board 1.
+	| Every move played ends up on another board (the "other side of the mirror").
+	| So there are two boards. All pieces start on board 1.
 
 h3 Specifications
 
@@ -14,7 +15,8 @@ ul
 h3 Basics
 
 p
-	| Two boards are used in this variant. Pieces from board 2 are represented on the main board, upside down.
+	| Two boards are used in this variant. Pieces from board 2 are represented on
+	| the main board, upside down.
 	| Any move played must be valid on the board it is played on.
 	| In addition, the final square should not be occupied by a piece from the other board
 	| (thus allowing to represent all on one board).
@@ -35,7 +37,9 @@ figure.diagram-container
 
 h3 End of the game
 
-p As in the orthodox game, win by checkmating the king. It shouldn't be able to escape the check, not even by moving to the other board.
+p
+	| As in the orthodox game, win by checkmating the king. It shouldn't be able to
+	| escape the check, not even by moving to the other board.
 
 p Note: en-passant and castle occur as they do in the standard game.
 
@@ -45,5 +49,6 @@ p
 	| Alice chess pages on 
 	a(href="https://www.chessvariants.com/other.dir/alice.html") chessvariants.com 
 	| and on 
-	a(href="https://www.schemingmind.com/journalarticle.aspx?article_id=9") schemingmind.com
+	a(href="https://www.schemingmind.com/journalarticle.aspx?article_id=9")
+		| schemingmind.com
 	| .
diff --git a/views/rules/Antiking.pug b/views/rules/Antiking.pug
index 28e264b7..8272e2b0 100644
--- a/views/rules/Antiking.pug
+++ b/views/rules/Antiking.pug
@@ -1,6 +1,6 @@
 p.boxed
-	| You have a king and an antiking. King must stay away from checks, but antiking must always stay in check.
-	| Antiking captures his own kind.
+	| You have a king and an antiking. King must stay away from checks, but antiking
+	| must always stay in check. Antiking captures his own kind.
 
 h3 Specifications
 
@@ -16,7 +16,8 @@ h3 Basics
 
 p
 	| The additional piece is a royal figure, thus cannot be captured.
-	| It captures the pieces of his color (to help checkmate opponent antiking, but by doing so it also make standard checkmate more difficult...).
+	| It captures the pieces of his color (to help checkmate opponent antiking,
+	| but by doing so it also make standard checkmate more difficult...).
 	| It should always remains under check (if it cannot, game is over).
 
 figure.diagram-container
@@ -34,12 +35,15 @@ p ...Or maybe do both at the same time?
 
 p Note 1: athough antiking captures his color, it doesn't check his king.
 
-p Note 2: since it would allow a basic tactic (keep antiking touching opponent's king), kings do not attack antikings.
+p
+	| Note 2: since it would allow a basic tactic (keep antiking touching opponent's
+	| king), kings do not attack antikings.
 
 p Note 3: an antiking does not check opponent's antiking.
 
 h3 Credits
 
 p
-	a(href="https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html") Antiking chess 
+	a(href="https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html")
+		| Antiking chess 
 	| on chessvariants.com.
diff --git a/views/rules/Atomic.pug b/views/rules/Atomic.pug
index 008a35ce..7b776685 100644
--- a/views/rules/Atomic.pug
+++ b/views/rules/Atomic.pug
@@ -1,5 +1,6 @@
 p.boxed
-	| All captures result in an "explosion" through which all surrounding white and black pieces other than pawns are removed from play.
+	| All captures result in an "explosion" through which all surrounding
+	| pieces other than pawns are removed from the board.
 
 h3 Specifications
 
@@ -14,7 +15,8 @@ ul
 h3 Basics
 
 p
-	| When a piece captures an opponent figure on some square S, all pieces sitting on a square reachable by a king move from S are removed.
+	| When a piece captures an opponent figure on some square S, all pieces sitting
+	| on a square reachable by a king move from S are removed.
 	| The pawns, however, remain: they have to be taken directly to disappear.
 
 figure.diagram-container
@@ -31,10 +33,14 @@ ol
 
 p Explosions have priority: a checkmate followed by a king explosion loses.
 
-p Note: since suicide is forbidden, a king can touch the opponent king - and become immune to checks.
+p
+	| Note: since suicide is forbidden, a king can touch the opponent king -
+	| and become immune to checks.
 
 h3 Credits
 
 p
-	| Many resources can be found on the web (this variation is played on lichess and FICS, among others).
-	| This game was played first in 1995 at the German Internet Chess Server (GICS) according to Wikipedia.
+	| Many resources can be found on the web (this variation is played on lichess and
+	| FICS, among others).
+	| This game was played first in 1995 at the German Internet Chess Server (GICS)
+	| according to Wikipedia.
diff --git a/views/rules/Checkered.pug b/views/rules/Checkered.pug
index cf347665..96e4475e 100644
--- a/views/rules/Checkered.pug
+++ b/views/rules/Checkered.pug
@@ -1,5 +1,6 @@
 p.boxed
-	| The capture of an enemy piece produces a new "checkered" piece belonging to both players.
+	| The capture of an enemy piece produces a new "checkered" piece belonging
+	| to both players.
 
 figure.showPieces.center-align
 	img(src="/images/tmp_checkered/cp.png")
@@ -27,9 +28,15 @@ h2.stageDelimiter Stage 1
 h3 Basics
 
 ol
-	li Each capture produces a new piece, taking on nature of the capturing or captured one.
-	li The new piece arising from a capture has a new color: "checkered", as illustrated above.
-	li All checkered pieces belong to the player in turn and can capture the opponents pieces.
+	li
+		| Each capture produces a new piece, taking on nature of
+		| the capturing or captured one.
+	li
+		| The new piece arising from a capture has a new color:
+		| "checkered", as illustrated above.
+	li
+		| All checkered pieces belong to the player in turn and can
+		| capture the opponents pieces.
 
 span Remarks:
 ul
@@ -40,7 +47,8 @@ figure.diagram-container
 	.diagram
 		| fen:2kr4/pp6/2p5/4ss1r/1P2ns1P/2Np4/P1P1P1BP/R2o1RK1:
 	figcaption.
-		Black plays Rxh4=P. (Checkered pawn to) h5 is allowed then, because piece's nature changed.
+		Black plays Rxh4=P. (Checkered pawn to) h5 is allowed then,
+		because piece's nature changed.
 
 h3 Pawn moves
 
@@ -53,7 +61,8 @@ h2.stageDelimiter Stage 2
 p.warn This stage is not (and probably will never be) implemented.
 
 p.
-	During the game one of the two players can decide to take control of the checkered pieces.
+	During the game one of the two players can decide to take control of the
+	checkered pieces.
 	They thus become autonomous and vulnerable to being captured - stage 2 begins.
 	The other player is in charge of both the white and black pieces, and tries to
 	eliminate checkered pieces.
@@ -61,7 +70,8 @@ p.
 
 h4 Variant of stage 2
 p.
-	An observer could decide to join the game by taking the checkered pieces at any moment.
+	An observer could decide to join the game by taking the checkered pieces
+	at any moment.
 	It then becomes a chess game with three players, with some subtelties to be resolved.
 	It was tested in some (real life) games organised by the variant creator.
 
diff --git a/views/rules/Crazyhouse.pug b/views/rules/Crazyhouse.pug
index 3928c309..7a3d91b7 100644
--- a/views/rules/Crazyhouse.pug
+++ b/views/rules/Crazyhouse.pug
@@ -1,5 +1,6 @@
 p.boxed
-	| Every captured piece can be re-used by the capturer, landing it anywhere instead of moving a piece.
+	| Every captured piece can be re-used by the capturer,
+	| landing it anywhere instead of moving a piece.
 
 h3 Specifications
 
diff --git a/views/rules/Grand.pug b/views/rules/Grand.pug
index 67b849fe..acafca81 100644
--- a/views/rules/Grand.pug
+++ b/views/rules/Grand.pug
@@ -1,5 +1,6 @@
 p.boxed
-	| Two new pieces: marshall and cardinal. Bigger board. Orthodox rules with a few adaptations.
+	| Two new pieces: marshall and cardinal. Bigger board.
+	| Orthodox rules with a few adaptations.
 
 h3 Specifications
 
@@ -42,7 +43,8 @@ p As in the orthodox game, win by checkmating the king.
 
 p.
 	Note: I changed the author's starting position, to facilitate random start.
-	Thus the castling rule was introduced compared to the rules described on chessvariants.com.
+	Thus the castling rule was introduced compared to the rules described
+	on chessvariants.com.
 
 h3 Credits
 
diff --git a/views/rules/Magnetic.pug b/views/rules/Magnetic.pug
index ac90eeb6..7a0ac2ec 100644
--- a/views/rules/Magnetic.pug
+++ b/views/rules/Magnetic.pug
@@ -1,5 +1,6 @@
 p.boxed
-	| Each piece has a charge generating a magnetic field, attracting enemy pieces while repelling others.
+	| Each piece has a charge generating a magnetic field,
+	| attracting enemy pieces while repelling others.
 
 h3 Specifications
 
@@ -14,8 +15,10 @@ ul
 h3 Basics
 
 p
-	| Every piece has a charge generating a magnetic field, except the two kings which have a neutral charge.
-	| Pieces of the same color have let's say a positive charge, while the others have a negative charge.
+	| Every piece has a charge generating a magnetic field, except the two kings
+	| which have a neutral charge.
+	| Pieces of the same color have let's say a positive charge,
+	| while the others have a negative charge.
 	| So, after each move some pieces are attracted while others are repelled.
 
 figure.diagram-container
diff --git a/views/rules/Switching.pug b/views/rules/Switching.pug
index 3104734b..4d1dcd52 100644
--- a/views/rules/Switching.pug
+++ b/views/rules/Switching.pug
@@ -1,5 +1,6 @@
 p.boxed
-	| In addition to standard moves, a piece can be exchanged with an adjacent friendly unit.
+	| In addition to standard moves, a piece can be exchanged
+	| with an adjacent friendly unit.
 
 h3 Specifications
 
diff --git a/views/rules/Wildebeest.pug b/views/rules/Wildebeest.pug
index 8a14fb2f..dbfa3f52 100644
--- a/views/rules/Wildebeest.pug
+++ b/views/rules/Wildebeest.pug
@@ -1,5 +1,6 @@
 p.boxed
-	| Two new pieces: camel and wildebeest. Bigger board. Orthodox rules with a few adaptations.
+	| Two new pieces: camel and wildebeest. Bigger board.
+	| Orthodox rules with a few adaptations.
 
 h3 Specifications
 
diff --git a/views/rules/Zen.pug b/views/rules/Zen.pug
index 41140433..bc5f9f58 100644
--- a/views/rules/Zen.pug
+++ b/views/rules/Zen.pug
@@ -1,6 +1,7 @@
 p.boxed
 	| Zen chess only change one thing to the standard rules:
-	| a piece A captures an opponent piece B if and only if B can take A according to the orthodox rules.
+	| a piece A captures an opponent piece B if and only if B can take A
+	| according to the orthodox rules.
 
 figure.diagram-container
 	.diagram
@@ -26,16 +27,20 @@ figure.diagram-container
 		| fen:8/8/8/3r1k2/8/8/3K4/8:
 	figcaption The white king can take the rook
 
-p However, the king is attacked in the same way as in regular chess - and it's the only exception.
+p.
+	However, the king is attacked in the same way as in regular chess -
+	and it's the only exception.
 
 figure.diagram-container
 	.diagram
 		| fen:r7/2n5/1q6/5k2/8/8/K7/8:
-	figcaption The king cannot take on a8 because it's guarded by the knight: it's checkmate
+	figcaption.
+		The king cannot take on a8 because it's guarded by the knight: it's checkmate
 
 h3 Credits
 
 p.
-	Very few resources about this variation: #[a(href="http://play.chessvariants.org/erf/ZenChess.html") this webpage] 
+	Very few resources about this variation:
+	#[a(href="http://play.chessvariants.org/erf/ZenChess.html") this webpage] 
 	and #[a(href="http://www.pathguy.com/chess/ZenChess.htm") this one].
 	Ed Friedlander developed the Zen Chess applet from the link above.
diff --git a/views/variant.pug b/views/variant.pug
index 8ca9db49..561663c7 100644
--- a/views/variant.pug
+++ b/views/variant.pug
@@ -1,14 +1,15 @@
 extends layout
 
 block css
-	link(rel="stylesheet", href="//fonts.googleapis.com/icon?family=Material+Icons")
-	link(rel="stylesheet", href="/stylesheets/variant.css")
+	link(rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons")
+	link(rel="stylesheet" href="/stylesheets/variant.css")
 
 block content
 	.container#variantPage
 		.row
 			.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
-				h4.rulesTitle.text-center(v-on:click="displayRules=!displayRules") #{variant} Rules
+				h4.rulesTitle.text-center(v-on:click="displayRules=!displayRules")
+					| #{variant} Rules
 				my-rules(v-show="displayRules")
 			.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
 				my-game
-- 
2.44.0