Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
152 lines (152 loc) · 7.98 KB
/
Copy pathindex.html
File metadata and controls
152 lines (152 loc) · 7.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<!doctype html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8" />
<script>
// Dark is the only theme. Pin it before first paint, and overwrite any
// 'light' a visitor stored back when the header carried a theme toggle.
(function () {
try {
localStorage.setItem('theme', 'dark');
} catch (e) {}
document.documentElement.setAttribute('data-theme', 'dark');
})();
</script>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta
name="description"
content="Interactive Merkle tree lab: build a binary hash tree with real SHA-256, generate O(log n) inclusion proofs, and watch tamper detection invalidate the root."
/>
<meta name="theme-color" content="#161715" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#efe9dc" media="(prefers-color-scheme: light)" />
<title>Merkle Vault — Interactive Merkle Tree & Inclusion Proof Lab</title>
</head>
<body>
<style>
.cl-topbar{
/* The bar is always dark (#0b1512) regardless of page theme, so header
text must always be light. Mix each lab's --accent toward near-white so
the hue still tints the brand but contrast stays >=4.5:1 on the bar even
when a lab's accent is dark (e.g. a light-theme #1a56db). */
--cl-ink: color-mix(in srgb, var(--accent, #35d6bb) 60%, #eafff8);
display:flex;align-items:center;justify-content:space-between;gap:1rem;
padding:.7rem clamp(.9rem,3vw,2rem);
background:#0b1512;
border-bottom:1px solid color-mix(in srgb, var(--accent, #35d6bb) 22%, transparent);
font-family:ui-monospace,"SFMono-Regular",Menlo,Consolas,monospace;
position:sticky;top:0;z-index:1000;
}
.cl-brand{display:flex;align-items:center;gap:.8rem;text-decoration:none}
.cl-badge{
flex:0 0 auto;display:grid;place-items:center;width:40px;height:40px;
border:1.5px solid var(--accent, #35d6bb);border-radius:10px;
color:var(--cl-ink);font-weight:700;font-size:.95rem;letter-spacing:.05em;
}
.cl-brand-text{display:flex;flex-direction:column;line-height:1.15}
.cl-title{color:#eafff8;font-weight:700;font-size:1.05rem;letter-spacing:.12em}
.cl-sub{color:#5e8b80;font-size:.72rem;letter-spacing:.04em}
.cl-actions{display:flex;align-items:center;gap:.5rem}
.cl-btn{
display:inline-flex;align-items:center;gap:.4rem;
padding:.5rem .85rem;border-radius:10px;
border:1px solid color-mix(in srgb, var(--accent, #35d6bb) 38%, transparent);
background:transparent;color:var(--cl-ink);cursor:pointer;
font:inherit;font-size:.78rem;font-weight:700;letter-spacing:.1em;
text-transform:uppercase;text-decoration:none;transition:background .15s,border-color .15s,color .15s;
}
.cl-btn:hover{
background:color-mix(in srgb, var(--accent, #35d6bb) 14%, transparent);
border-color:var(--accent, #35d6bb);color:#eafff8;
}
.cl-btn svg{width:15px;height:15px}
.cl-icon{padding:.5rem .6rem;font-size:.95rem}
/* Skip-to-content link: first focusable element so keyboard users can bypass
this sticky header (WCAG 2.4.1). Hidden until focused. Fixed light-on-dark
pair (not --accent, which can be a dark hue on some labs) keeps contrast AA. */
.cl-skip-link{
position:absolute;left:.5rem;top:-3rem;z-index:1001;
padding:.55rem 1rem;border-radius:0 0 10px 10px;
background:#eafff8;color:#0b1512;
font-family:ui-monospace,"SFMono-Regular",Menlo,Consolas,monospace;
font-weight:700;font-size:.8rem;letter-spacing:.04em;text-decoration:none;
transition:top .15s ease;
}
.cl-skip-link:focus{top:0;outline:2px solid var(--accent,#35d6bb);outline-offset:2px;}
/* Dark is the only theme, so hide any in-page theme toggle a lab still
ships. The element stays in the DOM so the lab's theme JS keeps working. */
body :is(#theme-toggle,#themeToggle,.theme-toggle,.theme-toggle-btn,[data-theme-toggle]){display:none!important}
@media(max-width:560px){
.cl-sub{display:none}
.cl-btn span{display:none}
.cl-btn svg{margin:0}
}
/* cl-a11y-touch: WCAG 2.5.5 target size on touch pointers only, so the
bar keeps its density on desktop where a mouse is precise. */
@media (pointer: coarse){
/* Height is free -- it costs no horizontal room, so every touch target
gets the full 44px vertically. */
.cl-btn,.cl-icon{min-height:44px}
}
/* Width only where the bar can afford it. Below ~380px, three 44px-wide
buttons plus the brand overflow the viewport, and forcing horizontal
scroll would trade a 2.5.5 (AAA) gain for a 1.4.10 reflow failure.
The buttons stay ~31px wide there, comfortably over the 24px 2.5.8 (AA) floor. */
@media (pointer: coarse) and (min-width: 380px){
.cl-btn,.cl-icon{min-width:44px}
}
</style>
<a class="cl-skip-link" href="#app">Skip to content</a>
<header class="cl-topbar" role="banner" aria-label="Crypto Lab">
<a class="cl-brand" href="https://crypto-lab.systemslibrarian.dev/" aria-label="Crypto Lab home">
<span class="cl-badge">CL</span>
<span class="cl-brand-text">
<span class="cl-title">CRYPTO LAB</span>
<span class="cl-sub">systemslibrarian.dev</span>
</span>
</a>
<nav class="cl-actions" aria-label="Crypto Lab navigation">
<a class="cl-btn" href="https://crypto-lab.systemslibrarian.dev/" aria-label="Crypto Lab menu">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
<span>Menu</span>
</a>
<a class="cl-btn" href="https://github.com/systemslibrarian/crypto-lab-merkle-vault" target="_blank" rel="noopener" aria-label="View this project on GitHub">
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.2.8-.6v-2c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1.1-.7 0-.7 0-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.3 1.2a11.4 11.4 0 0 1 6 0C17.3 5 18.3 5.3 18.3 5.3c.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.3c0 .4.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"/></svg>
<span>GitHub</span>
</a>
</nav>
</header>
<script>
(function () {
// A page may have only one banner landmark. This shared site header is it;
// demote any banner a lab also declared on its own page header (explicit
// role="banner", or an implicit <header> child of <body>) so landmarks stay unique.
function dedupeBanner() {
var bar = document.querySelector('.cl-topbar');
if (!bar) return;
document.querySelectorAll('[role="banner"]').forEach(function (el) {
if (el !== bar) el.setAttribute('role', 'group');
});
Array.prototype.forEach.call(document.querySelectorAll('header'), function (el) {
if (el === bar || el.hasAttribute('role')) return;
// <header> only implies banner when it is NOT scoped inside sectioning content.
// Checking closest() catches heroes nested in #app, which body.children missed.
if (el.closest('main, article, aside, nav, section')) return;
el.setAttribute('role', 'group');
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', dedupeBanner);
else dedupeBanner();
})();
</script>
<a class="skip-link" href="#main">Skip to main content</a>
<div id="app"></div>
<!--
Persistent screen-reader announcer. It lives outside #app because the app
re-renders #app.innerHTML wholesale, which would otherwise destroy any
live region (and its pending announcement) before assistive tech reads it.
-->
<div id="sr-announcer" class="sr-only" role="status" aria-live="polite" aria-atomic="true"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>