Add PIN lock screen and block search engines

DECK and HST frontends:
- Add 4-digit PIN lock screen (default: 1234)
- PIN stored in sessionStorage (persists during browser session)
- Keypad with keyboard support
- Add meta robots noindex/nofollow tags
- Created robots.txt to disallow all crawlers

Security note: This is client-side only, not cryptographically secure,
but sufficient to prevent casual access and search engine indexing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ARCHITECT
2026-01-17 23:28:09 +00:00
parent 26c9f1f402
commit c152cacb90
2 changed files with 363 additions and 3 deletions

View File

@@ -5,6 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>deck</title> <title>deck</title>
<meta name="description" content="DECK Tag Management System"> <meta name="description" content="DECK Tag Management System">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<!-- <!--
DECK FRONTEND v4.6 - EventBus decoupling DECK FRONTEND v4.6 - EventBus decoupling
@@ -618,10 +620,88 @@ body {
scroll-behavior: auto !important; scroll-behavior: auto !important;
} }
} }
/* -----------------------------------------------------------------------------
* 9. LOCK SCREEN
* ----------------------------------------------------------------------------- */
.lock-screen {
position: fixed;
inset: 0;
background: var(--bg-primary);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
}
.lock-screen.hidden { display: none; }
.lock-logo { font-size: 2rem; font-weight: 700; color: var(--accent); margin-bottom: 2rem; letter-spacing: 0.1em; }
.lock-dots { display: flex; gap: 12px; margin-bottom: 2rem; }
.lock-dot {
width: 14px; height: 14px;
border-radius: 50%;
background: var(--border-color);
transition: all 0.15s ease;
}
.lock-dot.filled { background: var(--accent); }
.lock-dot.error { background: #ff4444; animation: shake 0.3s ease; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-4px); }
75% { transform: translateX(4px); }
}
.lock-keypad {
display: grid;
grid-template-columns: repeat(3, 70px);
gap: 12px;
}
.lock-key {
width: 70px; height: 70px;
border-radius: 50%;
border: 2px solid var(--border-color);
background: transparent;
color: var(--text-primary);
font-size: 1.5rem;
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
justify-content: center;
}
.lock-key:hover { border-color: var(--accent); background: var(--bg-hover); }
.lock-key:active { transform: scale(0.95); }
.lock-key.empty { visibility: hidden; }
.lock-key.backspace { font-size: 1.2rem; }
</style> </style>
</head> </head>
<body> <body>
<!-- LOCK SCREEN -->
<div id="lock-screen" class="lock-screen">
<div class="lock-logo">DECK</div>
<div class="lock-dots">
<div class="lock-dot"></div>
<div class="lock-dot"></div>
<div class="lock-dot"></div>
<div class="lock-dot"></div>
</div>
<div class="lock-keypad">
<button class="lock-key" data-key="1">1</button>
<button class="lock-key" data-key="2">2</button>
<button class="lock-key" data-key="3">3</button>
<button class="lock-key" data-key="4">4</button>
<button class="lock-key" data-key="5">5</button>
<button class="lock-key" data-key="6">6</button>
<button class="lock-key" data-key="7">7</button>
<button class="lock-key" data-key="8">8</button>
<button class="lock-key" data-key="9">9</button>
<button class="lock-key empty"></button>
<button class="lock-key" data-key="0">0</button>
<button class="lock-key backspace" data-key="back"></button>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════════ <!-- ══════════════════════════════════════════════════════════════════════════
HTML STRUCTURE HTML STRUCTURE
══════════════════════════════════════════════════════════════════════════ --> ══════════════════════════════════════════════════════════════════════════ -->
@@ -730,6 +810,91 @@ body {
* EventBus pattern for module decoupling * EventBus pattern for module decoupling
*/ */
// =============================================================================
// 0. LOCK SCREEN
// =============================================================================
const Lock = {
PIN: "1234", // PIN code - can be changed
entered: "",
init() {
if (sessionStorage.getItem("deck_unlocked") === "true") {
this.unlock();
return true;
}
this.bind();
return false;
},
bind() {
const keypad = document.querySelector(".lock-keypad");
if (!keypad) return;
keypad.addEventListener("click", (e) => {
const key = e.target.closest(".lock-key");
if (!key) return;
const value = key.dataset.key;
if (value === "back") {
this.entered = this.entered.slice(0, -1);
} else if (value && this.entered.length < 4) {
this.entered += value;
}
this.updateDots();
if (this.entered.length === 4) {
setTimeout(() => this.check(), 150);
}
});
// Keyboard support
document.addEventListener("keydown", (e) => {
if (document.getElementById("lock-screen")?.classList.contains("hidden")) return;
if (e.key >= "0" && e.key <= "9" && this.entered.length < 4) {
this.entered += e.key;
this.updateDots();
if (this.entered.length === 4) setTimeout(() => this.check(), 150);
} else if (e.key === "Backspace") {
this.entered = this.entered.slice(0, -1);
this.updateDots();
}
});
},
updateDots() {
const dots = document.querySelectorAll(".lock-dot");
dots.forEach((dot, i) => {
dot.classList.toggle("filled", i < this.entered.length);
dot.classList.remove("error");
});
},
check() {
if (this.entered === this.PIN) {
sessionStorage.setItem("deck_unlocked", "true");
this.unlock();
} else {
this.showError();
}
},
showError() {
const dots = document.querySelectorAll(".lock-dot");
dots.forEach(dot => dot.classList.add("error"));
setTimeout(() => {
this.entered = "";
this.updateDots();
}, 500);
},
unlock() {
const screen = document.getElementById("lock-screen");
if (screen) screen.classList.add("hidden");
}
};
// ============================================================================= // =============================================================================
// 1. CONFIG // 1. CONFIG
// ============================================================================= // =============================================================================
@@ -1678,7 +1843,22 @@ const App = {
} }
}; };
document.addEventListener("DOMContentLoaded", () => App.init()); document.addEventListener("DOMContentLoaded", () => {
const unlocked = Lock.init();
if (unlocked) {
App.init();
} else {
// Watch for unlock to init app
const observer = new MutationObserver((mutations) => {
const screen = document.getElementById("lock-screen");
if (screen?.classList.contains("hidden")) {
observer.disconnect();
App.init();
}
});
observer.observe(document.getElementById("lock-screen"), { attributes: true });
}
});
</script> </script>
</body> </body>
</html> </html>

View File

@@ -4,7 +4,9 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hst</title> <title>hst</title>
<meta name="description" content="DECK Tag Management System"> <meta name="description" content="HST Tag Management System">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<!-- <!--
DECK FRONTEND v4.6 - EventBus decoupling DECK FRONTEND v4.6 - EventBus decoupling
@@ -618,10 +620,88 @@ body {
scroll-behavior: auto !important; scroll-behavior: auto !important;
} }
} }
/* -----------------------------------------------------------------------------
* 9. LOCK SCREEN
* ----------------------------------------------------------------------------- */
.lock-screen {
position: fixed;
inset: 0;
background: var(--bg-primary);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
}
.lock-screen.hidden { display: none; }
.lock-logo { font-size: 2rem; font-weight: 700; color: var(--accent); margin-bottom: 2rem; letter-spacing: 0.1em; }
.lock-dots { display: flex; gap: 12px; margin-bottom: 2rem; }
.lock-dot {
width: 14px; height: 14px;
border-radius: 50%;
background: var(--border-color);
transition: all 0.15s ease;
}
.lock-dot.filled { background: var(--accent); }
.lock-dot.error { background: #ff4444; animation: shake 0.3s ease; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-4px); }
75% { transform: translateX(4px); }
}
.lock-keypad {
display: grid;
grid-template-columns: repeat(3, 70px);
gap: 12px;
}
.lock-key {
width: 70px; height: 70px;
border-radius: 50%;
border: 2px solid var(--border-color);
background: transparent;
color: var(--text-primary);
font-size: 1.5rem;
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
justify-content: center;
}
.lock-key:hover { border-color: var(--accent); background: var(--bg-hover); }
.lock-key:active { transform: scale(0.95); }
.lock-key.empty { visibility: hidden; }
.lock-key.backspace { font-size: 1.2rem; }
</style> </style>
</head> </head>
<body> <body>
<!-- LOCK SCREEN -->
<div id="lock-screen" class="lock-screen">
<div class="lock-logo">HST</div>
<div class="lock-dots">
<div class="lock-dot"></div>
<div class="lock-dot"></div>
<div class="lock-dot"></div>
<div class="lock-dot"></div>
</div>
<div class="lock-keypad">
<button class="lock-key" data-key="1">1</button>
<button class="lock-key" data-key="2">2</button>
<button class="lock-key" data-key="3">3</button>
<button class="lock-key" data-key="4">4</button>
<button class="lock-key" data-key="5">5</button>
<button class="lock-key" data-key="6">6</button>
<button class="lock-key" data-key="7">7</button>
<button class="lock-key" data-key="8">8</button>
<button class="lock-key" data-key="9">9</button>
<button class="lock-key empty"></button>
<button class="lock-key" data-key="0">0</button>
<button class="lock-key backspace" data-key="back"></button>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════════ <!-- ══════════════════════════════════════════════════════════════════════════
HTML STRUCTURE HTML STRUCTURE
══════════════════════════════════════════════════════════════════════════ --> ══════════════════════════════════════════════════════════════════════════ -->
@@ -713,6 +793,91 @@ body {
* EventBus pattern for module decoupling * EventBus pattern for module decoupling
*/ */
// =============================================================================
// 0. LOCK SCREEN
// =============================================================================
const Lock = {
PIN: "1234", // PIN code - can be changed
entered: "",
init() {
if (sessionStorage.getItem("hst_unlocked") === "true") {
this.unlock();
return true;
}
this.bind();
return false;
},
bind() {
const keypad = document.querySelector(".lock-keypad");
if (!keypad) return;
keypad.addEventListener("click", (e) => {
const key = e.target.closest(".lock-key");
if (!key) return;
const value = key.dataset.key;
if (value === "back") {
this.entered = this.entered.slice(0, -1);
} else if (value && this.entered.length < 4) {
this.entered += value;
}
this.updateDots();
if (this.entered.length === 4) {
setTimeout(() => this.check(), 150);
}
});
// Keyboard support
document.addEventListener("keydown", (e) => {
if (document.getElementById("lock-screen")?.classList.contains("hidden")) return;
if (e.key >= "0" && e.key <= "9" && this.entered.length < 4) {
this.entered += e.key;
this.updateDots();
if (this.entered.length === 4) setTimeout(() => this.check(), 150);
} else if (e.key === "Backspace") {
this.entered = this.entered.slice(0, -1);
this.updateDots();
}
});
},
updateDots() {
const dots = document.querySelectorAll(".lock-dot");
dots.forEach((dot, i) => {
dot.classList.toggle("filled", i < this.entered.length);
dot.classList.remove("error");
});
},
check() {
if (this.entered === this.PIN) {
sessionStorage.setItem("hst_unlocked", "true");
this.unlock();
} else {
this.showError();
}
},
showError() {
const dots = document.querySelectorAll(".lock-dot");
dots.forEach(dot => dot.classList.add("error"));
setTimeout(() => {
this.entered = "";
this.updateDots();
}, 500);
},
unlock() {
const screen = document.getElementById("lock-screen");
if (screen) screen.classList.add("hidden");
}
};
// ============================================================================= // =============================================================================
// 1. CONFIG // 1. CONFIG
// ============================================================================= // =============================================================================
@@ -1645,7 +1810,22 @@ const App = {
} }
}; };
document.addEventListener("DOMContentLoaded", () => App.init()); document.addEventListener("DOMContentLoaded", () => {
const unlocked = Lock.init();
if (unlocked) {
App.init();
} else {
// Watch for unlock to init app
const observer = new MutationObserver((mutations) => {
const screen = document.getElementById("lock-screen");
if (screen?.classList.contains("hidden")) {
observer.disconnect();
App.init();
}
});
observer.observe(document.getElementById("lock-screen"), { attributes: true });
}
});
</script> </script>
</body> </body>
</html> </html>