# shared-patterns Full Instructions

## Universal Validation Checklist

Apply to ALL pages after making changes:

- **Quick check:** Run `make validate` before major changes (runs lint, format, schema, PHPStan, links). See `docs/development/DEV_TOOLING.md`

### Schema Validation

- [ ] Copy JSON-LD into Google Rich Results Test or jsonlint.com
- [ ] Verify all required fields present for schema type
- [ ] Check URLs are absolute and correct
- [ ] Verify dates use ISO 8601 format and current year (check with `date +%Y` - currently 2026)
- [ ] Confirm pricing and ratings are up-to-date (€89/Standort/Monat)

### Meta Tags Validation

- [ ] Title < 60 characters, includes year, keyword, "Ordio"
- [ ] Description 155-160 characters, du tone, benefit-driven
- [ ] Canonical URL absolute and correct
- [ ] Open Graph tags complete (og:title, og:description, og:url, og:image, og:type)
- [ ] OG image meta: og:image:width, og:image:height, og:image:type, og:image:secure_url, og:image:alt present
- [ ] Twitter Card tags complete

### Content Validation

- [ ] All copy uses du tone (informal "du" not formal "Sie")
- [ ] Ordio mentioned naturally once per major section
- [ ] No competitor praise
- [ ] Paragraphs short (2-3 sentences)
- [ ] Benefits-focused (not feature lists)
- [ ] Anchor text has no leading or trailing whitespace

### Accessibility Validation

- [ ] All images have descriptive alt attributes
- [ ] Headings follow hierarchy (H1 → H2 → H3)
- [ ] Color contrast meets WCAG AA (4.5:1 for text)
- [ ] Focus states visible on all interactive elements
- [ ] Keyboard navigation works correctly
- [ ] Touch targets minimum 44x44px

### Performance Validation

- [ ] Hero images preloaded with fetchpriority="high"
- [ ] All images have explicit width and height attributes
- [ ] Images use WebP format with srcset for responsive variants
- [ ] Below-fold images lazy-loaded
- [ ] No layout shifts (CLS < 0.1)

### Mobile Optimization Validation

- [ ] No horizontal scrolling (overflow-x: hidden on html/body)
- [ ] Standardized mobile padding (px-4 for mobile, sm:px-6 for larger screens)
- [ ] All containers use max-width with mx-auto and responsive padding
- [ ] CTA buttons have consistent padding across all device sizes
- [ ] Tested on multiple device sizes (360px, 375px, 390px, 412px, 430px)
- [ ] No empty space on right side of viewport
- [ ] Images/videos use max-width: 100% to prevent overflow
- [ ] Logo sliders and animations don't cause horizontal overflow
- [ ] CSS and JavaScript deferred where appropriate

### Functional Validation

- [ ] All CTAs functional and correctly linked
- [ ] Forms validate properly (client-side and server-side)
- [ ] No console errors in browser
- [ ] No console.log/error/warn statements in source code (use structured logger if needed)
- [ ] Page loads locally at http://localhost:8003 without errors (use existing Docker/server; do not start php -S)
- [ ] Test on desktop, tablet, mobile viewports

---

## Universal Copy Guidelines

Apply to ALL customer-facing content across the entire Ordio website:

### Du Tone (Informal "You")

- Use informal "du" pronouns, not formal "Sie"
- Conversational and friendly language
- Active voice, present tense
- Examples: "erfasst du Arbeitszeiten" not "erfassen Sie Arbeitszeiten"

### Ordio Mentions

- Mention Ordio naturally once per major section (hero, benefits, FAQ)
- Integrate into context (not forced "Mit Ordio kannst du..." every paragraph)
- Implicit in CTAs (no need to say "Ordio" in button text like "Jetzt starten")
- Let Ordio's value be demonstrated, not constantly stated
- **Contextual feature selection** – Use the Ordio feature that matches the **section** topic (see ordio-promotion-contextual.mdc). Different sections can promote different features: Urlaub section → Abwesenheiten; Lohnarten section → Ordio Payroll (/payroll); Personalakte section → Digitale Personalakte.
- **Avoid defaulting** – Do not default to Zeiterfassung/Dienstplanung when Abwesenheiten, Ordio Payroll, Dokumentenmanagement, Checklists, or Events fit the topic better
- **Natural, human, no AI tells** – Vary sentence structure; avoid repetitive patterns; 1–2 mentions per section max; read aloud—if it sounds forced, rewrite

### Competitor Language

- Never praise competitors or alternative approaches
- Position Ordio as smarter choice with neutral/factual competitor descriptions
- Focus on Ordio's benefits, not competitor weaknesses
- Generic tools (Excel, Word) can be mentioned when positioning Ordio as better

### Copy Quality

- Keep paragraphs short (2-3 sentences max)
- Use active voice and present tense
- Lead with benefits, not features ("erfasst du Arbeitszeiten gesetzeskonform" not "Zeiterfassung-Feature")
- Avoid jargon; explain technical terms when necessary
- Be concise and direct (no marketing fluff or redundancy)

### Content Writing Best Practices

**See:** `.cursor/rules/content-writing.mdc` for comprehensive content writing guidelines including:
- Human-first content principles
- Natural language writing techniques
- AI content avoidance
- SEO-optimized natural content
- E-E-A-T implementation
- Content structure and formatting
- Keyword integration
- Meta tags optimization
- GEO/AEO optimization

**Key Principles:**
- Write for humans first, optimize for search engines second
- Use natural, conversational language (avoid AI content tells)
- Vary sentence length and structure
- Include personal insights and specific examples
- Integrate keywords naturally (not forced)
- Focus on user value and intent

**Related Documentation:**
- `docs/content/CONTENT_WRITING_BEST_PRACTICES_2026.md` - Comprehensive content writing guide
- `docs/content/AI_CONTENT_AVOIDANCE_GUIDE.md` - AI content avoidance guide
- `docs/content/CONTENT_STRUCTURE_FORMATTING_GUIDE.md` - Content structure and formatting guide

### HTML Formatting Standards

- **Anchor text**: No leading or trailing whitespace
  - ❌ Wrong: `<a href="/test"> Schichtplanung </a>`
  - ✅ Correct: `<a href="/test">Schichtplanung</a>`
- Anchor text is automatically normalized during content processing
- Use validation script to check: `python3 v2/scripts/validate-blog-anchor-text.py`
- See `docs/content/blog/ANCHOR_TEXT_FORMATTING.md` for complete guidelines

---

## Standard Terminology

**Current Standards (as of January 2026):**

- **Du tone:** Informal "du" pronouns (not formal "Sie")
- **Ordio mention:** Once per major section, naturally integrated
- **Competitor language:** Neutral/factual, never praise
- **Current pricing:** €89/Standort/Monat
- **Current year:** 2026 (always check with `date +%Y`)
- **Leap years:** 2024, 2028, 2032 have 366 days

---

## Common Schema Patterns

### WebPage Schema (Base Pattern)

All pages should include a base WebPage schema:

```json
{
  "@context": "https://schema.org",
  "@type": "WebPage",
  "name": "[Page Title]",
  "description": "[Page Description]",
  "url": "https://www.ordio.com/[page-slug]",
  "inLanguage": "de-DE",
  "isPartOf": {
    "@type": "WebSite",
    "name": "Ordio",
    "url": "https://www.ordio.com"
  },
  "publisher": {
    "@type": "Organization",
    "name": "Ordio GmbH"
  },
  "datePublished": "2026-[month]-[day]",
  "dateModified": "2026-[month]-[day]"
}
```

### BreadcrumbList Schema (Base Pattern)

For pages with breadcrumbs:

```json
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://www.ordio.com"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "[Page Name]",
      "item": "https://www.ordio.com/[page-slug]"
    }
  ]
}
```

---

## Common Meta Tag Patterns

### Title Pattern

```html
<title>[Page Name] [Year]: [Action/Benefit] - Ordio</title>
```

**Requirements:**

- Max 60 characters
- Include current year (check with `date +%Y` - currently 2026)
- Include keyword
- End with "Ordio"

### Description Pattern

```html
<meta
  name="description"
  content="[Page description in du tone, benefit-driven, 155-160 characters]"
/>
```

**Requirements:**

- 155-160 characters
- Du tone (informal "du")
- Benefit-driven (not feature list)
- Include year if relevant

### Canonical URL Pattern

```html
<link rel="canonical" href="https://www.ordio.com/[page-slug]" />
```

**Requirements:**

- Absolute URL
- HTTPS
- No trailing slash
- Matches actual page URL

### Open Graph Tags Pattern

```html
<meta property="og:title" content="[Page Title]" />
<meta property="og:description" content="[Page Description]" />
<meta property="og:url" content="https://www.ordio.com/[page-slug]" />
<meta
  property="og:image"
  content="https://www.ordio.com/v2/img/[image-name].webp"
/>
<meta property="og:type" content="website" />
```

---

## Performance Baseline Patterns

### Image Optimization Pattern

```html
<img
  src="/v2/img/hero-640w.webp"
  srcset="/v2/img/hero-640w.webp 640w, /v2/img/hero-1280w.webp 1280w"
  sizes="(max-width: 768px) 640px, 1280px"
  alt="Descriptive alt text"
  width="1280"
  height="720"
  fetchpriority="high"
/>
```

**Requirements:**

- WebP format
- Responsive srcset (multiple sizes)
- Explicit width and height attributes
- fetchpriority="high" for LCP images
- loading="lazy" for below-fold images

### CSS Loading Pattern

```php
<link rel="stylesheet" href="/v2/css/[file].min.css?v=<?php echo filemtime(__DIR__ . '/../css/[file].min.css'); ?>" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/v2/css/[file].min.css"></noscript>
```

**Requirements:**

- Use minified files (.min.css)
- Cache busting with filemtime()
- Media="print" trick for async loading
- Noscript fallback

### JavaScript Loading Pattern

```php
<script src="/v2/js/[file].min.js?v=<?php echo filemtime(__DIR__ . '/../js/[file].min.js'); ?>" defer></script>
```

**Requirements:**

- Use minified files (.min.js)
- Cache busting with filemtime()
- Defer attribute for non-critical scripts
- No console.log/error/warn statements

### Mobile Container Pattern

```html
<div class="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
  <!-- Content -->
</div>
```

**Requirements:**

- Always use `mx-auto` with `max-w-*` for centering
- Standardized padding: `px-4` (mobile), `sm:px-6` (tablet), `lg:px-8` (desktop)
- Never use fixed widths that exceed viewport
- Ensure `overflow-x: hidden` on html/body

### Mobile CTA Button Pattern

```html
<div
  class="flex flex-col sm:flex-row w-full max-w-[400px] gap-3 sm:gap-4 px-4 sm:px-0 mx-auto"
>
  <button
    class="font-inter500 text-sm sm:text-base bg-ordio-blue text-white p-4 px-6 rounded-full min-h-[48px]"
  >
    Primary CTA
  </button>
  <button
    class="font-inter500 text-sm sm:text-base bg-white border text-[#333] p-4 px-6 rounded-full min-h-[48px]"
  >
    Secondary CTA
  </button>
</div>
```

**Requirements:**

- Use `w-full` on mobile, `max-w-[400px]` for constraint
- Add `px-4 sm:px-0` to container for mobile padding
- Consistent button padding: `p-4 px-6`
- Minimum touch target: `min-h-[48px]`
- Stack vertically on mobile (`flex-col`), horizontal on larger screens (`sm:flex-row`)

### Hero Pill Pattern (Rounded Badge Above H1)

Hero pills are the rounded rectangular labels above H1 headings (e.g., "Mitarbeiter-App", "Tools & Rechner", "Vorlagen"). **Border must be neutral by default and blue only on hover.**

**Standard pattern:**

```html
<a class="group flex mt-3 lg:mt-0 items-center shadow-xs justify-center hover:cursor-default py-1 rounded-full w-[140px] px-2 bg-white/40 mx-auto mb-8 lg:mb-12 outline-none border border-transparent transition-all duration-100 hover:outline hover:outline-2 hover:outline-blue-200 hover:outline-offset-2">
  <span class="text-[14px]">Badge Text</span>
</a>
```

**Key classes:** `outline-none border border-transparent` (no visible border by default); `hover:outline hover:outline-2 hover:outline-blue-200 hover:outline-offset-2` (blue outline only on hover).

**Dark theme pages** (e.g., product_events, product_appstore): Use `hover:outline-[#333333]` instead of `hover:outline-blue-200`.

**Landing pages with dark mode:** Use `hover:outline-blue-200 hover:dark:outline-[#333333]` so light mode gets blue, dark mode gets dark outline on hover.

**Reference:** `v2/components/blog/BlogIndexHero.php` (blog-hero-pill), `v2/components/template-hero.php`.

---

## Form Tracking

All acquisition forms MUST implement GTM form tracking. See `.cursor/rules/form-tracking.mdc` for complete patterns and requirements.

**Quick Reference:**

- Use `window.GTMFormTracker` utility (loaded in `head.php`)
- Track before form submission (not after)
- Always check for utility existence before calling
- Include HubSpot form GUID when available
- Update form inventory when adding new forms

**Documentation:**

- Implementation: `docs/forms/GTM_FORM_TRACKING_GUIDE.md`
- Developer Guide: `docs/forms/FORM_TRACKING_DEVELOPER_GUIDE.md`
- Best Practices: `docs/forms/FORM_TRACKING_BEST_PRACTICES.md`

---

## Alpine.js Transition Patterns

### Fixed-Height Container with Smooth Card Transitions

When implementing tabbed interfaces or card carousels with Alpine.js, use this pattern to prevent layout shifts and ensure smooth transitions:

**Key Principles:**

1. Container maintains fixed height (prevents layout shifts)
2. Panels use absolute positioning (overlay instead of stacking)
3. Only active panel visible (z-index management)
4. Smooth fade + translate transitions (avoid scale for better performance)

**CSS Pattern:**

```css
/* Container - fixed min-height prevents layout shifts */
.feature-panels-container {
  position: relative;
  overflow: hidden;
  min-height: 700px; /* Mobile: based on tallest panel */
}

@media (min-width: 1024px) {
  .feature-panels-container {
    min-height: 500px; /* Desktop: based on side-by-side layout */
  }
}

/* All panels absolute positioned for overlay transitions */
[id^="feature-panel-"] {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  will-change: opacity, transform;
  backface-visibility: hidden;
}

/* Active panel on top */
[id^="feature-panel-"].z-10 {
  z-index: 10;
}

/* Inactive panels behind */
[id^="feature-panel-"].z-0 {
  z-index: 0;
  pointer-events: none;
}
```

**Alpine.js Pattern:**

```javascript
function featuresCarousel() {
  return {
    activeTab: 0,
    containerHeight: "auto",
    resizeObserver: null,
    isTransitioning: false,

    getActivePanel() {
      const panelRefs = ["panel0", "panel1", "panel2", "panel3"];
      return this.$refs[panelRefs[this.activeTab]];
    },

    updateHeight() {
      const activePanel = this.getActivePanel();
      if (activePanel) {
        requestAnimationFrame(() => {
          this.containerHeight = activePanel.scrollHeight + "px";
        });
      }
    },

    init() {
      // Setup ResizeObserver for dynamic height
      if (window.ResizeObserver) {
        this.resizeObserver = new ResizeObserver(() => {
          if (!this.isTransitioning) this.updateHeight();
        });
      }

      // Watch for tab changes
      this.$watch("activeTab", () => {
        this.isTransitioning = true;
        setTimeout(() => {
          this.updateHeight();
          setTimeout(() => {
            this.isTransitioning = false;
          }, 350);
        }, 50);
      });
    },
  };
}
```

**HTML Pattern:**

```html
<div class="feature-panels-container" :style="'height: ' + containerHeight">
  <div
    x-show="activeTab === 0"
    x-cloak
    x-ref="panel0"
    x-transition:enter="transition ease-out duration-300"
    x-transition:enter-start="opacity-0 translate-y-4"
    x-transition:enter-end="opacity-100 translate-y-0"
    x-transition:leave="transition ease-in duration-200"
    x-transition:leave-start="opacity-100 translate-y-0"
    x-transition:leave-end="opacity-0 translate-y-4"
    :class="{ 'z-10': activeTab === 0, 'z-0': activeTab !== 0 }"
  >
    <!-- Panel content -->
  </div>
</div>
```

**Best Practices:**

- Use `x-cloak` to prevent flash of unstyled content
- Remove scale transforms (use fade + translate only for better performance)
- Enter: 300ms ease-out, Leave: 200ms ease-in
- Use ResizeObserver for dynamic height calculation
- Set `isTransitioning` flag to prevent height updates during transitions
- Always use absolute positioning for panels (mobile + desktop)

**Reference Implementation:**

- `v2/pages/paid_nonbrand.php` - Feature panels carousel (lines 724-860, CSS 258-320, Alpine.js 865-1040)

---

## Reference Usage

**In page-type rule files, reference shared patterns like this:**

```markdown
## Validation Checklist

See `.cursor/rules/shared-patterns.mdc` for universal validation checklist.

**Page-Specific Validation:**

- [ ] [Page-specific item 1]
- [ ] [Page-specific item 2]
```

```markdown
## Copy Guidelines

See `.cursor/rules/shared-patterns.mdc` for universal copy guidelines (du tone, Ordio mentions, competitor language).

**Page-Specific Copy:**

- [Page-specific guidance]
```

---

## Internal Linking Best Practices

### Link Quality Standards

When adding internal links (especially from Ahrefs opportunities):

- **Context relevance:** Link must appear in meaningful context (50+ characters)
- **Natural placement:** Link should flow naturally in content, not disrupt readability
- **Anchor text quality:** Use keyword-relevant, varied anchor text (avoid generic: "hier", "mehr", "klicken")
- **Topical relevance:** Source and target pages must be topically related
- **Pillar-cluster model:** Link from detailed blog posts to broader pillar pages
- **Link density:** Maximum 20 internal links per page
- **Word boundaries:** Use German word boundary detection to prevent partial matches
- **Preserve word form:** Maintain original capitalization and pluralization

### Ahrefs Link Opportunities Process (Enhanced)

When processing Ahrefs CSV exports, use the enhanced workflow:

1. **Enhanced Analysis** (`analyze-ahrefs-opportunities-enhanced.py`)
   - Parse CSV with comprehensive statistics
   - Generate PR distribution, traffic distribution, keyword volume analysis
   - Identify duplicate opportunities
   - Validate target page existence
   - Create link inventory database

2. **Existing Links Audit** (`audit-existing-links.py`)
   - Scan all blog posts for existing internal links
   - Identify pages at/near link density limit
   - Map current pillar-cluster link structure
   - Generate link density analysis

3. **Enhanced Quality Filtering** (`filter-ahrefs-opportunities-enhanced.py`)
   - **Enhanced Priority Scoring:**
     - Source PR (30%), URL Rating (20%), Traffic (10%)
     - Keyword volume (15%), Difficulty (inverse, 10%)
     - Content placement bonus (5% for first 30%)
     - Pillar/cluster boost (1.5x for pillar pages)
     - Target ranking position factor
   - **Enhanced Context Quality:**
     - Validate paragraph placement (not table/list)
     - Check content placement (prefer intro/early sections)
     - Ensure complete sentences
     - Verify natural keyword appearance
   - **Enhanced Anchor Text Validation:**
     - Expanded German-specific generic anchor blacklist
     - Check anchor text diversity per page
     - Validate grammatical correctness
     - Ensure anchor matches actual word form
   - **Topical Relevance Scoring:**
     - Calculate semantic similarity (0-1 score)
     - Validate pillar-cluster relationships
     - Check keyword matches target topic
     - Ensure category alignment
   - **Dynamic Link Density:**
     - Dynamic limit based on content length (1 link per 200 words, max 20)
     - Ensure minimum 200 characters between links
     - Check link distribution (avoid clustering)
   - **Automated Classification:**
     - Auto-approve: High-value (pillar links, high PR+traffic, high volume+low difficulty)
     - Review: Medium-value (passed filters, need manual review)
     - Auto-reject: Low-value (failed quality checks)

4. **Review Report Generation** (`generate-review-report.py`)
   - Generate markdown review report
   - Include opportunity details, context preview, anchor text suggestions
   - Priority breakdown and topical relevance scores
   - Implementation recommendations

5. **Implementation** (`add-ahrefs-links-enhanced.py`)
   - Group by source page
   - Use German word boundary detection
   - **Protected Areas (Never Add Links):**
     - Headers (h1-h6) - Detected via `is_inside_header()`
     - FAQ questions - Detected via `check_faq_question_contains_keyword()`
     - Related content carousel - Checked via `check_related_posts_duplicate()`
     - Script/style tags - Already protected
     - HTML tag attributes - Already protected
     - Existing links - Already checked
   - **Safe Areas (Can Add Links):**
     - Paragraphs (`<p>`) in `content.html` with natural context
     - List items (`<li>`) with sufficient context (minimum 15 chars)
     - FAQ answers (`faqs[].answer`) - HTML content separate from questions
     - Table cells (`<td>`) - Only if natural sentence context (rare)
   - Find safe insertion points with enhanced protection:
     - Header protection (`is_inside_header()`)
     - Paragraph detection (`is_in_safe_paragraph()`)
     - Header proximity check (`is_too_close_to_header()`, minimum 50 chars)
     - Minimum distance from existing links (200 chars)
   - Process FAQ answers separately (`process_faq_answer_link()`)
   - Check carousel duplicates (`should_add_despite_carousel()`)
   - Preserve original word form
   - Update both HTML and text content
   - Add to `internal_links` array with metadata
   - Track links per page for minimum distance enforcement

6. **Validation** (`validate-added-links.py`, `test-added-links.py`)
   - Check HTML structure (well-formed, no broken tags)
   - Verify link density (dynamic limit based on content length)
   - Test link functionality (URLs accessible, anchor text present)

7. **Monitoring** (`monitor-link-health.py`)
   - Periodic checks for broken links (404s)
   - Track redirected links
   - Monitor link distribution

### Link Implementation Checklist (Enhanced)

**Protected Areas (Never Add Links):**
- [ ] Link is NOT in header (h1-h6) - Protected area
- [ ] Link is NOT in FAQ question - Protected area
- [ ] Link is NOT in related content carousel (unless high-value) - Checked and logged
- [ ] Link is NOT in script/style tag - Already protected
- [ ] Link is NOT in HTML tag attribute - Already protected
- [ ] Link is NOT inside existing link - Already protected

**Safe Areas (Can Add Links):**
- [ ] Link is in safe paragraph (`<p>`) with natural context (minimum 20 chars)
- [ ] Link is in list item (`<li>`) with sufficient context (minimum 15 chars) OR in paragraph
- [ ] Link is in FAQ answer (`faqs[].answer`) - Separate from questions
- [ ] Link is minimum 50 characters from headers - Header proximity check
- [ ] Link is minimum 200 characters from other links - Prevents clustering

**Quality Checks:**

---

## Universal FAQ Patterns

**Note:** These patterns apply to both blog FAQs and tools page FAQs. See page-specific rules for implementation details:
- `blog-faq-optimization-core.mdc` - Blog FAQ JSON structure and blog-specific patterns
- `tools-pages-faq.mdc` - Tools page HTML structure and tools-specific patterns

### FAQ Count Guidelines

**Optimal FAQ Count:**

- **Minimum:** 10 FAQs (for comprehensive coverage)
- **Optimal:** 10-20 FAQs (varies by page type - see page-specific rules)
- **Maximum:** 30 FAQs (beyond this, consider splitting into multiple sections)

**Rationale:**

- Too few FAQs (< 10): Missed keyword opportunities, incomplete coverage
- Too many FAQs (> 30): User fatigue, decreased engagement, potential performance impact
- 10-20 FAQs: Optimal for Featured Snippets, user engagement, and SEO

**Page-Specific Guidelines:**

- **Blog Posts:** 10-15 FAQs optimal (see `blog-faq-optimization-core.mdc`)
- **Tools Pages:** 15-20 FAQs optimal (see `tools-pages-faq.mdc`)

### Answer Length Requirements

**Optimal Answer Length:**

- **Minimum:** 40 words (comprehensive answer)
- **Optimal:** 40-80 words (best balance of detail and readability)
- **Maximum:** 120 words (beyond this, consider splitting into multiple paragraphs)

**Rationale:**

- Too short (< 40 words): Insufficient detail, missed keyword opportunities
- Too long (> 120 words): User fatigue, decreased engagement, potential Featured Snippet issues
- 40-80 words: Optimal for Featured Snippets, user engagement, and SEO

**Quality Standards:**

- Answers must be comprehensive and informative
- Answers should directly address the question
- Answers should include relevant keywords naturally
- Answers should use du tone (informal "du" pronouns)
- Answers should avoid template language and boilerplate text

### FAQ Ordering Strategy

**Universal Logical Flow (Recommended):**

1. **Definition Questions** ("Was ist...?", "Was bedeutet...?") - Foundational understanding
2. **How-To Questions** ("Wie funktioniert...?", "Wie erstellt man...?") - Practical implementation
3. **Requirements/What Questions** ("Was muss ich...?", "Was sollte...?") - Requirements and guidelines
4. **When/Why Questions** ("Wann...?", "Warum...?") - Context and reasoning
5. **Which Questions** ("Welche...?", "Welcher...?") - Selection and comparison
6. **Yes/No Questions** ("Ist...?", "Gibt es...?") - Binary answers
7. **Cost/Duration Questions** ("Was kostet...?", "Wie lange...?") - Specific metrics
8. **Edge Cases** - Special scenarios and exceptions

**Priority Within Types:**

- High-volume queries first (from GSC data)
- People Also Ask questions second
- Related keywords third
- Standard questions last

**Page-Specific Ordering:**

- **Blog Posts:** Follow logical flow above (see `blog-faq-optimization-core.mdc`)
- **Tools Pages:** Priority by search volume and user intent (see `tools-pages-faq.mdc`)

### Duplicate Detection

**CRITICAL:** Prevent duplicate FAQs to ensure quality and SEO compliance.

**Question Similarity Threshold:** < 0.7

- Questions with similarity ≥ 0.7 are considered duplicates
- Keep best FAQ from duplicate pair
- Remove duplicates

**Answer Similarity Threshold:** < 0.8

- Answers with similarity ≥ 0.8 are considered repetitive
- Rewrite with unique angle or merge

**Prevention:**

- Check for existing FAQs before adding new ones
- Normalize questions for comparison (remove punctuation, lowercase)
- Use validation scripts before publishing

### Topic Relevance

**Threshold:** ≥ 0.3 (minimum similarity to page topic)

**Calculation:**

- Word overlap ratio (50%)
- Title similarity (30%)
- Slug similarity (20%)

**Action:**

- FAQs below 0.3 threshold are considered off-topic
- Remove off-topic FAQs
- Replace with topic-relevant FAQs if needed

### Pattern Violations

**High Severity (Must Remove):**

- "Was kostet [abstract concept]?" patterns (abstract concepts don't have costs)
- "Wie lange dauert [abstract concept]?" patterns (abstract concepts have no duration)
- Brand questions on non-brand pages (unless naturally relevant)
- Nonsensical questions (grammatically/logically incorrect)

**Medium Severity (Review and Fix):**

- Malformed questions (fragments, multiple question marks)
- Questions with separators before question mark

### Schema Requirements

**FAQPage Schema:**

- Required for all pages with FAQs
- Must include all FAQ questions and answers
- Answers should be cleaned of HTML for schema (plain text)
- Questions should match exactly between HTML and schema

**Validation:**

- Test with Google Rich Results Test
- Verify all FAQs included in schema
- Check for schema errors

### SEO/AEO/GEO Optimization

**Keyword Integration:**

- Primary keyword in 3-5 FAQs
- Related keywords naturally integrated
- Long-tail keywords in questions
- Semantic variations included

**Featured Snippet Optimization:**

- Answer length 40-80 words (optimal for snippets)
- Direct answer to question
- Structured format (lists, tables when appropriate)
- Clear, concise language

**People Also Ask Optimization:**

- Include PAA questions when relevant
- Order by search volume
- Match user intent

---

## Universal Schema Patterns

**Note:** These patterns apply to all page types. See page-specific rules for implementation details:
- `comparison-pages-schema-meta.mdc` - Comparison page schemas (Table, Product)
- `tools-pages-schema.mdc` - Tools page schemas (HowTo)
- `blog-templates-seo.mdc` - Blog post schemas (Article, FAQPage)

### Standard Schemas (All Pages)

**WebPage Schema:**

Required for all pages:

```json
{
  "@context": "https://schema.org",
  "@type": "WebPage",
  "@id": "https://www.ordio.com/[page-url]#webpage",
  "url": "https://www.ordio.com/[page-url]",
  "name": "[Page Title]",
  "description": "[Meta description]",
  "inLanguage": "de-DE",
  "isPartOf": {
    "@type": "WebSite",
    "name": "Ordio",
    "url": "https://www.ordio.com"
  }
}
```

**BreadcrumbList Schema:**

Required for all pages with breadcrumbs:

```json
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://www.ordio.com"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "[Page Name]",
      "item": "https://www.ordio.com/[page-url]"
    }
  ]
}
```

### Schema Validation Requirements

**Universal Requirements:**

- All URLs must be absolute (https://www.ordio.com/...)
- Dates must use ISO 8601 format (YYYY-MM-DD)
- Current year must be accurate (check with `date +%Y` - currently 2026)
- Pricing must be up-to-date (€89/Standort/Monat)
- Schema must match visible content exactly
- Test with Google Rich Results Test before deploying

**Common Schema Types:**

- **WebPage** - All pages (required)
- **BreadcrumbList** - All pages with breadcrumbs (required)
- **Article** - Blog posts (see `blog-templates-seo.mdc`)
- **FAQPage** - Pages with FAQs (see `blog-faq-optimization-core.mdc`, `tools-pages-faq.mdc`)
- **HowTo** - Tools/calculator pages (see `tools-pages-schema.mdc`)
- **Product** - Product pages, comparison pages (see `comparison-pages-schema-meta.mdc`)
- **Table** - Comparison pages (see `comparison-pages-schema-meta.mdc`)

**Quality Checks:**
- [ ] Context is meaningful (50+ characters, in natural paragraph)
- [ ] Keyword appears naturally in context (not in generic template)
- [ ] Context forms complete sentence
- [ ] Keyword placement in first 30% of content (bonus)
- [ ] Anchor text is keyword-relevant (not generic, German-specific check)
- [ ] Anchor text diversity per page (no repeated anchors)
- [ ] Anchor text matches actual word form (case, pluralization)
- [ ] Link supports pillar-cluster model (pillar links get priority boost)
- [ ] Topical relevance score > 0.5 (semantic similarity)
- [ ] Source and target categories align
- [ ] Link density within dynamic limit (1 link per 200 words, max 20)
- [ ] Minimum 200 characters between links
- [ ] Link distribution avoids clustering
- [ ] Original word form preserved
- [ ] URL is absolute and correct
- [ ] Link flows naturally in content
- [ ] Added to `internal_links` array with metadata
- [ ] Both `content.html` and `content.text` updated
- [ ] FAQ structure preserved (if FAQ answer updated)
- [ ] Classification: auto-approve/review/reject decision documented
- [ ] Carousel duplicates handled (logged if skipped)

### Common Pitfalls

- **Keyword not found:** Check for different word forms (plural/singular, capitalization)
- **Link already exists:** Review `internal_links` array before adding
- **Link density exceeded:** Prioritize highest-value opportunities
- **Unnatural placement:** Links should enhance, not disrupt readability
- **Generic anchors:** Avoid "hier", "mehr", "klicken" - use descriptive text
- **Link in header:** Headers should never contain links - check if keyword appears in paragraphs
- **Link in FAQ question:** FAQ questions should never contain links - check if keyword appears in FAQ answer
- **Target in carousel:** Check if target URL is in `related_posts` - only add if high-value (pillar, high PR, different anchor)
- **Too close to header:** Links must be minimum 50 characters from headers - check if keyword appears elsewhere
- **Too close to other links:** Links must be minimum 200 characters apart - prevents clustering

**Reference:** `docs/seo/ahrefs-link-opportunities-process.md` for complete process documentation

---

## Related Documentation

See [docs/ai/RULE_TO_DOC_MAPPING.md](../../docs/ai/RULE_TO_DOC_MAPPING.md) for complete mapping.

**Key Documentation:**

- [docs/guides/PAGE_TYPE_GUIDES.md](../../docs/guides/PAGE_TYPE_GUIDES.md) - `docs/guides/PAGE_TYPE_GUIDES.md` - Universal patterns
- [docs/DOCUMENTATION_STANDARDS.md](../../docs/DOCUMENTATION_STANDARDS.md) - `docs/DOCUMENTATION_STANDARDS.md` - Documentation standards
- [docs/AI_AGENT_GUIDE.md](../../docs/AI_AGENT_GUIDE.md) - `docs/AI_AGENT_GUIDE.md` - AI agent patterns
- [docs/seo/ahrefs-link-opportunities-process.md](../../docs/seo/ahrefs-link-opportunities-process.md) - Ahrefs link opportunities process