A Tiny Proof‑of‑Concept:
“Collaborative Plates → Daily‑Drip Values”

1  Abstract (100 words)
We pair a split‑QR key fob, fabricated on any hobby‑grade 3‑D printer, with a single‑page client‑only website that decrypts and reveals one organisational value per day.
Two opaque plates—A and B—each contain square holes that expose either the white or the black modules of a QR matrix. Only when teammates snap the plates together (physical collaboration) does the pattern scan, launching a URL that already carries the pass‑phrase to unlock the page (digital collaboration). Everything runs offline, needs no backend, and costs nothing beyond a few grams of filament.

2  Why it matters
Team‑building gadget — People must literally “collaborate” to assemble the QR.

Slow learning — The manifesto appears one value per day, forcing reflection rather than scroll‑skimming.

Zero infrastructure — All crypto is Web Crypto; plates are plain STL.

Security — Key never touches a server; even the split pattern can be regenerated with a new salt.

3  Code Listing A – mystery.html
(website that decrypts & drip‑feeds)

html
Copy
Edit
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">
<title>Daily Values</title>
<style>
 body{font-family:system-ui,sans-serif;margin:0 auto;max-width:46rem;padding:2.5rem}
 #secret{white-space:pre-wrap} #secret span{opacity:0;display:inline-block;animation:fade .4s forwards}
 @keyframes fade{to{opacity:1;transform:translateY(-4px)}}
 .shake{animation:shake .4s}@keyframes shake{10%,90%{transform:translateX(-2px)}20%,80%{transform:translateX(4px)}
 30%,50%,70%{transform:translateX(-6px)}40%,60%{transform:translateX(6px)}}
</style></head><body>
<h2>Message forthcoming…</h2><div id="secret"></div>
<div id="pwBox"><input id="pw" type="password" placeholder="Enter password">
<button onclick="reveal()">Reveal 🗝️</button></div>

<script>
/* 1. AES‑GCM ciphertext (Base‑64) generated by encrypt.html */
const encryptedBase64="PASTE_YOUR_BLOB_HERE";

/* 2. first day to unlock (UTC) */
const START_DATE=new Date(Date.UTC(2025,6,24)); // 24‑Jul‑2025

/* === decryption helpers === */
const bin=Uint8Array.from(atob(encryptedBase64),c=>c.charCodeAt(0));
const iv=bin.slice(0,12),data=bin.slice(12),enc=new TextEncoder(),dec=new TextDecoder();

/* auto‑unlock if ?key= present */
(()=>{const p=new URLSearchParams(location.search);const k=p.get("key");if(k)reveal(k);})();

async function reveal(manual){
 const pass=manual??document.getElementById('pw').value.trim(),box=document.getElementById('pwBox');
 try{
   const km=await crypto.subtle.importKey("raw",enc.encode(pass),"PBKDF2",false,["deriveKey"]);
   const key=await crypto.subtle.deriveKey({name:"PBKDF2",salt:enc.encode(pass),iterations:1e5,hash:"SHA-256"},
                                           km,{name:"AES-GCM",length:256},false,["decrypt"]);
   const plain=dec.decode(await crypto.subtle.decrypt({name:"AES-GCM",iv},key,data));
   box.remove(); drip(plain);
 }catch(e){ if(!manual){const p=document.getElementById('pw');p.classList.add('shake');
   setTimeout(()=>p.classList.remove('shake'),420);p.value='';p.focus();}}
}

function drip(text){
 const day=Math.floor((Date.now()-START_DATE)/864e5)+1;  // +1 so day 0 shows first value
 const parts=text.split(/(?=^Value\s+\d+\s*:)/m);
 const shown=Math.max(0,Math.min(parts.length,day));
 const tgt=document.getElementById('secret');tgt.textContent='';
 if(!shown){tgt.textContent="Too early – come back "+START_DATE.toISOString().slice(0,10);return;}
 parts.slice(0,shown).forEach(b=>animate(tgt,b.trim()));
 if(shown<parts.length){const p=document.createElement('p');p.style.opacity=.6;
   p.textContent="Come back tomorrow…";tgt.appendChild(p);}
}

function animate(target,str){let i=0;for(const ch of str){
 if(ch==='\n'){target.appendChild(document.createElement('br'));continue;}
 if(ch===' '){target.appendChild(document.createTextNode(' '));continue;}
 const s=document.createElement('span');s.textContent=ch;s.style.animationDelay=(i*0.015)+'s';
 target.appendChild(s);i++;}}
</script></body></html>
4  Code Listing B – square.py
(CLI to create two STL plates; salt flag added)

python
Copy
Edit
#!/usr/bin/env python
"""
square.py – generate two 3‑D printable contrast plates that form a QR only
when stacked.  Requires CadQuery >= 2, qrcode[pil].
"""
import argparse, hashlib, itertools, sys, cadquery as cq, qrcode
from pathlib import Path
# ---------- CLI --------------------------------------------------------
ap = argparse.ArgumentParser()
ap.add_argument("text", help="Text/URL encoded in QR")
ap.add_argument("--salt", default="", help="Randomises A/B split")
ap.add_argument("--base-name", default="plate", help="prefix for STL names")
ap.add_argument("-o","--output-dir", type=Path, default=Path(".")) 
args = ap.parse_args(); args.output_dir.mkdir(exist_ok=True)

# ---------- QR to matrix ----------------------------------------------
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=1, border=2)
qr.add_data(args.text); qr.make(fit=True); M = qr.get_matrix(); N=len(M)
# ---------- split into holes ------------------------------------------
A,B=[],[]
for r,c in itertools.product(range(N),repeat=2):
    if not M[r][c]:          # white -> hole on both plates
        A.append((r,c)); B.append((r,c))
    else:                    # black -> hole on exactly one plate
        h=int(hashlib.sha1(f"{args.salt}:{r},{c}".encode()).hexdigest(),16)
        (A if h&1 else B).append((r,c))
# ---------- cadquery helper -------------------------------------------
def mk_plate(letter, holes):
    SZ=3.0; TH=1.5; HOLE=SZ*0.85; BORDER=5
    plate = cq.Workplane("XY").box(N*SZ+2*BORDER,N*SZ+2*BORDER,TH,centered=(1,1,0))
    ox=-N*SZ/2; oy=-N*SZ/2
    for r,c in holes:
        x=ox+(c+0.5)*SZ; y=oy+(N-1-r+0.5)*SZ
        plate = plate.cut( cq.Workplane("XY")
                 .box(HOLE,HOLE,TH+0.2,centered=(1,1,1))
                 .translate((x,y,TH/2)) )
    return plate

modelA=mk_plate('A',A); modelB=mk_plate('B',B)
h=hashlib.sha1(args.text.encode()).hexdigest()[:8]
outA=args.output_dir/f"{args.base_name}_{h}_Plate-A.stl"
outB=args.output_dir/f"{args.base_name}_{h}_Plate-B.stl"
cq.exporters.export(modelA, str(outA)); cq.exporters.export(modelB, str(outB))
print("Wrote", outA, "and", outB)
5  Workflow
Encrypt content → paste ciphertext into mystery.html.

Upload mystery.html to GitHub Pages so the URL is
…mystery.html?key=LetsCollaborate.

Generate plates

bash
Copy
Edit
python square.py "https://…mystery.html?key=LetsCollaborate" \
       --salt friday --base-name collab --output-dir out
Print Plate‑A and Plate‑B in opaque filament; snap together on a white card.

Teammates scan → page auto‑unlocks; one new value appears every day.

6  Future micro‑papers
V2: triple‑plate secret, time‑locked by week.

V3: embed plate files & ciphertext inside a single QR that self‑bootstraps.

Happy collaborating!