Press shapes in the correct 5-step sequence. Labels are decoys, use data-k.
#l1panel.dataset.perm as indices mapping to [triangle,square,circle].
Invisible characters encode bits. Map \u200b → 0 and \u200d → 1 in the paragraph below, then parse as ASCII.
Hidden
Array.from(zwsp.textContent).filter(c=>c==='\u200b'||c==='\u200d').map(c=>c==='\u200d'?1:0).join('') → group by 8 → String.fromCharCode(parseInt(byte,2)).
Code chars are in the red channel but shuffled by a seeded LCG. Seed derives from #l3carrier.dataset.seed.
There is a base64 payload. XOR each byte with the first N bytes of SHA‑256(salt). Salt is in #l4salt.dataset.s.
k = SHA-256('kestrel') → bytes; plain[i] = b64[i] ^ k[i] cycling key.
A Web Worker sends a nonce. Reply with HMAC‑SHA256 using secret 'l5-secret'. Correct MAC reveals the code.
window.L5.n, compute mac = HMAC(n, key='l5-secret') and send L5.postMessage({mac}).
Code is in a closed shadow root but exposed via ::part(code) once the host has attribute unlock equal to SHA‑256('open‑sesame').
let d = await crypto.subtle.digest('SHA-256', new TextEncoder().encode('open-sesame')); → hex; then document.querySelector('locked-box').setAttribute('unlock', hex).
Red bytes contain the message in a permuted order (different LCG). Use #l7cv.
initL7() for the exact LCG and permutation build.
The code is stored in indexedDB database puzzle8, store kv, key 'code'.
#l8fb.dataset.hex holds the hex of UTF‑8 bytes.let r = await new Promise((res)=>{ let o = indexedDB.open('puzzle8'); o.onsuccess=e=>{let db=e.target.result; let tx=db.transaction('kv'); tx.objectStore('kv').get('code').onsuccess=ev=>res(ev.target.result)} })
An inlined iframe responds with a code after a handshake via postMessage.
l9f.contentWindow.postMessage({cmd:'hello'}, '*') and listen on window.onmessage.
Concatenate codes 1–9 (no separators), compute SHA‑256, then compute SHA‑256('pepper:'+hex). Final code is MASTER- + first 16 Base32 chars (RFC4648, no padding).
localStorage.getItem(PROGRESS_KEY) and use the helper base32 in the page.