app_revisions: 01kwjk7289pmy4h2xrkpct13y5, 1
This data as json
| app_id | version | actor_id | name | description | html | is_private | sql_databases | stored_queries | csp_origins | changed_fields | created_at |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 01kwjk7289pmy4h2xrkpct13y5 | 1 | root | A Candid World | <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>A Candid World: The Declaration at 250</title> <style> :root{ --bg:#FBF7EF; --card:#FFFDF7; --ink:#26251F; --muted:#6E6A5E; --navy:#3C3B6E; --red:#B22234; --tabbar:#F1EADB; --line:rgba(60,59,110,0.16); --line-strong:rgba(60,59,110,0.25); --chip-bg:rgba(60,59,110,0.09); --chip-line:rgba(60,59,110,0.18); --serif:Georgia,'Times New Roman',serif; --sans:system-ui,'Segoe UI',Helvetica,Arial,sans-serif; } html,body{margin:0;padding:0;background:var(--bg);} *{box-sizing:border-box;} body{color:var(--ink);font-family:var(--sans);} input,select,button{font:inherit;} .paper{position:fixed;inset:0;pointer-events:none;z-index:0; background-image:repeating-linear-gradient(0deg, rgba(60,59,110,0.022) 0px, rgba(60,59,110,0.022) 1px, transparent 1px, transparent 4px), repeating-linear-gradient(90deg, rgba(178,34,52,0.012) 0px, rgba(178,34,52,0.012) 1px, transparent 1px, transparent 7px);} .shell{position:relative;z-index:1;max-width:820px;margin:0 auto;padding:40px 20px 72px;} header.hd{text-align:center;display:flex;flex-direction:column;align-items:center;gap:14px;margin-bottom:28px;} .badge{display:inline-flex;align-items:center;gap:8px;background:var(--navy);color:var(--bg); border-radius:999px;padding:6px 16px;font-size:11px;font-weight:600;letter-spacing:.14em;} .badge span.st{color:#fff;font-size:10px;} h1{margin:0;font-family:var(--serif);font-weight:400;font-size:clamp(36px,7vw,52px);line-height:1.05;color:var(--ink);letter-spacing:-.01em;} .gloss{margin:0;max-width:560px;font-family:var(--serif);font-style:italic;font-size:15px;line-height:1.55;color:var(--muted);} .gloss b{font-style:normal;font-weight:700;color:var(--navy);} .subtitle{margin:0;font-size:14px;color:var(--muted);} .rule{display:flex;align-items:center;gap:10px;color:var(--red);font-size:11px;letter-spacing:.4em;margin-top:2px;} .tabbar{display:inline-flex;background:var(--tabbar);border:1px solid var(--chip-line);border-radius:999px;padding:4px;gap:4px;margin-top:6px;} .tab{border:none;background:transparent;color:var(--navy);border-radius:999px;padding:9px 20px;font-size:14px;font-weight:600;cursor:pointer;} .tab:hover{background:rgba(60,59,110,0.08);} .tab.active{background:var(--navy);color:var(--bg);} .tab.active:hover{background:var(--navy);} .panel{display:none;} .panel.active{display:flex;flex-direction:column;gap:12px;} .ctl{display:flex;flex-wrap:wrap;align-items:center;gap:10px;} .ctl input[type=search],.ctl select{padding:10px 14px;border:1px solid var(--line-strong);border-radius:8px;background:var(--card);color:var(--ink);font-size:14px;} .ctl input[type=search]{flex:1 1 200px;min-width:160px;outline-color:var(--navy);} .ctl select{color:var(--navy);font-weight:500;cursor:pointer;padding:10px 12px;} .count{font-size:13px;color:var(--muted);margin-left:auto;white-space:nowrap;} .plainbtn{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--line-strong);background:var(--card);color:var(--navy); border-radius:999px;padding:9px 14px;font-size:13px;font-weight:600;cursor:pointer;} .plainbtn:hover{background:rgba(60,59,110,0.06);} .plainbtn .dot{width:14px;height:14px;border-radius:50%;border:2px solid rgba(60,59,110,0.4);display:inline-block;} .plainbtn.on{border-color:var(--navy);background:var(--navy);color:var(--bg);} .plainbtn.on .dot{background:var(--bg);border-color:var(--bg);} .decl{border:1px solid var(--chip-line);border-radius:10px;background:rgba(60,59,110,0.045);overflow:hidden;} .decl>button{width:100%;display:flex;align-items:center;justify-content:space-between;gap:10px;border:none;background:transparent; padding:13px 16px;cursor:pointer;color:var(--navy);font-size:14px;font-weight:600;text-align:left;} .decl .chev{font-size:12px;color:var(--muted);} .decl .doc{padding:2px 18px 18px;} .decl .doc h4{margin:14px 0 2px;font-family:var(--serif);font-size:14px;color:var(--red);} .decl .doc p{margin:0 0 10px;font-family:var(--serif);font-size:15px;line-height:1.7;color:var(--ink);} .card{background:var(--card);border:1px solid var(--line);border-radius:10px;box-shadow:0 1px 2px rgba(60,59,110,0.05);transition:border-color .15s,box-shadow .15s;} .card:hover{border-color:rgba(60,59,110,0.4);box-shadow:0 2px 8px rgba(60,59,110,0.10);} .chev{font-size:12px;color:var(--muted);} /* grievance */ .grow{display:flex;align-items:baseline;gap:14px;padding:16px 18px;cursor:pointer;} .gnum{font-family:var(--serif);font-size:26px;line-height:1;color:var(--red);min-width:34px;text-align:right;font-variant-numeric:oldstyle-nums;} .gmid{flex:1;display:flex;flex-direction:column;gap:5px;min-width:0;} .gtop{display:flex;align-items:center;flex-wrap:wrap;gap:8px;} .gtitle{margin:0;font-family:var(--serif);font-weight:600;font-size:18px;color:var(--ink);} .chip{font-size:10.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--navy); background:var(--chip-bg);border:1px solid var(--chip-line);border-radius:999px;padding:3px 9px;} .chip.state{color:var(--bg);background:var(--navy);border-color:var(--navy);} .preview{margin:0;font-size:13.5px;line-height:1.5;color:var(--muted);overflow:hidden;text-overflow:ellipsis; display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;} .grow .chev{align-self:center;} .gexp{padding:0 18px 18px 66px;display:flex;flex-direction:column;gap:12px;} blockquote.orig{margin:0;padding:2px 0 2px 16px;border-left:3px solid var(--red);font-family:var(--serif);font-style:italic;font-size:16px;line-height:1.65;color:var(--ink);} .plainbox{background:rgba(60,59,110,0.06);border:1px solid rgba(60,59,110,0.14);border-radius:8px;padding:12px 14px;} .plainbox .lbl{font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--navy);margin-bottom:5px;} .plainbox p{margin:0;font-size:14.5px;line-height:1.6;color:var(--ink);} /* signer */ .srow{display:flex;align-items:center;gap:14px;padding:16px 18px;cursor:pointer;} .smid{flex:1;display:flex;flex-direction:column;gap:7px;min-width:0;} .sname{margin:0;font-family:var(--serif);font-weight:600;font-size:18px;color:var(--ink);} .schips{display:flex;align-items:center;flex-wrap:wrap;gap:6px;} .agebox{text-align:center;min-width:58px;} .agebox .n{font-family:var(--serif);font-size:30px;line-height:1;color:var(--red);font-variant-numeric:oldstyle-nums;} .agebox .n.q{font-size:24px;color:rgba(110,106,94,0.5);} .agebox .cap{font-size:9.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-top:3px;} .sexp{margin:0 18px 18px;border-top:1px solid rgba(60,59,110,0.12);padding-top:14px; display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:14px;} .fact .lbl{font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--navy);margin-bottom:4px;} .fact p{margin:0;font-size:14px;line-height:1.5;color:var(--ink);} .empty{text-align:center;padding:56px 20px;display:flex;flex-direction:column;align-items:center;gap:12px;} .empty .star{font-size:40px;color:rgba(60,59,110,0.25);letter-spacing:.3em;} .empty .big{margin:0;font-family:var(--serif);font-size:19px;color:var(--ink);} .empty .sm{margin:0;font-size:14px;color:var(--muted);} .clearbtn{margin-top:6px;border:1px solid var(--navy);background:transparent;color:var(--navy);border-radius:8px;padding:9px 18px;font-size:13.5px;font-weight:600;cursor:pointer;} .clearbtn:hover{background:var(--navy);color:var(--bg);} footer{margin-top:48px;text-align:center;display:flex;flex-direction:column;gap:8px;} footer .rule{justify-content:center;} footer p{margin:0;font-size:12px;color:var(--muted);} footer .src{max-width:640px;margin:0 auto;font-size:11px;line-height:1.5;color:rgba(110,106,94,0.85);} </style> </head> <body> <div class="paper"></div> <div class="shell"> <header class="hd"> <div class="badge"><span class="st">★</span><span>AMERICA 250</span><span class="st">★</span></div> <h1>A Candid World</h1> <p class="gloss">“To prove this, let Facts be submitted to a candid world.” Here <b>candid</b> means fair-minded and impartial.</p> <p class="subtitle">The 27 grievances and the 56 signers of the Declaration of Independence, in one small reference.</p> <div class="rule">✶ ✶ ✶</div> <div class="tabbar"> <button class="tab active" data-tab="grievances">The 27 Grievances</button> <button class="tab" data-tab="signers">The 56 Signers</button> </div> </header> <!-- GRIEVANCES --> <section class="panel active" id="panel-grievances"> <div class="ctl"> <input type="search" id="gq" placeholder="Search the grievances…" autocomplete="off"> <select id="gtheme"><option value="">All themes</option></select> <button class="plainbtn" id="gplain"><span class="dot"></span> Modern English</button> <span class="count" id="gcount"></span> </div> <div class="decl"> <button id="declToggle"><span>Read the Declaration itself</span><span class="chev" id="declChev">▼</span></button> <div class="doc" id="sections" style="display:none;"></div> </div> <div id="glist"></div> </section> <!-- SIGNERS --> <section class="panel" id="panel-signers"> <div class="ctl"> <input type="search" id="sq" placeholder="Search the signers…" autocomplete="off"> <select id="sstate"><option value="">All states</option></select> <select id="ssort"> <option value="seq">Order on the document</option> <option value="age_desc">Age: oldest first</option> <option value="age_asc">Age: youngest first</option> <option value="name">Name (A–Z)</option> <option value="state">By state</option> </select> <span class="count" id="scount"></span> </div> <div id="slist"></div> </section> <footer> <div class="rule">✶ ✶ ✶</div> <p>In Congress, July 4, 1776. The unanimous Declaration of the thirteen united States of America.</p> <p class="src">Built as a Datasette micro-app: one sandboxed HTML file over a governed SQLite layer via <code>datasette.query()</code>. Text: public domain (U.S. National Archives). Signer facts from public biographies; four birth dates are too uncertain to state an exact age. Modern-English restatements are editorial paraphrase.</p> </footer> </div> <script> const DB = "declaration250"; let GPLAIN=false, GEXP=null, SEXP=null, GROWS=[], SROWS=[]; async function q(sql, params){ const r = await datasette.query(DB, sql, params || {}); return r.rows || []; } function esc(s){return (s==null?"":String(s)).replace(/[&<>]/g,c=>({'&':'&','<':'<','>':'>'}[c]));} function ftsQuery(raw){const t=raw.toLowerCase().replace(/[^a-z0-9 ]/g," ").split(/\s+/).filter(Boolean);return t.length?t.map(x=>x+"*").join(" OR "):null;} /* tabs */ document.querySelectorAll(".tab").forEach(btn=>btn.addEventListener("click",()=>{ document.querySelectorAll(".tab").forEach(b=>b.classList.toggle("active",b===btn)); const n=btn.dataset.tab; document.getElementById("panel-grievances").classList.toggle("active",n==="grievances"); document.getElementById("panel-signers").classList.toggle("active",n==="signers"); })); /* declaration collapsible */ let declOpen=false; document.getElementById("declToggle").addEventListener("click",()=>{ declOpen=!declOpen; document.getElementById("sections").style.display=declOpen?"block":"none"; document.getElementById("declChev").innerHTML=declOpen?"▲":"▼"; }); /* grievances */ async function loadThemes(){ const rows=await q("select distinct theme from grievances order by theme",{}); const sel=document.getElementById("gtheme"); rows.forEach(r=>{const o=document.createElement("option");o.value=r.theme;o.textContent=r.theme;sel.appendChild(o);}); } async function loadSections(){ const rows=await q("select heading, body from sections order by section_order",{}); document.getElementById("sections").innerHTML=rows.map(r=>`<h4>${esc(r.heading)}</h4><p>${esc(r.body)}</p>`).join(""); } async function fetchGrievances(){ const fts=ftsQuery(document.getElementById("gq").value.trim()); const theme=document.getElementById("gtheme").value; let sql,params; if(fts&&theme){sql="select g.num,g.short_title,g.theme,g.text,g.plain_english from grievances_fts f join grievances g on cast(f.num as integer)=g.num where f.grievances_fts match :q and g.theme=:theme order by g.num";params={q:fts,theme};} else if(fts){sql="select g.num,g.short_title,g.theme,g.text,g.plain_english from grievances_fts f join grievances g on cast(f.num as integer)=g.num where f.grievances_fts match :q order by g.num";params={q:fts};} else if(theme){sql="select num,short_title,theme,text,plain_english from grievances where theme=:theme order by num";params={theme};} else{sql="select num,short_title,theme,text,plain_english from grievances order by num";params={};} try{GROWS=await q(sql,params);}catch(e){document.getElementById("glist").innerHTML=`<p class="empty sm">Query error: ${esc(e.message||e)}</p>`;return;} renderGrievances(); } function renderGrievances(){ document.getElementById("gcount").textContent=GROWS.length+(GROWS.length===1?" grievance":" grievances"); const list=document.getElementById("glist"); if(!GROWS.length){ list.innerHTML=`<div class="empty"><div class="star">✶</div><p class="big">No facts found for a candid world</p><p class="sm">Try a different word, or clear the search and theme filter.</p><button class="clearbtn" data-clear="g">Clear filters</button></div>`; return; } list.style.display="flex";list.style.flexDirection="column";list.style.gap="10px"; list.innerHTML=GROWS.map(r=>{ const exp=GEXP===r.num; const preview=GPLAIN?r.plain_english:r.text; return `<article class="card"> <div class="grow" data-g="${r.num}"> <span class="gnum">${r.num}</span> <div class="gmid"> <div class="gtop"><h3 class="gtitle">${esc(r.short_title)}</h3><span class="chip">${esc(r.theme)}</span></div> ${exp?"":`<p class="preview">${esc(preview)}</p>`} </div> <span class="chev">${exp?"▲":"▼"}</span> </div> ${exp?`<div class="gexp"> <blockquote class="orig">${esc(r.text)}</blockquote> <div class="plainbox"><div class="lbl">In modern English</div><p>${esc(r.plain_english)}</p></div> </div>`:""} </article>`; }).join(""); } /* signers */ async function loadStates(){ const rows=await q("select distinct state from signers order by state",{}); const sel=document.getElementById("sstate"); rows.forEach(r=>{const o=document.createElement("option");o.value=r.state;o.textContent=r.state;sel.appendChild(o);}); } function signerOrder(p){const x=p?"s.":"";switch(document.getElementById("ssort").value){ case"age_desc":return `order by ${x}age_at_signing is null, ${x}age_at_signing desc, ${x}seq`; case"age_asc":return `order by ${x}age_at_signing is null, ${x}age_at_signing asc, ${x}seq`; case"name":return `order by ${x}name`; case"state":return `order by ${x}state, ${x}seq`; default:return `order by ${x}seq`;}} async function fetchSigners(){ const fts=ftsQuery(document.getElementById("sq").value.trim()); const state=document.getElementById("sstate").value; const cols="seq,name,state,birth_year,age_at_signing,birthplace,education,occupation"; const sc=cols.split(",").map(c=>"s."+c).join(","); let sql,params; if(fts&&state){sql=`select ${sc} from signers_fts f join signers s on cast(f.seq as integer)=s.seq where f.signers_fts match :q and s.state=:state ${signerOrder(true)}`;params={q:fts,state};} else if(fts){sql=`select ${sc} from signers_fts f join signers s on cast(f.seq as integer)=s.seq where f.signers_fts match :q ${signerOrder(true)}`;params={q:fts};} else if(state){sql=`select ${cols} from signers where state=:state ${signerOrder(false)}`;params={state};} else{sql=`select ${cols} from signers ${signerOrder(false)}`;params={};} try{SROWS=await q(sql,params);}catch(e){document.getElementById("slist").innerHTML=`<p class="empty sm">Query error: ${esc(e.message||e)}</p>`;return;} renderSigners(); } function renderSigners(){ document.getElementById("scount").textContent=SROWS.length+(SROWS.length===1?" signer":" signers"); const list=document.getElementById("slist"); if(!SROWS.length){ list.innerHTML=`<div class="empty"><div class="star">✶</div><p class="big">No signers match</p><p class="sm">Try a different name, or clear the search and state filter.</p><button class="clearbtn" data-clear="s">Clear filters</button></div>`; return; } list.style.display="flex";list.style.flexDirection="column";list.style.gap="10px"; list.innerHTML=SROWS.map(r=>{ const exp=SEXP===r.seq; const born=esc(r.birthplace)+(r.birth_year?`, ${r.birth_year}`:""); const age = (r.age_at_signing==null) ? `<div class="agebox"><div class="n q">?</div><div class="cap">age not recorded</div></div>` : `<div class="agebox"><div class="n">${r.age_at_signing}</div><div class="cap">at signing</div></div>`; return `<article class="card"> <div class="srow" data-s="${r.seq}"> <div class="smid"> <h3 class="sname">${esc(r.name)}</h3> <div class="schips"><span class="chip state">${esc(r.state)}</span><span class="chip">${esc(r.occupation)}</span></div> </div> ${age} <span class="chev">${exp?"▲":"▼"}</span> </div> ${exp?`<div class="sexp"> <div class="fact"><div class="lbl">Born</div><p>${born}</p></div> <div class="fact"><div class="lbl">Education</div><p>${esc(r.education)}</p></div> <div class="fact"><div class="lbl">Represented</div><p>${esc(r.state)}</p></div> </div>`:""} </article>`; }).join(""); } /* events */ document.getElementById("glist").addEventListener("click",e=>{ const clear=e.target.closest("[data-clear]"); if(clear){document.getElementById("gq").value="";document.getElementById("gtheme").value="";fetchGrievances();return;} const row=e.target.closest("[data-g]"); if(row){const n=+row.dataset.g;GEXP=(GEXP===n)?null:n;renderGrievances();} }); document.getElementById("slist").addEventListener("click",e=>{ const clear=e.target.closest("[data-clear]"); if(clear){document.getElementById("sq").value="";document.getElementById("sstate").value="";fetchSigners();return;} const row=e.target.closest("[data-s]"); if(row){const n=+row.dataset.s;SEXP=(SEXP===n)?null:n;renderSigners();} }); let gdeb;document.getElementById("gq").addEventListener("input",()=>{clearTimeout(gdeb);gdeb=setTimeout(fetchGrievances,180);}); document.getElementById("gtheme").addEventListener("change",fetchGrievances); document.getElementById("gplain").addEventListener("click",()=>{GPLAIN=!GPLAIN;document.getElementById("gplain").classList.toggle("on",GPLAIN);renderGrievances();}); let sdeb;document.getElementById("sq").addEventListener("input",()=>{clearTimeout(sdeb);sdeb=setTimeout(fetchSigners,180);}); document.getElementById("sstate").addEventListener("change",fetchSigners); document.getElementById("ssort").addEventListener("change",fetchSigners); (async function init(){ try{ await loadThemes(); await loadSections(); await fetchGrievances(); await loadStates(); await fetchSigners(); }catch(e){ document.getElementById("glist").innerHTML=`<div class="empty"><p class="big">Could not reach the data layer</p><p class="sm">${esc(e.message||e)}. Is the <code>${DB}</code> database selected for this app?</p></div>`; } })(); </script> </body> </html> | 0 | ["declaration250"] | [] | [] | ["name", "description", "html", "is_private", "sql_databases", "stored_queries", "csp_origins"] | 2026-07-02T23:39:22.249956+00:00 |