diff --git a/.github/workflows/verify-cloudflare-facts.yml b/.github/workflows/verify-cloudflare-facts.yml index 4ef3eed..5745d30 100644 --- a/.github/workflows/verify-cloudflare-facts.yml +++ b/.github/workflows/verify-cloudflare-facts.yml @@ -141,7 +141,7 @@ jobs: --base dev \ --head "$BRANCH" \ --title "chore(verifier): cloudflare-facts value change (${DATE_TAG})" \ - --body "Automated PR from verify-cloudflare-facts.yml — at least one verified fact changed or went absent at the upstream source. Review the diff; the privacy pages render this data verbatim. The BASELINE prose in docs/BASELINE_COPY.md may need a paired update." + --body "Automated PR from verify-cloudflare-facts.yml — at least one verified fact's value changed at the upstream source (or, for DPF, the listed organisation is no longer present). Review the diff; the privacy pages render this data verbatim. The BASELINE prose in docs/BASELINE_COPY.md may need a paired update." - name: Open verifier-alert Issue on failure if: ${{ steps.run.outputs.has_failure == 'true' && steps.run.outputs.dry_run == 'false' }} diff --git a/CLAUDE.md b/CLAUDE.md index 86c1c6b..6231af0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,7 +78,7 @@ possible; Pass 2 should follow it. - Surface uncertainty as explicit open questions in plans, not resolved silently. -## Process rules from Pass 1 +## Process rules from Pass 1+2 Specific technical lessons worth codifying: @@ -107,6 +107,7 @@ Specific technical lessons worth codifying: carve-out only when a static page genuinely cannot serve the use case. The `wrangler.jsonc` comment block carries the same posture statement alongside its env-inheritance gotcha. +- **Workflow-permission verification for non-standard API endpoints (Pass 2 lesson).** When a GitHub Actions workflow change asserts that a `permissions:` scope (`actions: write`, `contents: write`, etc.) unlocks a given REST API endpoint, the assertion needs runtime verification — not just doc-reading. GitHub Actions' `permissions:` YAML enumerates a fixed list of scopes; not every API endpoint maps cleanly to one of those scopes. Variables in particular are not in the permissions list at all, and the default `GITHUB_TOKEN` returns HTTP 403 on `PATCH /repos/{owner}/{repo}/actions/variables/{name}` regardless of the declared scope. For any workflow change involving an API endpoint outside the well-known set (issues, pull-requests, contents), one of the following is required before merge: (a) runtime dispatch of the workflow against a real auth setup, (b) explicit citation of GitHub docs showing the endpoint accepts the declared token scope, or (c) a runbook step in the plan calling out "this requires a PAT, not `GITHUB_TOKEN`, because ." The Pass 2 G D.10/11 chain shipped a 403-on-first-run bug because none of the three was performed. ## Skills diff --git a/docs/CSS_CONVENTIONS.md b/docs/CSS_CONVENTIONS.md new file mode 100644 index 0000000..74a8942 --- /dev/null +++ b/docs/CSS_CONVENTIONS.md @@ -0,0 +1,79 @@ +# CSS conventions — Blackbrowed Labs site + +Codified during Phase J of Pass 3 (2026-05-12) as the first +multi-extraction CSS work crossed the threshold where conventions +needed to be explicit. Treat this doc as the authority for any +future CSS-sharing decision; deviate only with an explicit +rationale recorded in the deviating plan. + +## 1. Module location + +CSS shared across more than one page template or component lives at +`src/styles/.css`, where `` names the subtree +(`products.css`, `forms.css`, etc.). Each module is imported +exactly once via `@import` at the top of `src/styles/global.css`. +The whole site loads `global.css` (via `src/layouts/BaseLayout.astro`), +so the shared modules cascade everywhere. + +Per-component or per-page CSS that is genuinely local — rules that +do not appear elsewhere in the codebase — stays in the +component's scoped ` diff --git a/src/pages/datenschutz.astro b/src/pages/datenschutz.astro index dd321f3..fad96ec 100644 --- a/src/pages/datenschutz.astro +++ b/src/pages/datenschutz.astro @@ -382,6 +382,7 @@ const tocItems = [ } .datenschutz__content { max-width: 68ch; + margin-inline: auto; } .datenschutz__stand { @@ -526,7 +527,7 @@ const tocItems = [ row-gap: 0.5rem; } .datenschutz__section { - margin-top: 2rem; + margin-top: 1.75rem; } .datenschutz__content h3 { font-size: 1.125rem; diff --git a/src/pages/en/contact.astro b/src/pages/en/contact.astro index a0b403b..52de52a 100644 --- a/src/pages/en/contact.astro +++ b/src/pages/en/contact.astro @@ -77,17 +77,17 @@ const t = getUiStrings('en'); margin: 0 auto; padding: 6rem 3rem 4rem; } - .contact h1 { margin: 0 0 3rem; max-width: 68ch; } + .contact h1 { margin: 0 auto 3rem; max-width: 68ch; } .contact__content { max-width: 68ch; - margin: 0 0 3rem; + margin: 0 auto 3rem; } .contact__content p { max-width: 68ch; - margin: 0 0 1.5rem; + margin: 0 auto 1.5rem; } .contact__content h2 { - margin: 3rem 0 1rem; + margin: 3rem auto 1rem; font-size: var(--text-h3); color: var(--color-heading); max-width: 68ch; @@ -100,7 +100,7 @@ const t = getUiStrings('en'); font-size: 2.25rem; line-height: 1.1; letter-spacing: -0.015em; - margin-bottom: 1.5rem; + margin: 0 0 1.5rem; max-width: 14ch; } .contact__content, diff --git a/src/pages/en/legal.astro b/src/pages/en/legal.astro index 068f8e2..528dfde 100644 --- a/src/pages/en/legal.astro +++ b/src/pages/en/legal.astro @@ -149,6 +149,7 @@ const description = } .impressum__content { max-width: 68ch; + margin-inline: auto; } .impressum__content h1 { @@ -160,20 +161,21 @@ const description = line-height: var(--leading-small); color: var(--color-text-muted); max-width: 48ch; - margin: -2rem 0 2.5rem; - padding-top: 0.75rem; + margin: 0 0 1.75rem; + padding: 0.75rem 0 0; border-top: 1px solid var(--color-border); + font-style: italic; } .impressum__lede { max-width: 68ch; - margin: 0 0 1.5rem; + margin: 0 auto 1.5rem; } .impressum__address { max-width: 68ch; line-height: 1.5; - margin: 0 0 2.5rem; + margin: 0 auto 2.5rem; font-style: normal; } @@ -188,7 +190,7 @@ const description = } .impressum__p { max-width: 68ch; - margin: 0 0 0.75rem; + margin: 0 auto 0.75rem; } .impressum__p:last-child { margin-bottom: 0; @@ -212,7 +214,6 @@ const description = } .impressum__courtesy { max-width: none; - margin: -0.25rem 0 1.75rem; } .impressum__lede, .impressum__address, diff --git a/src/pages/en/privacy.astro b/src/pages/en/privacy.astro index cbd3e0d..c938ad6 100644 --- a/src/pages/en/privacy.astro +++ b/src/pages/en/privacy.astro @@ -376,6 +376,7 @@ const tocItems = [ } .datenschutz__content { max-width: 68ch; + margin-inline: auto; } .datenschutz__courtesy { @@ -531,7 +532,7 @@ const tocItems = [ row-gap: 0.5rem; } .datenschutz__section { - margin-top: 2rem; + margin-top: 1.75rem; } .datenschutz__content h3 { font-size: 1.125rem; diff --git a/src/pages/en/products/[slug].astro b/src/pages/en/products/[slug].astro index 1e7cb53..7a8f434 100644 --- a/src/pages/en/products/[slug].astro +++ b/src/pages/en/products/[slug].astro @@ -136,246 +136,3 @@ const renderedReleaseBodies: string[] = await Promise.all( - - diff --git a/src/pages/en/products/index.astro b/src/pages/en/products/index.astro index a1e8948..c57488d 100644 --- a/src/pages/en/products/index.astro +++ b/src/pages/en/products/index.astro @@ -80,192 +80,3 @@ const description = - - diff --git a/src/pages/impressum.astro b/src/pages/impressum.astro index 2d389f9..ecab903 100644 --- a/src/pages/impressum.astro +++ b/src/pages/impressum.astro @@ -143,6 +143,7 @@ const description = } .impressum__content { max-width: 68ch; + margin-inline: auto; } .impressum__content h1 { @@ -151,13 +152,13 @@ const description = .impressum__lede { max-width: 68ch; - margin: 0 0 1.5rem; + margin: 0 auto 1.5rem; } .impressum__address { max-width: 68ch; line-height: 1.5; - margin: 0 0 2.5rem; + margin: 0 auto 2.5rem; font-style: normal; } @@ -172,7 +173,7 @@ const description = } .impressum__p { max-width: 68ch; - margin: 0 0 0.75rem; + margin: 0 auto 0.75rem; } .impressum__p:last-child { margin-bottom: 0; diff --git a/src/pages/kontakt.astro b/src/pages/kontakt.astro index a27c59d..dfeb9df 100644 --- a/src/pages/kontakt.astro +++ b/src/pages/kontakt.astro @@ -80,17 +80,17 @@ const t = getUiStrings('de'); margin: 0 auto; padding: 6rem 3rem 4rem; } - .contact h1 { margin: 0 0 3rem; max-width: 68ch; } + .contact h1 { margin: 0 auto 3rem; max-width: 68ch; } .contact__content { max-width: 68ch; - margin: 0 0 3rem; + margin: 0 auto 3rem; } .contact__content p { max-width: 68ch; - margin: 0 0 1.5rem; + margin: 0 auto 1.5rem; } .contact__content h2 { - margin: 3rem 0 1rem; + margin: 3rem auto 1rem; font-size: var(--text-h3); color: var(--color-heading); max-width: 68ch; @@ -103,7 +103,7 @@ const t = getUiStrings('de'); font-size: 2.25rem; line-height: 1.1; letter-spacing: -0.015em; - margin-bottom: 1.5rem; + margin: 0 0 1.5rem; max-width: 14ch; } .contact__content, diff --git a/src/pages/produkte/[slug].astro b/src/pages/produkte/[slug].astro index f315291..f7122a6 100644 --- a/src/pages/produkte/[slug].astro +++ b/src/pages/produkte/[slug].astro @@ -168,243 +168,3 @@ const renderedReleaseBodies: string[] = await Promise.all( - - diff --git a/src/pages/produkte/index.astro b/src/pages/produkte/index.astro index 19916ce..ba2f842 100644 --- a/src/pages/produkte/index.astro +++ b/src/pages/produkte/index.astro @@ -90,193 +90,3 @@ const description = - - diff --git a/src/styles/forms.css b/src/styles/forms.css new file mode 100644 index 0000000..906ee92 --- /dev/null +++ b/src/styles/forms.css @@ -0,0 +1,146 @@ +/* ============================================================= + Forms — shared CSS module. + + Consumed by: + - src/components/forms/ContactForm.astro + + Per docs/CSS_CONVENTIONS.md §2, future forms get their own BEM + namespace (e.g. .subscribe-form__field, .feedback-form__submit) + and add their rule blocks below the .contact-form__* block in + this file. Visual treatment carries over by re-implementing the + look under the new namespace, not by class-attribute aliasing. + + Pre-extraction this CSS lived in ContactForm.astro's scoped +