/** * build-docs.js — renders doc/*.md into real pages under /docs/. * * The documentation was already written and already good; it just was not on the web. * It shipped in the image as raw Markdown, served as application/octet-stream, and * robots.txt disallowed /doc/ — so the only text the site owned that answers a real * question ("how does SecureBit do key exchange", "what does SAS actually verify") * was invisible to search. Meanwhile the site's entire indexable surface was one * address repeated in thirteen languages, which is why every category query lands on * page three or worse. * * So each document gets its own URL, its own
([\s\S]*?)<\/p>/); if (!match) return ''; const text = match[1].replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim(); if (text.length <= 155) return text; const cut = text.slice(0, 155); return `${cut.slice(0, cut.lastIndexOf(' ')).replace(/[,;:.]$/, '')}…`; } // Self-contained and script-free. The CSP below is stricter than the app's for the // same reason the page has no bundle: nothing here needs to execute. const STYLE = ` /* These pages carry no script — that is the point of them — so the theme can only come from the media query. There is no toggle here and no stored preference: a reference page follows the reader's system and nothing else. The values are the same two palettes as src/styles/theme.css, restated because these pages do not load the app's stylesheet and are not going to start for eleven declarations. */ :root { color-scheme: dark; --d-ink: 255, 255, 255; --d-bg: #0f0f11; --d-bg-deep: #0b0b0e; --d-code-bg: #17171c; --d-text: #d6d6dc; --d-heading: #f4f4f6; --d-strong: #e8e8eb; --d-body: #a9a9b3; --d-pre: #c9c9d1; --d-muted: #8a8a92; --d-faint: #6b6b73; --d-accent: #f0892a; --d-accent-rgb: 240, 137, 42; } @media (prefers-color-scheme: light) { :root { color-scheme: light; --d-ink: 0, 0, 0; --d-bg: #fbfbfc; --d-bg-deep: #f2f3f5; --d-code-bg: #f2f3f5; --d-text: #26262c; --d-heading: #0e0e12; --d-strong: #1b1b20; --d-body: #43434c; --d-pre: #2f3340; --d-muted: #63636c; --d-faint: #7c7c85; --d-accent: #b05c08; --d-accent-rgb: 176, 92, 8; } } * { box-sizing: border-box; } body { margin: 0; background: var(--d-bg); color: var(--d-text); font-family: Inter, system-ui, -apple-system, "Segoe UI", sans-serif; font-size: 16px; line-height: 1.68; } .wrap { max-width: 46rem; margin: 0 auto; padding: 28px 24px 90px; } .top { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: baseline; padding-bottom: 16px; margin-bottom: 40px; border-bottom: 1px solid rgba(var(--d-ink),.08); font-size: 13.5px; } .top a { color: var(--d-muted); text-decoration: none; } .top a:hover, .top a:focus-visible { color: var(--d-accent); } .top .brand { color: var(--d-accent); font-weight: 700; letter-spacing: .04em; } .top .here { color: var(--d-text); margin-inline-start: auto; } h1 { font-size: clamp(28px, 5vw, 36px); font-weight: 800; letter-spacing: -1px; line-height: 1.14; color: var(--d-heading); margin: 0 0 24px; } h2 { font-size: 22px; font-weight: 700; letter-spacing: -.4px; color: var(--d-heading); margin: 46px 0 12px; padding-top: 14px; border-top: 1px solid rgba(var(--d-ink),.07); } h3 { font-size: 17.5px; font-weight: 700; color: var(--d-strong); margin: 30px 0 8px; } h4 { font-size: 15.5px; font-weight: 700; color: var(--d-strong); margin: 22px 0 6px; } p, li { color: var(--d-body); } p { margin: 0 0 16px; } ul, ol { padding-inline-start: 22px; margin: 0 0 16px; } li { margin: 5px 0; } a { color: var(--d-accent); text-underline-offset: 2px; } strong { color: var(--d-strong); } code { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .88em; background: var(--d-code-bg); border: 1px solid rgba(var(--d-ink),.07); border-radius: 4px; padding: 1px 5px; color: var(--d-strong); } pre { background: var(--d-bg-deep); border: 1px solid rgba(var(--d-ink),.08); border-radius: 8px; padding: 14px 16px; overflow-x: auto; margin: 0 0 18px; } pre code { background: none; border: 0; padding: 0; font-size: 13px; line-height: 1.62; color: var(--d-pre); } .tablewrap { overflow-x: auto; margin: 0 0 20px; } table { border-collapse: collapse; width: 100%; font-size: 14.5px; min-width: 30rem; } th { text-align: start; color: var(--d-muted); font-weight: 600; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; padding: 0 14px 8px 0; border-bottom: 1px solid rgba(var(--d-ink),.12); } td { padding: 9px 14px 9px 0; border-bottom: 1px solid rgba(var(--d-ink),.06); vertical-align: top; color: var(--d-body); } td:first-child, th:first-child { padding-inline-start: 0; } blockquote { margin: 0 0 18px; padding: 2px 0 2px 16px; border-inline-start: 3px solid rgba(var(--d-accent-rgb),.4); color: var(--d-muted); } hr { border: 0; border-top: 1px solid rgba(var(--d-ink),.08); margin: 34px 0; } img { max-width: 100%; height: auto; } a:focus-visible { outline: 2px solid var(--d-accent); outline-offset: 2px; border-radius: 2px; } .more { margin-top: 64px; padding-top: 22px; border-top: 1px solid rgba(var(--d-ink),.08); } .more h2 { font-size: 13px; letter-spacing: .12em; text-transform: uppercase; color: var(--d-faint); border: 0; margin: 0 0 12px; padding: 0; font-weight: 700; } .more ul { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 4px 24px; } .more li { margin: 0; padding: 7px 0; border-bottom: 1px solid rgba(var(--d-ink),.05); font-size: 14.5px; } @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } } `; /** * FAQ markup, built from the document's own