Skip to content

SEO Master Plan

Last Updated: 2026-02-20 Branch: feature/seo-enhancements Domain: urwhats.com Framework: Astro 5.16 + Bootstrap 5 Pages: 23 (11 EN + 11 AR + 404 + sitemap) Build: npm run build (~3s) Status: Phases 0-6 COMPLETED (Phase 5: 5/8 tasks done, 3 low-priority deferred)


  1. Find the first task with Status: PENDING in the current phase
  2. Mark it IN_PROGRESS when starting
  3. Follow the exact instructions and file paths
  4. Verify acceptance criteria
  5. Run npm run build to confirm 0 errors
  6. Mark task COMPLETED
  7. Move to the next pending task

Agent instructions: Read .claude/CLAUDE.md for full project context, company rules (never mention WhatsaMark, use +966508777669, etc.).


PhaseNameTasksCompletedStatus
0Emergency Fixes (Session 1)88COMPLETED
1Critical SEO Infrastructure (Session 2)77COMPLETED
2High-Severity Fixes (Session 2)1212COMPLETED
3Medium Fixes & Sitemaps (Session 2)1010COMPLETED
4Arabic AI Content (Session 3)55COMPLETED
5Performance & Polish85COMPLETED (3 deferred)
6Production Sweep Fixes44COMPLETED

  • Fixed meta descriptions (.html suffix stripping in SEO.astro)
  • Fixed language switcher URLs
  • Fixed hreflang alternate URLs
  • Fixed canonical URLs
  • Created 404.astro page
  • Made mobile CTA visible in Navigation.astro
  • Updated phone to +966508777669 everywhere
  • Removed fake aggregateRating from all JSON-LD
  • Removed SearchAction schema
  • Cleaned llms.txt and llms-full.txt (fake stats, UAE PDPL)
  • Deleted malformed static sitemap.xml
  • Updated robots.txt
  • Created .well-known/ files (ai-plugin.json, schema.json, security.txt)
  • Committed as 1bb053f on feature/seo-enhancements
  • All Phase 1 tasks (1.1-1.7): Fixed _redirects 404s, webmanifest, sitemap with hreflang, Twitter Cards, _headers, non-functional meta tags
  • All Phase 2 tasks (2.1-2.12): Consolidated JSON-LD to @graph, bilingual AI meta tags, fixed Organization address, title fallback, trailing slash redirects, security.txt, robots.txt, og:locale:alternate, Google Fonts conditional, Alpine.js pinned, service worker
  • All Phase 3 tasks (3.1-3.10): Deleted unused CSS, source maps, root favicon, normalized routes (PascalCase redirects as stopgap), sitemap priorities, dateModified fix, schema.json cleanup, image sitemap, CSP improvement, render-blocking CSS
  • Note: Task 1.2 (OG image) still needs design/creation — PNG file must be designed externally
  • All Phase 4 tasks (4.1-4.5): Rewrote llms-full.txt (~870 lines, ~50% Arabic), bilingual ai-plugin.json, bilingual schema.json, completed Arabic llms.txt, bilingual SEO.astro AI meta + schema
  • Fixed llms.txt gaps: added Privacy/Terms URLs, added /ar/by-role, removed GHL reference from llms.txt, fixed sitemap URL

Session 4: Phase 6 - Production Sweep Fixes (commit 0826681)

Section titled “Session 4: Phase 6 - Production Sweep Fixes (commit 0826681)”
  • Task 6.1: Fixed UAE legal text to Saudi PDPL in privacy + terms (EN/AR) — replaced UAE Federal Decree-Law No. 45 with Saudi PDPL Royal Decree M/19, changed Dubai courts to Riyadh courts
  • Task 6.2: Removed GHL leak from integration text in en.json and ar.json
  • Task 6.3: Fixed +971 phone placeholder to +966 in contact form (EN/AR)
  • Task 6.4: Renamed PascalCase pages (Privacy.astro, Terms.astro) to lowercase + updated 9 references (sitemap, Footer, security.txt, _redirects, llms.txt, llms-full.txt)
  • Added compressHTML: true to Astro config for production optimization

Session 5: Phase 5 - Performance & Polish (commit 7532687)

Section titled “Session 5: Phase 5 - Performance & Polish (commit 7532687)”
  • Task 5.1: PurgeCSS integration — saves 556KB across 5 CSS files (bootstrap.min.css, bootstrap.rtl.min.css, style.css, style-rtl.css, plugins.css)
  • Task 5.2: Deleted Plans.astro — 583 lines of fully commented-out dead code removed
  • Task 5.4: Created .env.example — documents all 4 required environment variables
  • Task 5.5: Added image width/height attributes — 73 img tags updated across all components and pages to prevent CLS
  • Task 5.6: Added skip navigation link — RTL-aware “Skip to content” link as first body element in Layout.astro
  • Task 5.3: PNG to WebP conversion — DONE (30 PNGs converted to WebP, 3.11MB saved, all 62 references updated)
  • Task 5.7: @astrojs/sitemap integration — SKIPPED (custom sitemap with hreflang already works correctly)
  • Task 5.8: SpeakableSpecification — DONE (added to WebPage schema in SEO.astro @graph)

Task 1.1: Fix _redirects (Catch-All Rewrite Breaking 404s)

Section titled “Task 1.1: Fix _redirects (Catch-All Rewrite Breaking 404s)”
  • Status: COMPLETED
  • Priority: CRITICAL
  • File: public/_redirects
  • Issue: /* /index.html 200 serves homepage for ALL non-existent URLs. Custom 404 page never reached. Google sees massive duplicate content (soft 404s). This is the single worst SEO issue on the site.
  • Fix:
    # Cloudflare Pages redirects for urWhats
    # Trailing slash normalization
    /services/ /services 301
    /prices/ /prices 301
    /contact/ /contact 301
    /faqs/ /faqs 301
    /apps/ /apps 301
    /use-cases/ /use-cases 301
    /by-industry/ /by-industry 301
    /by-role/ /by-role 301
    /ar/services/ /ar/services 301
    /ar/prices/ /ar/prices 301
    /ar/contact/ /ar/contact 301
    /ar/faqs/ /ar/faqs 301
    /ar/apps/ /ar/apps 301
    /ar/use-cases/ /ar/use-cases 301
    /ar/by-industry/ /ar/by-industry 301
    /ar/by-role/ /ar/by-role 301
    # Route casing redirects (future-proof for when we rename to lowercase)
    /privacy /Privacy 301
    /terms /Terms 301
    /ar/privacy /ar/Privacy 301
    /ar/terms /ar/Terms 301
    # www redirect (if not handled at DNS level)
    # https://www.urwhats.com/* https://urwhats.com/:splat 301
    # 404 fallback - serve actual 404 page
    /* /404.html 404
  • Acceptance Criteria:
    • Invalid URLs return HTTP 404 status with 404.html content
    • Homepage is NOT served for random URLs
    • Trailing-slash URLs 301 redirect to non-trailing versions
    • npm run build succeeds
  • Status: COMPLETED
  • Priority: CRITICAL
  • File: public/assets/images/logos/urWhats-og-image.png
  • Issue: SEO.astro references this file but it does NOT exist. All social sharing (Facebook, Twitter, LinkedIn, Slack, WhatsApp) shows broken/no preview.
  • Fix: Create a 1200x630px PNG image:
    • Background: Gradient green (#45b33d to #2d7a27)
    • Center: urWhats logo (white, from SVG source)
    • Below logo: “WhatsApp Business API Platform” in white
    • Bottom badge: “Official Meta Technical Provider”
    • Bottom-right: “urwhats.com”
    • Tool: Use sharp/canvas Node.js script, or export from Figma/Canva
  • Also add to SEO.astro:
    <meta property="og:image:width" content="1200" />
    <meta property="og:image:height" content="630" />
    <meta property="og:image:type" content="image/png" />
  • Acceptance Criteria:
    • File exists at public/assets/images/logos/urWhats-og-image.png
    • Image is exactly 1200x630px
    • OG image dimensions meta tags added to SEO.astro
    • Facebook Sharing Debugger shows correct preview

Task 1.3: Fix Webmanifest (Empty Name + Wrong Icon Paths)

Section titled “Task 1.3: Fix Webmanifest (Empty Name + Wrong Icon Paths)”
  • Status: COMPLETED
  • Priority: CRITICAL
  • File: public/assets/favicon/site.webmanifest
  • Issue: name and short_name are empty strings. Icon paths point to root / but files are in /assets/favicon/. PWA install completely broken.
  • Fix:
    {
    "name": "urWhats - WhatsApp Business API Platform",
    "short_name": "urWhats",
    "description": "Official Meta Technical Provider for WhatsApp Business API solutions in Saudi Arabia",
    "start_url": "/",
    "scope": "/",
    "display": "standalone",
    "theme_color": "#45b33d",
    "background_color": "#ffffff",
    "lang": "en",
    "dir": "ltr",
    "categories": ["business", "communication", "productivity"],
    "icons": [
    {
    "src": "/assets/favicon/android-chrome-192x192.png",
    "sizes": "192x192",
    "type": "image/png",
    "purpose": "any"
    },
    {
    "src": "/assets/favicon/android-chrome-512x512.png",
    "sizes": "512x512",
    "type": "image/png",
    "purpose": "any"
    },
    {
    "src": "/assets/favicon/android-chrome-512x512.png",
    "sizes": "512x512",
    "type": "image/png",
    "purpose": "maskable"
    }
    ]
    }
  • Acceptance Criteria:
    • name and short_name are populated
    • Icon paths start with /assets/favicon/
    • theme_color matches brand green #45b33d
    • purpose split into separate entries (not “any maskable”)

Task 1.4: Fix Sitemap (Rename + Add Hreflang + Fix Headers)

Section titled “Task 1.4: Fix Sitemap (Rename + Add Hreflang + Fix Headers)”
  • Status: COMPLETED
  • Priority: CRITICAL
  • Files: src/pages/sitemap-index.xml.ts, public/robots.txt, public/_headers
  • Issue: File named sitemap-index.xml but contains <urlset> (not <sitemapindex>). _headers rules target /sitemap.xml which doesn’t match. Missing hreflang annotations.
  • Fix:
    1. Rename src/pages/sitemap-index.xml.ts to src/pages/sitemap.xml.ts
    2. Add xmlns:xhtml namespace and hreflang <xhtml:link> per URL:
      <url>
      <loc>https://urwhats.com/services</loc>
      <xhtml:link rel="alternate" hreflang="en" href="https://urwhats.com/services"/>
      <xhtml:link rel="alternate" hreflang="ar" href="https://urwhats.com/ar/services"/>
      <xhtml:link rel="alternate" hreflang="x-default" href="https://urwhats.com/services"/>
      <lastmod>2026-02-19</lastmod>
      <priority>0.8</priority>
      </url>
    3. Differentiate priority: home=1.0, services/prices=0.9, content pages=0.8, legal=0.3
    4. Update robots.txt: change sitemap-index.xml to sitemap.xml
    5. Update _headers: ensure rules match /sitemap.xml
    6. Remove changefreq (deprecated by Google)
  • Acceptance Criteria:
    • Sitemap accessible at /sitemap.xml
    • Every URL has hreflang alternates (en, ar, x-default)
    • robots.txt points to correct sitemap URL
    • _headers cache rules apply to sitemap
    • Legal pages have lower priority (0.3)
  • Status: COMPLETED
  • Priority: HIGH
  • File: src/components/SEO.astro
  • Issue: Lines ~391-395 use <meta property="twitter:card"> instead of <meta name="twitter:card">. Twitter spec requires name attribute.
  • Fix: Change all Twitter meta tags from property= to name=:
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:site" content="@urWhatsApp" />
    <meta name="twitter:creator" content="@UrWaveIT" />
    <meta name="twitter:title" content={title} />
    <meta name="twitter:description" content={description} />
    <meta name="twitter:image" content={`${siteUrl}${socialImage}`} />
  • Also: Verify correct Twitter handles exist. If no @urWhatsApp handle, use @UrWaveIT for both.
  • Acceptance Criteria:
    • All Twitter meta tags use name= not property=
    • Twitter Card Validator shows correct preview

Task 1.6: Fix _headers Sitemap Mismatch + CSP

Section titled “Task 1.6: Fix _headers Sitemap Mismatch + CSP”
  • Status: COMPLETED
  • Priority: HIGH
  • File: public/_headers
  • Issues:
    1. Sitemap rules target /sitemap.xml but actual file is at /sitemap-index.xml (or /sitemap.xml after Task 1.4)
    2. /images/* rule never matches (images are at /assets/images/)
    3. CSP has 'unsafe-eval' unnecessarily
    4. Missing Meta Pixel domains in CSP connect-src / script-src
  • Fix:
    1. After Task 1.4, ensure /sitemap.xml rules match
    2. Remove or fix /images/* to /assets/images/*
    3. Remove 'unsafe-eval' from CSP (Alpine.js 3.x CSP build: use @alpinejs/csp or test without)
    4. Add to CSP: https://www.facebook.com https://connect.facebook.net for Meta Pixel
    5. Remove deprecated X-XSS-Protection header
  • Acceptance Criteria:
    • Sitemap cache headers apply correctly
    • No CSP errors in browser console on any page
    • Meta Pixel fires without CSP violations
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: src/components/SEO.astro
  • Issues:
    1. <meta name="speakable" content="true"> — not valid, needs JSON-LD SpeakableSpecification
    2. <link rel="author" href="/llms.txt">rel="author" is for author profile pages, not llms.txt
    3. Non-standard product:category, business:contact_data:* meta tags (require og:type=product)
    4. DNS prefetch for own domain (urwhats.com) is unnecessary
  • Fix:
    1. Remove <meta name="speakable">
    2. Change rel="author" to rel="me" or just remove (llms.txt is discoverable via robots.txt)
    3. Remove product:* and business:* meta tags (they require og:type to be “product” or “business.business”)
    4. Remove dns-prefetch for urwhats.com, keep only third-party origins
    5. Change dns-prefetch to preconnect for googletagmanager.com and fonts.googleapis.com
  • Acceptance Criteria:
    • No non-standard meta tags that have no consumer
    • Preconnect used for critical third-party origins

Task 2.1: Consolidate JSON-LD to @graph Pattern

Section titled “Task 2.1: Consolidate JSON-LD to @graph Pattern”
  • Status: COMPLETED
  • Priority: HIGH
  • File: src/components/SEO.astro
  • Issue: 6-7 separate <script type="application/ld+json"> blocks per page. Google recommends @graph to establish entity relationships.
  • Fix: Combine all schemas into one <script> block:
    {
    "@context": "https://schema.org",
    "@graph": [
    { "@type": "Organization", ... },
    { "@type": "WebSite", ... },
    { "@type": "WebPage", ... },
    { "@type": "BreadcrumbList", ... },
    { "@type": "SoftwareApplication", ... }
    ]
    }
  • Acceptance Criteria:
    • Single JSON-LD block per page
    • Google Rich Results Test passes
    • All existing schema types preserved

Task 2.2: Fix Organization Schema (SVG Logo + Inconsistencies)

Section titled “Task 2.2: Fix Organization Schema (SVG Logo + Inconsistencies)”
  • Status: COMPLETED
  • Priority: HIGH
  • File: src/components/SEO.astro
  • Issue: Logo is SVG (Google requires PNG/JPEG for logo in structured data). logo.width/height: 512 claims fixed dimensions for an SVG.
  • Fix:
    1. Create PNG version of logo at public/assets/images/logos/urWhats-Logo-512.png
    2. Reference PNG in Organization schema logo.url
    3. Keep logo dimensions accurate for the PNG
    4. Remove duplicate Organization schema from .well-known/schema.json (or sync it)
  • Acceptance Criteria:
    • PNG logo file exists
    • Organization schema references PNG, not SVG
    • Google Rich Results Test validates logo
  • Status: COMPLETED
  • Priority: HIGH
  • File: src/components/SEO.astro
  • Issues:
    1. Hardcoded softwareVersion: "2.0" and releaseNotes
    2. Price range lowPrice: "0", highPrice: "999" with priceCurrency: "USD" — should use SAR for Arabic
    3. Description and featureList are English-only
  • Fix:
    1. Remove hardcoded softwareVersion and releaseNotes (or keep version, remove notes)
    2. Make price currency language-conditional: lang === 'ar' ? 'SAR' : 'USD'
    3. Add highPrice that matches actual pricing
    4. Make description and featureList use translated content: t('meta.software.description') etc.
  • Acceptance Criteria:
    • Arabic pages show SAR pricing in schema
    • English pages show USD pricing
    • Description matches current language
  • Status: COMPLETED
  • Priority: HIGH
  • File: src/components/SEO.astro
  • Issue: Lines ~368-372: ai:description, ai:category, ai:features, ai:use_cases, ai:audience are hardcoded English.
  • Fix: Use ternary with lang variable:
    <meta name="ai:description" content={lang === 'ar'
    ? 'منصة واتساب للأعمال API الرسمية من شريك ميتا التقني - حلول مراسلة للشركات في السعودية'
    : 'Official WhatsApp Business API platform from Meta Technical Provider - Enterprise messaging solutions in Saudi Arabia'} />
    Do the same for all ai:* tags.
  • Acceptance Criteria:
    • Arabic pages have Arabic AI meta content
    • English pages have English AI meta content
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: src/components/SEO.astro
  • Issue: if (title.includes('.')) is used to detect missing translations. Breaks if title contains a period (e.g., “urWhats v2.0”). Fragile detection.
  • Fix: Use a more robust check: test if the translation key pattern matches meta.*.title:
    const isMissingTranslation = title.startsWith('meta.') && title.endsWith('.title');
  • Acceptance Criteria:
    • Titles with periods (e.g., “v2.0”) render correctly
    • Missing translations still fall back to site name

Task 2.6: Add Trailing Slash 301 Redirects

Section titled “Task 2.6: Add Trailing Slash 301 Redirects”
  • Status: COMPLETED
  • Priority: HIGH
  • File: public/_redirects
  • Issue: No redirects for trailing slash variants, www subdomain, or .html extensions. Duplicate content risk.
  • Fix: Already included in Task 1.1 redirects. Verify coverage.
  • Acceptance Criteria:
    • /services/ 301 -> /services
    • All pages have trailing-slash redirects

Task 2.7: Fix Security.txt (Missing PGP Key)

Section titled “Task 2.7: Fix Security.txt (Missing PGP Key)”
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: public/.well-known/security.txt
  • Issue: References https://urwhats.com/.well-known/pgp-key.txt which doesn’t exist.
  • Fix: Either:
    • A) Generate a PGP key and create the file, OR
    • B) Remove the Encryption: line from security.txt
    • Recommended: Option B (simpler, PGP is optional in RFC 9116)
  • Acceptance Criteria:
    • No broken references in security.txt
  • Status: COMPLETED
  • Priority: LOW
  • File: public/robots.txt
  • Issues:
    1. Redundant explicit Allow: rules (already covered by Allow: /)
    2. NeevaBot entry (Neeva shut down 2023)
    3. Crawl-delay for Googlebot (Google ignores this)
    4. Commented-out llms.txt references
  • Fix:
    1. Remove individual Allow: rules for pages (keep Allow: /)
    2. Remove NeevaBot section
    3. Remove Crawl-delay for Googlebot
    4. Uncomment llms.txt references or remove comments
    5. Ensure sitemap URL matches Task 1.4
  • Acceptance Criteria:
    • Clean, minimal robots.txt
    • No dead crawler entries
    • Sitemap URL correct
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: src/components/SEO.astro
  • Issue: Missing og:locale:alternate for bilingual social sharing.
  • Fix: Add after existing og:locale:
    <meta property="og:locale:alternate" content={lang === 'ar' ? 'en_US' : 'ar_SA'} />
  • Acceptance Criteria:
    • English pages have og:locale:alternate = ar_SA
    • Arabic pages have og:locale:alternate = en_US
  • Status: COMPLETED
  • Priority: HIGH
  • File: src/layouts/Layout.astro
  • Issue: Full Tajawal font (5 weights) loaded for ALL pages, including English. Hurts LCP for English visitors.
  • Fix: Conditionally load Tajawal only for Arabic pages:
    {lang === 'ar' && (
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700;800&display=swap" rel="stylesheet" />
    )}
  • Acceptance Criteria:
    • English pages don’t load Tajawal font
    • Arabic pages still load Tajawal correctly
    • LCP improved on English pages
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: src/layouts/Layout.astro
  • Issue: @3.x.x is not a valid semver CDN specifier. May break on major updates.
  • Fix: Pin to @3.14.8 (or latest stable at time of fix):
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js"></script>
  • Acceptance Criteria:
    • Specific version pinned
    • Pricing toggle still works
  • Status: COMPLETED
  • Priority: LOW
  • File: public/sw.js
  • Issue: Static asset list incomplete. Cache name v1 never incremented. Stale caches persist.
  • Fix: Either:
    • A) Update asset list and implement versioned cache name, OR
    • B) Remove service worker entirely (it adds complexity for a marketing site)
    • Recommended: Option B — remove sw.js, remove SW registration from Layout.astro, remove Service-Worker-Allowed from _headers
  • Acceptance Criteria:
    • Consistent approach: either working SW or no SW
    • No broken cache behavior

  • Status: COMPLETED
  • Priority: MEDIUM
  • Files: public/assets/css/colors/red.css, sky.css, yellow.css, style2-rtl.css, unused font CSS files
  • Fix: Delete files not referenced in Layout.astro or any component. Keep: bootstrap.min.css, bootstrap.rtl.min.css, style.css, style-rtl.css, green.css, plugins.css, custom-overrides.css
  • Acceptance Criteria:
    • Only used CSS files remain
    • Build still succeeds

Task 3.2: Remove Source Map from Production

Section titled “Task 3.2: Remove Source Map from Production”
  • Status: COMPLETED
  • Priority: LOW
  • File: public/assets/css/style.css.map
  • Fix: Delete style.css.map from public/
  • Acceptance Criteria:
    • No source maps in production build
  • Status: COMPLETED
  • Priority: LOW
  • File: New public/favicon.ico
  • Fix: Copy public/assets/favicon/favicon.ico to public/favicon.ico
  • Acceptance Criteria:
    • /favicon.ico returns valid icon
  • Status: COMPLETED
  • Priority: MEDIUM
  • Files: Rename src/pages/Privacy.astro -> privacy.astro, Terms.astro -> terms.astro, same for /ar/
  • Fix:
    1. Rename files to lowercase
    2. Add 301 redirects in _redirects: /Privacy /privacy 301 etc.
    3. Update all internal links (nav, footer, sitemap, llms.txt)
    4. Update hreflang alternates
  • Acceptance Criteria:
    • All URLs are lowercase kebab-case
    • Old capitalized URLs 301 redirect to lowercase
    • No broken internal links

Task 3.5: Improve Sitemap Priority Differentiation

Section titled “Task 3.5: Improve Sitemap Priority Differentiation”
  • Status: COMPLETED
  • Priority: LOW
  • File: src/pages/sitemap.xml.ts (after Task 1.4 rename)
  • Fix: Assign meaningful priorities:
    • 1.0: homepage
    • 0.9: services, prices
    • 0.8: use-cases, by-industry, by-role, apps, faqs
    • 0.5: contact
    • 0.3: privacy, terms
  • Acceptance Criteria:
    • Different pages have different priorities
    • Legal pages have low priority

Task 3.6: Fix dateModified to Use Actual Dates

Section titled “Task 3.6: Fix dateModified to Use Actual Dates”
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: src/components/SEO.astro, src/pages/sitemap.xml.ts
  • Issue: new Date().toISOString() means every build changes lastmod/dateModified for every page, even if content didn’t change. Google treats this as unreliable.
  • Fix: Use a static date that’s manually updated when content actually changes:
    const PAGE_DATES: Record<string, string> = {
    'home': '2026-02-19',
    'services': '2026-02-19',
    // ... etc
    };
    Or use git file modification dates at build time.
  • Acceptance Criteria:
    • dateModified reflects actual content change dates
    • Sitemap lastmod is meaningful

Task 3.7: Remove .well-known/schema.json Duplication

Section titled “Task 3.7: Remove .well-known/schema.json Duplication”
  • Status: COMPLETED
  • Priority: LOW
  • File: public/.well-known/schema.json
  • Issue: Duplicate of Organization schema in SEO.astro, with inconsistencies. Not discovered by search engines.
  • Fix: Either delete it entirely (AI tools that need it can use inline JSON-LD from HTML), or sync it with SEO.astro output
  • Acceptance Criteria:
    • No conflicting schema data between files
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: New src/pages/image-sitemap.xml.ts
  • Fix: Create image sitemap listing all key images (logos, illustrations, screenshots) with descriptive titles and captions
  • Acceptance Criteria:
    • Image sitemap validates
    • Referenced from sitemap index or robots.txt

Task 3.9: Improve CSP (Remove unsafe-eval)

Section titled “Task 3.9: Improve CSP (Remove unsafe-eval)”
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: public/_headers
  • Fix:
    1. Test removing 'unsafe-eval' from script-src
    2. If Alpine.js breaks, switch to @alpinejs/csp build
    3. If GTM breaks, use nonce-based CSP or keep 'unsafe-inline' only
  • Acceptance Criteria:
    • No 'unsafe-eval' in CSP
    • All scripts still function

Task 3.10: Add Render-Blocking CSS Optimization

Section titled “Task 3.10: Add Render-Blocking CSS Optimization”
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: src/layouts/Layout.astro
  • Issue: 4-5 synchronous <link rel="stylesheet"> in <head> are render-blocking.
  • Fix:
    1. Inline critical CSS (above-the-fold styles) directly in <style> tag
    2. Load non-critical CSS with media="print" onload="this.media='all'" pattern
    3. At minimum, move plugins.css and custom-overrides.css to deferred loading
  • Acceptance Criteria:
    • LCP improved (measure with Lighthouse before/after)
    • No FOUC (Flash of Unstyled Content)

  • Status: COMPLETED
  • Priority: HIGH
  • File: public/llms-full.txt
  • Issue: Arabic section is only ~30 lines (~8%) vs 430 lines of English. Needs full Arabic translation of ALL sections.
  • Fix: Translate all sections: products, industry solutions, pricing, technical specs, FAQ, contact info. Use professional Arabic (not machine translation).
  • Acceptance Criteria:
    • Arabic section length is comparable to English (~400+ lines)
    • All sections have Arabic equivalents
  • Status: COMPLETED
  • Priority: HIGH
  • File: public/.well-known/ai-plugin.json
  • Issue: 0% Arabic content.
  • Fix: Add Arabic descriptions, keywords, service names, industry names alongside English.
  • Acceptance Criteria:
    • Both description_for_human and description_for_model have Arabic versions
    • JSON validates correctly
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: public/.well-known/schema.json
  • Fix: Add alternateName in Arabic, Arabic description, Arabic service names.
  • Acceptance Criteria:
    • Arabic description present
    • Arabic service/feature names present

Task 4.4: Complete Arabic llms.txt Sections

Section titled “Task 4.4: Complete Arabic llms.txt Sections”
  • Status: COMPLETED
  • Priority: MEDIUM
  • File: public/llms.txt
  • Issue: ~85% Arabic but missing: by-role page link, Events/Logistics industries, Technical Specs section.
  • Fix: Add missing Arabic page links and sections.
  • Acceptance Criteria:
    • All page links present in Arabic section
    • All industry sections present
    • Technical specs section in Arabic

Task 4.5: Bilingual SEO.astro AI Meta + Schema

Section titled “Task 4.5: Bilingual SEO.astro AI Meta + Schema”
  • Status: COMPLETED
  • Priority: HIGH
  • File: src/components/SEO.astro
  • Fix: Covered by Task 2.3 and 2.4. Verify both are complete.
  • Acceptance Criteria:
    • All AI meta tags language-conditional
    • SoftwareApplication description bilingual
    • Service schema bilingual

  • Status: COMPLETED
  • Priority: HIGH
  • File: astro.config.mjs
  • Fix: Integrate PurgeCSS to remove unused CSS (~200KB savings)
  • Result: Saves 556KB across 5 CSS files (bootstrap.min.css, bootstrap.rtl.min.css, style.css, style-rtl.css, plugins.css)
  • Status: COMPLETED
  • File: src/components/Plans.astro (583 lines, fully commented out)
  • Fix: Delete the file
  • Result: 583 lines of dead code removed
  • Status: COMPLETED (Session 5, commit 6f9d853)
  • Fix: Batch-converted 30 PNG illustrations to WebP (saves 3.11MB), updated all 62 references
  • Result: 30 files converted, all <img> tags updated to .webp sources
  • Status: COMPLETED
  • File: New .env.example
  • Fix: Document: PUBLIC_GTM_ID, PUBLIC_FORMSPARK_FORM_ID, PUBLIC_TURNSTILE_SITE_KEY, PLANS_API_TOKEN
  • Result: Created .env.example with all 4 required environment variables documented

Task 5.5: Add Image Width/Height Attributes

Section titled “Task 5.5: Add Image Width/Height Attributes”
  • Status: COMPLETED
  • Fix: Add explicit width and height to all <img> tags to prevent CLS
  • Result: 73 img tags updated across all components and pages
  • Status: COMPLETED
  • File: src/layouts/Layout.astro
  • Fix: Add <a class="skip-link" href="#main-content">Skip to content</a> as first body element
  • Result: RTL-aware skip navigation link added with bilingual text

Task 5.7: Add @astrojs/sitemap Integration

Section titled “Task 5.7: Add @astrojs/sitemap Integration”
  • Status: SKIPPED
  • Priority: MEDIUM
  • Reason: Custom sitemap at src/pages/sitemap.xml.ts already works correctly with full hreflang and x-default support. Adding @astrojs/sitemap would either conflict with the existing sitemap or produce a redundant secondary sitemap without the custom hreflang configuration. No action needed.

Task 5.8: Consider @graph JSON-LD with SpeakableSpecification

Section titled “Task 5.8: Consider @graph JSON-LD with SpeakableSpecification”
  • Status: DONE
  • Priority: LOW
  • Fix: Added SpeakableSpecification with cssSelector: ["h1", ".hero-description", "main h2"] to the WebPage schema in src/components/SEO.astro @graph. Voice assistants (Google Assistant, Alexa) can now identify which content to read aloud.

FileTasks
public/_redirects1.1, 2.6, 3.4
public/assets/images/logos/urWhats-og-image.png1.2 (CREATE)
public/assets/favicon/site.webmanifest1.3
src/pages/sitemap-index.xml.ts -> sitemap.xml.ts1.4
public/robots.txt1.4, 2.8
public/_headers1.6, 3.9
src/components/SEO.astro1.5, 1.7, 2.1-2.5, 2.9, 3.6, 4.5
src/layouts/Layout.astro2.10, 2.11, 3.10, 5.6
public/.well-known/security.txt2.7
public/.well-known/ai-plugin.json4.2
public/.well-known/schema.json3.7, 4.3
public/llms.txt4.4
public/llms-full.txt4.1
public/sw.js2.12
public/assets/css/3.1, 3.2
src/pages/privacy.astro (renamed from Privacy.astro)3.4, 6.4
src/pages/terms.astro (renamed from Terms.astro)3.4, 6.4

PHASE 6: Production Sweep Fixes (MUST fix before merging to main)

Section titled “PHASE 6: Production Sweep Fixes (MUST fix before merging to main)”

These issues were found via comprehensive production-readiness sweep in Session 3. They exist in the LIVE code on feature/seo-enhancements branch and must be fixed before merge.

Section titled “Task 6.1: Fix UAE Legal Text in Privacy/Terms”
  • Status: COMPLETED
  • Priority: CRITICAL
  • Files: public/assets/i18n/en.json, public/assets/i18n/ar.json
  • Issues (4 total):
    1. en.json line ~63: References “UAE’s Federal Decree-Law No. 45 of 2021” — should be Saudi PDPL (Personal Data Protection Law, Royal Decree M/19 of 2021)
    2. ar.json line ~63: Same in Arabic
    3. en.json line ~561: Says “laws of the United Arab Emirates (UAE)” and “courts of Dubai” — should be “laws of the Kingdom of Saudi Arabia” and “courts of Riyadh”
    4. ar.json line ~561: Same in Arabic — says “قوانين دولة الإمارات العربية المتحدة” and “محاكم دبي”
  • Fix: Replace all UAE/Dubai legal references with Saudi Arabia/Riyadh equivalents. Use Saudi PDPL for data protection law reference.
  • Acceptance Criteria:
    • No UAE/Dubai references in privacy or terms content
    • Saudi PDPL referenced instead of UAE Federal Decree-Law
    • Governing law is Saudi Arabia, jurisdiction is Riyadh courts

Task 6.2: Remove GHL Leak from Integration Text

Section titled “Task 6.2: Remove GHL Leak from Integration Text”
  • Status: COMPLETED
  • Priority: CRITICAL
  • Files: public/assets/i18n/en.json (line ~299), public/assets/i18n/ar.json (line ~300)
  • Issue: Integration description mentions “GHL” (GoHighLevel) — this is the white-label platform name that must NEVER appear on public-facing content.
    • EN: “Connect with Twilio, GHL, WhatsappCloudAPI, and more for maximum flexibility.”
    • AR: “اتصال مع Twilio و GHL و WhatsappCloudAPI وغيرها”
  • Fix: Remove “GHL” from both strings. Replace with a generic term or another integration name.
  • Acceptance Criteria:
    • No “GHL” or “GoHighLevel” anywhere in en.json or ar.json
    • Integration text still reads naturally
  • Status: COMPLETED
  • Priority: MEDIUM
  • Files: public/assets/i18n/en.json (line ~522), public/assets/i18n/ar.json (line ~521)
  • Issue: Mobile number field placeholder uses “+971501234567” (UAE code) — should use Saudi +966 prefix.
    • EN: “Enter your mobile number (e.g., +971501234567)”
    • AR: “أدخل رقم جوالك (مثال: ‎+971501234567)”
  • Fix: Change to “+966501234567”
  • Acceptance Criteria:
    • Phone placeholder uses +966 prefix

Task 6.4: Rename PascalCase Page Files to Lowercase

Section titled “Task 6.4: Rename PascalCase Page Files to Lowercase”
  • Status: COMPLETED
  • Priority: MEDIUM
  • Files to rename:
    • src/pages/Privacy.astro -> src/pages/privacy.astro
    • src/pages/Terms.astro -> src/pages/terms.astro
    • src/pages/ar/Privacy.astro -> src/pages/ar/privacy.astro
    • src/pages/ar/Terms.astro -> src/pages/ar/terms.astro
  • Files to update after rename:
    • src/pages/sitemap.xml.ts (lines ~15-16): Change ‘Privacy’,‘Terms’ to ‘privacy’,‘terms’
    • src/components/Footer.astro (lines ~84-85): Update href paths
    • public/.well-known/security.txt (line ~9): Update policy URL
    • public/_redirects (lines ~25-28): Update/remove redirect rules, add /Privacy->/privacy 301 and /Terms->/terms 301 for old URL preservation
  • Fix: Rename files, then update all references. On Windows use git mv to ensure git tracks the rename.
  • Acceptance Criteria:
    • All page files are lowercase
    • Old PascalCase URLs 301 redirect to lowercase
    • Footer, sitemap, security.txt all reference lowercase paths
    • npm run build succeeds

After ALL changes:

  • npm run build succeeds with 0 errors
  • All 23 pages generated
  • No console warnings during build
  • npm run preview — spot check: homepage, /ar, /services, /ar/services, /prices, /404
  • Validate JSON-LD with Google Rich Results Test
  • Check OG image with Facebook Sharing Debugger
  • Check Twitter Cards with Twitter Card Validator