Projects STRLCPY link-lock Commits b7f4cba4
🤬
  • Add hidden bookmark functionality

    Squashed commit of the following:
    
    commit 0e443f82b1933b4fb70eacd1fcf913dbc4ac1e18
    Author: Jacob Strieb <[email protected]>
    Date:   Sat Jan 2 00:34:54 2021 -0500
    
        Remove live.js
    
    commit f581bef0a14f2cac3016f33eded3732e07a6828b
    Author: Jacob Strieb <[email protected]>
    Date:   Sat Jan 2 00:34:24 2021 -0500
    
        Additional text changes from Amy
    
    commit b9162ff5dc2a8a138afe609293ff9a5ba53ea99a
    Author: Jacob Strieb <[email protected]>
    Date:   Sat Jan 2 00:05:38 2021 -0500
    
        Add advanced options for decrypt bookmark
    
    commit 2affa90588ae3dc1c6ad89b1a18b146e021bf9b5
    Author: Jacob Strieb <[email protected]>
    Date:   Fri Jan 1 19:41:57 2021 -0500
    
        Add more changes at the suggestion of Ari/Dad
    
    commit 7021f23710a272c5bf7473d468f28cd5dfbc968f
    Author: Jacob Strieb <[email protected]>
    Date:   Thu Dec 31 19:28:02 2020 -0500
    
        Apply additional Ari changes
    
    commit 8184f56cbbde024718be4cbe4336f3c3c23749d1
    Author: Jacob Strieb <[email protected]>
    Date:   Thu Dec 31 19:22:13 2020 -0500
    
        Add more Ari changes
    
    commit f50881ff497cf6c4ecfdaebcf369d865c0b945fe
    Author: Jacob Strieb <[email protected]>
    Date:   Thu Dec 31 18:24:11 2020 -0500
    
        Clarifications based on Ari and Mom user tests
    
    commit a4b2fdb8148e16759fe37418da819ee1382ec8a2
    Author: Jacob Strieb <[email protected]>
    Date:   Thu Dec 31 17:58:01 2020 -0500
    
        Use "decoy" bookmark terminology
    
    commit 700d946d22f50982408ed565cff5c1371c19e996
    Author: Jacob Strieb <[email protected]>
    Date:   Wed Dec 30 16:27:55 2020 -0500
    
        Add "Hidden Bookmark" button to main Link Lock page
    
    commit 6a0b71791491fdb997c2104f87dbcae35e379157
    Author: Jacob Strieb <[email protected]>
    Date:   Tue Dec 29 20:56:57 2020 -0500
    
        Add better description
    
    commit 781949df547dfc397ee2dca31283058638b0869a
    Author: Jacob Strieb <[email protected]>
    Date:   Tue Dec 29 01:31:45 2020 -0500
    
        Minor updates
    
    commit 294a6a2bd09571a9ae906915090f45af73c7f46f
    Author: Jacob Strieb <[email protected]>
    Date:   Mon Dec 28 23:14:18 2020 -0500
    
        Fix errors
    
    commit 39ed6ca172028f6650b70502089dca7c846e187b
    Author: Jacob Strieb <[email protected]>
    Date:   Mon Dec 28 23:08:15 2020 -0500
    
        Add main hidden bookmarks functionality
    
    commit 9b8fcf018a74c98e2d632ebd684a119c27567189
    Author: Jacob Strieb <[email protected]>
    Date:   Mon Dec 28 22:37:47 2020 -0500
    
        Minor improvements
    
        - Remove extraneous code
        - Add styling for outputs
    
    commit 585b9999430ef13b12d85bc9bd3b1b199634deb0
    Author: Jacob Strieb <[email protected]>
    Date:   Mon Dec 28 22:01:26 2020 -0500
    
        Add initial description and decrypt bookmarklet
    
    commit d63d5c4fd2f2c82e0e31e8bd06ff5516ad98d53f
    Author: Jacob Strieb <[email protected]>
    Date:   Mon Dec 28 19:12:58 2020 -0500
    
        Create hidden bookmarks page
    
        Add button to randomly get Wikipedia URLs
  • Loading...
  • Jacob Strieb committed 3 years ago
    b7f4cba4
    1 parent c63439a3
  • ■ ■ ■ ■ ■ ■
    create/create.js
    skipped 125 lines
    126 126   document.querySelector("#output").value = output;
    127 127   highlight("output");
    128 128   
     129 + // Adjust "Hidden Bookmark" link
     130 + document.querySelector("#bookmark").href = `https://jstrieb.github.io/link-lock/hidden/#${encrypted}`;
     131 + 
    129 132   // Adjust "Open in New Tab" link
    130 133   document.querySelector("#open").href = output;
    131 134   
    132 135   // Adjust "Get TinyURL" button
    133 136   document.querySelector("#tinyurl").value = output;
     137 + 
     138 + // Scroll to the bottom so the user sees where the bookmark was created
     139 + window.scrollTo({
     140 + top: document.body.scrollHeight,
     141 + behavior: "smooth",
     142 + });
    134 143  }
    135 144   
    136 145   
    skipped 31 lines
  • ■ ■ ■ ■ ■ ■
    create/index.html
    skipped 66 lines
    67 67   <!-- Project description -->
    68 68   <h1>Link Lock</h1>
    69 69   <div>
    70  - <p>Link Lock is a tool for encrypting and decrypting URLs. When a user visits an encrypted URL, they will be prompted for a password. If the password is correct, Link Lock retrieves the original URL and then redirects there. Otherwise, an error is displayed. Users can also add hints to display near the password prompt.</p>
    71  - <p>Each encrypted URL is stored entirely within the link generated by the application. As a result, users control all the data they create with Link Lock. Nothing is ever stored on a server, and there are no cookies, tracking, or signups. View <a target="_blank" href="https://github.com/jstrieb/link-lock">on GitHub</a> for more information, including translated versions.</p>
    72  - <p>Link Lock has many uses:</p>
     70 + <p>Link Lock is a tool for adding a password to a link; in other words, for encrypting and decrypting URLs. When a user visits an encrypted URL, they will be prompted for a password. If the password is correct, Link Lock sends them to the hidden website. Otherwise, an error is displayed. Users can also add hints to remind them of the password.</p>
     71 + <p>Each encrypted URL is stored entirely within the link generated by this application. As a result, users control all the data they create with Link Lock. Nothing is ever stored on a server, and there are no cookies, tracking, or signups. View <a target="_blank" href="https://github.com/jstrieb/link-lock">on GitHub</a> for more information, including translated versions.</p>
     72 + <p>Link Lock has many uses, for example:</p>
    73 73   <ul>
    74 74   <li><a target="_blank" href="https://jstrieb.github.io/link-lock/#eyJ2IjoiMC4wLjEiLCJlIjoiU1ZBemc0NUVoeXJMR1hXYmRUMXpLSFFIa0hiR2F3SzlMaWZzWW5SL0ZiaGp1cnZqMGg5VTE0bG9kVGs3S3B0TjdhcjZ2T3FvRjJLNkxMcDByL05PZE5nUTJ3UlhVOWM2RmFJdXNGajdrNkFkTC82OVJ6dmlFV2R0dWVacFM1dS9SN2w4L3Mzc1pMTVJNeHdhTVhVenYxTjZUVkdWTGloaXc3ZXlGY093Nkp2ZVN3aGl0OW9XWW84Yk9CMkpkTTF4ZnFRSGExbEoiLCJoIjoi8J+lkSIsImkiOiI5L3pmdHFmeHdoWFh4bDc4In0=">Store private bookmarks on a shared computer</a> - Password: avocado</li>
    75 75   <li>Add a password to shared Dropbox or Google Drive links</li>
    skipped 9 lines
    85 85   <!-- Main form -->
    86 86   <div class="form">
    87 87   <div class="labeled-input">
    88  - <label for="url">link</label>
    89  - <input type="url" id="url" placeholder="https://" oninvalid="if (!this.validity.customError) this.setCustomValidity('Please enter a valid URL. Make sure to include \'http://\' or \'https://\' at the beginning.')" oninput="this.setCustomValidity('')" autofocus required />
     88 + <label for="url">secret link</label>
     89 + <input type="url" id="url" placeholder="https://" oninvalid="if (!this.validity.customError) this.setCustomValidity('Please enter a valid URL. Make sure to include \'http://\' or \'https://\' at the beginning.')" oninput="this.setCustomValidity('')" required />
    90 90   </div>
    91 91   <div class="labeled-input hint">
    92 92   <label for="url">hint (optional)</label>
    skipped 34 lines
    127 127   <label for="output">output</label>
    128 128   <input type="text" id="output" readonly/>
    129 129   <button id="copy" onclick="onCopy('output')">Copy</button>
     130 + <a href="../hidden" id="bookmark" target="_blank"><button>Create Hidden Bookmark</button></a>
    130 131   <a href="" id="open" target="_blank"><button>Open in New Tab</button></a>
    131 132   <!-- Special incantation to make TinyURL work -->
    132 133   <form action="http://tinyurl.com/create.php" method="get" target="_blank" style="display: inline;">
    skipped 16 lines
  • ■ ■ ■ ■ ■ ■
    hidden/hidden.js
     1 +/**
     2 + * Created by Jacob Strieb
     3 + * December 2020
     4 + */
     5 + 
     6 + 
     7 + 
     8 +/*******************************************************************************
     9 + * Helper Functions
     10 + ******************************************************************************/
     11 + 
     12 +// Display a message in the "alert" area
     13 +function error(text) {
     14 + const alertText = document.querySelector(".alert");
     15 + alertText.innerHTML = text;
     16 + alertText.style.opacity = 1;
     17 +}
     18 + 
     19 + 
     20 + 
     21 +/*******************************************************************************
     22 + * Main UI Functions
     23 + ******************************************************************************/
     24 + 
     25 +async function onHide() {
     26 + // Fail if the b64 library or API was not loaded
     27 + if (!("b64" in window && "apiVersions" in window)) {
     28 + error("Important libraries not loaded!");
     29 + return;
     30 + }
     31 + 
     32 + // Try to get page data from the input hidden URL if possible
     33 + let urlText = document.querySelector("#encrypted-url").value;
     34 + let hiddenUrl;
     35 + try {
     36 + hiddenUrl = new URL(urlText);
     37 + } catch {
     38 + error("Hidden URL is not valid. Make sure it includes \"https://\" too!");
     39 + return;
     40 + }
     41 + 
     42 + // Try to get page data from the input bookmark URL if possible
     43 + urlText = document.querySelector("#bookmark-url").value;
     44 + let bookmarkUrl;
     45 + try {
     46 + bookmarkUrl = new URL(urlText);
     47 + } catch {
     48 + error("Bookmark URL is not valid. Make sure it includes \"https://\" too!");
     49 + return;
     50 + }
     51 + 
     52 + // Ensure that the Link Lock URL is valid
     53 + let hash = hiddenUrl.hash.slice(1);
     54 + try {
     55 + let _ = JSON.parse(b64.decode(hash));
     56 + } catch {
     57 + error("The hidden link appears corrupted. It must be a <a href=\"https://jstrieb.github.io/link-lock\">Link Lock</a> URL.");
     58 + return;
     59 + }
     60 + 
     61 + let output = document.querySelector("#output");
     62 + 
     63 + // Set the output href to be the hidden URL with the old URL hash
     64 + bookmarkUrl.hash = hiddenUrl.hash;
     65 + output.setAttribute("href", bookmarkUrl.toString());
     66 + 
     67 + // Enable clicking and dragging the output bookmark
     68 + output.setAttribute("aria-disabled", "false");
     69 + 
     70 + // Change the output bookmark title to match the user input
     71 + output.innerText = document.querySelector("#bookmark-title").value;
     72 + 
     73 + error("Bookmark created below.");
     74 + 
     75 + // Scroll to the bottom so the user sees where the bookmark was created
     76 + window.scrollTo({
     77 + top: document.body.scrollHeight,
     78 + behavior: "smooth",
     79 + });
     80 +}
     81 + 
     82 +function onChangeDecrypt() {
     83 + let newUrl;
     84 + try {
     85 + const newUrlInput = document.querySelector("#decrypt-bookmark-disguise");
     86 + const _ = new URL(newUrlInput.value);
     87 + newUrl = newUrlInput.value;
     88 + } catch (_) {
     89 + return;
     90 + }
     91 + 
     92 + const decryptBookmark = document.querySelector("#decrypt-bookmark");
     93 + decryptBookmark.href = decryptBookmark.href.replace(/replace\("[^"]*"\)/, `replace("${newUrl}")`);
     94 + console.log(decryptBookmark.href);
     95 +}
     96 + 
     97 +async function randomLink() {
     98 + let page = await fetch("https://en.wikipedia.org/w/api.php?"
     99 + + "format=json"
     100 + + "&action=query"
     101 + + "&generator=random"
     102 + + "&grnnamespace=0" /* Only show articles, not users */
     103 + + "&prop=info"
     104 + + "&inprop=url" /* Get URLs, they're not there by default */
     105 + + "&origin=*") /* https://mediawiki.org/wiki/API:Cross-site_requests */
     106 + .then(r => r.json())
     107 + .then(d => {
     108 + let pages = d.query.pages;
     109 + return pages[Object.keys(pages)[0]];
     110 + });
     111 + 
     112 + document.querySelector("#bookmark-url").value = await page.canonicalurl;
     113 + document.querySelector("#bookmark-title").value = await page.title;
     114 +}
     115 + 
     116 +function main() {
     117 + if (window.location.hash) {
     118 + document.querySelector("#encrypted-url").value =
     119 + `https://jstrieb.github.io/link-lock/${window.location.hash}`;
     120 + 
     121 + window.location.hash = "";
     122 + }
     123 +}
     124 + 
  • ■ ■ ■ ■ ■ ■
    hidden/index.html
     1 +<!DOCTYPE html>
     2 +<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
     3 + 
     4 +<head>
     5 + <!-- Metadata -->
     6 + <meta charset="utf-8" />
     7 + <meta name="author" content="Jacob Strieb" />
     8 + <meta name="description" content="Create hidden, secret bookmarks for locked, password-protected links." />
     9 + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
     10 + 
     11 + <link rel="shortcut icon" type="image/x-icon" href="../favicon.ico">
     12 + 
     13 + <title>Create Hidden Bookmarks</title>
     14 + 
     15 + <!-- Styles -->
     16 + <link rel="stylesheet" href="../style.css" type="text/css" />
     17 + 
     18 + <!-- Scripts -->
     19 + <script type="text/javascript" src="../b64.js"></script>
     20 + <script type="text/javascript" src="../api.js"></script>
     21 + <script type="text/javascript" src="hidden.js"> </script>
     22 +</head>
     23 + 
     24 +<body onload="main()">
     25 + <!-- View on GitHub ribbon -->
     26 + <a href="https://github.com/jstrieb/link-lock" target="_blank">
     27 + <img class="ribbon" src="../corner-ribbon-minified.svg" alt="View on GitHub" />
     28 + </a>
     29 + 
     30 + <!-- Explanation for those who do not have JavaScript enabled -->
     31 + <noscript>
     32 + <div class="red-border">
     33 + <p>If you are seeing this, it means that you have JavaScript disabled. Please enable JavaScript to access the locked link.</p>
     34 + 
     35 + <p>This application is entirely programmed in JavaScript. This was done intentionally, so that all encryption and decryption happens client-side. This means the code runs as a distributed application, relying only on GitHub Pages for infrastructure. It also means that no data about locked links is ever stored on a server. The code is designed to be auditable so users can investigate what is happening behind the scenes.</p>
     36 + 
     37 + <p>If you still want to run the application, I encourage you to clone the <a href="https://github.com/jstrieb/link-lock">source code on GitHub</a>. That way you can disable JavaScript only for trusted files on your local machine.</p>
     38 + </div>
     39 + </noscript>
     40 + 
     41 + <!-- Display errors in a big red box -->
     42 + <div class="error red-border" style="display: none">
     43 + <p id="errortext">Error</p>
     44 + <button onclick="main()">Try again</button>
     45 + <a href="https://jstrieb.github.io/link-lock"><button>Lock a link</button></a>
     46 + </div>
     47 + 
     48 + <h1>Create Hidden Bookmarks</h1>
     49 + <p>It is possible to protect bookmarks with a password using <a href="https://jstrieb.github.io/link-lock" target="_blank">Link Lock</a>, but a link that needs a password may appear suspicious to someone else seeing it. Hidden bookmarks solve this problem.</p>
     50 + <p>Hidden bookmarks are disguised to be identical to normal bookmarks, with one exception: clicking them in the right order will open a hidden link. <b>To open the hidden link, click the disguised bookmark first, and then click the decrypt bookmark next.</b> The same decrypt bookmark works for all disguised bookmarks.</p>
     51 + 
     52 + <p>Here is how to create hidden bookmarks:</p>
     53 + <ol>
     54 + <li>
     55 + Drag the "decrypt" bookmark below to your bookmarks bar.
     56 + 
     57 + <ul><li style="margin-bottom: 0.5em;">Clicking the decrypt bookmark goes to <code>gmail.com</code> unless the current page is a disguised link. Use the "advanced" options to have it go somewhere else instead.</li></ul>
     58 + 
     59 + <style>
     60 + .inline-button-container {
     61 + display: flex;
     62 + width: 100%;
     63 + }
     64 + 
     65 + .inline-button-container * {
     66 + margin: 5px;
     67 + }
     68 + 
     69 + .inline-button-container input {
     70 + margin-left: 0;
     71 + }
     72 + 
     73 + #advanced-label {
     74 + margin-top: 0em;
     75 + }
     76 + 
     77 + code {
     78 + font-family: monospace, monospace;
     79 + padding: 0 3px;
     80 + }
     81 + 
     82 + .advanced p {
     83 + margin-top: 0;
     84 + margin-bottom: 0.5em;
     85 + }
     86 + 
     87 + button {
     88 + white-space: nowrap;
     89 + }
     90 + </style>
     91 + <details>
     92 + <summary id="advanced-label">advanced</summary>
     93 + <div class="advanced" id="advanced">
     94 + <!--
     95 + <p>Input a new location decoy location for the decrypt bookmark:</p>
     96 + -->
     97 + <label for="decrypt-bookmark-disguise">decoy location for decrypt bookmark</label>
     98 + <div class="inline-button-container">
     99 + <input type="text" id="decrypt-bookmark-disguise" value="https://gmail.com" />
     100 + <button onclick="onChangeDecrypt()">Change Location</button>
     101 + <div>
     102 + </div>
     103 + </details>
     104 + 
     105 + <p><a class="bookmark" id="decrypt-bookmark" onclick="return false;" href='javascript:(() => {
     106 + 
     107 +var b64 = (() => {
     108 + 
     109 + function generateIndexDict(a) {
     110 + let result = {};
     111 + for (let i = 0; i < a.length; i++) {
     112 + result[a[i]] = i;
     113 + }
     114 + return result;
     115 + }
     116 + 
     117 + const _a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     118 + const _aRev = generateIndexDict(_a);
     119 + _aRev["-"] = _aRev["+"];
     120 + _aRev["_"] = _aRev["/"];
     121 + 
     122 + const _enc = new TextEncoder("utf-8");
     123 + const _dec = new TextDecoder("utf-8");
     124 + 
     125 + return {
     126 + 
     127 + decode: function(s) {
     128 + return this.binaryToAscii(this.base64ToBinary(s));
     129 + },
     130 + 
     131 + encode: function(s) {
     132 + return this.binaryToBase64(this.asciiToBinary(s));
     133 + },
     134 + 
     135 + asciiToBinary: function(text) {
     136 + return _enc.encode(text);
     137 + },
     138 + 
     139 + 
     140 + binaryToAscii: function(binary) {
     141 + return _dec.decode(binary);
     142 + },
     143 + 
     144 + 
     145 + binaryToBase64: function(originalBytes) {
     146 + let length = originalBytes.length;
     147 + let added = (length % 3 == 0) ? 0 : (3 - length % 3);
     148 + let bytes = new Uint8Array(length + added);
     149 + bytes.set(originalBytes);
     150 + 
     151 + let output = "";
     152 + for (let i = 0; i < bytes.length; i += 3) {
     153 + output += _a[ bytes[i] >>> 2 ];
     154 + output += _a[ ((bytes[i] & 0x3) << 4) | (bytes[i + 1] >>> 4) ];
     155 + output += _a[ ((bytes[i + 1] & 0xF) << 2) | (bytes[i + 2] >>> 6) ];
     156 + output += _a[ bytes[i + 2] & 0x3F ];
     157 + }
     158 + 
     159 + if (added > 0) {
     160 + output = output.slice(0, -added) + ("=".repeat(added));
     161 + }
     162 + 
     163 + return output;
     164 + },
     165 + 
     166 + base64ToBinary: function(s) {
     167 + let bytes = [];
     168 + 
     169 + if (s.length % 4 == 1) {
     170 + throw "Invalid base64 input";
     171 + } else if (s.length % 4 != 0) {
     172 + s += "=".repeat(4 - (s.length % 4));
     173 + }
     174 + 
     175 + for (let i = 0; i <= (s.length - 4); i += 4) {
     176 + for (let j = 0; j < 4; j++) {
     177 + if (s[i + j] != "=" && !(s[i + j] in _aRev)) {
     178 + throw "Invalid base64 input";
     179 + } else if (s[i + j] == "=" && Math.abs(s.length - (i + j)) > 2) {
     180 + throw "Invalid base64 input";
     181 + }
     182 + }
     183 + 
     184 + bytes.push((_aRev[s[i]] << 2) | (_aRev[s[i + 1]] >>> 4));
     185 + if (s[i + 2] != "=") {
     186 + bytes.push(((_aRev[s[i + 1]] & 0xF) << 4) | (_aRev[s[i + 2]] >>> 2));
     187 + }
     188 + if (s[i + 3] != "=") {
     189 + bytes.push(((_aRev[s[i + 2]] & 0x3) << 6) | _aRev[s[i + 3]]);
     190 + }
     191 + }
     192 + 
     193 + return new Uint8Array(bytes);
     194 + }
     195 + 
     196 + }
     197 +})();
     198 + 
     199 +const hash = window.location.hash.slice(1);
     200 +try {
     201 + const decoded = b64.decode(hash);
     202 + const params = JSON.parse(decoded);
     203 + window.location.href = "https://jstrieb.github.io/link-lock/" + window.location.hash;
     204 +} catch {
     205 + window.location.replace("https://gmail.com");
     206 +}
     207 + 
     208 +})();'>Decrypt</a></p>
     209 + </li>
     210 + <li>It may be a good idea to rename the decrypt bookmark to "Gmail" by right clicking, and either clicking "Edit" or "Properties."</li>
     211 + <li>
     212 + Fill in the hidden URL below (if it is not already filled in). Then, fill in the disguised bookmark name and link.
     213 + <ul><li>There is a button to generate random disguise links if you do not want to pick your own.</li></ul>
     214 + </li>
     215 + <li>Press the button to create the bookmark. Once created, drag the disguised bookmark to your bookmarks bar.</li>
     216 + </ol>
     217 + 
     218 + <hr />
     219 + 
     220 + <div class="form">
     221 + <label for="encrypted-url">hidden url</label>
     222 + <input type="url" id="encrypted-url" oninput="document.querySelector('.alert').style.opacity = 0" />
     223 + <label for="bookmark-title">disguised bookmark name</label>
     224 + <input type="text" id="bookmark-title" />
     225 + <label for="bookmark-url">bookmark disguise url</label>
     226 + <input type="url" id="bookmark-url" required />
     227 + <button onclick="onHide()">Create Disguised Bookmark</button>
     228 + <button onClick="randomLink()">Use A Random Disguise Link</button>
     229 + <p class="alert">INVISIBLE</p>
     230 + </div>
     231 + 
     232 + <hr />
     233 + 
     234 + <!-- Output area -->
     235 + <style>
     236 + .output label[for="output"] {
     237 + margin-bottom: 1em;
     238 + }
     239 + </style>
     240 + <div class="output">
     241 + <label for="output">output</label>
     242 + <!-- (Leg) disabled (by acid) -->
     243 + <a class="bookmark" href="" onclick="return false;" aria-disabled="true" id="output">No Disguised Bookmark Created Yet</a>
     244 + <p>Drag the disguised bookmark above to the bookmarks bar.</p>
     245 + <p>To access the hidden link, click the disguised bookmark, then the decrypt bookmark (which may have been renamed to "Gmail").</p>
     246 + </div>
     247 + 
     248 + <!-- Page footer -->
     249 + <footer>
     250 + <hr />
     251 + <p class="copyright">Created by <a href="https://jstrieb.github.io">Jacob Strieb</a>.</p>
     252 + </footer>
     253 +</body>
     254 +</body>
     255 + 
     256 +</html>
     257 + 
  • ■ ■ ■ ■ ■ ■
    style.css
    skipped 16 lines
    17 17   padding: 15px;
    18 18   display: block;
    19 19   margin: auto;
    20  - max-width: 80ch;
     20 + max-width: 66ch;
    21 21  }
    22 22   
    23 23  p {
    24 24   text-align: justify;
    25 25  }
    26 26   
     27 +ul, ol {
     28 + padding-left: 1em;
     29 +}
     30 + 
     31 +ol li {
     32 + margin-bottom: 1em;
     33 +}
     34 + 
    27 35  button {
    28 36   padding: 11px;
    29 37   background: rgb(95, 158, 160);
    30 38   color: white;
    31 39   border: 0.5px solid white;
    32 40   border-radius: 5px;
    33  - font-weight: bold;
    34 41   margin-top: 5px;
    35 42   margin-right: 5px;
    36 43   cursor: pointer;
     44 + font-weight: bold;
    37 45  }
    38 46   
    39 47  label {
    skipped 38 lines
    78 86   
    79 87  footer p {
    80 88   text-align: center;
     89 +}
     90 + 
     91 +*[aria-disabled="true"] {
     92 + pointer-events: none;
     93 + cursor: not-allowed;
     94 + user-select: none;
    81 95  }
    82 96   
    83 97   
    skipped 86 lines
    170 184   transition: opacity 0.25s;
    171 185  }
    172 186   
     187 + 
     188 +/* Bookmarks that look like buttons, but different */
     189 +.bookmark {
     190 + padding: 11px;
     191 + background: none;
     192 + color: rgb(95, 158, 160);
     193 + border: 3px solid;
     194 + border-radius: 5px;
     195 + font-weight: bold;
     196 + text-decoration: none;
     197 + margin-top: 5px;
     198 + margin-right: 5px;
     199 + cursor: pointer;
     200 + display: inline-block;
     201 + margin: auto;
     202 + text-align: center;
     203 +}
     204 + 
     205 +.bookmark:hover {
     206 + background: rgb(95, 158, 160);
     207 + color: white;
     208 + border: 3px solid rgb(95, 158, 160);
     209 +}
     210 + 
     211 +.bookmark[aria-disabled="true"] {
     212 + background: rgba(235, 235, 235, 1);
     213 + color: gray;
     214 +}
     215 + 
Please wait...
Page is in error, reload to recover