From 6b6d79ffc986b7959a8c418069e047e60f79b8c0 Mon Sep 17 00:00:00 2001
From: Brummel
Date: Wed, 20 May 2026 23:43:53 +0200
Subject: [PATCH] docs: spec for Visual Parity Pass (Phase 1.5)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Restores markup contracts the theme CSS expects: section-title macro,
single-page-title banner, footer partial, navbar logo+Home link, plus
config.extra-driven footer values shared with the contact page.
Scope is markup, not modernization — Bootstrap 3 and theme CSS stay.
---
.../2026-05-20-praxis-visual-parity-design.md | 193 ++++++++++++++++++
1 file changed, 193 insertions(+)
create mode 100644 docs/specs/2026-05-20-praxis-visual-parity-design.md
diff --git a/docs/specs/2026-05-20-praxis-visual-parity-design.md b/docs/specs/2026-05-20-praxis-visual-parity-design.md
new file mode 100644
index 0000000..1f5d063
--- /dev/null
+++ b/docs/specs/2026-05-20-praxis-visual-parity-design.md
@@ -0,0 +1,193 @@
+# Praxis Visual Parity Pass — Design Spec
+
+**Status:** draft
+**Date:** 2026-05-20
+**Phase:** 1.5 (finish-up after Phase 1 sign-off)
+
+## Context
+
+Phase 1 produced a Zola-based staging site at `http://preview.praxis-am-lienhardplatz.de/` that builds, deploys, and renders all content from the live Grav site. The user signed off Phase 1 acknowledging "MENGE Unterschiede" against the live look — accepting them as follow-up work.
+
+A visual diff against the live HTML (`https://www.praxis-am-lienhardplatz.de/`) revealed ten material gaps that prevent the theme CSS in `sass/_praxis_theme.scss` from applying. The theme CSS itself was lifted verbatim from the Grav theme, so making it work is mostly a question of restoring the markup contracts (class names, element nesting) it expects.
+
+This spec covers that visual-parity pass: closing the markup gaps so the staging site **looks** identical to the live site, while keeping the HTML clean (no Grav-era quirks reproduced).
+
+## Goals
+
+- The staging site, rendered in a browser, is visually indistinguishable from the live site on the four routes that exist on both sides: `/`, `/services`, `/team`, `/kontakt` (live) ↔ `/contact` (staging), `/impressum`.
+- The theme CSS in `sass/_praxis_theme.scss` is not modified — markup conforms to it.
+- Recurring patterns (section-title, single-page-title, footer) are factored into Tera macros / partials so Phase-2 modernization has a single point of change.
+- Footer content (hours, contact, address) lives in `config.toml` `[extra]` as a single source of truth, shared by the footer partial and the `/contact` page.
+
+## Non-Goals (deferred to Phase 2)
+
+- Markup modernization: no semantic HTML5 rewrite, no CSS Grid, no replacement of Bootstrap 3 grid classes.
+- URL escaping in templates (`/`): cosmetic in view-source, functional in browser. Phase 2.
+- Content drift (e.g., hours displayed on home news section vs. footer): content is the user's responsibility, not markup parity.
+- Accessibility audit, WCAG fixes, alt-text rewrite.
+- Reproducing Grav HTML quirks (double `...
` wrappers, `...` tag mismatches): we keep clean markup.
+
+## Reference
+
+Live HTML snapshots were captured to `/tmp/praxis-diff/live/` (transient — do not check in). The authoritative reference during implementation is what the live site currently serves; if in doubt, re-fetch.
+
+## Architecture
+
+### New macros
+
+**`templates/macros/section_title.html`** — `section_title(title, anchor="")`
+Renders the bordered-icon section-title pattern used on home (4×), services (6×), team (2+ ×). When `anchor` is given, wraps the heading in ``.
+
+```html
+{% macro section_title(title, anchor="") %}
+
+
+ {% if anchor %} {% endif %}
+ {{ title }}
+ {% if anchor %}{% endif %}
+
+
+
+{% endmacro %}
+```
+
+**`templates/macros/page_title.html`** — `page_title(title)`
+Renders the ` ` banner that sits between navbar and content on every subpage.
+
+```html
+{% macro page_title(title) %}
+
+{% endmacro %}
+```
+
+### New partial
+
+**`templates/partials/footer.html`** — three-column footer widget block + copyright bar, drawing all values from `config.extra`.
+
+Structure (matching live):
+```html
+
+```
+
+`base.html` includes it before ` |