diff --git a/README.md b/README.md
index f56245ed..df23c9ea 100644
--- a/README.md
+++ b/README.md
@@ -121,37 +121,73 @@ instead. To embed an video simply type the following in markdown:
### Embedding ToS checkbox
-A ToS acceptance checkbox can be added to any release page to gate binary
-downloads behind a user acknowledgement. The feature is opt-in per page.
+A ToS acceptance checkbox can be added to any page to gate binary downloads
+and/or prose content behind a user acknowledgement. The feature is opt-in per
+page.
-**1. Add the checkbox macro** above the content you want to protect:
+#### Gating download buttons
+
+Use the `tos_gated_downloads` macro and pass a unique `section_id` together
+with the list of files. Use the **group format** to preserve subsection
+headings matching the original release layout:
```md
-{{ tos_checkbox("unique-section-id") }}
+{{ tos_gated_downloads("vendor-board-vXYZ-binaries", [
+ {"group": "Raw firmware image", "note": "(vendor_board_vX.Y.Z.rom)", "items": [
+ {"label": "sha256", "url": "https://dl.3mdeb.com/.../file.rom.sha256"},
+ {"label": "sha256.sig", "url": "https://dl.3mdeb.com/.../file.rom.sha256.sig"}
+ ]},
+ {"group": "SBOM CycloneDX", "items": [
+ {"label": "sbom.json", "url": "https://dl.3mdeb.com/.../file.sbom.json"},
+ {"label": "sha256", "url": "https://dl.3mdeb.com/.../file.sbom.json.sha256"},
+ {"label": "sha256.sig", "url": "https://dl.3mdeb.com/.../file.sbom.json.sha256.sig"}
+ ]}
+]) }}
```
-**2. Wrap the protected content** in an HTML div with a matching `id`:
+Download links are **not** emitted into the HTML source - they are
+base64-encoded in a `data-payload` attribute and injected by `tos-gate.js` only
+after the user checks the box. This keeps URLs out of page source and prevents
+them from being indexed.
+
+Download buttons with groups example:
+[`docs/variants/gigabyte_mz33-ar1/releases.md`][tos-example]
+
+#### Optional argument (`tos_url`)
+
+Overrides the default Terms of Service link:
```md
-
+{{ tos_gated_downloads("...", [...], tos_url="https://example.com/terms") }}
+```
-...binary download buttons and related content...
+#### Optional hiding prose content
+
+Pass `prose_section_id` to make the same checkbox also show/hide one or more
+blocks of descriptive text. Wrap each prose block in a div with a matching
+`data-prose-group` attribute:
+
+```md
+{{ tos_gated_downloads("vendor-board-vXYZ-blobs",
+ [{"label": "blobs.zip", "url": "https://dl.3mdeb.com/.../blobs.zip"}],
+ prose_section_id="vendor-board-vXYZ-blobs-prose") }}
+
+
+
+Further instructions that only make sense after downloading...
```
-`tos_checkbox` accepts an optional `tos_url` argument to override the default
-Terms of Service link:
+Multiple divs sharing the same `data-prose-group` value are all toggled by
+the single checkbox, so one gate can reveal several separate prose sections.
-```md
-{{ tos_checkbox("unique-section-id", tos_url="https://example.com/terms") }}
-```
-
-For pages with multiple release sections, use a distinct `section-id` per
-release (e.g. `vendor-board-v100-binaries`, `vendor-board-v090-binaries`). See
-[`docs/variants/gigabyte_mz33-ar1/releases.md`][tos-example] for example.
+Combined buttons + prose example:
+[`docs/variants/gigabyte_mz33-ar1/building-manual.md`][tos-prose-example]
[tos-example]: docs/variants/gigabyte_mz33-ar1/releases.md
+[tos-prose-example]: docs/variants/gigabyte_mz33-ar1/building-manual.md
### Embedding subscribe forms
diff --git a/docs/javascripts/tos-gate.js b/docs/javascripts/tos-gate.js
new file mode 100644
index 00000000..15ff882d
--- /dev/null
+++ b/docs/javascripts/tos-gate.js
@@ -0,0 +1,28 @@
+function revealGated(cb) {
+ const el = document.getElementById(cb.dataset.section);
+ const proseEls = cb.dataset.proseSection
+ ? document.querySelectorAll(`[data-prose-group="${cb.dataset.proseSection}"]`)
+ : [];
+
+ if (!cb.checked) {
+ el.innerHTML = "";
+ proseEls.forEach(p => { p.style.display = "none"; });
+ return;
+ }
+
+ const files = JSON.parse(atob(el.dataset.payload));
+ // rel="noreferrer" is intentionally absent — it would strip the Referer
+ // header and break the MinIO bucket policy gate
+ el.innerHTML = files.map(f => {
+ if (f.group) {
+ const btns = f.items.map(item =>
+ `${item.label}`
+ ).join(" ");
+ const note = f.note ? ` ${f.note}` : "";
+ return `
${f.group}
${btns}${note}
`;
+ }
+ return `${f.label}`;
+ }).join("\n");
+
+ proseEls.forEach(p => { p.style.display = ""; });
+}
diff --git a/docs/variants/gigabyte_mz33-ar1/building-manual.md b/docs/variants/gigabyte_mz33-ar1/building-manual.md
index a92942d9..de169fe9 100644
--- a/docs/variants/gigabyte_mz33-ar1/building-manual.md
+++ b/docs/variants/gigabyte_mz33-ar1/building-manual.md
@@ -46,14 +46,14 @@ then follow the steps below:
3. Download the package with necessary blobs and put it in proper directory:
-{{ tos_checkbox("gigabyte-mz33-ar1-v090-blobs") }}
+{{ tos_gated_downloads("gigabyte-mz33-ar1-v090-blobs",
+ [{"label": "Turin.zip", "url": "https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/Turin.zip"}],
+ prose_section_id="gigabyte-mz33-ar1-v090-blobs-prose") }}
-
-- Download the [blobs
-package](https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/Turin.zip).
- Then unzip it to the following directory:
```bash
diff --git a/docs/variants/gigabyte_mz33-ar1/releases.md b/docs/variants/gigabyte_mz33-ar1/releases.md
index 493e1689..b30e0c17 100644
--- a/docs/variants/gigabyte_mz33-ar1/releases.md
+++ b/docs/variants/gigabyte_mz33-ar1/releases.md
@@ -73,23 +73,21 @@ Test results for this release can be found
### Binaries
-#### Raw Dasharo image
-
-[sha256][gigabyte_mz33_ar1_v0.9.0.rom_hash]{.md-button}
-[sha256.sig][gigabyte_mz33_ar1_v0.9.0.rom_sig]{.md-button}
-(gigabyte_mz33_ar1_v0.9.0.rom)
-
-#### Update via BMC image
-
-[sha256][gigabyte_mz33_ar1_v0.9.0.rbu_hash]{.md-button}
-[sha256.sig][gigabyte_mz33_ar1_v0.9.0.rbu_sig]{.md-button}
-(gigabyte_mz33_ar1_v0.9.0.rbu)
-
-#### SBOM CycloneDX
-
-[gigabyte_mz33_ar1_v0.9.0.sbom.json][gigabyte_mz33_ar1_v0.9.0.sbom.json_file]{.md-button}
-[sha256][gigabyte_mz33_ar1_v0.9.0.sbom.json_hash]{.md-button}
-[sha256.sig][gigabyte_mz33_ar1_v0.9.0.sbom.json_sig]{.md-button}
+{{ tos_gated_downloads("gigabyte-mz33-ar1-v090-binaries", [
+ {"group": "Raw Dasharo image", "note": "(gigabyte_mz33_ar1_v0.9.0.rom)", "items": [
+ {"label": "sha256", "url": "https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.rom.sha256"},
+ {"label": "sha256.sig", "url": "https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.rom.sha256.sig"}
+ ]},
+ {"group": "Update via BMC image", "note": "(gigabyte_mz33_ar1_v0.9.0.rbu)", "items": [
+ {"label": "sha256", "url": "https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.rbu.sha256"},
+ {"label": "sha256.sig", "url": "https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.rbu.sha256.sig"}
+ ]},
+ {"group": "SBOM CycloneDX", "items": [
+ {"label": "sbom.json", "url": "https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.sbom.json"},
+ {"label": "sha256", "url": "https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.sbom.json.sha256"},
+ {"label": "sha256.sig", "url": "https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.sbom.json.sha256.sig"}
+ ]}
+]) }}
This is a Dasharo Pro Package release. For this platform, access to pre-built
binaries is provided exclusively through the
@@ -126,12 +124,3 @@ and their versions used to build the firmware images. The published SBOM
artifact is in CycloneDX format and can be viewed by SBOM tools, for example
[sbom-tools](https://github.com/sbom-tool/sbom-tools).
-[gigabyte_mz33_ar1_v0.9.0.rom_hash]: https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.rom.sha256
-[gigabyte_mz33_ar1_v0.9.0.rom_sig]: https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.rom.sha256.sig
-
-[gigabyte_mz33_ar1_v0.9.0.rbu_hash]: https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.rbu.sha256
-[gigabyte_mz33_ar1_v0.9.0.rbu_sig]: https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.rbu.sha256.sig
-
-[gigabyte_mz33_ar1_v0.9.0.sbom.json_file]: https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.sbom.json
-[gigabyte_mz33_ar1_v0.9.0.sbom.json_hash]: https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.sbom.json.sha256
-[gigabyte_mz33_ar1_v0.9.0.sbom.json_sig]: https://dl.3mdeb.com/open-source-firmware/Dasharo/gigabyte_mz33_ar1/uefi/v0.9.0/gigabyte_mz33_ar1_v0.9.0.sbom.json.sha256.sig
diff --git a/main.py b/main.py
index ef827382..bd1fb00d 100644
--- a/main.py
+++ b/main.py
@@ -1,6 +1,41 @@
+import base64
+import json
+
+
def define_env(env):
""" Define macros for MkDocs """
+ @env.macro
+ def tos_gated_downloads(section_id, files, tos_url="https://www.dasharo.com/pages/terms/",
+ prose_section_id=None):
+ """Render a ToS checkbox that reveals base64-encoded download links on acceptance.
+
+ files: flat list of {"label": str, "url": str} dicts, or grouped list of
+ {"group": str, "items": [...], "note": str (optional)} dicts.
+ URLs are not emitted into HTML source — base64-encoded in a data
+ attribute and injected by tos-gate.js when the checkbox is checked.
+
+ prose_section_id: when set, tos-gate.js also toggles all elements carrying
+ data-prose-group="{prose_section_id}". Multiple elements may share
+ the same value, allowing one checkbox to reveal several prose blocks.
+ """
+ payload = base64.b64encode(json.dumps(files).encode()).decode()
+ checkbox_id = f"tos-cb-{section_id}"
+ prose_attr = f' data-prose-section="{prose_section_id}"' if prose_section_id else ""
+ return (
+ '
'
+ f''
+ f''
+ '
'
+ )
+
@env.macro
def tos_checkbox(section_id, tos_url="https://www.dasharo.com/pages/terms/"):
"""Render a ToS acceptance checkbox that reveals the section with the given id."""
diff --git a/mkdocs.yml b/mkdocs.yml
index a442bb85..69ce1dc5 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -35,6 +35,7 @@ extra:
extra_css:
- stylesheets/extra.css
extra_javascript:
+ - javascripts/tos-gate.js
- https://cdn.jsdelivr.net/npm/vega@5
- https://cdn.jsdelivr.net/npm/vega-lite@5
- https://cdn.jsdelivr.net/npm/vega-embed@6
diff --git a/overrides/main.html b/overrides/main.html
new file mode 100644
index 00000000..c2f60a42
--- /dev/null
+++ b/overrides/main.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+
+{% block extrahead %}
+ {{ super() }}
+
+{% endblock %}