diff --git a/img/save.svg b/img/save.svg
new file mode 100755
index 0000000..7d81658
--- /dev/null
+++ b/img/save.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<path style="fill:#DCF4FF;" d="M512,492H0V20h403.213L512,128.787V492z"/>
+<polygon style="fill:#CCEFFF;" points="403.213,20 256,20 256,492 512,492 512,128.787 "/>
+<path style="fill:#0A4EAF;" d="M403.213,20H396v175H116V20H0v472h116V315c0-16.568,13.431-30,30-30h220c16.569,0,30,13.432,30,30
+	v177h116V128.787L403.213,20z"/>
+<g>
+	<rect x="296" y="20" style="fill:#063E8B;" width="40" height="115"/>
+	<path style="fill:#063E8B;" d="M403.213,20H396v175H256v90h110c16.569,0,30,13.432,30,30v177h116V128.787L403.213,20z"/>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
diff --git a/style.css b/style.css
index 0cd223c..45c4980 100644
--- a/style.css
+++ b/style.css
@@ -236,7 +236,7 @@ input[type="number"]::-webkit-calendar-picker-indicator {
     cursor: not-allowed;
 }
 
-.tools button.warning {
+.tools button.warning:hover {
     background: #ff5050;
     border-color: #ff5050;
 }
diff --git a/style.css~ b/style.css~
new file mode 100644
index 0000000..0cd223c
--- /dev/null
+++ b/style.css~
@@ -0,0 +1,292 @@
+body {
+    font-family: sans-serif;
+    width: min-content;
+    margin: auto;
+}
+
+h1 {
+    text-align: center;
+    margin: 1rem;
+}
+
+section, div, footer {
+    align-items: center;
+    align-items: center;
+    justify-content: center;
+    text-align: center;
+    margin: 0.8rem 0;
+}
+
+div {
+    display: flex;
+    flex-wrap: wrap;
+    row-gap: 0.5rem;
+    column-gap: 0.3rem;
+    margin: 0.5rem auto;
+}
+
+
+.grid {
+    border-spacing: 0;
+    border: 1px solid black;
+    border-radius: 6px;
+    margin: auto;
+    cursor: url(img/ink-pen.svg) 2 22, text;
+}
+
+.grid td, tr {
+    padding: 0;
+}
+
+.grid tr:first-child td:first-child {
+    border-top-left-radius: 5px;
+}
+
+.grid tr:first-child td:first-child input {
+    border-top-left-radius: 4px;
+}
+
+.grid tr:first-child td:last-child {
+    border-top-right-radius: 5px;
+}
+
+.grid tr:first-child td:last-child input {
+    border-top-right-radius: 4px;
+}
+
+.grid tr:last-child td:first-child {
+    border-bottom-left-radius: 5px;
+}
+
+.grid tr:last-child td:first-child > input {
+    border-bottom-left-radius: 4px;
+}
+
+.grid tr:last-child td:last-child {
+    border-bottom-right-radius: 5px;
+}
+
+.grid tr:last-child td:last-child input {
+    border-bottom-right-radius: 4px;
+}
+
+.grid tr:nth-child(3n+1) td {
+    border-top: 1px solid black;
+}
+
+.grid tr:nth-child(3n+2) td {
+    border-top: 1px solid grey;
+    border-bottom: 1px solid grey;
+}
+
+.grid tr:nth-child(3n) td {
+    border-bottom: 1px solid black;
+}
+
+.grid td:nth-child(3n+1) {
+    border-left: 1px solid black;
+}
+
+.grid td:nth-child(3n+2) {
+    border-left: 1px solid grey;
+    border-right: 1px solid grey;
+}
+
+.grid td:nth-child(3n+3) {
+    border-right: 1px solid black;
+}
+
+
+.grid input {
+    width: 2.5rem;
+    height: 2.5rem;
+    font-size: 1.5rem;
+    border: 0;
+    border-radius: 0;
+    padding: 0;
+    text-align: center;
+    transition: background 0.5s;
+    -moz-appearance: textfield;
+}
+
+input[type="number"]::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+    -webkit-appearance: none;
+    margin: 0;
+}
+
+input[type="number"]::-webkit-calendar-picker-indicator {
+    display: none;
+}
+
+.grid input:enabled {
+    background: white;
+    color: darkblue;
+    cursor: inherit;
+}
+
+.grid input.pencil,
+.grid input::placeholder {
+    color: #666 !important;
+    font-size: 0.9rem !important;
+}
+
+.grid input:disabled {
+    color: white !important;
+    background: #6666ff !important;
+    cursor: not-allowed !important;
+}
+
+.grid input.forbidden:enabled {
+    background: #ffffaa !important;
+    cursor: not-allowed;
+}
+
+.grid input.same-value:enabled {
+    background: #ffff33;
+    cursor: not-allowed !important;
+}
+
+.grid input.forbidden:disabled {
+    color: #ffffaa;
+    background: #6666ff;
+}
+
+.grid input.same-value:disabled,
+.tools button.same-value:enabled,
+.tools input:enabled:checked + label {
+    color: #ffffaa !important;
+    background: #00b359 !important;
+}
+
+.grid input.one-candidate {
+    cursor: help !important;
+}
+
+
+.tools button,
+.tools input + label {
+    color: white;
+    text-shadow: -1px -1px #5b6c9e;
+    background: #8ca6f2;
+    border: 2px outset #8ca6f2;
+    border-radius: 4px;
+    font-size: 1.3rem;
+    min-width:20px;
+    padding: 4px 5px 5px 5px;
+    margin:  0px 1px 1px 1px;
+    cursor: pointer;
+}
+
+.tools img {
+    display: block;
+    width: 24px;
+    height: 24px;
+}
+
+.tools input {
+    position: fixed;
+    opacity: 0;
+    width: 0;
+    pointer-events: none;
+}
+
+.tools button:enabled:hover,
+.tools button:enabled:focus,
+.tools input:enabled:hover + label,
+.tools input:enabled:focus + label {
+    border-width: 1px;
+    border-style: outset;
+    padding: 5px 5px 5px 6px;
+    margin:  1px 1px 1px 2px;
+}
+
+.tools input:enabled:checked:hover + label,
+.tools input:enabled:checked:focus + label {
+    border-width: 3px;
+    border-style: inset;
+    padding: 4px 2px 2px 5px;
+    margin:  1px 1px 1px 2px;
+}
+
+.tools input:enabled:checked + label {
+    text-shadow: -1px -1px #005f2f;
+    border: 2px inset #00b359;
+    background: #00b359;
+    padding: 4px 4px 4px 5px;
+    margin:  1px 1px 0px 2px;
+}
+
+.tools button:enabled:active,
+.tools input:enabled:active + label {
+    border-width: 4px !important;
+    border-style: inset !important;
+    padding: 4px 0px 0px 5px !important;
+    margin:  0px 1px 0px 2px !important;
+}
+
+.tools button:disabled,
+.tools input:disabled + label {
+    text-shadow: -1px -1px #555;
+    color: #ccc;
+    background: darkgrey;
+    border: 1px outset darkgrey;
+    padding: 5px 6px 6px 6px;
+    margin:  0px 1px 1px 1px;
+    cursor: not-allowed;
+}
+
+.tools button.warning {
+    background: #ff5050;
+    border-color: #ff5050;
+}
+
+
+.context-menu {
+    display: none;
+    z-index: 1000;
+    position: absolute;
+    overflow: hidden;
+    border: 1px solid #CCC;
+    white-space: nowrap;
+    font-family: sans-serif;
+    background: #EEE;
+    color: #333;
+    border-top-right-radius: 3px;
+    border-bottom-left-radius: 3px;
+    border-bottom-right-radius: 3px;
+    padding: 0;
+    margin: 0;
+}
+
+.context-menu li {
+    padding: 6px 10px;
+    cursor: default;
+    list-style-type: none;
+    transition: all .3s ease;
+    font-size: 1rem;
+}
+
+.context-menu li:hover {
+    background-color: #DEF;
+}
+
+.context-menu li.error {
+    color: #888
+}
+
+.context-menu li.error:hover {
+    background-color: #EEE;
+}
+
+
+a {
+    text-decoration: none;
+}
+
+
+.credits {
+    font-size: 0.8rem;
+    margin: 0;
+}
+
diff --git a/sudoku.js b/sudoku.js
index c190432..b556e2e 100644
--- a/sudoku.js
+++ b/sudoku.js
@@ -10,6 +10,7 @@ let suggestionTimer = null
 let valueToInsert = ""
 let history = []
 let accessKeyModifiers = "AccessKey+"
+let changesToSave = false
 
 function shuffle(iterable) {
     array = Array.from(iterable)
@@ -142,6 +143,7 @@ function onclick() {
 function oninput() {
     history.push({ box: this, value: this.previousValue, placeholder: this.previousPlaceholder })
     undoButton.disabled = false
+    changesToSave = true
     if (pencilRadio.checked) {
         this.value = Array.from(new Set(this.value)).sort().join("")
         this.previousValue = ""
@@ -157,17 +159,10 @@ function oninput() {
 }
 
 function refreshBox(box) {
-    saveGame()
     checkBox(box)
     refreshUI()
 }
 
-function saveGame() {
-    let saveGame = boxes.map(box => box.value || UNKNOWN).join("")
-    localStorage[location.pathname] = saveGame
-    fixGridLink.href = saveGame
-}
-
 function checkBox(box) {
     box.neighbourhood.concat([box]).forEach(neighbour => {
         searchCandidatesOf(neighbour)
@@ -200,6 +195,7 @@ function checkBox(box) {
     } else { // Errors on grid
         box.form.reportValidity()
     }
+}
 
 function refreshUI() {
     enableRadio()
@@ -316,6 +312,20 @@ function restart() {
     }
 }
 
+function save() {
+    let saveGame = boxes.map(box => box.value || UNKNOWN).join("")
+    localStorage[location.pathname] = saveGame
+    fixGridLink.href = saveGame
+    changesToSave = false
+}
+
+window.onbeforeunload = function(event) {
+    if (changesToSave) {
+        event.preventDefault()
+        event.returnValue = ""
+    }
+}
+
 function showSuggestion() {
     const easyBoxes = boxes.filter(box => box.value == "" && box.candidates.size == 1)
     if (easyBoxes.length) {
diff --git a/sudoku.php b/sudoku.php
index 52eb670..42fc230 100644
--- a/sudoku.php
+++ b/sudoku.php
@@ -101,6 +101,7 @@
                 <input type='radio' id='eraserRadio' name='tool' onclick='grid.style.cursor = "url(img/eraser.svg) 2 22, auto"'/><label for='eraserRadio' title='Effacer une case'><img src='img/eraser.svg' alt='Gomme'/></label>
                 <button type='button' class='warning' onclick='restart()' title='Recommencer'><img src='img/restart.svg' alt='Recommencer'/></button>
                 <button id='undoButton' type='button' onclick='undo()' disabled title='Annuler' accesskey='z'><img src='img/undo.svg' alt='Annuler'/></button>
+                <button id='undoButton' type='button' onclick='save()' title='Sauvegarder' accesskey='s'><img src='img/save.svg' alt='Disquette'/></button>
             </div>
         </section>
         <section>