# FAQ standards across Ordio (hub)

**Last Updated:** 2026-04-10

Single entry point for **FAQ answer format**, **question-set rework**, **Ordio mentions**, **data inputs**, and **iteration**—across blog, tools, templates, product, downloads, industry, comparison, and pricing pages. Surface-specific workflows stay in existing guides; this document defines shared rules and points to them.

## Google Search: FAQ rich results (Aug 2023)

Google [limited FAQ rich results](https://developers.google.com/search/blog/2023/08/howto-faq-changes) primarily to **well-known, authoritative government and health** sites. For most commercial sites, **do not** optimize FAQs mainly for expanded SERP FAQ UI.

**Still worth doing:**

- **On-page UX** (scannable answers, trustworthy legal/compliance tone on tools).
- **AEO / GEO**: concise, **self-contained** answers that models and assistants can quote accurately.
- **Structured data**: valid **FAQPage** where used—supports consistency, testing, and non-Google consumers; unused markup does not harm Search.

**Implementation rules of thumb** ([FAQPage structured data](https://developers.google.com/search/docs/appearance/structured-data/faqpage)) — unchanged in 2026: JSON-LD with vocabulary scope, visible Q&amp;A parity, one FAQPage per URL.

- **One** `FAQPage` per page URL; do not output duplicate FAQ JSON-LD (e.g. head `@graph` plus a second script) or redundant FAQ microdata alongside JSON-LD unless documented.
- Markup must reflect **visible** FAQ content on the page; do not inflate or fabricate Q&amp;A for SEO.
- Use a single pipeline for plain-text answers ([`faq_answer_html_to_schema_plain_text()`](../../v2/helpers/faq-schema-plain-text.php)) so HTML edits stay aligned with `acceptedAnswer.text`.
- **Vocabulary scope (`@context`):** Validators (and Google) must resolve `FAQPage` / `Question` / `Answer` to **https://schema.org/**. That requires either (a) `"@context": "https://schema.org"` on the **root object** of a standalone `application/ld+json` script, or (b) the same on the parent of a `@graph` that contains the FAQPage node. **Blog/lexikon, templates, ShiftOps, product `@graph` pages, branchen gastro head graph, pendlerpauschale `schema-graph.json`, and legacy compare `generateSchema()` already did (a)/(b).** Tools + marketing + compare pages that used **only** the post-footer helper previously lacked (a); [`ordio_faqpage_jsonld_standalone_document()`](../../v2/helpers/faq-jsonld.php) fixes that—**after deploy**, their FAQ blocks behave like lexikon in the Schema Validator (type **FAQPage**, not `https://www.ordio.com/tools/FAQPage`).

## FAQ section shell (canonical HTML/CSS)

**Do not hand-roll** duplicate `<section id="faq">` + `<details>` blocks on marketing pages. Use one of:

| Mechanism | When |
|-----------|------|
| [`v2/components/site-faq-section.php`](../../v2/components/site-faq-section.php) | Set `$site_faq_items` (question/answer), optional `$site_faq_zu_zur_zum` + `$site_faq_topic_plain`, optional subtitle / schema microdata / `raw_trusted` answers. |
| [`v2/components/render-faq-json.php`](../../v2/components/render-faq-json.php) | Load items from `v2/data/**` JSON (`zu_zur_zum`, `topic_plain`, `items[]`, optional `subtitle_plain`, `h2_id`, `schema_microdata`). Pair with [`include-marketing-faq-jsonld.php`](../../v2/components/include-marketing-faq-jsonld.php) after `footer.php` when using `misc-faqs/`, `industry-faqs/`, or `pricing-faq.json` (set `$ordio_faq_json_relpath`). |
| [`v2/components/render-compare-faq.php`](../../v2/components/render-compare-faq.php) | Comparison pages: load `v2/data/compare-faqs/{slug}.json` (set `$compare_faq_slug`). Pair with [`include-compare-faq-jsonld.php`](../../v2/components/include-compare-faq-jsonld.php) after `footer.php` for FAQPage JSON-LD. |
| [`v2/components/blog/BlogFAQ.php`](../../v2/components/blog/BlogFAQ.php) | Blog posts; delegates to `site-faq-section.php`. |

### Public homepage `/` vs LP “Volle Transparenz” card grid

**Root homepage (`v2/start-v2.php`):** Uses the **same accordion shell** as feature pages — [`site-faq-section.php`](../../v2/components/site-faq-section.php). Copy comes from [`homepage-faq-items.php`](../../v2/data/homepage-faq-items.php) (`ordio_homepage_faq_items()`; map `title`/`description` → `question`/`answer` in PHP). Section `id="faq"`; FAQPage JSON-LD post-footer via `ordio_echo_homepage_faq_jsonld_script()` (`@id` `https://www.ordio.com/#faq`). Placed **before** `footer.php`.

**Landing pages / variants that keep the card grid:** This is a **different pattern** from `site-faq-section.php`: a pill label (“Häufige Fragen”), fixed H2 **Volle Transparenz, keine Überraschungen.**, optional live search, and Q&amp;A cards in a responsive grid with Alpine `searchableContent` (filter + highlight). Data: [`landing-transparency-faq-items.php`](../../v2/data/landing-transparency-faq-items.php) (LP SSOT, not the root homepage file). Used on select LPs (not the canonical `/`).

| Asset | Role |
|-------|------|
| [`v2/data/homepage-faq-items.php`](../../v2/data/homepage-faq-items.php) | Root `/` only: `ordio_homepage_faq_items()` — accordion in `start-v2.php` + `ordio_echo_homepage_faq_jsonld_script()`. |
| [`v2/data/landing-transparency-faq-items.php`](../../v2/data/landing-transparency-faq-items.php) | LP card-grid: eight `title` / `description` pairs (`ordio_landing_transparency_faq_items()`), not the homepage list. |
| [`v2/sections/partials/landing-transparency-faq-section.php`](../../v2/sections/partials/landing-transparency-faq-section.php) | Markup, spacing (v2 canonical), accessibility (mobile expand/chevron, `aria-*`), optional CTA row. |
| [`v2/sections/partials/include-ordio-searchable-content-js.php`](../../v2/sections/partials/include-ordio-searchable-content-js.php) | Loads `ordio-searchable-content.min.js` once per page (registers `Alpine.data('searchableContent', …)`). |
| [`v2/js/ordio-searchable-content.js`](../../v2/js/ordio-searchable-content.js) | Shared component; product feature grids reuse the same include (same Alpine name, no duplicate inline scripts). |

**Do not** duplicate this block in PHP pages—edit the data file and/or partial. **Optional variables** on include: `$landing_transparency_faq_show_cta`, `$landing_transparency_faq_cta_include_basename` (e.g. `include_ctabuttons_free_trial.php` on `kostenlos_testen_neu.php`), outer wrapper classes, section/H2 ids.

**Copy pattern:** `Häufige Fragen` + grammatically correct **`zu` / `zur` / `zum`** + blue topic (`text-ordio-blue`). Build safe H2 inner HTML with [`ordio_faq_heading_inner_html()`](../../v2/helpers/faq-heading.php) or pass `topic_plain` via JSON. Do **not** auto-guess gender for arbitrary phrases—set the prefix per page.

**Examples:** `zum Elterngeld-Rechner`, `zur Arbeitszeiterfassung`, `zum Ordio Loop Partnerprogramm`, `zu Payroll Plus`.

**Hierarchy:** one **H2** per FAQ block; no redundant **H3** (e.g. “Ordio FAQ”) under the FAQ heading. Optional subtitle: `<p>` under H2 (see pricing JSON).

**Audit:** `php v2/scripts/dev-helpers/audit-faq-section-markup.php` (rough consistency check).

### HTML sanitization (required for visible answers)

[`site-faq-section.php`](../../v2/components/site-faq-section.php) renders FAQ answers with `sanitizeHtmlOutput()` from [`blog-template-helpers.php`](../../v2/config/blog-template-helpers.php). The component **loads that dependency automatically** if the current page has not already included it (e.g. blog `post.php` loads it; most tools/industry/comparison pages did not—this caused escaped HTML and “broken” links). **Do not** remove that guard without replacing it with an equivalent allowlisted sanitizer.

### FAQPage JSON-LD (marketing surfaces)

- **Post-footer standalone scripts (`ordio_echo_tools_faq_jsonld_script` / marketing / compare includes):** the emitted JSON-LD root object **must** include `"@context": "https://schema.org"`. Without it, processors resolve `@type` values like `FAQPage` as **relative IRIs** against the page URL (e.g. `https://www.ordio.com/tools/minijob-rechner` + `FAQPage` → `https://www.ordio.com/tools/FAQPage`), which is **not** `https://schema.org/FAQPage` and breaks Google’s FAQPage parsing. The helper wraps nodes via [`ordio_faqpage_jsonld_standalone_document()`](../../v2/helpers/faq-jsonld.php); do not hand-roll a second script without `@context`.
- **Plain text for `acceptedAnswer.text`:** always derive from the same HTML strings as the visible FAQ using [`faq_answer_html_to_schema_plain_text()`](../../v2/helpers/faq-schema-plain-text.php).
- **Build helper:** [`ordio_faqpage_schema_node()`](../../v2/helpers/faq-jsonld.php) returns one `FAQPage` object for `@graph` from an `items` array + canonical URL (used inside graphs that already set `@context`, or wrapped for standalone output).
- **Pricing:** [`v2/data/pricing-faq.json`](../../v2/data/pricing-faq.json) + [`render-faq-json.php`](../../v2/components/render-faq-json.php); FAQPage JSON-LD via [`include-marketing-faq-jsonld.php`](../../v2/components/include-marketing-faq-jsonld.php) after `footer.php` (not in `<head>` `@graph`).
- **Marketing (product/industry/pillar/downloads/content/webinar/partner/kunden):** `v2/data/misc-faqs/*.json` or `industry-faqs/*.json` with the same include; canonical URLs resolved in [`ordio_marketing_faq_canonical_from_data_relpath()`](../../v2/helpers/faq-jsonld.php) (optional `canonical_url` in JSON overrides).
- **Tools:** [`ordio_echo_tools_faq_jsonld_script()`](../../v2/helpers/faq-jsonld.php) after `footer.php`; data in `v2/data/tools-faqs/*.json` (same file as visible FAQ). Do **not** put FAQPage in the `<head>` `@graph`.
- **Comparison (`/alternativen/...`):** Single source [`v2/data/compare-faqs/{slug}.json`](../../v2/data/compare-faqs/); visible FAQ via `render-compare-faq.php`; FAQPage JSON-LD via [`include-compare-faq-jsonld.php`](../../v2/components/include-compare-faq-jsonld.php) after `footer.php` (uses [`ordio_compare_faq_canonical_from_faq_slug()`](../../v2/helpers/faq-jsonld.php) for the public URL). Do **not** embed FAQPage in the `<head>` `@graph`.
- **ShiftOps (`/shiftops`):** FAQs are inline in [`shiftops.php`](../../v2/pages/shiftops.php), not `misc-faqs` JSON. One `FAQPage` in `<head>` is the single emission path—do **not** add a second post-footer FAQ script unless FAQs are extracted to shared JSON + `render-faq-json.php`.
- **Docs pipeline product/industry pages:** Some pages load `docs/content/pages/.../faq-answers-optimized.json` and [`site-faq-section.php`](../../v2/components/site-faq-section.php), building FAQPage in PHP for `<head>` from that same file—**no** duplicate script; migrating to `misc-faqs` + post-footer include is optional for pattern alignment.
- **Single representation:** Prefer **one** FAQPage per URL. Avoid duplicating full FAQPage **microdata** on the same page unless you have a documented exception—[`pricing-faq.json`](../../v2/data/pricing-faq.json) sets `schema_microdata` to `false` so the accordion does not duplicate FAQ structured data.

**Checks:**

- `php v2/scripts/dev-helpers/audit-faq-jsonld-context.php` — quick smoke test (tools + misc + compare sample); asserts `@context` on standalone output.
- `php v2/scripts/dev-helpers/verify-faq-jsonld-parity.php` — default `pricing-faq.json` vs generated plain text; also asserts standalone document has `@context` + `@type` FAQPage.
- `php v2/scripts/dev-helpers/verify-faq-jsonld-parity.php --all-tools` — all `tools-faqs/*.json`.
- `php v2/scripts/dev-helpers/verify-faq-jsonld-parity.php --all-compare` — all `compare-faqs/*.json` (canonical from JSON `canonical_url` if set, else [`ordio_compare_faq_canonical_from_faq_slug()`](../../v2/helpers/faq-jsonld.php)).
- `php v2/scripts/dev-helpers/audit-compare-faq-ssot.php` — pages with `render-compare-faq.php` include post-footer JSON-LD; no duplicate FAQPage in PHP source.
- `php v2/scripts/dev-helpers/verify-faq-jsonld-parity.php --all-misc-faqs` — `misc-faqs/*.json` and `industry-faqs/*.json` (placeholder-only JSON files are skipped).
- `php v2/scripts/dev-helpers/audit-marketing-faq-ssot.php` — non-tool pages with `render-faq-json.php`: post-footer marketing FAQ include, no head `FAQPage`.
- `php v2/scripts/dev-helpers/audit-faq-json-internal-links.php '--glob=v2/data/compare-faqs/*.json'` — internal `href`s in compare FAQ answers (quote the glob in zsh).
- `php v2/scripts/dev-helpers/audit-faq-json-internal-links.php '--glob=v2/data/misc-faqs/*.json'` — same for misc FAQ answers.
- `php v2/scripts/dev-helpers/audit-faq-json-self-links-and-anchors.php --all` — FAQ JSON answers: no internal link to the **same** canonical URL as the page; no weak anchor text (raw path `/foo`, full `https://…` URL as visible text, first-party URL text, or bare hostname/path like `jobs.example.com/team`). Optional: `--file=…`, `--glob=…` (paths relative to repo root).

**After deploy (manual):** [Rich Results Test](https://search.google.com/test/rich-results) on representative URLs (e.g. `/tools/minijob-rechner`, `/preise`, one `/alternativen/...-vergleich`) — FAQPage should map to **schema.org**, not the site origin. **GSC URL Inspection** may still omit FAQ under **enhancements** for most commercial sites ([Aug 2023 FAQ rich result limits](https://developers.google.com/search/blog/2023/08/howto-faq-changes)); valid markup remains useful for consistency and AEO/GEO.

## Answer format: paragraphs vs lists

**Default:** one or two short **paragraphs** (`<p>`), **du** tone, **40–80 words** for blog, templates, and product FAQs (same band as existing validators).

**Use a short `<ul>` (3–5 items)** when the question clearly asks for enumeration:

- Voraussetzungen, Worauf achten?, Unterschiede, Vorteile/Nachteile, Checkliste-style intents.

**Use `<ol>`** when **order matters** (Schritt 1–3); otherwise prefer compact prose or a ul.

**Guardrails:**

- No nested lists on marketing surfaces; avoid more than **~5** bullets per answer.
- Keep **total** word count in the **40–80** band (blog/template/product); tools may follow [tools-pages-faq.mdc](../../.cursor/rules/tools-pages-faq.mdc) targets—do not inflate with long bullets.
- **Visible HTML** may include lists; **JSON-LD `acceptedAnswer.text`** is **plain text**. Use [`faq_answer_html_to_schema_plain_text()`](../../v2/helpers/faq-schema-plain-text.php) (via blog schema generation) so list items do not glue together when tags are stripped.

## Question set rework (not “keep all + add a few”)

Use the stepwise runbook: [FAQ_REWORK_DECISION_TREE.md](FAQ_REWORK_DECISION_TREE.md).

**Principles:**

1. **Prune** low-value, off-topic, or duplicate questions before adding new ones.
2. **Map** top **GSC** queries and **PAA** (SISTRIX / Serper) to questions; close gaps with new FAQs.
3. **Remove or rephrase** FAQs that **overlap H2s** or body sections (blog: `check-h2-faq-overlap.php`; templates: `check-template-block-faq-overlap.php`).
4. **Reorder**: definition → how-to → requirements → edge cases (see blog FAQ rules).
5. **Validate** schema and quality scripts for that surface.

## Ordio mentions and internal links

- **Ordio**: only when the answer naturally involves product **workflow** or a **relevant next step**—not every answer. See [`.cursor/rules/shared-patterns.mdc`](../../.cursor/rules/shared-patterns.mdc) and [`.cursor/rules/ordio-promotion-contextual.mdc`](../../.cursor/rules/ordio-promotion-contextual.mdc).
- **Internal links**: opportunity-driven; **1:1** lexikon/tool targets when the term is central to the answer. Avoid link stuffing (blog quality tooling flags excess links).

### Link anchor text (visible `<a>` labels)

Use **descriptive, sentence-natural** anchor text—what the user gets when they follow the link—not bare labels that mirror the URL or a single menu word.

- **Do not** use as anchor text: raw paths (`/preise`), full URLs (`https://…`), bare domains (`www.example.de`, `jobs.vendor.com/path`), or a lone nav label when it reads like a stub (`Schichtplan`, `Impressum`) **unless** the surrounding sentence already makes the intent obvious and the anchor still reads as a phrase (prefer **„… auf der Seite zur Schichtplanung mit Ordio“** / **„… in den rechtlichen Angaben im Impressum“**).
- **Do** use: short phrases that describe the destination (**„Funktionen und Paketen (Preise)“**, **„Übersicht aller Branchen“**, **„Karriereportal bei Ashby“**).
- **External** links: same rule—natural, descriptive anchor text. Do **not** append noisy labels like „(externer Link)“; `rel="noopener noreferrer"` (and opening in a new tab when the page implements it) is enough for machine context; the sentence can already say „bei Ashby“ or similar.
- After edits to `v2/data/**` FAQ JSON, run `audit-faq-json-self-links-and-anchors.php` on the file or `--all`.

## Surface matrix (SSOT and research)

| Surface | FAQ count (typical) | Primary research | Canonical workflow / SSOT |
|--------|---------------------|------------------|----------------------------|
| Blog | 10–15 (max ~20) | PAA, GSC, SISTRIX | `faqs` in post JSON; [FAQ_SOURCE_OF_TRUTH.md](blog/FAQ_SOURCE_OF_TRUTH.md) |
| Tools | 10–30 (per page) | Tool SERP, PAA, GSC for `/tools/...` | PHP `#faq`; [tools-pages-faq.mdc](../../.cursor/rules/tools-pages-faq.mdc) |
| Templates | 8–12 | PAA, GSC, template pipeline | [templates-pages-faq.mdc](../../.cursor/rules/templates-pages-faq.mdc), `TEMPLATE_FAQ_WORKFLOW.md` |
| Product | 8–15 | GSC, PAA, competitor scrape | JSON-driven: `misc-faqs/product_*.json` or docs pipeline; [PRODUCT_PAGE_FAQ_GUIDE.md](pages/product-pages/PRODUCT_PAGE_FAQ_GUIDE.md); FAQ JSON-LD: `include-marketing-faq-jsonld.php` |
| Downloads | Per page | Gated intent, GSC | [download-pages.mdc](../../.cursor/rules/download-pages.mdc); `misc-faqs/download_*.json` + marketing include |
| Industry | Fewer, highly targeted; **12–15** for flagship Branchen LPs when GSC/SISTRIX justify depth (e.g. `/branchen/gastronomie`: `docs/.../gastronomie/faq-answers-optimized.json`) | PAA, GSC query export, SISTRIX | `industry-faqs/*.json` + `render-faq-json.php` + marketing include; docs-pipeline JSON + `site-faq-section.php` on some pages; [industry-pages.mdc](../../.cursor/rules/industry-pages.mdc) |
| Pricing | Fewer, highly targeted | PAA, competitor scrape | `pricing-faq.json` + marketing include; [static-pages.mdc](../../.cursor/rules/static-pages.mdc) |
| Pillar / content / partner / webinar / kunden | Varies | PAA, GSC | `misc-faqs/*.json` + marketing include |
| Comparison | 6–12 (per competitor) | PAA, GSC for `/alternativen/...` | `v2/data/compare-faqs/{slug}.json` + `render-compare-faq.php` + `include-compare-faq-jsonld.php`; [comparison-pages-schema-meta.mdc](../../.cursor/rules/comparison-pages-schema-meta.mdc) |
| Homepage `/` | 8–12 typical (data-driven) | GSC path `/`, SISTRIX, Serper PAA optional | `v2/data/homepage-faq-items.php` + `site-faq-section.php`; JSON-LD `ordio_echo_homepage_faq_jsonld_script()`; LP card-grid uses **`landing-transparency-faq-items.php`** (split SSOT) — [homepage-documentation.md](../content/pages/homepage/homepage-documentation.md) |

**Do not** force identical counts across surfaces; **do** apply the same **quality gates** (self-contained, du tone, visible vs schema parity, no pointless duplication of main content).

## Data-driven iteration loop

1. **Inputs:** GSC (queries + page), SISTRIX PAA, optional **Firecrawl** markdown scrape of **your live URL** and **competitor** FAQ/main content (`firecrawl_scrape`, formats `markdown`) per [mcp-usage.mdc](../../.cursor/rules/mcp-usage.mdc). Cross-surface checklist: [PAGE_IMPROVEMENT_ITERATION_CHECKLIST.md](PAGE_IMPROVEMENT_ITERATION_CHECKLIST.md) §4 (Firecrawl + credit discipline).
2. **Edit:** prune → merge → reorder → add from gaps; regenerate with **`--use-ai`** pipelines where documented; **manual** review for legal/calculator pages.
3. **Verify:** Rich Results Test (syntax); blog `validate-faq-schema.php` / `validate-faq-quality.php`; template `validate-template-faq-schema.php` / quality scripts; product flows in PRODUCT_PAGE_FAQ_GUIDE.
4. **Measure:** revisit GSC slice after 4–8 weeks; treat **CTR/position** as primary signals, not FAQ rich-result appearance in Google. For period-over-period query trends, use `v2/scripts/tools/compare-gsc-query-exports.php` or GSC UI Compare (see [PAGE_IMPROVEMENT_DATA_PLAYBOOK.md](PAGE_IMPROVEMENT_DATA_PLAYBOOK.md) §5).

**Quarterly cadence:** align priority pages with [FAQ_QUARTERLY_REVIEW_PROCESS.md](blog/FAQ_QUARTERLY_REVIEW_PROCESS.md) (data collection, analysis, implementation).

## GA4 (implemented baseline)

**Script:** [`v2/js/faq-tracking.js`](../../v2/js/faq-tracking.js) (minified: `faq-tracking.min.js`). Loaded **once per page** from [`v2/base/footer.php`](../../v2/base/footer.php) (defer). Blog posts no longer inject a second copy from `post.php`.

**Events** (via `gtag` when defined; requires analytics consent / GTM as elsewhere on the site):

| Event | When |
|--------|------|
| `faq_section_viewed` | FAQ block ≥50% visible (`.schema-faq` **or** `section#faq`). |
| `faq_question_clicked` | Legacy: click on `.schema-faq-question`. **Or** `section#faq details` **opens** (`toggle`, `open`); includes `faq_ui: "details"`, `faq_id`, `faq_index`, `faq_question`. |
| `faq_scroll_depth` | User scrolls until the same FAQ block enters the viewport (once). |

**Coverage:** Blog `BlogFAQ.php` (`section#faq` + `details`), tools and product pages with the same pattern, and any page that still uses embedded `.schema-faq` HTML.

**Register in GA4 / GTM:** Mark custom events as needed for reports; optional custom dimensions for `faq_id` / `faq_question` (truncate long text in GA4 UI if required).

## Technical: FAQ plain text for schema (blog + marketing)

- Helper: [`v2/helpers/faq-schema-plain-text.php`](../../v2/helpers/faq-schema-plain-text.php) — `faq_answer_html_to_schema_plain_text()`.
- JSON-LD node builder: [`v2/helpers/faq-jsonld.php`](../../v2/helpers/faq-jsonld.php) — `ordio_faqpage_schema_node()`.
- Sanity scripts: `php v2/scripts/dev-helpers/verify-faq-schema-plain-text.php`; `php v2/scripts/dev-helpers/verify-faq-jsonld-parity.php`.

## Deep-dive documentation (by area)

- Blog: [FAQ_BEST_PRACTICES.md](blog/FAQ_BEST_PRACTICES.md), [FAQ_CREATION_WORKFLOW_2026.md](blog/FAQ_CREATION_WORKFLOW_2026.md), [FAQ_GAP_REMEDIATION_RUNBOOK.md](blog/FAQ_GAP_REMEDIATION_RUNBOOK.md), [FAQ_SCHEMA_BEST_PRACTICES.md](blog/FAQ_SCHEMA_BEST_PRACTICES.md)
- Cursor: [`.cursor/rules/faq-cross-surface.mdc`](../../.cursor/rules/faq-cross-surface.mdc) (routing + related rules)

## Pilot checks (2026-03-29)

- **Blog:** `php v2/scripts/blog/validate-faq-schema.php --post=stundensatz --category=lexikon` and `--post=zeiterfassung-app --category=ratgeber` — valid; `validate-faq-quality.php` on same posts — excellent scores (see script output; quality report file may be regenerated).
- **Product:** `php v2/scripts/product-pages/extract-product-faqs.php --page=payroll` — extracted FAQs to `docs/content/pages/product-pages/payroll/current-faqs.json`.
- **Tools:** `php -l v2/pages/tools_minijob_rechner.php` — OK. Re-run **Rich Results Test** on production URLs after deploy (manual).

**Canonical FAQ counts:** blog/tool-specific targets may differ (e.g. blog 10–15 vs tools 15–20). Treat **this hub** as the **cross-surface** source; if a legacy doc disagrees, prefer the **surface rule** linked in the matrix above and update the legacy doc with “See [FAQ_WEBSITE_STANDARD.md](FAQ_WEBSITE_STANDARD.md).”

## Next steps (operational)

1. **After deploy:** run [Google Rich Results Test](https://search.google.com/test/rich-results) on high-traffic URLs with FAQ blocks (e.g. `/tools/urlaubsanspruch-rechner`, key product pages).
2. **GA4:** confirm `faq_section_viewed`, `faq_question_clicked`, and `faq_scroll_depth` appear in DebugView on a staging or production URL with consent granted.
3. **CI / local:** `make validate` (or your usual subset) after touching `footer.php` or FAQ markup.
4. **Rules index:** run `make rules` before push (uses project Python; `.venv` recommended if `yaml` missing on system `python3`).
5. **Agent skill:** `.cursor/skills/ordio-faq-workflow/SKILL.md` for routed FAQ tasks.
