From fb44fa9beeae159e0279ad3ca0d404de3ade4e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:42:05 -0700 Subject: [PATCH 1/3] Use one-choice semantics for homepage hero (1/3) --- docs/index.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/index.html b/docs/index.html index 274df342..211d285f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -19,7 +19,7 @@ *{box-sizing:border-box} html{scroll-behavior:smooth} body{margin:0;color:var(--ink);background:radial-gradient(circle at 10% 0,rgba(54,104,255,.22),transparent 34rem),radial-gradient(circle at 90% 12%,rgba(0,236,255,.13),transparent 30rem),var(--bg);font:16px/1.55 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif} a{color:inherit}.shell{width:min(1180px,calc(100% - 32px));margin:auto}.nav{display:flex;align-items:center;justify-content:space-between;padding:24px 0}.brand{font-weight:900;letter-spacing:.14em}.navlinks{display:flex;gap:10px;flex-wrap:wrap}.button,.ghost{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;padding:11px 16px;text-decoration:none;font-weight:850;border:1px solid var(--line)}.button{background:var(--cyan);color:#041018}.ghost{background:rgba(255,255,255,.04)} .hero{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(380px,.95fr);align-items:center;gap:clamp(24px,5vw,68px);padding:64px 0 42px}.eyebrow{color:var(--cyan);font-size:.78rem;font-weight:900;letter-spacing:.18em}.hero h1{margin:18px 0;font-size:clamp(3.3rem,6.3vw,6rem);line-height:.96;letter-spacing:-.06em}.gradient{background:linear-gradient(90deg,var(--cyan),#a994ff,var(--gold));background-clip:text;color:transparent}.lede{max-width:630px;margin:24px 0;color:#c7d0df;font-size:clamp(1.05rem,2vw,1.28rem)}.hero-actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:27px}.equation{display:inline-block;margin-top:28px;padding:12px 15px;border:1px solid rgba(125,249,255,.34);border-radius:12px;background:rgba(0,0,0,.3);color:var(--cyan);font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}.hero-note{max-width:620px;color:var(--muted);font-size:.82rem} - .proof-console{border:1px solid rgba(125,249,255,.3);border-radius:24px;background:linear-gradient(150deg,rgba(21,40,65,.95),rgba(8,14,29,.98));box-shadow:0 30px 90px rgba(0,0,0,.34),0 0 70px rgba(0,225,240,.06);overflow:hidden}.console-top{display:flex;align-items:center;justify-content:space-between;padding:17px 20px;border-bottom:1px solid var(--line);color:#a6c3d2;font:700 11px ui-monospace,monospace;letter-spacing:.13em}.console-dots{display:flex;gap:6px}.console-dots i{width:7px;height:7px;border-radius:50%;background:#557989}.console-dots i:first-child{background:var(--cyan)}.console-body{padding:23px}.console-label{margin:0 0 13px;color:#8da7bc;font:700 11px ui-monospace,monospace;letter-spacing:.14em}.console-values{display:grid;grid-template-columns:1fr 1fr;gap:10px}.console-value{padding:14px;border:1px solid var(--line);border-radius:13px;background:rgba(255,255,255,.035)}.console-value span{display:block;color:var(--muted);font-size:.77rem}.console-value b{display:block;margin-top:3px;font:700 1.65rem ui-monospace,monospace}.console-rule{margin:16px 0;color:#a9c8ce;font:12px ui-monospace,monospace}.verdict{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:17px;border:1px solid rgba(255,102,136,.43);border-radius:14px;background:rgba(255,102,136,.1)}.verdict span{color:#c4d0de;font-size:.78rem}.verdict strong{color:#ff8aa0;font:900 clamp(1.3rem,2.5vw,2rem) ui-monospace,monospace;letter-spacing:.03em}.verdict.allow{border-color:rgba(156,255,176,.42);background:rgba(156,255,176,.09)}.verdict.allow strong{color:var(--green)}.case-buttons{display:flex;gap:9px;margin-top:15px}.case-buttons button{flex:1;padding:10px;border:1px solid var(--line);border-radius:11px;background:rgba(255,255,255,.04);color:var(--ink);font:700 .82rem ui-sans-serif,system-ui;cursor:pointer}.case-buttons button[aria-pressed=true]{border-color:var(--cyan);background:rgba(125,249,255,.12);color:var(--cyan)}.console-foot{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-top:17px;color:var(--muted);font-size:.75rem}.console-foot a{color:var(--cyan);font-weight:800;text-decoration:none}.console-foot a:hover{text-decoration:underline}.paths{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:10px 0 38px}.path{display:block;padding:19px;border:1px solid var(--line);border-radius:18px;background:rgba(255,255,255,.035);text-decoration:none}.path:hover,.path:focus-visible{border-color:var(--cyan);background:rgba(125,249,255,.075)}.path span{display:block;color:var(--cyan);font:800 .72rem ui-monospace,monospace;letter-spacing:.13em}.path b{display:block;margin:9px 0 3px;font-size:1.08rem}.path small{color:var(--muted);font-size:.83rem}.button:focus-visible,.ghost:focus-visible,.case-buttons button:focus-visible{outline:3px solid var(--gold);outline-offset:3px} + .proof-console{border:1px solid rgba(125,249,255,.3);border-radius:24px;background:linear-gradient(150deg,rgba(21,40,65,.95),rgba(8,14,29,.98));box-shadow:0 30px 90px rgba(0,0,0,.34),0 0 70px rgba(0,225,240,.06);overflow:hidden}.console-top{display:flex;align-items:center;justify-content:space-between;padding:17px 20px;border-bottom:1px solid var(--line);color:#a6c3d2;font:700 11px ui-monospace,monospace;letter-spacing:.13em}.console-dots{display:flex;gap:6px}.console-dots i{width:7px;height:7px;border-radius:50%;background:#557989}.console-dots i:first-child{background:var(--cyan)}.console-body{padding:23px}.console-label{margin:0 0 13px;color:#8da7bc;font:700 11px ui-monospace,monospace;letter-spacing:.14em}.console-values{display:grid;grid-template-columns:1fr 1fr;gap:10px}.console-value{padding:14px;border:1px solid var(--line);border-radius:13px;background:rgba(255,255,255,.035)}.console-value span{display:block;color:var(--muted);font-size:.77rem}.console-value b{display:block;margin-top:3px;font:700 1.65rem ui-monospace,monospace}.console-rule{margin:16px 0;color:#a9c8ce;font:12px ui-monospace,monospace}.verdict{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:17px;border:1px solid rgba(255,102,136,.43);border-radius:14px;background:rgba(255,102,136,.1)}.verdict span{color:#c4d0de;font-size:.78rem}.verdict strong{color:#ff8aa0;font:900 clamp(1.3rem,2.5vw,2rem) ui-monospace,monospace;letter-spacing:.03em}.verdict.allow{border-color:rgba(156,255,176,.42);background:rgba(156,255,176,.09)}.verdict.allow strong{color:var(--green)}.case-buttons{display:flex;gap:9px;margin-top:15px}.case-buttons button{flex:1;padding:10px;border:1px solid var(--line);border-radius:11px;background:rgba(255,255,255,.04);color:var(--ink);font:700 .82rem ui-sans-serif,system-ui;cursor:pointer}.case-buttons button[aria-checked=true]{border-color:var(--cyan);background:rgba(125,249,255,.12);color:var(--cyan)}.console-foot{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-top:17px;color:var(--muted);font-size:.75rem}.console-foot a{color:var(--cyan);font-weight:800;text-decoration:none}.console-foot a:hover{text-decoration:underline}.paths{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:10px 0 38px}.path{display:block;padding:19px;border:1px solid var(--line);border-radius:18px;background:rgba(255,255,255,.035);text-decoration:none}.path:hover,.path:focus-visible{border-color:var(--cyan);background:rgba(125,249,255,.075)}.path span{display:block;color:var(--cyan);font:800 .72rem ui-monospace,monospace;letter-spacing:.13em}.path b{display:block;margin:9px 0 3px;font-size:1.08rem}.path small{color:var(--muted);font-size:.83rem}.button:focus-visible,.ghost:focus-visible,.case-buttons button:focus-visible{outline:3px solid var(--gold);outline-offset:3px} .section{padding:34px 0}.section-head{display:flex;align-items:end;justify-content:space-between;gap:20px;margin-bottom:18px}.section h2{margin:0;font-size:clamp(1.75rem,4vw,3rem);letter-spacing:-.04em}.section-head p{max-width:600px;margin:0;color:var(--muted)} .compare{display:grid;grid-template-columns:1fr 1fr;gap:18px}.panel{border:1px solid var(--line);border-radius:26px;background:var(--panel);box-shadow:0 18px 80px rgba(0,0,0,.2);backdrop-filter:blur(18px);overflow:hidden}.panel-head{padding:22px;border-bottom:1px solid var(--line)}.panel-head h3{margin:0 0 5px}.panel-head p{margin:0;color:var(--muted)}.stack{display:grid;gap:12px;padding:18px}.element{position:relative;padding:17px;border:1px solid var(--line);border-radius:19px;background:rgba(255,255,255,.045);transition:.35s ease}.element strong{display:block;margin-bottom:2px}.small{font-size:.88rem;color:var(--muted)}.status{display:inline-block;margin-top:12px;border-radius:999px;padding:5px 9px;font-size:.72rem;font-weight:950;letter-spacing:.08em}.traditional .status{background:rgba(255,255,255,.1)}.allowed{border-color:rgba(156,255,176,.45);box-shadow:inset 3px 0 var(--green)}.allowed .status{background:rgba(156,255,176,.13);color:var(--green)}.degraded{opacity:.7;border-style:dashed;border-color:rgba(242,206,114,.55)}.degraded .status{background:rgba(242,206,114,.13);color:var(--gold)}.suppressed{opacity:.38;filter:saturate(.35);border-color:rgba(255,102,136,.55)}.suppressed .status{background:rgba(255,102,136,.14);color:var(--red)} .lab{display:grid;grid-template-columns:.9fr 1.1fr;gap:18px}.controls{padding:22px}.control{padding:16px 0;border-bottom:1px solid var(--line)}.control:last-child{border:0}.control-top{display:flex;justify-content:space-between;gap:10px}.value{color:var(--cyan);font-family:ui-monospace,monospace}.range-row{display:grid;grid-template-columns:1fr auto;gap:12px;align-items:center;margin-top:10px}input[type=range]{width:100%;accent-color:var(--cyan)}.cost{font-size:.75rem;color:var(--muted)}pre{margin:0;padding:22px;min-height:100%;overflow:auto;white-space:pre-wrap;color:#dffcff;background:rgba(0,0,0,.28);font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace}.principles{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.principle{padding:22px;border:1px solid var(--line);border-radius:22px;background:rgba(255,255,255,.035)}.principle b{color:var(--cyan)}.principle p{margin:8px 0 0;color:var(--muted)} @@ -32,7 +32,7 @@ @media(max-width:900px){.hero{grid-template-columns:1fr;padding-top:42px}.hero h1{max-width:690px}.proof-console{max-width:690px}.paths{grid-template-columns:1fr 1fr}} @media(max-width:800px){.navlinks .ghost{display:none}.compare,.lab,.principles,.paths,.continuity-steps,.challenge-feature,.builder-invite,.atlas-list{grid-template-columns:1fr}.section-head{align-items:start;flex-direction:column}.hero h1{font-size:clamp(3.2rem,12vw,5rem)}.console-foot,.scope-teaser{align-items:flex-start;flex-direction:column}} @media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}.element{transition:none}} - .boundary-switch{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:9px;background:#06111e;border-bottom:1px solid var(--line)}.boundary-switch button{padding:11px 8px;border:1px solid transparent;border-radius:11px;background:transparent;color:#a9bfce;font:800 .77rem ui-sans-serif,system-ui;cursor:pointer}.boundary-switch button[aria-pressed=true]{border-color:rgba(125,249,255,.43);background:rgba(125,249,255,.12);color:var(--cyan)}.boundary-switch button:focus-visible{outline:3px solid var(--gold);outline-offset:1px}.console-body[hidden]{display:none}.action-target{padding:15px;border:1px solid var(--line);border-radius:13px;background:rgba(255,255,255,.035)}.action-target span{display:block;color:var(--muted);font-size:.76rem}.action-target strong{display:block;margin-top:5px;color:var(--red);font:700 clamp(.91rem,1.3vw,1.03rem) ui-monospace,monospace;overflow-wrap:anywhere}.action-target.approved strong{color:var(--green)}.action-rule{margin:15px 0;color:#a9c8ce;font:12px/1.5 ui-monospace,monospace}.action-rule b{color:var(--gold);font-weight:700}.verdict.approval{border-color:rgba(242,206,114,.45);background:rgba(242,206,114,.1)}.verdict.approval strong{color:var(--gold);font-size:clamp(1rem,1.9vw,1.35rem)}.action-count{margin:8px 2px 0;color:var(--muted);font-size:.77rem}.action-case button:focus-visible{outline:3px solid var(--gold);outline-offset:2px}.home-evidence{margin-top:12px;padding:12px 14px;border:1px solid rgba(125,249,255,.24);border-radius:11px;background:rgba(0,0,0,.2)}.home-evidence span{display:block;color:var(--cyan);font:800 .65rem ui-monospace,monospace;letter-spacing:.13em}.home-evidence code{display:block;margin:7px 0;color:#d8f5f7;font:11px/1.45 ui-monospace,monospace;overflow-wrap:anywhere}.home-evidence small{color:var(--muted);font-size:.72rem}.gate-slot{min-height:66px;display:grid;place-items:center;margin-top:12px;border:1px dashed rgba(125,249,255,.3);border-radius:11px;color:var(--muted);font-size:.77rem;text-align:center;padding:11px}.gate-slot aml-gate[hidden],#hero-gate-placeholder[hidden]{display:none}.gate-slot aml-gate button{padding:11px 16px;background:var(--cyan);color:#06111e;border:0;border-radius:9px;font:800 .82rem ui-sans-serif,system-ui}.gate-event{margin:7px 0 0;color:#a9c8ce;font:11px ui-monospace,monospace} + .boundary-switch{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:9px;background:#06111e;border-bottom:1px solid var(--line)}.boundary-switch button{padding:11px 8px;border:1px solid transparent;border-radius:11px;background:transparent;color:#a9bfce;font:800 .77rem ui-sans-serif,system-ui;cursor:pointer}.boundary-switch button[aria-checked=true]{border-color:rgba(125,249,255,.43);background:rgba(125,249,255,.12);color:var(--cyan)}.boundary-switch button:focus-visible{outline:3px solid var(--gold);outline-offset:1px}.console-body[hidden]{display:none}.action-target{padding:15px;border:1px solid var(--line);border-radius:13px;background:rgba(255,255,255,.035)}.action-target span{display:block;color:var(--muted);font-size:.76rem}.action-target strong{display:block;margin-top:5px;color:var(--red);font:700 clamp(.91rem,1.3vw,1.03rem) ui-monospace,monospace;overflow-wrap:anywhere}.action-target.approved strong{color:var(--green)}.action-rule{margin:15px 0;color:#a9c8ce;font:12px/1.5 ui-monospace,monospace}.action-rule b{color:var(--gold);font-weight:700}.verdict.approval{border-color:rgba(242,206,114,.45);background:rgba(242,206,114,.1)}.verdict.approval strong{color:var(--gold);font-size:clamp(1rem,1.9vw,1.35rem)}.action-count{margin:8px 2px 0;color:var(--muted);font-size:.77rem}.action-case button:focus-visible{outline:3px solid var(--gold);outline-offset:2px}.home-evidence{margin-top:12px;padding:12px 14px;border:1px solid rgba(125,249,255,.24);border-radius:11px;background:rgba(0,0,0,.2)}.home-evidence span{display:block;color:var(--cyan);font:800 .65rem ui-monospace,monospace;letter-spacing:.13em}.home-evidence code{display:block;margin:7px 0;color:#d8f5f7;font:11px/1.45 ui-monospace,monospace;overflow-wrap:anywhere}.home-evidence small{color:var(--muted);font-size:.72rem}.gate-slot{min-height:66px;display:grid;place-items:center;margin-top:12px;border:1px dashed rgba(125,249,255,.3);border-radius:11px;color:var(--muted);font-size:.77rem;text-align:center;padding:11px}.gate-slot aml-gate[hidden],#hero-gate-placeholder[hidden]{display:none}.gate-slot aml-gate button{padding:11px 16px;background:var(--cyan);color:#06111e;border:0;border-radius:9px;font:800 .82rem ui-sans-serif,system-ui}.gate-event{margin:7px 0 0;color:#a9c8ce;font:11px ui-monospace,monospace} .action-trace{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:5px;margin:13px 0 3px}.trace-step{position:relative;min-width:0;padding:10px 7px;border:1px solid rgba(125,249,255,.25);border-radius:10px;background:linear-gradient(160deg,rgba(125,249,255,.1),rgba(125,249,255,.025))}.trace-step:not(:last-child)::after{content:"→";position:absolute;z-index:1;right:-9px;top:50%;transform:translateY(-50%);color:var(--cyan);font-weight:900}.trace-step span{display:block;color:#91adb9;font:800 .57rem ui-monospace,monospace;letter-spacing:.08em}.trace-step b{display:block;margin-top:4px;color:#e7fbff;font:800 .7rem/1.25 ui-sans-serif,system-ui;overflow-wrap:anywhere}.trace-step[data-stage=decision]{border-color:rgba(255,102,136,.53);background:rgba(255,102,136,.12)}.trace-step[data-stage=decision] b{color:#ff9bb0}.action-trace[data-result=approval] .trace-step[data-stage=decision]{border-color:rgba(242,206,114,.5);background:rgba(242,206,114,.12)}.action-trace[data-result=approval] .trace-step[data-stage=decision] b{color:var(--gold)}.action-trace[data-result=pending] .trace-step[data-stage=decision],.action-trace[data-result=unavailable] .trace-step[data-stage=decision]{border-color:var(--line);background:rgba(255,255,255,.035)}.action-trace[data-result=pending] .trace-step[data-stage=decision] b,.action-trace[data-result=unavailable] .trace-step[data-stage=decision] b{color:var(--muted)}.trace-step[data-stage=dispatch]{border-style:dashed;border-color:rgba(255,255,255,.22);background:rgba(255,255,255,.025)}.trace-step[data-stage=dispatch] b{color:#b7c6d2} .builder-invite{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(290px,.9fr);align-items:center;gap:clamp(22px,5vw,68px);margin:0 0 38px;padding:clamp(24px,4vw,42px);border:1px solid rgba(125,249,255,.4);border-radius:27px;background:radial-gradient(circle at 95% 0,rgba(125,249,255,.15),transparent 45%),linear-gradient(125deg,#102139,#09111f 68%);box-shadow:0 24px 70px #0004}.builder-kicker{color:var(--cyan);font:850 .72rem ui-monospace,monospace;letter-spacing:.15em}.builder-invite h2{margin:10px 0 12px;font-size:clamp(2rem,4.4vw,3.45rem);line-height:1.05;letter-spacing:-.055em}.builder-invite h2 em{font-style:normal;color:var(--cyan)}.builder-invite p{max-width:610px;color:#cbd9e6}.builder-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:22px}.builder-actions a{padding:11px 16px;border-radius:999px;font-weight:850;text-decoration:none}.builder-actions a:first-child{background:var(--cyan);color:#07141b}.builder-actions a:last-child{border:1px solid rgba(125,249,255,.4);color:#ddf8fa}.builder-actions a:focus-visible{outline:3px solid var(--gold);outline-offset:3px}.builder-score{padding:23px;border:1px solid rgba(125,249,255,.25);border-radius:19px;background:rgba(3,11,23,.75)}.builder-score>span{display:block;margin-bottom:17px;color:#9bc5cf;font:800 .67rem ui-monospace,monospace;letter-spacing:.12em}.score-row{display:grid;grid-template-columns:1fr auto;gap:7px 14px;margin:0 0 21px}.score-row:last-of-type{margin-bottom:12px}.score-row b{font-size:.86rem}.score-row strong{font:900 1rem ui-monospace,monospace;color:var(--gold)}.score-row.reference strong{color:var(--green)}.score-track{grid-column:1/-1;height:8px;border-radius:99px;background:#26384a;overflow:hidden}.score-track i{display:block;height:100%;width:68.42%;border-radius:inherit;background:linear-gradient(90deg,#f7a968,var(--gold))}.score-row.reference .score-track i{width:100%;background:linear-gradient(90deg,#75e9dd,var(--green))}.builder-score small{display:block;color:#a8bbc9;font-size:.73rem;line-height:1.5} .atlas-empty{background:linear-gradient(135deg,rgba(125,249,255,.08),rgba(242,206,114,.045));text-align:left}.atlas-empty h3{margin:0 0 6px;color:var(--ink);font-size:1.15rem}.atlas-empty p{margin:0 0 17px;color:#bfcede}.atlas-empty-actions{display:flex;gap:8px;flex-wrap:wrap}.atlas-empty button{padding:9px 13px;border:1px solid rgba(125,249,255,.37);border-radius:999px;background:#102434;color:#d5faff;font:800 .77rem ui-sans-serif,system-ui;cursor:pointer}.atlas-empty button:focus-visible{outline:3px solid var(--gold);outline-offset:2px}.atlas-empty [hidden]{display:none} @@ -61,7 +61,7 @@

AI proposes.
The host decides.

ĀML / TWO BOUNDARIES
-
+

AGENT PROPOSAL · SEND_MESSAGE

Requested destinationoutsider@example.test
@@ -70,7 +70,7 @@

AI proposes.
The host decides.

01 / PROPOSALsend_message
02 / HOST RULEExact route
03 / PLANCalculating
04 / DISPATCHNot connected

No tool callback is connected to this page.

CANONICAL PROPOSAL / SHA-256Connecting to planner…Project vector self-check pending.
-
+
Project-authored preview · no live messageInspect this path ↗
@@ -306,7 +306,7 @@

No magic. No false certainty.

"Continuity guard passes · a call and email route remain in both states."; })); - + From bd5cd49be50722567b3cd37cdbc01a2afb638b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:42:06 -0700 Subject: [PATCH 2/3] Use one-choice semantics for homepage hero (2/3) --- docs/home-hero.js | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/docs/home-hero.js b/docs/home-hero.js index e920c54b..c5cd7869 100644 --- a/docs/home-hero.js +++ b/docs/home-hero.js @@ -11,8 +11,27 @@ let actionGeneration = 0; let bridgeReady = false; function selectButton(selector, key, value) { - document.querySelectorAll(selector).forEach(button => - button.setAttribute("aria-pressed", String(button.dataset[key] === value))); + document.querySelectorAll(selector).forEach(button => { + const selected = button.dataset[key] === value; + button.setAttribute("aria-checked", String(selected)); + button.tabIndex = selected ? 0 : -1; + }); +} + +function wireChoiceGroup(selector, key, choose) { + const buttons = [...document.querySelectorAll(selector)]; + buttons.forEach((button, index) => { + button.addEventListener("click", () => choose(button.dataset[key])); + button.addEventListener("keydown", event => { + const next = event.key === "Home" ? 0 : event.key === "End" ? buttons.length - 1 : + ["ArrowRight", "ArrowDown"].includes(event.key) ? (index + 1) % buttons.length : + ["ArrowLeft", "ArrowUp"].includes(event.key) ? (index + buttons.length - 1) % buttons.length : null; + if (next === null) return; + event.preventDefault(); + buttons[next].focus(); + choose(buttons[next].dataset[key]); + }); + }); } function showTrace(result) { @@ -119,12 +138,9 @@ function showBoundary() { if (action) showAction(); else showInterface(); } -document.querySelectorAll("[data-boundary]").forEach(button => - button.addEventListener("click", () => { boundary = button.dataset.boundary; showBoundary(); })); -document.querySelectorAll("[data-action-destination]").forEach(button => - button.addEventListener("click", () => { recipient = button.dataset.actionDestination; showAction(); })); -document.querySelectorAll("[data-hero-value]").forEach(button => - button.addEventListener("click", () => { restoration = Number(button.dataset.heroValue); showInterface(); })); +wireChoiceGroup("[data-boundary]", "boundary", value => { boundary = value; showBoundary(); }); +wireChoiceGroup("[data-action-destination]", "actionDestination", value => { recipient = value; showAction(); }); +wireChoiceGroup("[data-hero-value]", "heroValue", value => { restoration = Number(value); showInterface(); }); showBoundary(); try { From 31c73a43ee2691aa2e21902986577a544835eb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jacob=20Read=20IV=20=E2=80=94=20Creator=20of=20?= =?UTF-8?q?=C4=80ML=E2=84=A2?= Date: Tue, 13 Oct 2026 11:42:08 -0700 Subject: [PATCH 3/3] Test homepage hero radio semantics and asset hash --- test/home-boundary-selection.test.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/home-boundary-selection.test.js diff --git a/test/home-boundary-selection.test.js b/test/home-boundary-selection.test.js new file mode 100644 index 00000000..8074c4e8 --- /dev/null +++ b/test/home-boundary-selection.test.js @@ -0,0 +1,22 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; +import test from "node:test"; + +test("homepage boundary preview has one-choice semantics and a fresh module URL", () => { + const page = readFileSync(new URL("../docs/index.html", import.meta.url), "utf8"); + const script = readFileSync(new URL("../docs/home-hero.js", import.meta.url), "utf8"); + const hash = createHash("sha256").update(script).digest("hex").slice(0, 12); + assert.ok(page.includes(`home-hero.js?v=${hash}`)); + assert.match(page, /class="boundary-switch" role="radiogroup"/); + assert.match(page, /data-boundary="action" aria-checked="true"/); + assert.match(page, /data-boundary="interface" aria-checked="false" tabindex="-1"/); + assert.match(page, /class="case-buttons action-case" role="radiogroup"/); + assert.match(page, /data-action-destination="team" aria-checked="false" tabindex="-1"/); + assert.match(page, /aria-label="Choose a declared restoration value"/); + assert.match(page, /data-hero-value="5" aria-checked="false" tabindex="-1"/); + assert.match(script, /button\.setAttribute\("aria-checked", String\(selected\)\)/); + assert.match(script, /button\.tabIndex = selected \? 0 : -1/); + assert.match(script, /\["ArrowRight", "ArrowDown"\]/); + assert.match(script, /wireChoiceGroup\("\[data-hero-value\]"/); +});