diff --git a/.gitattributes b/.gitattributes
index 5c7b689..127d3c3 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -8,6 +8,17 @@ examples/aspnet/*.md text eol=lf
examples/aspnet/LICENSE text eol=lf
examples/aspnet/Assets/*.html text eol=lf
examples/aspnet/Assets/*.css text eol=lf
+examples/dotnet-accessibility/*.cs text eol=lf
+examples/dotnet-accessibility/*.csproj text eol=lf
+examples/dotnet-accessibility/*.json text eol=lf
+examples/dotnet-accessibility/*.md text eol=lf
+examples/dotnet-accessibility/LICENSE text eol=lf
+examples/dotnet-accessibility/Assets/*.html text eol=lf
+examples/dotnet-accessibility/Assets/*.css text eol=lf
+examples/dotnet-accessibility/Assets/*.svg text eol=lf
+examples/dotnet-accessibility/Assets/*-OFL.txt -text whitespace=-blank-at-eol
+docs/assets/dotnet-accessibility/** -text whitespace=cr-at-eol
+docs/assets/dotnet-accessibility/*.pdf binary
docs/assets/playground/LICENSES.txt text eol=lf whitespace=-blank-at-eol
docs/assets/pdf-regression/*.json text eol=lf
# Evidence downloads and their manifests are verified by byte hashes.
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 6edc18d..e8dc7bd 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -100,6 +100,38 @@ jobs:
output/aspnet-verification/*.log
output/aspnet-verification/preview/*
retention-days: 14
+ tagged-notice:
+ name: C# tagged notice (${{ matrix.os }})
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v7
+ - uses: actions/setup-python@v6
+ with:
+ python-version: '3.12'
+ - uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '10.0.x'
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '21'
+ - run: python -m pip install pypdf==6.19.0
+ - name: Verify the downloaded ZIP and isolated published tagged notice
+ run: python tools/verify_dotnet_accessibility.py
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: dotnet-accessibility-verification-${{ matrix.os }}
+ path: |
+ output/dotnet-accessibility-verification/verification.json
+ output/dotnet-accessibility-verification/*.txt
+ output/dotnet-accessibility-verification/ua1/
+ output/dotnet-accessibility-verification/ua2/
+ retention-days: 14
build:
runs-on: ubuntu-latest
steps:
@@ -305,7 +337,7 @@ jobs:
path: site
deploy:
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
- needs: [build, aspnet, chinese, markdown]
+ needs: [build, aspnet, chinese, markdown, tagged-notice]
runs-on: ubuntu-latest
permissions:
pages: write
diff --git a/.gitignore b/.gitignore
index 1239467..3fccb14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,9 @@ examples/dotnet/obj/
examples/aspnet/bin/
examples/aspnet/obj/
examples/aspnet/output/
+examples/dotnet-accessibility/bin/
+examples/dotnet-accessibility/obj/
+examples/dotnet-accessibility/output/
docs/assets/browser-demo/
docs/assets/react-demo/
diff --git a/README.md b/README.md
index 2968aa0..ebe2b84 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,19 @@ pages, checks their PDF text and page counts, and exercises the documented proje
commands. CI retains the PDFs, previews, and structured report. Older showcase,
playground, and tutorial downloads keep their own verified release pins.
+The tagged C# notice lives in `examples/dotnet-accessibility`. When updating it,
+restore its lockfile from public NuGet, render both `ua1` and `ua2`, and review
+the final PDFs and native preview before replacing `docs/assets/dotnet-accessibility`.
+Run `python tools/build_dotnet_accessibility_starter.py` to rebuild the stable
+ZIP and source manifest, then `python tools/verify_dotnet_accessibility.py`.
+The verifier needs the .NET 10 SDK, Java 21 or later, and `pypdf==6.19.0`; it
+downloads a checksum-pinned veraPDF 1.30.2 validator. These are maintainer checks,
+not dependencies of the console application. Pass `--refresh-evidence` when
+updating the downloadable reports; it writes `verification.json`,
+`verapdf-ua1.json` and `verapdf-ua2.json` only after rendering and validation pass.
+CI verifies the actual ZIP in isolated published applications on Windows and
+Linux and checks that the retained reports still describe those specimens.
+
When changing the canonical domain, update `site_url`, `docs/robots.txt`, and this README, configure GitHub Pages, and verify DNS, HTTPS, sitemap URLs, and redirects. Keep the GitHub Pages URL until the custom domain is working.
Fullbleed is MIT licensed. Do not publish unsupported performance comparisons, standards conformance claims, or stale licensing statements. Retain old page URLs through the redirect map when consolidating reference material.
diff --git a/docs/accessibility/overview.md b/docs/accessibility/overview.md
index 7964f59..8292389 100644
--- a/docs/accessibility/overview.md
+++ b/docs/accessibility/overview.md
@@ -5,6 +5,10 @@ description: Author semantic documents, generate tagged PDF output, and retain a
Fullbleed provides semantic Python components, tagged PDF output profiles, inspection, and accessibility verification tools. Start with clear headings, logical reading order, meaningful table headers, document language, and appropriate alternative text.
+For .NET, use the [editable C# tagged-notice starter](../guides/csharp-tagged-pdf.md).
+It includes styled HTML/CSS, explicit fonts, a scoped table and a captioned
+illustration, with PDF/UA-1 and PDF/UA-2 output and retained verification.
+
## Try the checked example
[Open the service-notice PDF](../assets/examples/accessible.pdf) · [View HTML](../assets/examples/accessible.html) · [View CSS](../assets/examples/accessible.css)
diff --git a/docs/assets/dotnet-accessibility/notice-ua1.pdf b/docs/assets/dotnet-accessibility/notice-ua1.pdf
new file mode 100644
index 0000000..2ff882d
Binary files /dev/null and b/docs/assets/dotnet-accessibility/notice-ua1.pdf differ
diff --git a/docs/assets/dotnet-accessibility/notice-ua2.pdf b/docs/assets/dotnet-accessibility/notice-ua2.pdf
new file mode 100644
index 0000000..18c7d52
Binary files /dev/null and b/docs/assets/dotnet-accessibility/notice-ua2.pdf differ
diff --git a/docs/assets/dotnet-accessibility/notice.png b/docs/assets/dotnet-accessibility/notice.png
new file mode 100644
index 0000000..542e461
Binary files /dev/null and b/docs/assets/dotnet-accessibility/notice.png differ
diff --git a/docs/assets/dotnet-accessibility/project.zip b/docs/assets/dotnet-accessibility/project.zip
new file mode 100644
index 0000000..b5f842b
Binary files /dev/null and b/docs/assets/dotnet-accessibility/project.zip differ
diff --git a/docs/assets/dotnet-accessibility/source.json b/docs/assets/dotnet-accessibility/source.json
new file mode 100644
index 0000000..ddbd4c8
--- /dev/null
+++ b/docs/assets/dotnet-accessibility/source.json
@@ -0,0 +1,79 @@
+{
+ "source": "https://github.com/fullbleed-engine/docs/tree/main/examples/dotnet-accessibility",
+ "framework": "net10.0",
+ "package": "FullBleed.DotNet",
+ "packageVersion": "0.1.7",
+ "projectZipSha256": "bff5801de233ea3bca593a0656decd739373ec51ab5e9c1f2526d0d67eae8f02",
+ "files": [
+ {
+ "path": "AccessibleNotice.csproj",
+ "bytes": 563,
+ "sha256": "7d9a7a9d1851786c8db87013078cfffb49b1f55928f88dd9d3d8b9a4d9508e3b"
+ },
+ {
+ "path": "Assets/DMSerifDisplay-OFL.txt",
+ "bytes": 4605,
+ "sha256": "a3e5cdd67d4571dd0a24fcc968de0efde7ae97ef752daf0906e4767619dd7231"
+ },
+ {
+ "path": "Assets/DMSerifDisplay-Regular.ttf",
+ "bytes": 76580,
+ "sha256": "8cc3643535edf039aa5d95440a8542735e9197e4f4b8d9303e980fefbf5ab616"
+ },
+ {
+ "path": "Assets/Inter-OFL.txt",
+ "bytes": 4377,
+ "sha256": "5b9321a4298cfeb6b34354164a1c3afc3db114569984c502b9b35d988fd58c57"
+ },
+ {
+ "path": "Assets/Inter-Variable.ttf",
+ "bytes": 876576,
+ "sha256": "29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031"
+ },
+ {
+ "path": "Assets/notice.css",
+ "bytes": 1504,
+ "sha256": "3994f4ca6c1f8539bed60fda207980a9646ad3c5a111f647da8b742cb7158495"
+ },
+ {
+ "path": "Assets/notice.html",
+ "bytes": 1957,
+ "sha256": "e4426b77b847f02394c66a97a756878e12d1b9ec27affb4716c36f4b9c8ef7b9"
+ },
+ {
+ "path": "Assets/route.svg",
+ "bytes": 596,
+ "sha256": "09c4e6146359bc1c68cec600e2ef649ac6f4caf9762366cd19e9d7ad7b1fbea7"
+ },
+ {
+ "path": "LICENSE",
+ "bytes": 1079,
+ "sha256": "36a2115938f6e68cf0d87eccff7a6a3e57c17017e909cbd640d51aff1fa0c5a9"
+ },
+ {
+ "path": "Program.cs",
+ "bytes": 2955,
+ "sha256": "d3e996ccfcdb81f9808f3a4fa208dae570af53a6775a00297eb68d5f7fc944e3"
+ },
+ {
+ "path": "README.md",
+ "bytes": 1958,
+ "sha256": "e175125529180af2457b6ba97111c62e32bfd7a48c401d822dfbaacbfbbd9f8c"
+ },
+ {
+ "path": "global.json",
+ "bytes": 111,
+ "sha256": "20c93ca7812d0a9825eef71217156f95ab2f4fe5aadfeb22ebc1c24b630018d3"
+ },
+ {
+ "path": "packages.lock.json",
+ "bytes": 306,
+ "sha256": "612f1930c91b980f19cd58755526d4a78f67117163d761e4ff683817e1eef4af"
+ }
+ ],
+ "outputs": {
+ "notice-ua1.pdf": "2aab47bbb10a7ff165c62c3d663e1638dc0abd37982e7bfa9501cdc7cdb091d8",
+ "notice-ua2.pdf": "20df3d1684cfc9dea142d1bf36cfb20790c9f33931979e420a4fe61c0f217fe1",
+ "notice.png": "445e9b5c417399eb0ada4de06ea10fad89126f2fe8d937c64f40dd57e2c8c87b"
+ }
+}
diff --git a/docs/assets/dotnet-accessibility/verapdf-ua1.json b/docs/assets/dotnet-accessibility/verapdf-ua1.json
new file mode 100644
index 0000000..b67fdef
--- /dev/null
+++ b/docs/assets/dotnet-accessibility/verapdf-ua1.json
@@ -0,0 +1,63 @@
+{"report":{"buildInformation":{"releaseDetails":[{
+ "id" : "core",
+ "version" : "1.30.2",
+ "buildDate" : 1780501380000
+},{
+ "id" : "validation-model",
+ "version" : "1.30.2",
+ "buildDate" : 1780501560000
+},{
+ "id" : "apps",
+ "version" : "1.30.2",
+ "buildDate" : 1780502100000
+}]},"jobs":[{"itemDetails":{
+ "name" : "C:\\dev\\workbench\\fullbleed-official\\target\\growth\\dotnet-accessibility-20261007\\docs-final\\starter-verification\\ua1\\notice.pdf",
+ "size" : 115082
+},"validationResult":[ {
+ "details" : {
+ "passedRules" : 106,
+ "failedRules" : 0,
+ "passedChecks" : 3348,
+ "failedChecks" : 0,
+ "ruleSummaries" : [ ]
+ },
+ "jobEndStatus" : "normal",
+ "profileName" : "PDF/UA-1 validation profile",
+ "statement" : "PDF file is compliant with Validation Profile requirements.",
+ "compliant" : true
+} ],"processingTime":{
+ "start" : 1791379588643,
+ "finish" : 1791379590061,
+ "difference" : 1418,
+ "duration" : "00:00:01.418"
+}}],"batchSummary":{
+ "duration" : {
+ "start" : 1791379588399,
+ "finish" : 1791379590135,
+ "difference" : 1736,
+ "duration" : "00:00:01.736"
+ },
+ "totalJobs" : 1,
+ "outOfMemory" : 0,
+ "veraExceptions" : 0,
+ "failedEncryptedJobs" : 0,
+ "validationSummary" : {
+ "nonCompliantPdfaCount" : 0,
+ "failedJobCount" : 0,
+ "compliantPdfaCount" : 1,
+ "totalJobCount" : 1,
+ "successfulJobCount" : 1
+ },
+ "failedParsingJobs" : 0,
+ "repairSummary" : {
+ "failedJobCount" : 0,
+ "totalJobCount" : 0,
+ "successfulJobCount" : 0
+ },
+ "featuresSummary" : {
+ "failedJobCount" : 0,
+ "totalJobCount" : 0,
+ "successfulJobCount" : 0
+ },
+ "multiJob" : false
+}}}
\ No newline at end of file
diff --git a/docs/assets/dotnet-accessibility/verapdf-ua2.json b/docs/assets/dotnet-accessibility/verapdf-ua2.json
new file mode 100644
index 0000000..7222906
--- /dev/null
+++ b/docs/assets/dotnet-accessibility/verapdf-ua2.json
@@ -0,0 +1,63 @@
+{"report":{"buildInformation":{"releaseDetails":[{
+ "id" : "core",
+ "version" : "1.30.2",
+ "buildDate" : 1780501380000
+},{
+ "id" : "validation-model",
+ "version" : "1.30.2",
+ "buildDate" : 1780501560000
+},{
+ "id" : "apps",
+ "version" : "1.30.2",
+ "buildDate" : 1780502100000
+}]},"jobs":[{"itemDetails":{
+ "name" : "C:\\dev\\workbench\\fullbleed-official\\target\\growth\\dotnet-accessibility-20261007\\docs-final\\starter-verification\\ua2\\notice.pdf",
+ "size" : 115301
+},"validationResult":[ {
+ "details" : {
+ "passedRules" : 1727,
+ "failedRules" : 0,
+ "passedChecks" : 5068,
+ "failedChecks" : 0,
+ "ruleSummaries" : [ ]
+ },
+ "jobEndStatus" : "normal",
+ "profileName" : "PDF/UA-2 + Tagged PDF validation profile",
+ "statement" : "PDF file is compliant with Validation Profile requirements.",
+ "compliant" : true
+} ],"processingTime":{
+ "start" : 1791379595808,
+ "finish" : 1791379597275,
+ "difference" : 1467,
+ "duration" : "00:00:01.467"
+}}],"batchSummary":{
+ "duration" : {
+ "start" : 1791379595574,
+ "finish" : 1791379597365,
+ "difference" : 1791,
+ "duration" : "00:00:01.791"
+ },
+ "totalJobs" : 1,
+ "outOfMemory" : 0,
+ "veraExceptions" : 0,
+ "multiJob" : false,
+ "featuresSummary" : {
+ "totalJobCount" : 0,
+ "failedJobCount" : 0,
+ "successfulJobCount" : 0
+ },
+ "repairSummary" : {
+ "totalJobCount" : 0,
+ "failedJobCount" : 0,
+ "successfulJobCount" : 0
+ },
+ "validationSummary" : {
+ "totalJobCount" : 1,
+ "compliantPdfaCount" : 1,
+ "failedJobCount" : 0,
+ "nonCompliantPdfaCount" : 0,
+ "successfulJobCount" : 1
+ },
+ "failedParsingJobs" : 0,
+ "failedEncryptedJobs" : 0
+}}}
\ No newline at end of file
diff --git a/docs/assets/dotnet-accessibility/verification.json b/docs/assets/dotnet-accessibility/verification.json
new file mode 100644
index 0000000..1b07978
--- /dev/null
+++ b/docs/assets/dotnet-accessibility/verification.json
@@ -0,0 +1,63 @@
+{
+ "ok": true,
+ "checkedAt": "2026-10-07T13:26:37.436229+00:00",
+ "sdk": "10.0.401",
+ "package": "FullBleed.DotNet",
+ "packageVersion": "0.1.7",
+ "engineVersion": "2.5.13",
+ "packageSha256": "4334469bb70fe5896c479f9bb68025cd0fefafa280429dd367055f1818ecd1ee",
+ "projectZipSha256": "bff5801de233ea3bca593a0656decd739373ec51ab5e9c1f2526d0d67eae8f02",
+ "freshNugetCache": true,
+ "nativeLibraryOverride": false,
+ "isolatedPublishedApp": true,
+ "commands": [
+ "sdk",
+ "restore",
+ "publish",
+ "ua1",
+ "ua1-repeat",
+ "ua2",
+ "ua2-repeat"
+ ],
+ "profiles": [
+ {
+ "profile": "ua1",
+ "pdfSha256": "2aab47bbb10a7ff165c62c3d663e1638dc0abd37982e7bfa9501cdc7cdb091d8",
+ "previewSha256": "445e9b5c417399eb0ada4de06ea10fad89126f2fe8d937c64f40dd57e2c8c87b",
+ "textSha256": "b6416f0d3b3bec1137e6d3b4bac864e1c3a3b1289283bcbd43d4cf908e8aa9da",
+ "repeatPdfBytesIdentical": true,
+ "veraPDF": {
+ "version": "1.30.2",
+ "profile": "ua1",
+ "failedChecks": 0,
+ "installerUrl": "https://software.verapdf.org/rel/1.30/verapdf-greenfield-1.30.2-installer.zip",
+ "installerSha256": "6cc6341cb1af644044054b81f00a6590a7918abb18f762243de115258bcad838"
+ }
+ },
+ {
+ "profile": "ua2",
+ "pdfSha256": "20df3d1684cfc9dea142d1bf36cfb20790c9f33931979e420a4fe61c0f217fe1",
+ "previewSha256": "445e9b5c417399eb0ada4de06ea10fad89126f2fe8d937c64f40dd57e2c8c87b",
+ "textSha256": "b6416f0d3b3bec1137e6d3b4bac864e1c3a3b1289283bcbd43d4cf908e8aa9da",
+ "repeatPdfBytesIdentical": true,
+ "veraPDF": {
+ "version": "1.30.2",
+ "profile": "ua2",
+ "failedChecks": 0,
+ "installerUrl": "https://software.verapdf.org/rel/1.30/verapdf-greenfield-1.30.2-installer.zip",
+ "installerSha256": "6cc6341cb1af644044054b81f00a6590a7918abb18f762243de115258bcad838"
+ }
+ }
+ ],
+ "runtimeIdentity": {
+ "runtime": "10.0.12",
+ "framework": ".NETCoreApp,Version=v10.0",
+ "rid": "win-x64",
+ "nativeLibrarySha256": "7252e7add0c8639992bff4d6d01842a0584b68d57db0432e456054138eec41a9"
+ },
+ "scope": "Downloaded starter, retained final PDFs and machine-verifiable PDF/UA checks. No blanket accessibility or assistive-technology claim.",
+ "validatorReports": {
+ "verapdf-ua1.json": "a7a8007e02693e46ab9d10214a90b0b58d7a4d79469008f341805c84f7e17723",
+ "verapdf-ua2.json": "2d56867945fb3394152ee45be9edae80bb2f3c159748d5e6dcacce2c980cd59c"
+ }
+}
diff --git a/docs/examples.md b/docs/examples.md
index 853253f..6c97039 100644
--- a/docs/examples.md
+++ b/docs/examples.md
@@ -41,6 +41,8 @@ Start with the [invoice guide](guides/invoices.md), or
[serve an invoice from FastAPI, Flask, or Django](guides/web-frameworks.md).
The Python starter also includes a [tested Docker image](guides/python-docker.md).
For C# web applications, try the [ASP.NET Core download starter](guides/aspnet-pdf.md).
+For a tagged document, use the [editable C# library notice](guides/csharp-tagged-pdf.md)
+with PDF/UA profiles, embedded fonts and retained validation reports.
diff --git a/docs/getting-started/dotnet.md b/docs/getting-started/dotnet.md
index daf3f58..86b6e0b 100644
--- a/docs/getting-started/dotnet.md
+++ b/docs/getting-started/dotnet.md
@@ -152,6 +152,8 @@ document and retain the relevant verification evidence before making an
accessibility, archival, or print-standard claim.
For a web application, download the [ASP.NET Core PDF starter](../guides/aspnet-pdf.md).
+For tagged output, use the [C# library-notice starter](../guides/csharp-tagged-pdf.md)
+with PDF/UA profiles and independent validation.
It includes a download page, styled invoice, private responses, and checks against
the published application artifact.
diff --git a/docs/guides/csharp-tagged-pdf.md b/docs/guides/csharp-tagged-pdf.md
new file mode 100644
index 0000000..b7fa185
--- /dev/null
+++ b/docs/guides/csharp-tagged-pdf.md
@@ -0,0 +1,122 @@
+---
+title: Create tagged PDFs in C# from HTML and CSS
+description: Download a .NET 10 starter for PDF/UA-1 and PDF/UA-2 output with editable HTML/CSS, embedded fonts, semantic tables, alternative text, and retained verification evidence.
+---
+# Create tagged PDFs in C\#
+
+Generate a designed library notice with the `FullBleed.DotNet` NuGet package.
+The starter includes editable HTML and CSS, two embedded fonts, a route
+illustration and a choice of PDF/UA-1 or PDF/UA-2 output. C# calls the native
+Rust engine in your application process.
+
+[Download the C# starter](../assets/dotnet-accessibility/project.zip){ .md-button .md-button--primary }
+[Open the PDF/UA-1 specimen](../assets/dotnet-accessibility/notice-ua1.pdf){ .md-button }
+[Open the PDF/UA-2 specimen](../assets/dotnet-accessibility/notice-ua2.pdf){ .md-button }
+
+[](../assets/dotnet-accessibility/notice-ua1.pdf)
+
+The event and location are fictional. Use the design and source as a starting
+point, then review your own document's semantics and rendered pages.
+
+## Run the editable example
+
+Install the .NET 10 SDK. Extract the download, open `fullbleed-tagged-notice`,
+and run:
+
+```sh
+dotnet restore --locked-mode
+dotnet run -c Release --no-restore -- --profile ua1 --out output/ua1
+dotnet run -c Release --no-restore -- --profile ua2 --out output/ua2
+```
+
+Each output directory contains `notice.pdf`, PNG previews rendered from that
+PDF, and `render.json` with a SHA-256 digest, inspection and rendering
+diagnostics. The application fails on missing glyphs. Templates, fonts and
+the SVG are local assets; the document makes no network requests.
+
+The lockfile pins FullBleed.DotNet 0.1.7, containing engine 2.5.13. The
+[C# quickstart](../getting-started/dotnet.md) covers ordinary PDF generation;
+the [ASP.NET guide](aspnet-pdf.md) covers HTTP downloads.
+
+## Configure the output profile
+
+The relevant options in `Program.cs` are:
+
+```csharp
+using var engine = new FullBleedEngine(new FullBleedEngineOptions
+{
+ PdfProfile = PdfProfile.PdfUa1,
+ DocumentLanguage = "en-US",
+ DocumentTitle = "Your next chapter: Riverton Library workshop",
+ Assets = [
+ FullBleedAsset.FromPath(interPath, FullBleedAssetKind.Font),
+ FullBleedAsset.FromPath(displayFontPath, FullBleedAssetKind.Font),
+ FullBleedAsset.FromPath(routePath, FullBleedAssetKind.Svg, "route.svg"),
+ ],
+});
+var result = engine.RenderPdfWithDiagnostics(html, css);
+```
+
+The complete source resolves these paths from the application's copied
+`Assets` directory. Choose `PdfProfile.PdfUa2` for the second profile. Selecting
+a profile configures output; it does not establish that your final document
+satisfies every accessibility requirement.
+
+## Preserve the source meaning
+
+The notice uses one `h1`, ordered `h2` sections, a list, a table with `scope`
+on its row and column headers, and a figure with an authored description and
+caption. Its source order follows its visual reading sequence.
+
+```html
+
+
+ Step-free route: main entrance (left) → corridor → Learning Room (right).
+
+```
+
+Change the illustration, description and caption together. Keep real headings
+and table headers when adjusting the design. Set the language and title for
+your actual document, and choose embeddable fonts covering its characters.
+The bundled fonts include their OFL notices.
+
+`example.org/workshops` is printed sample text. This example does not create
+an interactive PDF link for that address.
+
+## Validate the final PDF
+
+The supplied specimens pass veraPDF 1.30.2's machine checks for their selected
+profiles. The [verification record](../assets/dotnet-accessibility/verification.json)
+records a fresh public NuGet restore, the published application's runtime and
+native-library identity, repeatable PDF bytes and specimen digests. The
+[source manifest](../assets/dotnet-accessibility/source.json) identifies every
+file in the download. Full validator reports are retained for
+[PDF/UA-1](../assets/dotnet-accessibility/verapdf-ua1.json) and
+[PDF/UA-2](../assets/dotnet-accessibility/verapdf-ua2.json).
+
+Inspect the final pages and `render.json`, then run an independent validator
+such as [veraPDF](https://docs.verapdf.org/cli/validation/):
+
+```sh
+verapdf --format json -f ua1 output/ua1/notice.pdf
+verapdf --format json -f ua2 output/ua2/notice.pdf
+```
+
+Keep the validator version, exact PDF, source assets, report and digest
+together. Repeat these checks after changing your content or design. Review
+reading order, heading levels, table relationships, alternative text,
+contrast and usefulness with the intended readers and assistive technology.
+Machine checks alone do not establish full PDF/UA or WCAG acceptance.
+
+## Publish the console application
+
+```sh
+dotnet publish -c Release --no-restore -o output/publish
+dotnet output/publish/AccessibleNotice.dll --profile ua1 --out output/published
+```
+
+Keep the complete publish directory, including native libraries, templates,
+fonts and notices. The package provides Windows x64, Linux x64 (glibc), Intel
+macOS and Apple Silicon macOS binaries. The application needs the matching
+.NET runtime; it does not need Python or a browser renderer.
diff --git a/examples/dotnet-accessibility/AccessibleNotice.csproj b/examples/dotnet-accessibility/AccessibleNotice.csproj
new file mode 100644
index 0000000..dfb077a
--- /dev/null
+++ b/examples/dotnet-accessibility/AccessibleNotice.csproj
@@ -0,0 +1,14 @@
+
+
+ Exe
+ net10.0
+ LatestPatch
+ enable
+ enable
+ true
+
+
+
+
+
+
diff --git a/examples/dotnet-accessibility/Assets/DMSerifDisplay-OFL.txt b/examples/dotnet-accessibility/Assets/DMSerifDisplay-OFL.txt
new file mode 100644
index 0000000..be384f0
--- /dev/null
+++ b/examples/dotnet-accessibility/Assets/DMSerifDisplay-OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2014-2018 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. Copyright 2019 Google LLC.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/examples/dotnet-accessibility/Assets/DMSerifDisplay-Regular.ttf b/examples/dotnet-accessibility/Assets/DMSerifDisplay-Regular.ttf
new file mode 100644
index 0000000..bc5ed0c
Binary files /dev/null and b/examples/dotnet-accessibility/Assets/DMSerifDisplay-Regular.ttf differ
diff --git a/examples/dotnet-accessibility/Assets/Inter-OFL.txt b/examples/dotnet-accessibility/Assets/Inter-OFL.txt
new file mode 100644
index 0000000..21f6aff
--- /dev/null
+++ b/examples/dotnet-accessibility/Assets/Inter-OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/examples/dotnet-accessibility/Assets/Inter-Variable.ttf b/examples/dotnet-accessibility/Assets/Inter-Variable.ttf
new file mode 100644
index 0000000..047c92f
Binary files /dev/null and b/examples/dotnet-accessibility/Assets/Inter-Variable.ttf differ
diff --git a/examples/dotnet-accessibility/Assets/notice.css b/examples/dotnet-accessibility/Assets/notice.css
new file mode 100644
index 0000000..7347e4f
--- /dev/null
+++ b/examples/dotnet-accessibility/Assets/notice.css
@@ -0,0 +1,23 @@
+@page { size: A4; margin: 17mm 20mm; }
+body { font-family: Inter, sans-serif; font-size: 10.5pt; line-height: 1.45; color: #183c38; }
+p { margin: 0 0 8pt; }
+.masthead { border-top: 4pt solid #183c38; padding-top: 9pt; font-size: 9pt; font-weight: 700; letter-spacing: 0.5pt; }
+.series { margin-left: 34pt; font-size: 8pt; letter-spacing: 0.6pt; }
+h1 { font-family: "DM Serif Display", serif; font-size: 42pt; font-weight: 400; line-height: 1.06; margin: 15pt 0 8pt; }
+.intro { font-size: 13pt; line-height: 1.4; max-width: 440pt; margin-bottom: 16pt; }
+h2 { font-size: 12pt; font-weight: 700; margin: 14pt 0 7pt; break-after: avoid; }
+.visit { background: #eef2e5; border-left: 4pt solid #183c38; padding: 11pt 14pt 5pt; }
+.visit h2 { margin-top: 0; }
+table { width: 100%; border-collapse: collapse; font-size: 9.5pt; }
+th, td { text-align: left; padding: 7pt 6pt; border-bottom: 0.7pt solid #a7b4a7; vertical-align: top; }
+thead th { background: #183c38; color: #fff; font-weight: 700; }
+tbody th { width: 52pt; font-weight: 700; }
+tr { break-inside: avoid; }
+figure { margin: 0; }
+figure img { width: 100%; height: 65pt; }
+figcaption { font-size: 9pt; margin-top: 4pt; }
+ul { padding-left: 15pt; margin: 0 0 8pt; }
+li { padding-left: 2pt; margin: 3pt 0; }
+a { color: #183c38; text-decoration: underline; font-weight: 700; }
+.more { margin-top: 12pt; padding-top: 9pt; border-top: 1pt solid #183c38; font-size: 9pt; }
+.sample-note { color: #455c53; font-size: 8pt; line-height: 1.35; margin-top: 5pt; }
diff --git a/examples/dotnet-accessibility/Assets/notice.html b/examples/dotnet-accessibility/Assets/notice.html
new file mode 100644
index 0000000..ecd8280
--- /dev/null
+++ b/examples/dotnet-accessibility/Assets/notice.html
@@ -0,0 +1,38 @@
+
+
+
Your next chapter: Riverton Library workshop
+
+
+
RIVERTON LIBRARY LEARN TOGETHER / 01
+
Your next chapter.
+
A friendly introduction to finding books, learning online, and making the most of your library.
+
+
Your visit
+
Saturday, 17 October 2026 · 10:00–11:30 Learning Room, ground floor · 24 Example Lane
+
Free to attend. No library card or previous experience needed.