diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
new file mode 100644
index 0000000..e1cb940
--- /dev/null
+++ b/.github/workflows/pages.yml
@@ -0,0 +1,321 @@
+name: Deploy to GitHub Pages
+
+on:
+ push:
+ branches: ["main"]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Generate Jekyll site
+ shell: bash
+ env:
+ REPO_NAME: ${{ github.event.repository.name }}
+ REPO_FULL: ${{ github.repository }}
+ REPO_DESC: ${{ github.event.repository.description }}
+ run: |
+ set -e
+
+ OWNER="${REPO_FULL%%/*}"
+ REPO="${REPO_NAME}"
+ DESC="${REPO_DESC:-$REPO}"
+ BASE_URL="/${REPO}"
+ STAGE="_site_source"
+
+ echo "::group::Staging allowed files"
+
+ # ── Stage ONLY allowed files into a clean directory ───────────
+ # Whitelist: *.md files + LICENSE + LICENSE.txt
+ # Everything else is excluded from the site.
+ mkdir -p "${STAGE}"
+
+ # Copy all .md files preserving directory structure
+ find . -name '*.md' \
+ -not -path './.git/*' \
+ -not -path "./${STAGE}/*" \
+ | while read -r f; do
+ dest="${STAGE}/${f#./}"
+ mkdir -p "$(dirname "$dest")"
+ cp "$f" "$dest"
+ done
+
+ # Copy LICENSE files (plain text)
+ for lf in LICENSE LICENSE.txt; do
+ [ -f "$lf" ] && cp "$lf" "${STAGE}/"
+ done
+
+ # Remove any .gitkeep files that got copied
+ find "${STAGE}" -name '.gitkeep' -delete
+
+ echo "Staged files:"
+ find "${STAGE}" -type f | sort
+ echo "::endgroup::"
+
+ # ── Everything below operates inside the staging dir ──────────
+ cd "${STAGE}"
+
+ echo "::group::Generating Jekyll config and layout"
+
+ # ── _config.yml ───────────────────────────────────────────────
+ cat > _config.yml << CONFIGEOF
+ title: "${REPO}"
+ description: "${DESC}"
+ permalink: pretty
+ baseurl: "${BASE_URL}"
+
+ defaults:
+ - scope:
+ path: "docs"
+ values:
+ layout: default
+ nav_section: docs
+ CONFIGEOF
+
+ # ── _layouts/default.html ─────────────────────────────────────
+ mkdir -p _layouts
+ cat > _layouts/default.html << 'LAYOUTEOF'
+
+
+
+
+
+ {{ page.title | default: site.title }}
+
+
+
+
+
+
+
+ {{ content }}
+
+
+
+ LAYOUTEOF
+ echo "::endgroup::"
+
+ echo "::group::Processing content files"
+
+ # ── README.md → homepage ──────────────────────────────────────
+ if [ -f "README.md" ] && ! head -1 README.md | grep -q '^\-\-\-'; then
+ TEMP=$(mktemp)
+ printf -- '---\nlayout: default\ntitle: Home\npermalink: /\n---\n\n' > "$TEMP"
+ cat README.md >> "$TEMP"
+ mv "$TEMP" README.md
+ echo "Processed README.md → homepage"
+ fi
+
+ # ── Docs: add front matter + create index ─────────────────────
+ if [ -d "docs" ]; then
+ for f in docs/*.md; do
+ [ -f "$f" ] || continue
+ [ "$(basename "$f")" = "index.md" ] && continue
+
+ if ! head -1 "$f" | grep -q '^\-\-\-'; then
+ BASENAME=$(basename "$f" .md)
+ SYNOPSIS=$(grep -m1 -A1 '## Synopsis' "$f" 2>/dev/null | tail -1 | sed 's/^[[:space:]]*//')
+ [ -z "$SYNOPSIS" ] && SYNOPSIS="$BASENAME"
+ TEMP=$(mktemp)
+ printf -- '---\nlayout: default\ntitle: %s\ndescription: "%s"\n---\n\n' "$BASENAME" "$SYNOPSIS" > "$TEMP"
+ cat "$f" >> "$TEMP"
+ mv "$TEMP" "$f"
+ echo "Processed $f"
+ fi
+ done
+
+ if [ ! -f "docs/index.md" ]; then
+ cat > docs/index.md << 'DOCSEOF'
+ ---
+ layout: default
+ title: Docs
+ permalink: /docs/
+ ---
+ # Function reference
+ {% assign doc_pages = site.pages | where_exp: "p", "p.path contains 'docs/'" | where_exp: "p", "p.name != 'index.md'" | sort: "title" %}
+ {% if doc_pages.size > 0 %}
+
+ {% for doc in doc_pages %}
+ {{ doc.title }} — {{ doc.description | default: doc.title }}
+ {% endfor %}
+
+ {% else %}
+ No function documentation found yet.
+ {% endif %}
+ DOCSEOF
+ echo "Created docs/index.md"
+ fi
+ fi
+
+ # ── Releases page ─────────────────────────────────────────────
+ cat > releases.md << RELEOF
+ ---
+ layout: default
+ title: Releases
+ permalink: /releases/
+ ---
+ # Releases
+
+
+ RELEOF
+ echo "Created releases.md"
+
+ # ── License page (from LICENSE/LICENSE.txt) ────────────────────
+ LICENSE_SRC=""
+ for lf in LICENSE LICENSE.txt; do
+ [ -f "$lf" ] && LICENSE_SRC="$lf" && break
+ done
+
+ if [ -n "$LICENSE_SRC" ] || [ -f "LICENSE.md" ]; then
+ SRC="${LICENSE_SRC:-LICENSE.md}"
+ LICENSE_TYPE="License"
+ grep -qi "apache" "$SRC" 2>/dev/null && LICENSE_TYPE="Apache License 2.0"
+ grep -qi "mit license" "$SRC" 2>/dev/null && LICENSE_TYPE="MIT License"
+ grep -qi "gnu general public" "$SRC" 2>/dev/null && LICENSE_TYPE="GPL"
+ grep -qi "bsd" "$SRC" 2>/dev/null && LICENSE_TYPE="BSD License"
+
+ if ! ([ -f "LICENSE.md" ] && head -1 LICENSE.md | grep -q '^\-\-\-'); then
+ cat > LICENSE.md << LICEOF
+ ---
+ layout: default
+ title: License
+ permalink: /license/
+ ---
+ # License
+ This project is licensed under the **${LICENSE_TYPE}**.
+
+ See the [LICENSE](https://github.com/${REPO_FULL}/blob/main/${SRC}) file for the full license text.
+ LICEOF
+ echo "Created LICENSE.md (${LICENSE_TYPE})"
+ fi
+ fi
+
+ # ── CONTRIBUTING.md ───────────────────────────────────────────
+ if [ -f "CONTRIBUTING.md" ] && ! head -1 CONTRIBUTING.md | grep -q '^\-\-\-'; then
+ TEMP=$(mktemp)
+ printf -- '---\nlayout: default\ntitle: Contributing\npermalink: /contributing/\n---\n\n' > "$TEMP"
+ cat CONTRIBUTING.md >> "$TEMP"
+ mv "$TEMP" CONTRIBUTING.md
+ echo "Processed CONTRIBUTING.md"
+ fi
+
+ # ── CODE_OF_CONDUCT.md ────────────────────────────────────────
+ if [ -f "CODE_OF_CONDUCT.md" ] && ! head -1 CODE_OF_CONDUCT.md | grep -q '^\-\-\-'; then
+ TEMP=$(mktemp)
+ printf -- '---\nlayout: default\ntitle: Code of Conduct\npermalink: /code-of-conduct/\n---\n\n' > "$TEMP"
+ cat CODE_OF_CONDUCT.md >> "$TEMP"
+ mv "$TEMP" CODE_OF_CONDUCT.md
+ echo "Processed CODE_OF_CONDUCT.md"
+ fi
+
+ echo "::endgroup::"
+
+ echo "Final site source contents:"
+ find . -type f | sort
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v5
+
+ - name: Build with Jekyll
+ uses: actions/jekyll-build-pages@v1
+ with:
+ source: ./_site_source
+ destination: ./_site
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ needs: build
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v5