From 67d170a5c0a6e3ab6005efe05fb5c2b925f2c77f Mon Sep 17 00:00:00 2001 From: bong-water-water-bong <277547417+bong-water-water-bong@users.noreply.github.com> Date: Thu, 24 Sep 2026 07:07:33 -0300 Subject: [PATCH 1/2] site: a blog, with an Atom feed, and its first post Posts are blog/YYYY-MM-DD-.md. tools/site.py renders each as blog-.html with its date under the title and newer/older links, lists them newest first on blog.html, and writes feed.xml (Atom, full content). The top bar gets a Blog link and every page advertises the feed. First post: 1bit.MONSTER is now 1bit engine. Co-Authored-By: Claude Opus 5.5 --- ...6-09-24-1bit-monster-is-now-1bit-engine.md | 38 +++++++++ site/style.css | 10 +++ site/template.html | 2 + tools/site.py | 79 ++++++++++++++++++- 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 blog/2026-09-24-1bit-monster-is-now-1bit-engine.md diff --git a/blog/2026-09-24-1bit-monster-is-now-1bit-engine.md b/blog/2026-09-24-1bit-monster-is-now-1bit-engine.md new file mode 100644 index 000000000..7c81cd7f0 --- /dev/null +++ b/blog/2026-09-24-1bit-monster-is-now-1bit-engine.md @@ -0,0 +1,38 @@ + + +# 1bit.MONSTER is now 1bit engine + +1bit.MONSTER grew fast: an inference engine, a model zoo, benchmarks, a store and a lot of +experiments, all in one repository with all of its history. The engine that came out of it is +now its own project, [1bit engine](../README.md), and this site is its home. + +The engine is a clean repository. It holds verified code only, without the development history, +and it is Apache-2.0. Each piece is ported over once it passes its end-to-end test on Strix Halo; +the [porting map](../docs/PORTING.md) shows what has moved and what is next. + +What it is: one engine behind an OpenAI-compatible API, running inside +[Lemonade](../docs/lemonade.md) as one of its backends. The same `1bit serve` drives the +[XDNA 2 NPU](../docs/npu.md), [HRX and Vulkan](../docs/hrx.md) on the Radeon iGPU, +[ZINC](../docs/zinc.md) for NVIDIA and Apple GPUs, and [MLX](../docs/apple.md) on Apple Silicon. +Measured numbers live on the [wiki](https://github.com/1bit-MONSTER/engine/wiki). + +The old 1bit.MONSTER site and its posts stay online in the +[1bit.MONSTER archive](https://1bit-monster.github.io/1bit-MONSTER/). Old links to it land on a +page that points at their archived copy. + +New posts will show up here, and feed readers can follow the blog's Atom feed. diff --git a/site/style.css b/site/style.css index 6f60ca91f..02a2de0bb 100644 --- a/site/style.css +++ b/site/style.css @@ -154,6 +154,16 @@ tr:last-child td { border-bottom: 0; } body.home article > h1 { font-size: 2.6rem; margin-bottom: 14px; } body.home article > h1 + p { font-size: 1.15rem; color: var(--fg-soft); } +/* blog */ +.date { margin: -12px 0 24px; color: var(--fg-soft); font-size: 14px; } +.post { padding: 20px 0; border-top: 1px solid var(--line); } +.post:first-of-type { border-top: 0; padding-top: 0; } +.post .date { margin: 0 0 4px; } +.post h2 { margin: 0 0 8px; padding: 0; border: 0; font-size: 1.3rem; } +.post h2 a { color: var(--fg); text-decoration: none; } +.post h2 a:hover { text-decoration: underline; } +.post p:last-child { margin: 0; color: var(--fg-soft); } + /* previous / next */ .pager { display: flex; justify-content: space-between; gap: 16px; max-width: 760px; margin-top: 48px; } .pager a { diff --git a/site/template.html b/site/template.html index 463af6d1e..bf0dc2192 100644 --- a/site/template.html +++ b/site/template.html @@ -22,6 +22,7 @@ {{title}} + @@ -29,6 +30,7 @@ 1b1bit engine diff --git a/tools/site.py b/tools/site.py index 4c2a18f0d..3f6ec01ec 100644 --- a/tools/site.py +++ b/tools/site.py @@ -19,7 +19,8 @@ # Builds the documentation site (GitHub Pages, .github/workflows/pages.yml) from # README.md and docs/*.md: README becomes index.html, docs/.md becomes # .html. Every doc is published; the ones NAV does not place land under -# "More", so a new doc never goes missing. Links to other docs become page links; +# "More", so a new doc never goes missing. Posts in blog/YYYY-MM-DD-.md become +# blog-.html, listed newest first on blog.html and in the Atom feed feed.xml. Links to other docs become page links; # links to other files in the repository go to them on GitHub. # # Visitor counts: with GOATCOUNTER set to a GoatCounter site code (pages.yml passes the @@ -27,6 +28,7 @@ # no cookies. Unset, as in a local preview, the pages carry no analytics at all. # # Needs python-markdown (pip install markdown). +import datetime import html import os import pathlib @@ -59,6 +61,27 @@ def sources(): return docs +def posts(): + """[(date, slug, path)], newest first.""" + out = [] + for p in (ROOT / "blog").glob("*.md"): + m = re.fullmatch(r"(\d{4}-\d{2}-\d{2})-([a-z0-9-]+)", p.stem) + if not m: + sys.exit(f"blog post names are YYYY-MM-DD-.md: {p.name}") + out.append((m.group(1), m.group(2), p)) + return sorted(out, reverse=True) + + +def summary_of(text): + """The first paragraph after the title, as plain text.""" + for para in re.split(r"\n\s*\n", strip_notice(text)): + para = para.strip() + if para and not para.startswith(("#", ">", "|", "-", "```")): + para = re.sub(r"\[([^\]]*)\]\([^)]*\)", r"\1", para) + return re.sub(r"[`*_]", "", " ".join(para.split())) + return "" + + def nav_groups(docs): placed = {name for _, items in NAV for name, _ in items} groups = [(t, [(n, l) for n, l in items if n in docs]) for t, items in NAV] @@ -177,6 +200,59 @@ def not_found(template): .replace("{{repo}}", REPO)) +def page(template, **fields): + for key, value in fields.items(): + template = template.replace("{{" + key + "}}", value) + return template.replace("{{repo}}", REPO) + + +def blog(template, out, docs, labels): + items = posts() + if not items: + return + names = [f"blog-{slug}" for _, slug, _ in items] + side = ('

Blog

") + + def sidebar_for(current): + text = side + for n in names: + text = text.replace("{{cur_" + n + "}}", ' class="current" aria-current="page"' if n == current else "") + return text + + rows = [] + for i, (date, slug, src) in enumerate(items): + text = strip_notice(src.read_text()) + title = title_of(text) + # the post's own date line under its title + body = render(rewrite_links(text, src, docs, labels)) + body = re.sub(r"()", rf'\1\n

', body, count=1) + newer = f'' if i > 0 else "" + older = f'' if i + 1 < len(items) else "" + (out / f"{names[i]}.html").write_text(page(template, title=html.escape(title) + " · 1bit engine", home="doc post", + sidebar=sidebar_for(names[i]), content=body, pager=f'', + source=f"{REPO}/blob/main/{src.relative_to(ROOT).as_posix()}")) + rows.append((date, names[i], title, summary_of(text), body)) + + listing = '

Blog

\n

Atom feed

\n' + "\n".join( + f'

' + f'

{html.escape(t)}

{html.escape(s)}

' for d, n, t, s, _ in rows) + (out / "blog.html").write_text(page(template, title="Blog · 1bit engine", home="doc", sidebar=sidebar_for(""), + content=listing, pager="", source=f"{REPO}/tree/main/blog")) + + def stamp(d): + return d + "T00:00:00Z" + entries = "".join( + f"{html.escape(t)}{SITE}{n}.html" + f"{stamp(d)}{html.escape(s)}" + f"{html.escape(b)}" for d, n, t, s, b in rows) + (out / "feed.xml").write_text( + '\n' + f'1bit engine{SITE}' + f"{stamp(rows[0][0])}bong-water-water-bong{entries}\n") + + def main(): out = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else "_site") if out.exists(): @@ -204,6 +280,7 @@ def main(): .replace("{{repo}}", REPO)) (out / f"{name}.html").write_text(page) (out / "404.html").write_text(not_found(template)) + blog(template, out, docs, labels) shutil.copy(ROOT / "site" / "style.css", out / "style.css") (out / ".nojekyll").write_text("") print(f"{len(docs)} pages -> {out}") From e6c3720d30de08b53bc65802fe2d0a5f8f67a000 Mon Sep 17 00:00:00 2001 From: bong-water-water-bong <277547417+bong-water-water-bong@users.noreply.github.com> Date: Thu, 24 Sep 2026 07:26:48 -0300 Subject: [PATCH 2/2] site: the 1bit.MONSTER template, a cleaned-up blog, three new posts The site now uses the 1bit.MONSTER site's template: its tokens, relay mark, top bar with the light/auto/dark switch, centered hero, site cards, blog log, article column and footer, with a docs layout in the same tokens. tools/site.py builds a home page (hero, latest post, a card per page), overview.html from the README, docs.html (the docs index), the docs with a grouped sidebar, the blog and its Atom feed, and the 404. The blog: three posts on the move from 1bit.MONSTER to 1bit engine (the new direction, the first week's milestones, HRX prefill with Vulkan decode on one KV cache), with every number taken from the docs, the wiki and the merged PRs. blog/archive.tsv lists the 15 old posts worth keeping, linked to the archived old site; the 115 machine-generated model posts, the posts flagged as corrected, and the ones about the old monorepo's products are left out. Posts may carry tags and link to each other. theme.js and the favicon come from the old site; copyright.py learns *.js and *.svg and exempts images, and pages.yml rebuilds on blog/ changes. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/pages.yml | 4 +- ...6-09-24-1bit-monster-is-now-1bit-engine.md | 38 -- blog/2026-09-24-first-week-milestones.md | 52 ++ blog/2026-09-24-hrx-prefill-vulkan-decode.md | 55 +++ ...6-09-24-new-direction-from-1bit-monster.md | 46 ++ blog/archive.tsv | 15 + site/assets/apple-touch-icon.png | Bin 0 -> 1018 bytes site/assets/favicon-16x16.png | Bin 0 -> 153 bytes site/assets/favicon-32x32.png | Bin 0 -> 205 bytes site/assets/favicon.ico | Bin 0 -> 297 bytes site/assets/favicon.svg | 44 ++ site/style.css | 449 ++++++++++++------ site/template.html | 79 +-- site/theme.js | 66 +++ tools/copyright.py | 4 +- tools/site.py | 442 +++++++++++------ 16 files changed, 913 insertions(+), 381 deletions(-) delete mode 100644 blog/2026-09-24-1bit-monster-is-now-1bit-engine.md create mode 100644 blog/2026-09-24-first-week-milestones.md create mode 100644 blog/2026-09-24-hrx-prefill-vulkan-decode.md create mode 100644 blog/2026-09-24-new-direction-from-1bit-monster.md create mode 100644 blog/archive.tsv create mode 100644 site/assets/apple-touch-icon.png create mode 100644 site/assets/favicon-16x16.png create mode 100644 site/assets/favicon-32x32.png create mode 100644 site/assets/favicon.ico create mode 100644 site/assets/favicon.svg create mode 100644 site/theme.js diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 7dee1e705..57aee0799 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -22,9 +22,9 @@ name: pages on: push: branches: [main] - paths: [README.md, "docs/**", "site/**", tools/site.py, .github/workflows/pages.yml] + paths: [README.md, "docs/**", "blog/**", "site/**", tools/site.py, .github/workflows/pages.yml] pull_request: - paths: [README.md, "docs/**", "site/**", tools/site.py, .github/workflows/pages.yml] + paths: [README.md, "docs/**", "blog/**", "site/**", tools/site.py, .github/workflows/pages.yml] workflow_dispatch: permissions: diff --git a/blog/2026-09-24-1bit-monster-is-now-1bit-engine.md b/blog/2026-09-24-1bit-monster-is-now-1bit-engine.md deleted file mode 100644 index 7c81cd7f0..000000000 --- a/blog/2026-09-24-1bit-monster-is-now-1bit-engine.md +++ /dev/null @@ -1,38 +0,0 @@ - - -# 1bit.MONSTER is now 1bit engine - -1bit.MONSTER grew fast: an inference engine, a model zoo, benchmarks, a store and a lot of -experiments, all in one repository with all of its history. The engine that came out of it is -now its own project, [1bit engine](../README.md), and this site is its home. - -The engine is a clean repository. It holds verified code only, without the development history, -and it is Apache-2.0. Each piece is ported over once it passes its end-to-end test on Strix Halo; -the [porting map](../docs/PORTING.md) shows what has moved and what is next. - -What it is: one engine behind an OpenAI-compatible API, running inside -[Lemonade](../docs/lemonade.md) as one of its backends. The same `1bit serve` drives the -[XDNA 2 NPU](../docs/npu.md), [HRX and Vulkan](../docs/hrx.md) on the Radeon iGPU, -[ZINC](../docs/zinc.md) for NVIDIA and Apple GPUs, and [MLX](../docs/apple.md) on Apple Silicon. -Measured numbers live on the [wiki](https://github.com/1bit-MONSTER/engine/wiki). - -The old 1bit.MONSTER site and its posts stay online in the -[1bit.MONSTER archive](https://1bit-monster.github.io/1bit-MONSTER/). Old links to it land on a -page that points at their archived copy. - -New posts will show up here, and feed readers can follow the blog's Atom feed. diff --git a/blog/2026-09-24-first-week-milestones.md b/blog/2026-09-24-first-week-milestones.md new file mode 100644 index 000000000..32101df95 --- /dev/null +++ b/blog/2026-09-24-first-week-milestones.md @@ -0,0 +1,52 @@ + +tags: milestones, npu, hrx, vulkan, zinc + +# The first week of 1bit engine + +The clean repository started on 2026-09-22. In its first three days, the engine came up on every device it targets, inside Lemonade, with each step verified on real hardware before it merged. These are the milestones, with the numbers and where they come from. + +## The NPU, on full ELFs generated in C++ + +The XDNA 2 NPU now runs without an xclbin and without per-context kernel files. The engine generates each kernel's full ELF (the design plus its control code) in C++ when the model loads, and XRT opens it directly; for each token, the host submits every layer and the lm head as one runlist ([#8](https://github.com/1bit-MONSTER/engine/pull/8), [#9](https://github.com/1bit-MONSTER/engine/pull/9)). + +- Qwen3-0.6B decodes at **91 tok/s** (11.0 ms per token), and **87 tok/s** served through Lemonade. +- The XDNA driver and its XRT are pinned to upstream and built privately ([#12](https://github.com/1bit-MONSTER/engine/pull/12)). +- The layer kernel itself is being rebuilt from scratch as our own open kernel ("dx", on IRON and Peano). RMSNorm, RoPE and attention pass their tests so far, correctly rounded to bf16. + +## HRX and Vulkan in one build, then both at once + +The Radeon iGPU has two devices in one llama.cpp build: `HRX0` on AMD's HRX runtime and `Vulkan0`. HRX follows AMD's live ggml-hrx, pinned to the pair AMD tests and bumped daily ([#11](https://github.com/1bit-MONSTER/engine/pull/11)); Vulkan has its own pin on upstream llama.cpp's latest release ([#28](https://github.com/1bit-MONSTER/engine/pull/28)). + +- **HRX went from 3-5x slower than Vulkan to close behind it.** Its prefill is now 1.3-1.7x Vulkan's, and its decode within 10% on 4-bit files. +- **Our patches make HRX correct on more models** ([#29](https://github.com/1bit-MONSTER/engine/pull/29)): an IQ3_XXS kernel, and op claims that only take the nodes HRX can actually run. `test-backend-ops` on HRX0 went from about 1150 failures to **791 passing, 0 failing**, and Unsloth's sub-4-bit files, which failed on HRX, now come within 1% of Vulkan's perplexity. +- **Prefill on HRX, decode on Vulkan, with zero copies** ([#30](https://github.com/1bit-MONSTER/engine/pull/30)): a whole 8192-token request on Qwen2.5-7B finishes **26% sooner**. [How it works](2026-09-24-hrx-prefill-vulkan-decode.md). + +## Beyond AMD + +- **NVIDIA, through ZINC's CUDA backend** ([#14](https://github.com/1bit-MONSTER/engine/pull/14)): Qwen3.5-9B at **167-173 tok/s** on an RTX 5090, and 295 tok/s through ZINC's Vulkan backend on Strix Halo. +- **Apple Silicon**, through Lemonade's `mlx` recipe and lemon-mlx-engine, verified on an M4 ([#10](https://github.com/1bit-MONSTER/engine/pull/10)). + +## Underneath + +- **Tokenizers:** Hugging Face `tokenizers` v0.23.2 behind our C ABI reads any model's `tokenizer.json`, byte-exact on 18 models ([#19](https://github.com/1bit-MONSTER/engine/pull/19)). +- **Quantization:** on the GPU, Unsloth's Dynamic UD-Q4_K_XL beats Q4_K_M: 17% lower KL divergence for 2% more size. On the NPU, a ternary model converts to 4-bit exactly, and XDNA 2 multiplies int8 by int4 natively ([wiki](https://github.com/1bit-MONSTER/engine/wiki/Quantization)). +- **The Linux kernel** is pinned to upstream v7.3-rc4 with `amdxdna` in-tree ([#17](https://github.com/1bit-MONSTER/engine/pull/17)), and the **Laya router**'s source and checkpoints are pinned and hash-verified ([#18](https://github.com/1bit-MONSTER/engine/pull/18)). + +## Next + +Step 4 is the Laya router, which decides where each request runs. After it: every Hugging Face architecture, kept current by a daily census. The [porting map](../docs/PORTING.md) tracks it, and the [wiki](https://github.com/1bit-MONSTER/engine/wiki) keeps every measured number with how it was taken. diff --git a/blog/2026-09-24-hrx-prefill-vulkan-decode.md b/blog/2026-09-24-hrx-prefill-vulkan-decode.md new file mode 100644 index 000000000..b28eb04fe --- /dev/null +++ b/blog/2026-09-24-hrx-prefill-vulkan-decode.md @@ -0,0 +1,55 @@ + +tags: hrx, vulkan, zero-copy + +# Prefill on HRX, decode on Vulkan, one KV cache + +On Strix Halo's Radeon iGPU, AMD's HRX runtime prefills a prompt faster than Vulkan, and Vulkan decodes faster than HRX. `1bit serve --device vulkan --prefill-device hrx` now uses both on the same request, and the key-value cache never gets copied between them. + +## The idea + +A request has two phases. **Prefill** reads the whole prompt at once and is compute-bound; **decode** makes one token at a time and is bound by memory bandwidth. Our one llama.cpp build carries both GPU backends, `HRX0` and `Vulkan0`, so the engine loads the model on each, runs the prompt prefix on HRX, and hands the rest of the request to Vulkan. + +The hand-off is the hard part. Prefill's output is the KV cache: every layer's keys and values for every prompt token. Copying it from one backend to the other would cost the time the split saves. + +## Zero copies + +So both contexts use **one** KV cache: + +- HRX allocates it in its own device memory and exports it as a **dma-buf** (through HSA). +- Vulkan imports the same memory with `VK_EXT_external_memory_dma_buf` and reads it in place, at 110.6 GB/s against 113.2 GB/s for its own allocations. +- The only thing that moves between them is the cache's bookkeeping: which cells hold which positions of which sequence. + +We tried sharing host memory first. It worked, but it cost Vulkan half its decode speed; device memory through a dma-buf does not. + +Both sides compute the cache's layout independently, so they must agree on it exactly: tensors on 4 KiB boundaries, split into chunks of at most 1 GiB (Vulkan reads garbage from a single buffer past about 4 GiB), and a hash of the layout checked on both sides. HRX's prefill is fastest in whole batches, so only whole ubatches of the prompt go to HRX, and short prompts skip the split: below 1024 tokens (`--prefill-min-tokens`) it does not pay. + +## Measured + +llama-server on Strix Halo, prefill and whole request: + +| Model | Prompt | Vulkan alone | HRX prefill + Vulkan | +|---|---|---|---| +| Qwen2.5-7B Q4_K_M | 2048 | 1541 / 4314 ms | **1078** / 3890 ms (-10%) | +| Qwen2.5-7B Q4_K_M | 8192 | 7347 / 10376 ms | **4646** / 7668 ms (**-26%**) | +| Qwen3-0.6B Q4_K_M | 8192 | 1289 / 2267 ms | **1089** / 2032 ms (-10%) | + +Decoding on the shared cache runs within 3% of Vulkan with its own cache. The output is the same model: against Vulkan alone, teacher-forced over 64 tokens, the mean KL divergence is 0.0002-0.0006 nats and the top token agrees on 64-65 of 65 positions. For scale, Q4_K_M quantization itself is 0.063 against BF16. + +## Where it lives + +The patches are in our llama.cpp fork, on top of AMD's tested HRX commit, and are rebased onto every new AMD pair automatically: dma-buf export in ggml-hrx, dma-buf import in ggml-vulkan, the cache sharing in llama, and the prefill hand-off in llama-server. The weights are loaded twice, once per device, so it suits machines with memory to spare; Strix Halo's unified memory is one. The details are in the [HRX docs](../docs/hrx.md). diff --git a/blog/2026-09-24-new-direction-from-1bit-monster.md b/blog/2026-09-24-new-direction-from-1bit-monster.md new file mode 100644 index 000000000..d726f108d --- /dev/null +++ b/blog/2026-09-24-new-direction-from-1bit-monster.md @@ -0,0 +1,46 @@ + +tags: announcement, engine + +# 1bit.MONSTER is now 1bit engine + +1bit.MONSTER grew fast: an inference engine, a model zoo, benchmarks, a store, a voice assistant and a lot of experiments, all in one repository with all of its history. The engine that came out of it is now its own project, **1bit engine**, and this site is its home. + +## What changed + +**A clean repository.** The engine lives in [1bit-MONSTER/engine](https://github.com/1bit-MONSTER/engine). It holds verified code only, without the development history. Each piece is ported over once it passes its end-to-end test on Strix Halo, and the [porting map](../docs/PORTING.md) shows what has moved and what is next. + +**Apache-2.0.** The old repository was GPL-3.0. The engine is Apache-2.0, with a copyright and license notice on every file, enforced in CI, and every third-party project it builds on credited in its NOTICE. + +**Inside Lemonade, not around it.** The old engine vendored its own copy of Lemonade. Now [Lemonade](../docs/lemonade.md) is the server you talk to (its catalog, downloads, router and UI), and it runs the engine as one of its backends, through `1bit serve`, the same way it runs `llama-server`. + +**Upstream, pinned and kept current.** The XDNA driver and XRT, AMD's HRX pair, llama.cpp for Vulkan, ZINC, the Linux kernel, Hugging Face tokenizers and the Laya router are each pinned to upstream, and the pins are bumped automatically, so new models and fixes arrive soon after upstream ships them. + +## What it runs on + +One `1bit serve` drives: + +- the [XDNA 2 NPU](../docs/npu.md), on full ELFs generated in C++; +- [HRX and Vulkan](../docs/hrx.md) on the Radeon iGPU, one llama.cpp build with both; +- [ZINC](../docs/zinc.md), which also reaches NVIDIA GPUs through CUDA; +- [MLX](../docs/apple.md) on Apple Silicon. + +Measured numbers live on the [wiki](https://github.com/1bit-MONSTER/engine/wiki). [The first week of 1bit engine](2026-09-24-first-week-milestones.md) lists what the first three days produced. + +## The old site + +The 1bit.MONSTER site and its posts stay online in the [1bit.MONSTER archive](https://1bit-monster.github.io/1bit-MONSTER/), and old links land on a page that points at their archived copy. The blog page here lists the old posts worth keeping. 1bit JARVIS, the voice assistant, is not part of the engine for now; it will come back later as its own piece. diff --git a/blog/archive.tsv b/blog/archive.tsv new file mode 100644 index 000000000..d14b13f8f --- /dev/null +++ b/blog/archive.tsv @@ -0,0 +1,15 @@ +2026-09-02 Byte-identical: 1,000 tokens of proof against AMD's real runtime 1bit-post-byte-identical.html +2026-09-02 The 35B MoE's NPU packing: 5,120-byte tiles, 4,736-byte rows, and an A/B interleave 1bit-post-35b-moe-layout.html +2026-09-01 Zaya 8B Q4NX on the AMD NPU 1bit-post-zaya-q4nx-hrx.html +2026-08-29 HRX: we put AMD's experimental IREE runtime inside our engine 1bit-post-hrx-engine.html +2026-08-25 One engine, every model 1bit-post-1775-models.html +2026-08-25 What 1-bit actually does to a model 1bit-post-1bit-explained.html +2026-08-25 ROCm vs CUDA, from the 1-bit trenches 1bit-post-rocm-vs-cuda.html +2026-08-25 Local RAG with our own embeddings 1bit-post-local-rag.html +2026-07-30 Qwen3.6-35B-A3B streams fully on the Ryzen AI NPU 1bit-post-qwen36.html +2026-07-26 Zyphra family complete 1bit-post-zyphra.html +2026-07-24 One binary, all formats 1bit-post-binary-formats.html +2026-07-23 Unsloth for AMD 1bit-post-unsloth.html +2026-07-11 TokenRouter: token-level multi-backend inference 1bit-post-tokenrouter.html +2026-07-06 Three bugs that broke 97 tok/s 1bit-post-three-bugs.html +2026-07-02 NPU v12: M=32 batch decode at 97 tok/s 1bit-post-npu-v12.html diff --git a/site/assets/apple-touch-icon.png b/site/assets/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5daf2e1bcc4d2ec958e673b3e2fb06c4088b192d GIT binary patch literal 1018 zcmeAS@N?(olHy`uVBq!ia0vp^TR@nD2}o{QKQWbof%%}Pi(^Q|oVRy#{q7h@v|jwM zapAfJ+3&e_DS6dLNCiId>V2zHR&$PtZU4LGU^nKvs`~o-53J9J>o*j3HhajpJW>#1 zpJ>r>#8HJ$sZan}C~o)uN1so7f4_d`_4(Vkf5v~QYqtOT%jP&o^yjObQ}0xK;4AFe zX)P~1<-}X|{NBf}*9)E6CE6(+Gqv#;MWR%S z$e%CV*_;tC^I2}K>tDv*PagQ8yA+G#DHj6T*t%ucv2 zx#y+c@3*_}N)Y4O{qJgD`TOtY$E$y;M64E^^<$N{s-1IG z{uv#+ET1FG&ujO_yIs8G9xUVjC(I+BeQm)sqpnwWuj->D_xHS=5$`Z-b7#FclW(A#;l-fi8?F0}**6v~Jhp$I(btWE zZ)6YNehy4ax5SU{IX`95uaia1>0TSN1=Mk*EdrjxohY$t+@jxiELu}9{O|VLzwceI zS{U1GVF^spTdeyk=j-%-Z@ly1vVIO*e5 z_2VwJU-rHzxnu|Q@c)RJi~5z<9hkeW{}rqG-PHZH(~ojo-#>T5s>rk zA3t0Wcl`b0j}u-CxErP^smmlMt6h3}^SW+dyP($2Cl}3}RBQj+U$N)QI}8pNTzTw> b;}`K60ZR=IFK$c%=4l2`S3j3^P6?DtyqwG)pj`}}u6{1-oD!M< D7gj!S literal 0 HcmV?d00001 diff --git a/site/assets/favicon-32x32.png b/site/assets/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..130110aeaf709fa1b2e77804f6c66e4383b504e9 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={Wrg*wIhE&XXJLMp6g948$cd4R! zL794i(h=r~4$4v5Umb2u?$rP9=&`@su<2RF)i3re!lFvsF4(d#MsnIHd1n5tbu_ws zdFQ2-KUI1@oKQ1x^mp7Td;P`E_!=PwnU$~K#k#QGb-%aFV!f_Wj`zQHc?aH~m|gN{ z!j_lGRVRD4u&WsyEN9%Wd|EBTH|Y=OsGuHM~o;Qvn}p9155w=MrhZ=KsX{e8WwGPAyXn0LUR0~Hza%{xu6EV&fL zX2-~;rpA`0)^>fpr1`oei+Z+)>^=u9woZR|NV2WIUh>_&G>aY2_lq35$szr3_wPUE zX6YxiQxlT^&!2NvLG9p&m&#(BAIv)@?8W1uc)CUG^AE=4N5X1s5h6;*`NAG=JYwR) r6n?28s_D!@(asa+(hjLMdJ8b<$_K4euw!Eb`jWxZ)z4*}Q$iB}K>Tx3 literal 0 HcmV?d00001 diff --git a/site/assets/favicon.svg b/site/assets/favicon.svg new file mode 100644 index 000000000..53eb2a35a --- /dev/null +++ b/site/assets/favicon.svg @@ -0,0 +1,44 @@ + + + 1bit engine + + + + + + diff --git a/site/style.css b/site/style.css index 02a2de0bb..05a86b6f2 100644 --- a/site/style.css +++ b/site/style.css @@ -15,187 +15,322 @@ See the License for the specific language governing permissions and limitations under the License. */ -/* 1bit engine documentation: black and white, one column of text, a sidebar, nothing else. */ +/* The 1bit.MONSTER site template (light modern-minimal + the 1-bit relay mark), carried over + to 1bit engine: tokens, chrome, hero, site cards, the blog log and the article column are + the old site's rules; the docs layout is new, in the same tokens. */ +/* ─── tokens ───────────────────────────────────────────────────────── */ :root { - --bg: #ffffff; - --bg-soft: #f6f6f6; - --fg: #111111; - --fg-soft: #666666; - --line: #e4e4e4; - --accent: #111111; - --accent-soft: #f1f1f1; - --code-bg: #f4f4f4; - --radius: 8px; - --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - --mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace; + --bg: oklch(99% 0.002 240); + --surface: oklch(100% 0 0); + --fg: oklch(18% 0.012 250); + --muted: oklch(54% 0.012 250); + --border: oklch(92% 0.005 250); + --accent: oklch(58% 0.18 255); + --status: oklch(52% 0.14 158); + + /* derived */ + --accent-soft: color-mix(in oklch, var(--accent) 12%, transparent); + --fg-soft: color-mix(in oklch, var(--fg) 5%, transparent); + --muted-strong: color-mix(in oklch, var(--muted) 76%, var(--fg)); + --accent-ink: color-mix(in oklch, var(--accent) 72%, var(--fg)); + --hairblock: color-mix(in oklch, var(--fg) 7%, transparent); + + --font-display: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif; + --font-body: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif; + --font-mono: ui-monospace, 'SF Mono', 'SFMono-Regular', Menlo, Consolas, monospace; + + --fs-h1: clamp(44px, 6vw, 76px); + --fs-h2: clamp(32px, 4vw, 48px); + --fs-h3: 20px; + --fs-lead: 19px; + --fs-body: 16px; + --fs-meta: 13px; + + --gap-xs: 8px; + --gap-sm: 12px; + --gap-md: 20px; + --gap-lg: 32px; + --gap-xl: 48px; + --gap-2xl: 80px; + --container: 1120px; + --gutter: 32px; + + --radius: 10px; + --radius-lg: 16px; color-scheme: light; } +/* dark: the old site's dark tokens, by choice (data-theme="dark") or following the OS (auto) */ +:root[data-theme="dark"] { + color-scheme: dark; + --bg: oklch(16.5% 0.014 250); + --surface: oklch(21% 0.016 250); + --fg: oklch(94% 0.01 250); + --muted: oklch(68% 0.02 250); + --border: oklch(29% 0.018 250); + --accent: oklch(74% 0.15 258); + --status: oklch(79% 0.13 156); +} +:root[data-theme="dark"] .btn-primary { background: color-mix(in oklch, var(--accent) 62%, black); color: oklch(98% 0.002 250); } +:root[data-theme="dark"] .btn-primary:hover { background: color-mix(in oklch, var(--accent) 74%, black); } @media (prefers-color-scheme: dark) { - :root { - --bg: #0b0b0b; - --bg-soft: #151515; - --fg: #ececec; - --fg-soft: #9a9a9a; - --line: #2a2a2a; - --accent: #ffffff; - --accent-soft: #1d1d1d; - --code-bg: #161616; + :root[data-theme="auto"] { color-scheme: dark; + --bg: oklch(16.5% 0.014 250); + --surface: oklch(21% 0.016 250); + --fg: oklch(94% 0.01 250); + --muted: oklch(68% 0.02 250); + --border: oklch(29% 0.018 250); + --accent: oklch(74% 0.15 258); + --status: oklch(79% 0.13 156); } + :root[data-theme="auto"] .btn-primary { background: color-mix(in oklch, var(--accent) 62%, black); color: oklch(98% 0.002 250); } + :root[data-theme="auto"] .btn-primary:hover { background: color-mix(in oklch, var(--accent) 74%, black); } } -* { box-sizing: border-box; } - -/* scrollbars follow the theme (a light native bar shows up white in dark mode) */ -* { scrollbar-color: var(--line) transparent; scrollbar-width: thin; } - +/* ─── reset & base ─────────────────────────────────────────────────── */ +*, *::before, *::after { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; } - body { margin: 0; background: var(--bg); color: var(--fg); - font: 16px/1.65 var(--font); + font-family: var(--font-body); + font-size: var(--fs-body); + line-height: 1.6; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; } +a { color: inherit; text-decoration: none; } +p { text-wrap: pretty; } +h1, h2, h3, h4 { text-wrap: balance; } +img, svg { display: block; max-width: 100%; } +button { font: inherit; cursor: pointer; } +::selection { background: var(--accent-soft); color: var(--fg); } +:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; } +* { scrollbar-color: var(--border) transparent; scrollbar-width: thin; } -a { color: var(--accent); text-decoration: none; } -a:hover { text-decoration: underline; } -article a { text-decoration: underline; text-decoration-color: var(--fg-soft); text-underline-offset: 3px; } -article a:hover { text-decoration-color: var(--accent); } -article a.anchor { text-decoration: none; } +/* ─── layout primitives ────────────────────────────────────────────── */ +.container { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); } +.section { padding-block: clamp(40px, 5vw, 72px); } +.section + .section { border-top: 1px solid var(--border); } +.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-md); } -/* top bar */ -.top { +/* ─── type ─────────────────────────────────────────────────────────── */ +h1 { font-family: var(--font-display); font-size: var(--fs-h1); line-height: 1.08; letter-spacing: -0.02em; margin: 0; font-weight: 600; } +h2 { font-family: var(--font-display); font-size: var(--fs-h2); line-height: 1.12; letter-spacing: -0.015em; margin: 0; font-weight: 600; } +h3 { font-size: var(--fs-h3); font-weight: 600; line-height: 1.3; letter-spacing: -0.005em; margin: 0; } +.lead { font-size: var(--fs-lead); line-height: 1.6; color: var(--muted-strong); max-width: 60ch; margin: 0; } +.meta { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--muted-strong); } +.eyebrow { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted-strong); margin: 0 0 18px; } + +/* ─── chrome: nav + footer ─────────────────────────────────────────── */ +.topnav { position: sticky; top: 0; z-index: 10; - display: flex; align-items: center; justify-content: space-between; - height: 56px; padding: 0 24px; - background: color-mix(in srgb, var(--bg) 88%, transparent); - backdrop-filter: saturate(1.4) blur(8px); - border-bottom: 1px solid var(--line); -} -.brand { display: flex; align-items: center; gap: 10px; color: var(--fg); font-weight: 650; letter-spacing: -0.01em; } -.brand:hover { text-decoration: none; } -.mark { - display: inline-grid; place-items: center; - width: 28px; height: 28px; border-radius: 7px; - background: var(--accent); color: var(--bg); - font: 700 13px/1 var(--mono); -} -.links { display: flex; gap: 22px; font-size: 15px; } -.links a { color: var(--fg-soft); } -.links a:hover { color: var(--fg); text-decoration: none; } - -/* sidebar + content */ -.layout { - display: grid; - grid-template-columns: 232px minmax(0, 1fr); - gap: 48px; - max-width: 1120px; - margin: 0 auto; - padding: 0 24px; -} -.side { position: sticky; top: 56px; align-self: start; max-height: calc(100vh - 56px); overflow-y: auto; padding: 28px 0; } -.side summary { display: none; } -.side .group { - margin: 20px 0 6px; font-size: 12px; font-weight: 650; - text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-soft); -} -.side .group:first-child { margin-top: 0; } -.side ul { list-style: none; margin: 0; padding: 0; } -.side li a { - display: block; padding: 5px 10px; margin-left: -10px; - border-radius: 6px; color: var(--fg); font-size: 15px; -} -.side li a:hover { background: var(--bg-soft); text-decoration: none; } -.side li a.current { background: var(--accent-soft); color: var(--accent); font-weight: 600; } - -main { min-width: 0; padding: 36px 0 64px; } -article { max-width: 760px; } - -/* text */ -article h1 { font-size: 2.1rem; line-height: 1.2; letter-spacing: -0.02em; margin: 0 0 20px; } -article h2 { font-size: 1.45rem; line-height: 1.3; margin: 44px 0 12px; padding-top: 8px; border-top: 1px solid var(--line); } -article h3 { font-size: 1.15rem; margin: 30px 0 8px; } -article h4 { font-size: 1rem; margin: 24px 0 6px; } -article p, article ul, article ol { margin: 0 0 16px; } -article li { margin: 4px 0; } -article li > ul, article li > ol { margin: 4px 0 0; } -article strong { font-weight: 650; } -article hr { border: 0; border-top: 1px solid var(--line); margin: 32px 0; } -article img { max-width: 100%; } - -.anchor { margin-left: 8px; color: var(--line); font-weight: 400; opacity: 0; } -h2:hover .anchor, h3:hover .anchor, h4:hover .anchor { opacity: 1; color: var(--fg-soft); } -.anchor:hover { text-decoration: none; } - -blockquote { - margin: 0 0 20px; padding: 14px 18px; - background: var(--accent-soft); border-left: 3px solid var(--accent); - border-radius: 0 var(--radius) var(--radius) 0; -} -blockquote p:last-child { margin-bottom: 0; } - -code { font: 0.88em/1.5 var(--mono); background: var(--code-bg); padding: 0.12em 0.36em; border-radius: 5px; } -pre { - margin: 0 0 20px; padding: 14px 16px; overflow-x: auto; - background: var(--code-bg); border: 1px solid var(--line); border-radius: var(--radius); -} -pre code { background: none; padding: 0; font-size: 0.86em; line-height: 1.6; } - -.table { overflow-x: auto; margin: 0 0 20px; border: 1px solid var(--line); border-radius: var(--radius); } -table { border-collapse: collapse; width: 100%; font-size: 0.93em; } -th, td { padding: 8px 12px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line); } -th { background: var(--bg-soft); font-weight: 600; white-space: nowrap; } -tr:last-child td { border-bottom: 0; } - -/* the home page leads with its title */ -body.home article > h1 { font-size: 2.6rem; margin-bottom: 14px; } -body.home article > h1 + p { font-size: 1.15rem; color: var(--fg-soft); } - -/* blog */ -.date { margin: -12px 0 24px; color: var(--fg-soft); font-size: 14px; } -.post { padding: 20px 0; border-top: 1px solid var(--line); } -.post:first-of-type { border-top: 0; padding-top: 0; } -.post .date { margin: 0 0 4px; } -.post h2 { margin: 0 0 8px; padding: 0; border: 0; font-size: 1.3rem; } -.post h2 a { color: var(--fg); text-decoration: none; } -.post h2 a:hover { text-decoration: underline; } -.post p:last-child { margin: 0; color: var(--fg-soft); } + background: color-mix(in oklch, var(--bg) 92%, transparent); + backdrop-filter: blur(12px); + border-bottom: 1px solid var(--border); +} +.topnav-inner { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-md); padding-block: 14px; } +.topnav .logo { display: inline-flex; align-items: center; gap: 10px; font-size: 17px; font-weight: 700; letter-spacing: -0.02em; white-space: nowrap; } +.topnav .logo svg { width: 26px; height: 26px; color: var(--fg); } +/* the 1bit relay mark: one block solid at a time, slow */ +.relay { fill: var(--surface); stroke: currentColor; stroke-opacity: 0.28; animation: logo-relay 80s ease-in-out infinite; } +.r1 { animation-delay: 0s; } +.r2 { animation-delay: 20s; } +.r3 { animation-delay: 40s; } +.r4 { animation-delay: 60s; } +@keyframes logo-relay { + 0% { fill: var(--surface); } + 13% { fill: var(--fg); } + 14% { fill: var(--fg); } + 25% { fill: var(--surface); } + 100% { fill: var(--surface); } +} +@media (prefers-reduced-motion: reduce) { .relay { animation: none; } .r1 { fill: var(--fg); } } -/* previous / next */ -.pager { display: flex; justify-content: space-between; gap: 16px; max-width: 760px; margin-top: 48px; } -.pager a { - flex: 1; max-width: 48%; padding: 12px 16px; - border: 1px solid var(--line); border-radius: var(--radius); color: var(--fg); +.topnav nav { display: flex; gap: var(--gap-lg); margin-left: auto; } +.topnav nav a { font-size: 14px; color: var(--muted-strong); white-space: nowrap; } +.topnav nav a:hover { color: var(--fg); } +.topnav nav a[aria-current="page"] { color: var(--fg); font-weight: 500; } + +.pagefoot { padding-block: var(--gap-xl); color: var(--muted-strong); font-size: 13px; border-top: 1px solid var(--border); } +.pagefoot .place, .pagefoot .quote { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.02em; color: var(--muted-strong); text-align: center; } +.pagefoot .place { margin: var(--gap-lg) auto 0; } +.pagefoot .quote { margin: var(--gap-sm) auto 0; } +.pagefoot a:hover { color: var(--fg); } +.pagefoot .row-between { flex-wrap: wrap; gap: var(--gap-md); } + +/* ─── theme switch (light / auto / dark) ───────────────────────────── */ +.theme-switch { + display: inline-flex; align-items: center; gap: 2px; + padding: 3px; border: 1px solid var(--border); border-radius: 999px; + background: color-mix(in oklch, var(--fg) 4%, transparent); + flex: 0 0 auto; } -.pager a:hover { border-color: var(--accent); text-decoration: none; } -.pager a span { display: block; font-size: 12px; color: var(--fg-soft); text-transform: uppercase; letter-spacing: 0.05em; } -.pager .next { text-align: right; margin-left: auto; } -.edit { max-width: 760px; margin-top: 24px; font-size: 14px; } -.edit a { color: var(--fg-soft); } +.theme-switch .ts-btn { + display: inline-flex; align-items: center; justify-content: center; + width: 26px; height: 24px; padding: 0; border: 0; border-radius: 999px; + background: transparent; color: var(--muted); cursor: pointer; + transition: background 0.15s ease, color 0.15s ease; +} +.theme-switch .ts-btn:hover { color: var(--fg); } +.theme-switch .ts-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; } +html[data-theme="light"] .theme-switch [data-choice="light"], +html[data-theme="auto"] .theme-switch [data-choice="auto"], +html[data-theme="dark"] .theme-switch [data-choice="dark"] { + background: var(--surface); color: var(--fg); + box-shadow: 0 1px 3px color-mix(in oklch, var(--fg) 18%, transparent); +} + +/* ─── buttons ──────────────────────────────────────────────────────── */ +.btn { + display: inline-flex; align-items: center; gap: 8px; + padding: 11px 20px; + border-radius: var(--radius); + border: 1px solid transparent; + font-size: 14px; font-weight: 500; letter-spacing: -0.005em; white-space: nowrap; + transition: transform 0.05s ease, background 0.15s ease, border-color 0.15s ease, color 0.15s ease; +} +.btn:active { transform: translateY(1px); } +.btn-primary { background: color-mix(in oklch, var(--accent) 78%, black); color: var(--surface); } +.btn-primary:hover { background: color-mix(in oklch, var(--accent) 68%, black); } +.btn-ghost { background: transparent; color: var(--fg); padding-inline: 8px; } +.btn-ghost:hover { color: var(--accent); } +.cta-row { display: flex; gap: var(--gap-sm); justify-content: center; margin-top: var(--gap-lg); flex-wrap: wrap; } + +/* ─── hero (centered) ──────────────────────────────────────────────── */ +.hero-center { padding-block: clamp(56px, 8vw, 112px); text-align: center; } +.hero-center h1 { margin-bottom: var(--gap-md); } +.hero-center h1 .mark { width: 0.72em; height: 0.72em; display: inline-block; vertical-align: -0.06em; margin-right: 0.18em; } +.hero-center .lead { margin: var(--gap-md) auto 0; } +.hero-narrow { max-width: 720px; margin-inline: auto; } -footer { - border-top: 1px solid var(--line); padding: 24px; text-align: center; - color: var(--fg-soft); font-size: 14px; +/* ─── site index grid ──────────────────────────────────────────────── */ +.site-badge { + display: inline-flex; align-items: center; gap: 8px; + font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; + color: var(--muted-strong); margin-bottom: var(--gap-lg); } -footer p { margin: 4px 0; } -footer a { color: var(--fg-soft); } +.site-badge .dot { width: 8px; height: 8px; border-radius: 999px; background: var(--status); } +.latest-link { + display: flex; flex-direction: column; gap: 8px; text-align: left; + max-width: 680px; margin: var(--gap-lg) auto 0; padding: 18px 22px; + background: var(--surface); border: 1px solid var(--border); + border-left: 3px solid var(--accent); border-radius: var(--radius-lg); + transition: border-color 0.15s ease; +} +.latest-link:hover { border-color: var(--accent); border-left-color: var(--accent); } +.latest-badge { + display: inline-flex; align-items: center; gap: 8px; + font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; + color: var(--accent); margin-bottom: 2px; +} +.latest-badge .dot { width: 8px; height: 8px; border-radius: 999px; background: var(--accent); } +.latest-title { font-size: 15px; line-height: 1.5; color: var(--fg); } +.latest-more { font-family: var(--font-mono); font-size: 12px; color: var(--muted-strong); } +.latest-more .ar { transition: transform 0.15s ease; display: inline-block; } +.latest-link:hover .latest-more .ar { transform: translateX(3px); } +.site-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--gap-md); margin-top: var(--gap-lg); } +.site-card { + display: flex; flex-direction: column; gap: 10px; + background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); + padding: 22px; position: relative; + transition: border-color 0.15s ease; +} +.site-card:hover { border-color: var(--fg); } +.site-title { font-size: 18px; font-weight: 600; letter-spacing: -0.01em; display: flex; align-items: baseline; gap: 8px; } +.site-title .ar { color: var(--muted-strong); font-size: 13px; transition: transform 0.15s ease; } +.site-card:hover .site-title .ar { transform: translateX(2px); color: var(--accent); } +.site-desc { margin: 0; font-size: 14px; color: var(--muted-strong); } +.site-path { font-family: var(--font-mono); font-size: 12px; color: var(--muted-strong); margin-top: auto; } +.pillars .site-card { text-align: center; } + +/* ─── blog log ─────────────────────────────────────────────────────── */ +.log-head { margin-bottom: clamp(20px, 4vw, 40px); align-items: flex-end; } +.log-head .links { display: flex; gap: var(--gap-md); flex-wrap: wrap; } +.log-row { display: grid; grid-template-columns: 150px 1fr; gap: var(--gap-lg); align-items: start; padding: clamp(14px, 2.4vw, 22px) 0; border-top: 1px solid var(--border); } +.log-row:last-of-type { border-bottom: 1px solid var(--border); } +.log-date { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--muted-strong); padding-top: 4px; } +.log-body h3 { font-size: 21px; letter-spacing: -0.01em; margin: 0 0 6px; } +.log-body p { margin: 0; color: var(--muted-strong); font-size: 15px; line-height: 1.6; max-width: 66ch; } +.log-row:hover h3 { color: var(--accent-ink); } +.archive-note { margin-top: 16px; } -/* phones: the sidebar becomes a collapsible block above the page */ +/* ─── article (blog post) ──────────────────────────────────────────── */ +.art { padding-block: clamp(48px, 7vw, 96px) clamp(40px, 6vw, 72px); } +.art .container { max-width: 760px; } +.art .back { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.06em; color: var(--muted-strong); display: inline-flex; gap: 6px; margin-bottom: var(--gap-lg); } +.art .back:hover { color: var(--fg); } +.art-meta { display: flex; flex-wrap: wrap; gap: var(--gap-md); align-items: center; margin-bottom: var(--gap-md); font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.04em; color: var(--muted-strong); } +.art-meta .tag { padding: 4px 10px; border: 1px solid var(--border); border-radius: 999px; } +.art h1 { font-size: clamp(38px, 5vw, 64px); margin: 0 0 var(--gap-md); } +.art .lead { max-width: 64ch; margin: 0 0 var(--gap-xl); } +.art .prose { font-size: 17px; line-height: 1.7; } + +/* ─── prose: posts and docs ────────────────────────────────────────── */ +.prose h2 { font-size: clamp(26px, 3vw, 34px); line-height: 1.16; margin: var(--gap-xl) 0 var(--gap-md); } +.prose h3 { margin: var(--gap-lg) 0 var(--gap-sm); } +.prose h4 { font-size: 16px; margin: var(--gap-md) 0 var(--gap-xs); } +.prose p, .prose ul, .prose ol { margin: 0 0 var(--gap-md); } +.prose li { margin: 4px 0; } +.prose li > ul, .prose li > ol { margin: 4px 0 0; } +.prose strong { font-weight: 600; } +.prose a { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: color-mix(in oklch, var(--accent) 45%, transparent); } +.prose a:hover { text-decoration-color: var(--accent); } +.prose a.anchor { margin-left: 8px; color: var(--muted); text-decoration: none; opacity: 0; font-weight: 400; } +.prose h2:hover .anchor, .prose h3:hover .anchor, .prose h4:hover .anchor { opacity: 1; } +.prose code { font-family: var(--font-mono); font-size: 0.9em; background: var(--hairblock); padding: 2px 6px; border-radius: 4px; } +.prose pre { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 20px; overflow-x: auto; font-family: var(--font-mono); font-size: 13px; line-height: 1.85; margin: 0 0 var(--gap-lg); } +.prose pre code { background: none; padding: 0; font-size: inherit; } +.prose blockquote { margin: 0 0 var(--gap-lg); padding: 4px 0 4px var(--gap-lg); border-left: 1px solid var(--accent); color: var(--muted-strong); } +.prose blockquote p:last-child { margin-bottom: 0; } +.prose hr { border: 0; border-top: 1px solid var(--border); margin: var(--gap-xl) 0; } +.prose img { max-width: 100%; } +.prose .table { overflow-x: auto; margin: 0 0 var(--gap-lg); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); } +.prose table { width: 100%; border-collapse: collapse; font-size: 15px; } +.prose th, .prose td { text-align: left; vertical-align: top; padding: 11px 14px; border-bottom: 1px solid var(--border); } +.prose tr:last-child td { border-bottom: 0; } +.prose th { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted-strong); font-weight: 500; white-space: nowrap; background: var(--fg-soft); } + +/* ─── docs: sidebar + page ─────────────────────────────────────────── */ +.docs { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: var(--gap-xl); padding-block: var(--gap-xl) var(--gap-2xl); } +.docs-side { position: sticky; top: 72px; align-self: start; max-height: calc(100vh - 88px); overflow-y: auto; padding-right: var(--gap-sm); } +.docs-side summary { display: none; } +.docs-side .group { margin: var(--gap-lg) 0 var(--gap-xs); font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted-strong); } +.docs-side .group:first-child { margin-top: 0; } +.docs-side ul { list-style: none; margin: 0; padding: 0; } +.docs-side li a { display: block; padding: 6px 10px; border-radius: 8px; font-size: 15px; color: var(--fg); } +.docs-side li a:hover { background: var(--fg-soft); } +.docs-side li a[aria-current="page"] { background: var(--accent-soft); color: var(--accent-ink); font-weight: 500; } +.docs-page { max-width: 780px; } +.docs-page h1 { font-size: clamp(34px, 4.4vw, 52px); margin: 0 0 var(--gap-md); } + +/* previous / next */ +.pager { display: flex; justify-content: space-between; gap: var(--gap-md); margin-top: var(--gap-xl); } +.pager a { flex: 1; max-width: 48%; padding: 14px 18px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); transition: border-color 0.15s ease; } +.pager a:hover { border-color: var(--fg); } +.pager a span { display: block; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted-strong); } +.pager .next { text-align: right; margin-left: auto; } +.source { margin-top: var(--gap-lg); } +.source a:hover { color: var(--fg); } + +/* ─── narrow screens ───────────────────────────────────────────────── */ +@media (max-width: 920px) { + .topnav-inner { flex-wrap: wrap; row-gap: 12px; } + .topnav nav { order: 3; width: 100%; gap: var(--gap-md); justify-content: space-between; flex-wrap: wrap; margin-left: 0; } +} @media (max-width: 860px) { - .layout { display: block; padding: 0 16px; } - .side { position: static; max-height: none; padding: 16px 0 0; border-bottom: 1px solid var(--line); } - .side summary { display: block; cursor: pointer; font-weight: 600; padding-bottom: 12px; list-style: none; } - .side summary::-webkit-details-marker { display: none; } - .side summary::marker { content: ""; } - .side summary::before { content: "▸"; display: inline-block; width: 1.2em; color: var(--fg-soft); } - .side[open] summary::before { transform: rotate(90deg); } - .top { padding: 0 16px; } - .links { gap: 16px; } - main { padding-top: 24px; } - article h1 { font-size: 1.75rem; } - body.home article > h1 { font-size: 2.05rem; } + .docs { display: block; padding-top: var(--gap-md); } + .docs-side { position: static; max-height: none; padding: 0 0 var(--gap-md); margin-bottom: var(--gap-lg); border-bottom: 1px solid var(--border); } + .docs-side summary { display: block; cursor: pointer; font-weight: 600; list-style: none; padding-bottom: var(--gap-sm); } + .docs-side summary::-webkit-details-marker { display: none; } + .docs-side summary::before { content: "▸"; display: inline-block; width: 1.2em; color: var(--muted-strong); } + .docs-side[open] summary::before { transform: rotate(90deg); } +} +@media (max-width: 720px) { + :root { --gutter: 16px; } + .log-row { grid-template-columns: 1fr; gap: 6px; } .pager a { max-width: none; } } diff --git a/site/template.html b/site/template.html index bf0dc2192..a35cdbce9 100644 --- a/site/template.html +++ b/site/template.html @@ -15,48 +15,61 @@ limitations under the License. --> - + {{title}} - - + + + + + + + + - + + - -
- 1b1bit engine - -
-
-
- Contents -
-
-
-{{content}} -
- {{pager}} -

View this page's source on GitHub

-
-
-