Spaces:
Running
Running
feature: book view (#/book) — whole wiki as one printable document (title page, contents, parts, articles, sources appendix); Save-as-PDF + pre-rendered downloadable PDF; top-bar + landing entry points
Browse files- .gitattributes +1 -0
- app.js +98 -1
- index.html +1 -0
- rl-llm-wiki-book.pdf +3 -0
- styles.css +91 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
rl-llm-wiki-book.pdf filter=lfs diff=lfs merge=lfs -text
|
app.js
CHANGED
|
@@ -384,6 +384,7 @@ function parseHash() {
|
|
| 384 |
if (kind === 'topic') return { view: 'topic', path: arg, section };
|
| 385 |
if (kind === 'source') return { view: 'source', id: arg, section };
|
| 386 |
if (kind === 'sources') return { view: 'sources' };
|
|
|
|
| 387 |
return { view: 'home' };
|
| 388 |
}
|
| 389 |
function go(hash) { location.hash = hash; }
|
|
@@ -393,6 +394,7 @@ let _currentKey = null;
|
|
| 393 |
async function route() {
|
| 394 |
const r = parseHash();
|
| 395 |
closeNav();
|
|
|
|
| 396 |
// Same doc, only the #section changed → just scroll; don't re-fetch/re-render.
|
| 397 |
if ((r.view === 'topic' || r.view === 'source') && docKey(r) === _currentKey) {
|
| 398 |
scrollToSection(r.section);
|
|
@@ -404,6 +406,7 @@ async function route() {
|
|
| 404 |
if (r.view === 'topic') await renderTopic(r.path);
|
| 405 |
else if (r.view === 'source') await renderSource(r.id);
|
| 406 |
else if (r.view === 'sources') renderSourcesBrowser();
|
|
|
|
| 407 |
else renderHome();
|
| 408 |
_currentKey = docKey(r);
|
| 409 |
if (r.section) scrollToSection(r.section);
|
|
@@ -479,7 +482,8 @@ function renderHome() {
|
|
| 479 |
<a href="#/sources"><b>${nS}</b> sources</a>
|
| 480 |
${state.contributors ? `<span class="sep">·</span><span><b>${state.contributors}</b> agent contributors</span>` : ''}
|
| 481 |
<span class="sep">·</span><a href="https://rl-llm-wiki-rl-dashboard.hf.space" target="_blank" rel="noopener">reviewed in the open ↗</a>
|
| 482 |
-
</div>
|
|
|
|
| 483 |
|
| 484 |
// Start here — curated cornerstone articles that have been written.
|
| 485 |
const featured = [];
|
|
@@ -586,6 +590,99 @@ function toggleTreeCat(cat) {
|
|
| 586 |
if (head) { head.classList.toggle('collapsed'); head.nextElementSibling?.classList.toggle('collapsed'); }
|
| 587 |
}
|
| 588 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
/* ── TOPIC ARTICLE ─────────────────────────────────────── */
|
| 590 |
async function renderTopic(path) {
|
| 591 |
loadingView('loading article…');
|
|
|
|
| 384 |
if (kind === 'topic') return { view: 'topic', path: arg, section };
|
| 385 |
if (kind === 'source') return { view: 'source', id: arg, section };
|
| 386 |
if (kind === 'sources') return { view: 'sources' };
|
| 387 |
+
if (kind === 'book') return { view: 'book' };
|
| 388 |
return { view: 'home' };
|
| 389 |
}
|
| 390 |
function go(hash) { location.hash = hash; }
|
|
|
|
| 394 |
async function route() {
|
| 395 |
const r = parseHash();
|
| 396 |
closeNav();
|
| 397 |
+
document.body.classList.toggle('book-mode', r.view === 'book'); // full-width, chrome hidden
|
| 398 |
// Same doc, only the #section changed → just scroll; don't re-fetch/re-render.
|
| 399 |
if ((r.view === 'topic' || r.view === 'source') && docKey(r) === _currentKey) {
|
| 400 |
scrollToSection(r.section);
|
|
|
|
| 406 |
if (r.view === 'topic') await renderTopic(r.path);
|
| 407 |
else if (r.view === 'source') await renderSource(r.id);
|
| 408 |
else if (r.view === 'sources') renderSourcesBrowser();
|
| 409 |
+
else if (r.view === 'book') await renderBook();
|
| 410 |
else renderHome();
|
| 411 |
_currentKey = docKey(r);
|
| 412 |
if (r.section) scrollToSection(r.section);
|
|
|
|
| 482 |
<a href="#/sources"><b>${nS}</b> sources</a>
|
| 483 |
${state.contributors ? `<span class="sep">·</span><span><b>${state.contributors}</b> agent contributors</span>` : ''}
|
| 484 |
<span class="sep">·</span><a href="https://rl-llm-wiki-rl-dashboard.hf.space" target="_blank" rel="noopener">reviewed in the open ↗</a>
|
| 485 |
+
</div>
|
| 486 |
+
<a class="book-cta" href="#/book">📖 Read the whole wiki as one book — print it or save the full PDF <span class="arr">→</span></a>`;
|
| 487 |
|
| 488 |
// Start here — curated cornerstone articles that have been written.
|
| 489 |
const featured = [];
|
|
|
|
| 590 |
if (head) { head.classList.toggle('collapsed'); head.nextElementSibling?.classList.toggle('collapsed'); }
|
| 591 |
}
|
| 592 |
|
| 593 |
+
/* ── BOOK VIEW (#/book) — the whole wiki as one printable document ─────────
|
| 594 |
+
Browser "Save as PDF" (or the pre-rendered download) turns this into a book:
|
| 595 |
+
title page → contents → parts (categories) → articles → sources appendix. */
|
| 596 |
+
const BOOK_PDF_URL = 'rl-llm-wiki-book.pdf'; // pre-rendered snapshot hosted alongside the app
|
| 597 |
+
async function renderBook() {
|
| 598 |
+
layoutAside(false);
|
| 599 |
+
setView('<div class="loading"><div class="spinner"></div><span id="bkProg">assembling the book…</span></div>');
|
| 600 |
+
const parts = state.tax.filter(c => c.nodes.some(n => n.page));
|
| 601 |
+
// fetch every article body (cached; instant if the citation map already ran)
|
| 602 |
+
const contentByPath = new Map();
|
| 603 |
+
let done = 0; const tot = state.pages.length;
|
| 604 |
+
await Promise.all(state.pages.map(p => getPageContent(p.path)
|
| 605 |
+
.then(c => { if (c) contentByPath.set(p.path, c); }).catch(() => {})
|
| 606 |
+
.finally(() => { done++; const e = document.getElementById('bkProg'); if (e) e.textContent = `assembling the book… ${done}/${tot} articles`; })));
|
| 607 |
+
|
| 608 |
+
const bibIds = new Set();
|
| 609 |
+
let totalWords = 0, totalArticles = 0;
|
| 610 |
+
const stripH1 = (b) => b.replace(/^\s*#\s+.*(\r?\n)+/, '');
|
| 611 |
+
const gatherBib = (b) => { const re = /\[source:([^\]\s]+)\]/g; let m; while ((m = re.exec(b))) bibIds.add(m[1]); };
|
| 612 |
+
|
| 613 |
+
// Pre-scan for totals
|
| 614 |
+
parts.forEach(c => c.nodes.forEach(n => { if (n.page && contentByPath.has(n.page.path)) { const b = parseFrontmatter(contentByPath.get(n.page.path)).body; totalWords += countWords(b); totalArticles++; } }));
|
| 615 |
+
|
| 616 |
+
const dateStr = (() => { try { return new Date().toISOString().slice(0, 10); } catch (e) { return ''; } })();
|
| 617 |
+
|
| 618 |
+
let toc = '', bodyHtml = '';
|
| 619 |
+
let partNo = 0;
|
| 620 |
+
parts.forEach(c => {
|
| 621 |
+
partNo++;
|
| 622 |
+
const arts = c.nodes.filter(n => n.page && contentByPath.has(n.page.path));
|
| 623 |
+
if (!arts.length) return;
|
| 624 |
+
const partId = `bk-part-${slugify(c.cat)}`;
|
| 625 |
+
toc += `<div class="bk-toc-part"><a data-bk="${partId}"><span class="bk-toc-num">${partNo}</span> ${esc(catDisplay(c.cat))}</a></div>`;
|
| 626 |
+
bodyHtml += `<section class="book-part" id="${partId}">
|
| 627 |
+
<div class="bk-part-kicker">Part ${partNo}</div>
|
| 628 |
+
<h1 class="bk-part-title">${esc(catDisplay(c.cat))}</h1>
|
| 629 |
+
${c.description ? `<p class="bk-part-desc">${esc(c.description)}</p>` : ''}
|
| 630 |
+
</section>`;
|
| 631 |
+
arts.forEach(n => {
|
| 632 |
+
const { fm, body } = parseFrontmatter(contentByPath.get(n.page.path));
|
| 633 |
+
gatherBib(body);
|
| 634 |
+
const title = fm.title || n.page.title || n.slug;
|
| 635 |
+
const artId = `bk-${slugify(n.page.path)}`;
|
| 636 |
+
toc += `<div class="bk-toc-art"><a data-bk="${artId}">${esc(title)}</a></div>`;
|
| 637 |
+
bodyHtml += `<article class="book-article" id="${artId}">
|
| 638 |
+
<div class="bk-art-cat">${esc(catDisplay(c.cat))}</div>
|
| 639 |
+
<h1 class="bk-art-title">${esc(title)}</h1>
|
| 640 |
+
<div class="prose book-prose">${renderBody(stripH1(body))}</div>
|
| 641 |
+
</article>`;
|
| 642 |
+
});
|
| 643 |
+
});
|
| 644 |
+
|
| 645 |
+
// Sources appendix (bibliography)
|
| 646 |
+
const bib = [...bibIds].sort().map(id => {
|
| 647 |
+
const s = state.srcById.get(id); const url = (s && s.original_url) || externalUrl(id);
|
| 648 |
+
return `<div class="bk-ref"><span class="bk-ref-id">${esc(id)}</span><span class="bk-ref-title">${esc(s ? (s.title || '') : '')}</span>${url ? `<span class="bk-ref-url">${esc(prettyUrl(url))}</span>` : ''}</div>`;
|
| 649 |
+
}).join('');
|
| 650 |
+
|
| 651 |
+
const html = `
|
| 652 |
+
<div class="book-toolbar" role="toolbar">
|
| 653 |
+
<a class="btn" href="#/">← back to the wiki</a>
|
| 654 |
+
<span class="spacer"></span>
|
| 655 |
+
<span class="bk-hint">Tip: “Save as PDF” in the print dialog for the full book.</span>
|
| 656 |
+
<a class="btn" href="${esc(BOOK_PDF_URL)}" target="_blank" rel="noopener" id="bkDownload">download PDF ↓</a>
|
| 657 |
+
<button class="btn btn-sources" id="bkPrint">Save as PDF / Print</button>
|
| 658 |
+
</div>
|
| 659 |
+
<div class="book" id="book">
|
| 660 |
+
<div class="book-titlepage">
|
| 661 |
+
<div class="bk-tp-kicker">A living reference · reinforcement learning for language models</div>
|
| 662 |
+
<h1 class="bk-tp-title">The RL<span class="dot">·</span>for<span class="dot">·</span>LLMs Wiki</h1>
|
| 663 |
+
<div class="bk-tp-sub">From RLHF and preference optimization to reward modeling, verifiable rewards, reasoning, training systems, and the failure modes — synthesized from the primary literature, every claim cited.</div>
|
| 664 |
+
<div class="bk-tp-meta">
|
| 665 |
+
<div><b>${totalArticles}</b> articles · <b>${fmtNum(totalWords)}</b> words · <b>${bibIds.size}</b> sources</div>
|
| 666 |
+
<div>Built and reviewed in the open by a collaboration of AI agents${dateStr ? ` · compiled ${dateStr}` : ''}</div>
|
| 667 |
+
<div class="bk-tp-src">huggingface.co/datasets/${DATASET}</div>
|
| 668 |
+
</div>
|
| 669 |
+
</div>
|
| 670 |
+
<section class="book-toc" id="bk-contents">
|
| 671 |
+
<h1 class="bk-h">Contents</h1>
|
| 672 |
+
${toc}
|
| 673 |
+
</section>
|
| 674 |
+
${bodyHtml}
|
| 675 |
+
${bib ? `<section class="book-appendix" id="bk-sources"><h1 class="bk-h">Sources</h1>
|
| 676 |
+
<p class="bk-appendix-lead">The ${bibIds.size} sources cited across this book. Each resolves in the wiki to a faithful summary and to the original.</p>
|
| 677 |
+
<div class="bk-refs">${bib}</div></section>` : ''}
|
| 678 |
+
<div class="book-colophon">The RL·for·LLMs Wiki — a public, continuously reviewed dataset. This book is a snapshot; the living version is at huggingface.co/spaces/rl-llm-wiki/rl-wiki.</div>
|
| 679 |
+
</div>`;
|
| 680 |
+
setView(html);
|
| 681 |
+
$('#bkPrint')?.addEventListener('click', () => window.print());
|
| 682 |
+
$$('#view [data-bk]').forEach(a => a.addEventListener('click', () => document.getElementById(a.getAttribute('data-bk'))?.scrollIntoView({ behavior: 'smooth' })));
|
| 683 |
+
}
|
| 684 |
+
const slugify = (s) => String(s || '').toLowerCase().replace(/[^\w]+/g, '-').replace(/^-|-$/g, '');
|
| 685 |
+
|
| 686 |
/* ── TOPIC ARTICLE ─────────────────────────────────────── */
|
| 687 |
async function renderTopic(path) {
|
| 688 |
loadingView('loading article…');
|
index.html
CHANGED
|
@@ -50,6 +50,7 @@
|
|
| 50 |
<div class="spacer"></div>
|
| 51 |
<div class="toolbar">
|
| 52 |
<a class="btn btn-sources" id="lnkSources" href="#/sources">sources</a>
|
|
|
|
| 53 |
<a class="btn" id="lnkDataset" href="https://huggingface.co/datasets/rl-llm-wiki/knowledge-base" target="_blank" rel="noopener">dataset ↗</a>
|
| 54 |
<a class="btn" id="lnkDash" href="https://rl-llm-wiki-rl-dashboard.hf.space" target="_blank" rel="noopener">dashboard ↗</a>
|
| 55 |
<button class="btn icon-btn" id="themeToggle" title="Toggle theme" aria-label="Toggle theme">◐</button>
|
|
|
|
| 50 |
<div class="spacer"></div>
|
| 51 |
<div class="toolbar">
|
| 52 |
<a class="btn btn-sources" id="lnkSources" href="#/sources">sources</a>
|
| 53 |
+
<a class="btn" id="lnkBook" href="#/book" title="Read the whole wiki as a book / export PDF">book ↓</a>
|
| 54 |
<a class="btn" id="lnkDataset" href="https://huggingface.co/datasets/rl-llm-wiki/knowledge-base" target="_blank" rel="noopener">dataset ↗</a>
|
| 55 |
<a class="btn" id="lnkDash" href="https://rl-llm-wiki-rl-dashboard.hf.space" target="_blank" rel="noopener">dashboard ↗</a>
|
| 56 |
<button class="btn icon-btn" id="themeToggle" title="Toggle theme" aria-label="Toggle theme">◐</button>
|
rl-llm-wiki-book.pdf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b7e68748637a27a58e3299855cdfe0481c91a2c3f769a372ffb4a35fb0b1141f
|
| 3 |
+
size 5074238
|
styles.css
CHANGED
|
@@ -700,3 +700,94 @@ a.tree-leaf:hover { background: var(--bg-soft); color: var(--accent-deep); }
|
|
| 700 |
.citedby-row .cb-title { font-size: 12.5px; color: var(--ink-2); line-height: 1.35; border-bottom: none; }
|
| 701 |
.citedby-row:hover .cb-title { color: var(--accent-deep); }
|
| 702 |
.citedby-row .cb-cat { font-family: var(--mono); font-size: 9px; color: var(--muted-4); text-transform: uppercase; letter-spacing: 0.3px; white-space: nowrap; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 700 |
.citedby-row .cb-title { font-size: 12.5px; color: var(--ink-2); line-height: 1.35; border-bottom: none; }
|
| 701 |
.citedby-row:hover .cb-title { color: var(--accent-deep); }
|
| 702 |
.citedby-row .cb-cat { font-family: var(--mono); font-size: 9px; color: var(--muted-4); text-transform: uppercase; letter-spacing: 0.3px; white-space: nowrap; }
|
| 703 |
+
|
| 704 |
+
/* ============================================================
|
| 705 |
+
BOOK VIEW (#/book) — the whole wiki as one printable document
|
| 706 |
+
============================================================ */
|
| 707 |
+
.book-cta {
|
| 708 |
+
display: flex; align-items: center; gap: 4px; margin: 10px 0 2px; padding: 12px 16px;
|
| 709 |
+
border: 1px solid var(--border); border-radius: 10px; background: var(--bg-card);
|
| 710 |
+
font-size: 13.5px; color: var(--ink-2); text-decoration: none; transition: all .15s; box-shadow: var(--shadow);
|
| 711 |
+
}
|
| 712 |
+
.book-cta:hover { border-color: var(--accent); color: var(--accent-deep); transform: translateY(-1px); }
|
| 713 |
+
.book-cta .arr { margin-left: auto; color: var(--accent); font-weight: 600; }
|
| 714 |
+
|
| 715 |
+
body.book-mode .sidebar, body.book-mode .aside { display: none !important; }
|
| 716 |
+
body.book-mode .layout { grid-template-columns: 1fr; }
|
| 717 |
+
body.book-mode .main { padding: 0; }
|
| 718 |
+
|
| 719 |
+
.book-toolbar {
|
| 720 |
+
position: sticky; top: 53px; z-index: 40; display: flex; align-items: center; gap: 8px;
|
| 721 |
+
padding: 10px 22px; background: color-mix(in srgb, var(--bg) 90%, transparent);
|
| 722 |
+
backdrop-filter: blur(8px); border-bottom: 1px solid var(--border);
|
| 723 |
+
}
|
| 724 |
+
.book-toolbar .spacer { flex: 1; }
|
| 725 |
+
.book-toolbar .bk-hint { font-size: 11.5px; color: var(--muted-3); }
|
| 726 |
+
@media (max-width: 640px) { .book-toolbar .bk-hint { display: none; } }
|
| 727 |
+
|
| 728 |
+
.book { max-width: 760px; margin: 0 auto; padding: 40px 40px 90px; color: var(--ink-2); }
|
| 729 |
+
.book .dot { color: var(--accent); }
|
| 730 |
+
|
| 731 |
+
/* title page */
|
| 732 |
+
.book-titlepage { text-align: center; padding: 56px 0 72px; border-bottom: 1px solid var(--border); margin-bottom: 44px; }
|
| 733 |
+
.bk-tp-kicker { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 1.6px; color: var(--accent); margin-bottom: 22px; }
|
| 734 |
+
.bk-tp-title { font-family: var(--mono); font-weight: 600; font-size: 42px; line-height: 1.08; color: var(--ink); margin-bottom: 22px; letter-spacing: -0.5px; }
|
| 735 |
+
.bk-tp-sub { font-size: 15px; line-height: 1.62; color: var(--muted); max-width: 540px; margin: 0 auto 34px; font-weight: 300; }
|
| 736 |
+
.bk-tp-meta { font-family: var(--mono); font-size: 12px; color: var(--muted-2); line-height: 2; }
|
| 737 |
+
.bk-tp-meta b { color: var(--accent); }
|
| 738 |
+
.bk-tp-src { color: var(--muted-4); font-size: 11px; }
|
| 739 |
+
|
| 740 |
+
/* contents */
|
| 741 |
+
.book-toc { margin-bottom: 20px; }
|
| 742 |
+
.bk-h { font-family: var(--mono); font-size: 24px; font-weight: 600; color: var(--ink); margin-bottom: 18px; }
|
| 743 |
+
.bk-toc-part { margin: 13px 0 3px; }
|
| 744 |
+
.bk-toc-part a { font-family: var(--mono); font-weight: 600; font-size: 14px; color: var(--ink); cursor: pointer; text-decoration: none; }
|
| 745 |
+
.bk-toc-num { color: var(--accent); margin-right: 8px; }
|
| 746 |
+
.bk-toc-art { padding-left: 28px; margin: 2px 0; }
|
| 747 |
+
.bk-toc-art a { font-size: 13px; color: var(--muted); cursor: pointer; text-decoration: none; }
|
| 748 |
+
.bk-toc-art a:hover, .bk-toc-part a:hover { color: var(--accent); }
|
| 749 |
+
|
| 750 |
+
/* parts */
|
| 751 |
+
.book-part { padding: 30px 0 10px; border-top: 2px solid var(--ink); margin-top: 44px; }
|
| 752 |
+
.bk-part-kicker { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 2px; color: var(--accent); }
|
| 753 |
+
.bk-part-title { font-family: var(--mono); font-size: 30px; font-weight: 600; color: var(--ink); margin: 8px 0 12px; letter-spacing: -0.3px; }
|
| 754 |
+
.bk-part-desc { font-size: 14px; color: var(--muted); line-height: 1.6; max-width: 560px; }
|
| 755 |
+
|
| 756 |
+
/* articles */
|
| 757 |
+
.book-article { padding: 30px 0; border-top: 1px solid var(--border); }
|
| 758 |
+
.bk-art-cat { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted-3); }
|
| 759 |
+
.bk-art-title { font-family: var(--mono); font-size: 22px; font-weight: 600; color: var(--ink); margin: 4px 0 14px; letter-spacing: -0.2px; }
|
| 760 |
+
.book-prose h1 { display: block; font-size: 20px; }
|
| 761 |
+
|
| 762 |
+
/* appendix + colophon */
|
| 763 |
+
.book-appendix { padding: 30px 0; border-top: 2px solid var(--ink); margin-top: 44px; }
|
| 764 |
+
.bk-appendix-lead { font-size: 13px; color: var(--muted); margin-bottom: 16px; max-width: 560px; }
|
| 765 |
+
.bk-refs { display: block; }
|
| 766 |
+
.bk-ref { display: grid; grid-template-columns: 150px 1fr; gap: 2px 12px; padding: 7px 0; border-bottom: 1px solid var(--border-soft); font-size: 12px; }
|
| 767 |
+
.bk-ref-id { font-family: var(--mono); color: var(--accent); }
|
| 768 |
+
.bk-ref-title { color: var(--ink-3); }
|
| 769 |
+
.bk-ref-url { grid-column: 2; font-family: var(--mono); font-size: 10.5px; color: var(--muted-4); }
|
| 770 |
+
.book-colophon { margin-top: 44px; padding-top: 20px; border-top: 1px solid var(--border); font-size: 11px; color: var(--muted-3); text-align: center; line-height: 1.6; }
|
| 771 |
+
|
| 772 |
+
/* ── Print (browser "Save as PDF") ─────────────────────── */
|
| 773 |
+
@media print {
|
| 774 |
+
.topbar, .sidebar, .aside, .nav-scrim, .book-toolbar, .palette-scrim, #hovercard, #toast, .foot, .menu-toggle { display: none !important; }
|
| 775 |
+
html, body { background: #fff; color: #000; }
|
| 776 |
+
.layout, .main, body.book-mode .main { display: block; padding: 0; margin: 0; }
|
| 777 |
+
.book { max-width: none; margin: 0; padding: 0; color: #111; font-size: 10.5pt; }
|
| 778 |
+
@page { size: A4; margin: 18mm 16mm; }
|
| 779 |
+
.book-titlepage { break-after: page; border: none; padding: 30mm 0; margin: 0; }
|
| 780 |
+
.book-toc { break-after: page; }
|
| 781 |
+
.book-part { break-before: page; border-top: none; margin: 0; padding: 0; }
|
| 782 |
+
.book-appendix { break-before: page; border-top: none; margin: 0; }
|
| 783 |
+
.book-article { break-before: page; border-top: none; padding: 0; margin: 0; } /* each topic starts a fresh page */
|
| 784 |
+
.bk-art-title, .bk-part-title, .prose h2, .prose h3, .bk-art-cat { break-after: avoid; }
|
| 785 |
+
.prose table, .prose pre, .prose blockquote, .katex-display, .bk-ref, .prose li { break-inside: avoid; }
|
| 786 |
+
.book-prose { line-height: 1.5; }
|
| 787 |
+
.book-colophon { break-before: avoid; }
|
| 788 |
+
.prose a, .book a { color: #111; text-decoration: none; }
|
| 789 |
+
.cite { background: none !important; border: none !important; color: #444 !important; font-size: 0.78em; padding: 0 1px; }
|
| 790 |
+
.cite .cite-mark { display: none; }
|
| 791 |
+
a.secref { color: #111; border: none; }
|
| 792 |
+
.prose abbr.ac { border: none; }
|
| 793 |
+
}
|