# MASTER IMPLEMENTATION PROMPT
## Godsfavour Okpara — Portfolio Website + Enterprise CMS (Laravel + Filament)

You are acting as a Principal Software Architect, Senior Laravel Engineer, Filament specialist, UX architect, database architect, and technical project manager. You are working inside an EXISTING Laravel repository in VS Code. Treat it as a production codebase.

Your mission: inspect this repository, then design and build a production-ready personal portfolio website for **Godsfavour Okpara (Project Manager | Product Manager | Scrum Master)** as a **fully CMS-driven application built with Laravel + Filament**. The site owner, who is not a developer, must be able to manage almost the entire public website from the Filament admin panel without code changes or redeployment.

Work in controlled phases (Section 20). Do not attempt the whole system in one uncontrolled pass.

---

## 0. SOURCE OF TRUTH AND PRECEDENCE

### 0.1 The PRD
The functional and UX source of truth is the PRD: **"Product Requirements Document — Personal Portfolio Website — Godsfavour Okpara (Draft v1.0)"**.

1. Locate it in the repository. Check `docs/`, `docs/prd/`, the project root, and `storage/`. If it is a `.docx`, convert it to Markdown (e.g. with `pandoc`) and save it as `docs/prd/portfolio-prd.md` so you can reference sections by number. Never modify the original file.
2. If you cannot find it, STOP and ask for it. Do not build from memory or assumptions.
3. Read the ENTIRE PRD before Phase 2. Refer to PRD sections by number (§1–§35, Appendix A) in your architecture document, code comments where useful, and the final checklist.
4. Verify every significant implementation decision against the PRD. Where you deviate, record the deviation and the reason in `docs/architecture/DECISIONS.md`.

### 0.2 Deliberate architectural override of the PRD
The PRD (§23.2, §32) recommends a headless CMS or local content files and is framework-agnostic. **That recommendation is overridden.** The mandated stack is a database-backed CMS built with Laravel + Filament, rendered server-side with Blade.

The PRD's *intent* still applies in full:
- content fully decoupled from presentation;
- content consumed through a single data/query layer, not ad-hoc queries in templates;
- structured, validated content models;
- no redeploy for content changes;
- static-generation-like performance, achieved through caching (Section 14).

Record this override in `DECISIONS.md`. Do not reduce any other PRD functional requirement.

### 0.3 Precedence when instructions conflict
1. Security, data safety, and the agent behaviour rules (Section 1).
2. This prompt's architectural mandates (Laravel + Filament CMS, page builder, decoupling).
3. PRD functional, UX, and acceptance requirements.
4. Existing repository conventions.
5. Your own preferences.

### 0.4 PRD invariants you must never lose
These rules are easy to miss. Each one must appear in the requirements traceability matrix (Section 20, Phase 8).

- **§6.2 / §6.3:** "Download CV" is a button-styled action in the header on ALL breakpoints, separate from the nav links. Contact/"Let's Connect" is persistent in the header, or sticky on scroll for long pages (case studies, articles).
- **§6.4:** On mobile, CV and Contact must be reachable within one tap, not only inside the hamburger menu. Use a persistent mobile CTA bar or equivalent.
- **§6.6 / §6.7:** Internal linking rules. Breadcrumbs appear ONLY on Project detail and Article detail pages.
- **§9.1:** The hero has at most THREE CTAs with distinct visual weight (primary filled, secondary outline/ghost). The hero must lay out gracefully when there is no photo.
- **§9.3:** The capabilities grid must support 5+ items without layout breakage.
- **§9.4:** Homepage featured projects come from the same Projects source. No duplication.
- **§9.8 / §17.4:** The recommendations preview is HIDDEN ENTIRELY when there are zero published recommendations. Never show placeholder testimonials.
- **§10.2:** Project filtering is client-side with no page reload. Featured projects are visually distinguished. There is an empty state with a "Clear filters" action, and pagination or "Load more" for growth.
- **§10.3 / §11.4:** Detail pages have Previous/Next navigation, "Back to Projects", and a Related Projects block. Long case studies have in-page section navigation (sticky sidebar or anchor nav).
- **§11.2:** Every case study visually and structurally separates "What I owned" (My Contribution) from what the wider team delivered.
- **§11.3:** Case-study sections can be added, removed, and reordered. Optional sections that are absent render NOTHING: no empty headers, no broken layout.
- **§12.2 / §12.3:** Experience uses a hybrid timeline with expandable cards. Achievements are visually emphasised over routine duties.
- **§13.2:** The About page reads as one cohesive narrative, not a form-like field list. It supports a photo with responsive stacking and an optional pull-quote.
- **§14.2:** Skills appear as categorised tags/lists. NO percentage bars or proficiency graphics, ever.
- **§15.2 / §17.3 / Appendix A:** Placeholder credentials and recommendations must never render as if real. Placeholders must look obviously like placeholders (e.g. `[Certification Name]`). Recommendations must be genuine, named, permission-granted, and source-attributed.
- **§16.1:** Articles have reading time (calculated or manual override), related articles (auto or manual), author structured for future guest authors, and social sharing with LinkedIn as the priority.
- **§18:** Booking uses an external provider via embed, with meeting types and a fallback (hosted booking link plus direct email) if the embed fails. Never leave a blank space. Booking is offered alongside the contact form, not instead of it.
- **§19.2:** The contact form handles all states: empty, validation, loading (button disabled), success, and error with a fallback email link. Default copy:
  - Success: "Message sent successfully. Thanks for reaching out."
  - Error: "Something went wrong while sending your message. Please try again or contact me directly."
  - Both must be CMS-editable, with these PRD strings as defaults.
- **§19.3 / §19.4:** Honeypot and/or invisible CAPTCHA, server-side rate limiting, `<label>` associations, and an `aria-live` region for errors.
- **§20.1:** "Download CV" appears in five places: Header, Homepage Hero, Experience page, Contact page, and Footer. Clicking it FIRST OPENS THE CV FOR VIEWING IN A NEW TAB; downloading is then optional. If the CV is unavailable, show a fallback message plus a direct email option (§33).
- **§21:** Structured-editorial design language. Avoid every item in §21.2: generic templates, developer/terminal aesthetic, corporate feel, résumé-as-HTML, artsy-blog look, decorative shapes, excessive gradients/glassmorphism/animation, stock photography, clutter.
- **§25:** Mobile-first. Breakpoints at ~375–428, ~768, ~1024+, and ~1440+. Grids go 3 columns → 2 → 1. No horizontal scrolling. Touch targets at least 44×44px.
- **§29:** Every listed analytics event is implemented, tagged by location where specified (e.g. `cv_download_header`, `cv_download_footer`, booking CTA by location, project card click by source section), including recommendation carousel interactions and case-study scroll depth/time where feasible.
- **§31:** Confidential projects support anonymised/redacted mode. The organisation field can be omitted per project without breaking templates.
- **§33:** Every error/empty state in the table is implemented.
- **§34 / §35:** These are the acceptance baseline and Definition of Done.

---

## 1. AGENT BEHAVIOUR RULES (NON-NEGOTIABLE)

1. Inspect before you modify. Never guess when repository evidence is available.
2. Never overwrite or remove existing functionality blindly. Preserve useful existing code unless there is a documented reason to replace it.
3. Never delete data, drop tables, or run destructive migrations or commands (`migrate:fresh`, `db:wipe`, dropping columns that contain data, `rm -rf` on user content) without explicit approval.
4. Never modify production configuration or production databases. Never manually edit database structure. All schema changes go through migrations.
5. Never print, log, commit, or echo secrets. When inspecting `.env`, report only which keys exist and whether they are set, never their values.
6. Never hardcode CMS content (professional copy, names, titles, links, CTAs, SEO text) in Blade, PHP, JS, or CSS. Brand-neutral UI microcopy, such as accessible labels for generic controls, may live in language files (`lang/`).
7. Do not create duplicate systems. If the repository already has settings, media, auth, permissions, or similar systems, evaluate them and reuse or extend them where sound.
8. Keep changes incremental and commit-sized. After each meaningful step, run the relevant tests and fix failures. Never hide errors by skipping tests, suppressing exceptions, or using `@`-silencing.
9. Explain architectural decisions in `docs/architecture/DECISIONS.md` (ADR-style: context, decision, consequences).
10. Follow Laravel and Filament conventions unless a documented reason exists. Avoid over-engineering: every abstraction must solve a real problem in this codebase.
11. Never mark TODOs, stubs, or partial work as complete. Use `// TODO(portfolio):` markers and list them in the final report.
12. Maintain backwards compatibility where practical (existing routes, existing users, existing data).
13. Use the latest stable versions of every package that are compatible with the PHP/Laravel/Filament versions actually in the repository. Before installing any package, confirm compatibility with `composer why-not`, a dry run, or the package's documented support matrix. If a package is not compatible, choose an alternative and document why.
14. When genuinely blocked or facing an irreversible decision, stop and ask. Do not improvise around it.
15. Keep a running log in `docs/implementation/PROGRESS.md`: phase, what changed, commands run, test results, open issues.

---

## 2. PHASE 1 — DISCOVERY (READ-ONLY)

Make NO code changes in this phase except creating documentation files.

Inspect and report on:

1. Full repository structure (top two to three levels) and any non-standard directories.
2. Laravel version (`composer show laravel/framework`, `php artisan --version`).
3. PHP version (`php -v`) and required extensions (gd/imagick, intl, exif, fileinfo, zip, pdo_mysql).
4. `composer.json`: dependencies, dev dependencies, scripts, autoload namespaces.
5. `package.json`: build tooling, Vite config, Tailwind version and config, Alpine/Livewire presence.
6. Database configuration: driver and version (MySQL/MariaDB) from config; no secrets. Check whether a local database is reachable. Run `php artisan migrate:status` (read-only).
7. Existing routes (`php artisan route:list`), noting route names, middleware, and possible conflicts with the required public URLs.
8. Existing models, relationships, migrations, factories, seeders.
9. Controllers, Form Requests, policies, services, actions, events, jobs, notifications.
10. Views, Blade components, layouts, existing CSS/JS.
11. Filament: installed or not, version, panels, resources, plugins, theme, panel path.
12. Authentication: starter kit (Breeze/Jetstream/Fortify/none), user model, guards, 2FA.
13. Existing admin functionality outside Filament.
14. Storage: disks, `storage:link` status, S3/cloud configuration presence.
15. Queue, cache, session, and mail drivers (key presence only).
16. Test setup: Pest or PHPUnit, existing tests, whether the suite passes right now. Run it and record the baseline.
17. Code quality tooling: Pint, Larastan/PHPStan, Rector, CI workflows.
18. Deployment hints: Dockerfiles, Forge/Envoyer/Ploi configs, GitHub Actions, Procfile, Supervisor configs.
19. Reusable code that fits the target architecture.
20. Conflicts with the target architecture: version incompatibilities, route collisions, naming clashes, an existing `pages` or `settings` table, an existing permission system, and so on.

**Deliverable:** `docs/implementation/01-DISCOVERY.md` containing:
- current architecture summary;
- version matrix;
- dependency inventory;
- existing frontend summary;
- Filament status;
- database status;
- auth status;
- reusable assets;
- conflicts and risks (with severity);
- recommended implementation strategy, including upgrade or install steps and which existing items are kept, extended, or replaced, with justification.

If the repository is effectively a fresh Laravel install, say so explicitly. Do not assume it.

---

## 3. PHASE 2 — ARCHITECTURE (DESIGN BEFORE BUILD)

Produce `docs/architecture/ARCHITECTURE.md` covering every topic in this section, plus an ERD (Mermaid `erDiagram`) and the section/block registry design.

**After Phase 2, STOP and present a concise summary of the Discovery findings and the proposed architecture. Wait for approval before Phase 3.** Proceed without waiting only if you have been explicitly told to continue autonomously.

### 3.1 Layered structure
Choose the final layout after Discovery, aligned with any existing conventions. Default proposal:

```
app/
  Domain/
    Content/        Pages, Sections/Blocks registry, Navigation, Redirects, Publishing
    Portfolio/      Projects, Case-study sections, Categories, Tags
    Career/         Experience, Skills, Certifications
    Insights/       Articles, Authors, Article categories/tags
    Endorsements/   Recommendations
    Engagement/     Contact submissions, Booking, CV/Documents
    Site/           Settings, SEO defaults, Social links, Footer
    Analytics/      Tracking contracts, drivers, event catalogue
    Media/          Media conventions, conversions, upload rules
    Shared/         Enums base, value objects, support traits, sanitisation
  (each module: Actions/, DTOs/, Enums/, Events/, Listeners/, Queries/, Services/, Contracts/, ValueObjects/ as needed)
  Models/           Eloquent models (keep here unless the repo already uses domain-located models; document the choice)
  Filament/         Resources, Pages, Widgets, Clusters, shared form/table schema builders
  Http/             Controllers (thin), Requests, Middleware
  View/Components/  Blade class components
  Policies/
  Providers/
resources/views/
  layouts/  components/  blocks/  pages/  partials/  errors/
```

Rules:
- **Controllers** are thin. They resolve the request, call a Query or Action, and return a view. No business logic.
- **Filament Resources** are thin. Shared form and table schemas live in reusable schema classes (e.g. `SeoFormSchema`, `MediaFieldSchema`, `PublishingFormSchema`). Business operations such as publishing, duplication, and CV replacement are Actions called from Filament actions.
- **Read side:** Query/Repository-like classes (e.g. `ProjectQuery::publishedListing()`, `PageResolver::resolve($slug)`) are the single content data layer for the frontend. This implements PRD §23.2 and §32. Use the repository pattern only where an interface swap is genuinely useful. Do not wrap Eloquent in repositories for its own sake.
- Use **DTOs/value objects** where they clarify contracts: block payloads, SEO metadata, CTA definitions, analytics events, booking meeting types.
- Use **PHP backed enums** for all statuses and types (`PublishStatus`, `BlockType`, `CaseStudySectionType`, `RecommendationRelationship`, `BookingProvider`, `UserRole`, `AnalyticsEvent`, etc.).
- Use **events/listeners** where they decouple concerns: e.g. `ContentPublished` → cache invalidation and sitemap regeneration; `ContactSubmissionReceived` → notifications and analytics; `SlugChanged` → automatic 301 redirect.
- Use **queued jobs** for mail, image conversions, and sitemap generation.
- Use **strict types** (`declare(strict_types=1);`), typed properties, and typed returns throughout new code.

### 3.2 Database design principles
- Use a balanced relational design: foreign keys with sensible `onDelete` behaviour, indexes on slugs, statuses, `published_at`, `sort_order`, and FK columns, and unique constraints on slugs and pivot pairs.
- Soft deletes only where recovery is valuable (pages, projects, articles, experience, recommendations, certifications). Document this.
- `sort_order` columns where an admin controls order. Timestamps everywhere.
- Use JSON ONLY for genuinely variable-shape data: block payloads, meeting types, CTA arrays, structured-data overrides. Never put the whole CMS in one JSON column. Never create a table per block type.
- Use polymorphism ONLY where it is justified. Expected justified uses: content sections attached to multiple owners, SEO metadata, media (if using a media library), activity logs, taggables if tags are shared.
- Check every migration for conflicts with existing tables found in Discovery.

### 3.3 Proposed content model
Validate this model against the PRD (§10–§18, §23.1) and refine it. All publishable entities share a `PublishStatus` enum (`draft`, `scheduled`, `published`, `archived`) plus `published_at`. A model is "live" when its status is published and `published_at <= now()`, or when it is scheduled and the time has passed. Implement this as one shared trait/scope and one shared query rule, not as repeated `where` clauses.

**pages**
- id, title, slug (unique), `system_key` (nullable, unique; e.g. `home`, `about`, `experience`, `projects_index`, `skills`, `certifications`, `insights_index`, `contact`, `book`, `not_found`), template (enum, default `default`), status, published_at, featured image, excerpt, is_homepage (exactly one, enforced), soft deletes, timestamps.
- System pages cannot be deleted, and their slugs are locked for routes that require fixed URLs. Their title, SEO, intro, and sections ARE editable.

**content_sections** (shared ordered section/block store; morph owner: Page, Project, optionally Article)
- id, sectionable_type, sectionable_id, block_type (string key validated against the registry), data (JSON, validated per block), sort_order, is_visible, anchor_id (nullable, slugified, unique per owner), label/admin_note, is_locked (for required system blocks), timestamps.
- Indexes: (sectionable_type, sectionable_id, sort_order).

**projects**
- id, title, slug, summary, role, organisation (nullable), organisation_logo (media), is_anonymised (bool), public_organisation_label (e.g. "a fintech client"), confidentiality_cleared (bool, required true to show real organisation/logo), timeline_label, start_date, end_date, challenge, objective, approach, solution, outcome_summary, key_contribution, is_featured, featured_order, thumbnail and gallery (media with alt text), status, published_at, soft deletes.
- Relationships: belongs to many categories (`project_categories`), tags/tools (shared `tags` with type scoping, or dedicated tables; justify the choice), related projects (manual pivot, falling back to automatic same-category/tag matching), experiences (pivot), recommendations (optional pivot for in-context quotes).
- `outcome_summary` is shown on cards (§10.1). Long-form narrative lives in case-study sections.
- When anonymised, NO template may render the real organisation name or logo, including in SEO meta, structured data, image alt text, or the sitemap. Test this.

**Case studies** are the project's `content_sections`, using case-study-scoped blocks (Section 4.3).

**experiences**
- organisation, role/job_title, location, employment_type (optional), start_date, end_date (nullable), is_current, context/overview (rich), responsibilities (ordered list), achievements (ordered list, emphasised), linked projects (pivot), tools (tags), sort_order override, status, published_at.
- Ordering: newest first by default. "Present" is derived from `is_current`, with validation that `end_date` is null when current and `end_date >= start_date`.

**skill_categories** (name, slug, description, sort_order) → **skills** (name, category, sort_order, is_visible). No proficiency fields.

**certifications**
- name, issuer, issued_at (or year), expires_at (nullable), credential_id, credential_url, logo (media + alt), sort_order, status, is_placeholder (bool).
- When `is_placeholder` is true, the frontend renders an obvious placeholder treatment and no outbound credential link.

**articles**
- title, slug, excerpt, content (sanitised rich text), cover image (+ alt), category, tags, author, reading_time_minutes (auto-calculated on save, with optional manual override), related articles (manual pivot, auto fallback by category/tag), status, published_at, soft deletes.

**article_categories**, **tags** (or typed tags), **authors** (name, title, bio, photo, links). Seed the site owner as the default author, structured for future guest authors (§16.1).

**recommendations**
- recommender_name, recommender_title, recommender_organisation, relationship (enum + custom label), quote, excerpt (optional, for previews), photo (+ alt), source_platform (e.g. LinkedIn), source_url, recommended_at, is_featured, sort_order, publication_consent_confirmed (bool, REQUIRED true to publish; enforced in the Publish action and validation), status.
- Optional pivots to experiences and projects.

**navigation_menus** (key: `primary`, `footer`, `footer_secondary`, `mobile_cta`) → **navigation_items**
- label, link_type (page / project / article / route / external URL / anchor / CV / booking), linkable morph or target value, open_in_new_tab, parent_id (one level of nesting maximum unless justified), sort_order, is_visible, analytics_event_key.

**redirects**
- from_path (unique), to_path, status_code (301/302), hits, is_active.
- Automatically created when a published slug changes.

**documents** (CV and other approved documents)
- type (`cv`, `other`), title, file (media, private or public decision documented), is_current (only one current CV), version label, uploaded_by.
- Replacing the CV creates a new version and marks it current. Old versions are retained (audit) but are not publicly accessible.

**contact_submissions**
- name, email, subject, message, company, opportunity_type, linkedin_url, ip_hash (hashed, not raw), user_agent (truncated), status (`new`, `read`, `replied`, `archived`, `spam`), read_at.
- Pruned according to a configurable retention period (Laravel `Prunable`). Mask personal data in listings for roles without permission.

**seo_meta** (morph, or embedded columns; justify)
- seo_title, meta_description, canonical_url, og_title, og_description, og_image, robots (`index`/`noindex`, `follow`/`nofollow`), structured_data_override (JSON, super admin only).

**Settings** (prefer `spatie/laravel-settings` + Filament settings pages, or the repository's existing equivalent). One typed class per group:
- `GeneralSettings`: site name, owner name, professional title, tagline, logo, favicon, default OG image.
- `ContactSettings`: public email, optional phone, location, notification recipients, success/error copy with PRD defaults, retention days.
- `SocialSettings`: ordered social links (platform enum, URL, label); LinkedIn as first-class.
- `SeoSettings`: title template, default meta description, default robots, Person schema fields.
- `BookingSettings`: enabled, provider enum (Calendly, SavvyCal, Cal.com, other), booking_url, embed_enabled, embed configuration, meeting_types[] (label, duration, provider URL/slug, description), fallback_url, fallback_email.
- `AnalyticsSettings`: driver, site ID, enabled, consent mode, scroll-depth tracking toggle.
- `FooterSettings`: identity text, copyright template with year token, CV visibility, column configuration.
- `CtaSettings`: global labels/targets for "Download CV", "Let's Connect", "Book a Call", "View My Work".
- `SecuritySettings` (super admin): CAPTCHA provider and enabled flag (keys come from `.env` only), allowed embed domains.

Secrets (API keys, CAPTCHA secrets, mail credentials) live ONLY in `.env` and config, never in database settings.

**media**: see Section 7.

**activity_log**: see Section 10.

**users**, **roles**, **permissions**: see Section 9.

### 3.4 Routing design
Public routes, all named:

| URL | Purpose |
|---|---|
| `/` | Homepage (page with `is_homepage`) |
| `/about` | About |
| `/experience` | Experience |
| `/projects`, `/projects/{slug}` | Project listing and detail |
| `/skills` | Skills |
| `/certifications` | Certifications |
| `/insights`, `/insights/{slug}` | Article listing and detail |
| `/insights/category/{slug}` | Optional; filters must also work via query string |
| `/contact` | Contact (`POST` endpoint for the contact form) |
| `/book` | Booking, when enabled |
| `/cv` | Opens the current CV inline in the browser |
| `/cv/download` | Forces download; both accept `?from=header`/`hero`/`experience`/`contact`/`footer` for analytics |
| `/sitemap.xml`, `/robots.txt` | Generated |
| `/preview/{type}/{id}` | Signed, authenticated draft preview |
| `/{slug}` | Catch-all for CMS-created pages, registered LAST |

Requirements:
- Maintain a **reserved slugs list** (all system routes, the admin path, `livewire`, `storage`, `build`, `sitemap.xml`, etc.) and validate page slugs against it.
- Redirect middleware runs before 404s.
- A custom 404 page whose content (heading, message, links to Home/Projects/Contact) is CMS-editable through the `not_found` system page, with safe hardcoded fallbacks if the database is unreachable.
- Also provide a 500 page and a 503 maintenance page.
- Route model binding resolves only live content for public visitors. Unpublished and soft-deleted content returns 404 (never 403) to the public.

### 3.5 System pages and the page builder working together
Every public route, including entity listings, is backed by a `Page` record (by `system_key`) that owns its SEO, intro, and sections. Listing pages contain a locked, required "primary listing" block (e.g. `projects_listing`, `insights_listing`, `experience_timeline_full`, `skills_full`, `certifications_full`, `contact_form`, `booking_embed`). Admins can move this block but cannot delete it. They can add, remove, and reorder all other blocks around it.

This makes every page CMS-editable while keeping dynamic listings code-driven. Seed all system pages with sensible default block arrangements derived from the PRD.

---

## 4. DYNAMIC PAGE BUILDER (CRITICAL)

### 4.1 Block registry architecture
Implement a `BlockRegistry` (singleton, populated from a config file or service provider) holding `Block` definitions. Each block class implements a `BlockContract`:

- `key(): string` — stable identifier stored in `content_sections.block_type`.
- `label()`, `icon()`, `description()` — for the admin picker.
- `scopes(): array` — where the block may be used: `page`, `case_study`, `homepage`.
- `schema(): array` — the Filament form schema for the block's `data`.
- `rules(): array` — server-side validation for `data`, enforced in an Action on save, not only in the form.
- `defaults(): array`.
- `resolve(array $data): BlockViewModel` — turns stored data into a typed view model. This includes resolving referenced entities (e.g. featured project IDs → eager-loaded published Project models, dropping unpublished or deleted ones) in a query-efficient way.
- `view(): string` — the Blade component that renders it.
- `shouldRender(BlockViewModel $vm): bool` — e.g. the recommendations block returns false when there are zero recommendations; any block returns false when its required content is empty.
- `requiresPermission(): ?string` — e.g. a custom embed block requires a super-admin-only permission.

Adding a new block type must require only:
1. a new Block class;
2. its Blade component;
3. registering it.

No page, controller, or migration changes. Document this in `docs/architecture/PAGE-BUILDER.md` with a worked example.

Rendering: a single `<x-sections :owner="$page" />` component (or a `SectionRenderer` service) loads visible sections in order. It batch-resolves entity references (collect IDs across all blocks, query once per entity type) to avoid N+1 queries, then renders each block. Unknown or removed block types are skipped and logged, never fatal.

### 4.2 Admin UX for sections
Use Filament's capabilities appropriately for the installed version. Either:
- a relationship-backed, reorderable Repeater over `content_sections`, where the selected `block_type` drives a dynamic schema from the registry; or
- a Builder field persisted to `content_sections` through a dedicated sync Action.

Choose after Discovery, document the decision, and satisfy all of the following:

- add, remove, reorder (drag and drop), duplicate a section, and toggle visibility;
- collapsible items with meaningful item labels (block label + headline);
- block picker grouped and searchable, filtered by the owner's scope;
- locked required blocks shown as non-deletable;
- per-section `anchor_id`, used for in-page navigation;
- "Preview" action opening the signed preview URL in a new tab;
- saving is transactional: all section changes persist or none do.

### 4.3 Block catalogue
Implement at least the following. Each block's fields must be CMS-editable, with alt text required for every image field.

**General page blocks**
- `hero`: eyebrow, name, professional title, value proposition, photo + alt, and up to 3 CTAs (enforced) each with label, type (View Work / CV / Contact / Booking / internal link / external URL), style (primary/secondary/tertiary), and analytics key. Lays out gracefully with no photo.
- `rich_text`: sanitised.
- `image_text`: image left/right, stacks on mobile.
- `two_column`
- `pull_quote` / `highlight_statement` (§13.2)
- `cta_banner` / `final_cta`: heading, text, and parallel "Let's Connect" + "Book a Call" actions (§9.9).
- `capabilities_grid`: repeater of icon (from ONE consistent icon set), title, one-line descriptor; 4+ items, no layout breakage at 5+.
- `process_steps` / "How I Work": 3–5 steps recommended; warn in the admin if more than 5.
- `metrics`: label, value, context, and `is_verified`. Only verified metrics render publicly; unverified ones show an admin-only warning.
- `featured_projects`: manual selection from Projects, or "auto: featured flag", count 3–4, "View All Projects" CTA.
- `project_grid`, `projects_listing` (locked on `/projects`)
- `experience_preview` (most recent 1–2 roles or a summary statement + CTA), `experience_timeline_full` (locked on `/experience`)
- `skills_grid` / `skills_full`
- `certifications_preview` (3–5 + CTA), `certifications_full`
- `recommendations_preview` (carousel or 2–3 cards; hidden when there are zero), `recommendations_grid`
- `article_grid` / `insights_listing`
- `contact_form` (locked on `/contact`), `booking_embed` / `booking_cta`
- `cv_cta`
- `gallery`: images + alt + captions.
- `video_embed`: allowlisted providers only (YouTube-nocookie, Vimeo, Loom), validated URL parsing; never raw iframe HTML from editors.
- `logo_grid`: honours confidentiality; no client logos unless cleared.
- `social_links`
- `related_content`
- `divider` / `spacer`: limited preset sizes only.
- `custom_embed`: SUPER ADMIN ONLY, sanitised against a strict allowlist, and disabled by default. Justify it or omit it.

**Case-study blocks** (scope `case_study`)
- `cs_section`: `section_type` enum covering the 16 PRD §11.1 types (Project Overview, Challenge, Objective, My Role, Approach, Requirements/Discovery, Prioritisation, Planning, Stakeholder Alignment, Execution/Delivery, Solution, Collaboration, Challenges, Outcome/Impact, Tools & Methods, Key Takeaway) plus `custom`. Fields: optional heading override, rich body, optional images/screenshots/diagrams with alt + caption, optional external links, include-in-section-nav toggle.
- `cs_contribution`: the "My Contribution / What I owned" vs "What the team delivered" callout. Two distinct, consistently styled panels (§11.2).
- `cs_metrics` (verified only), `cs_gallery`, `cs_embed`, `cs_links`, `cs_quote` (select a published recommendation).

**Project detail template**
- Auto-generates the in-page section navigation from visible sections that have headings/anchors: a sticky sidebar on desktop and an anchor bar on mobile.
- Appends Related Projects, Previous/Next, and Back to Projects automatically.
- Absent sections render nothing.

---

## 5. HOMEPAGE

The homepage is a `Page` with `is_homepage = true`, seeded with blocks in PRD §9 order:

hero → professional snapshot (`rich_text` or a dedicated snapshot block) → `capabilities_grid` → `featured_projects` → `process_steps` → `experience_preview` → `certifications_preview` → `recommendations_preview` → `final_cta`

Header and footer come from global navigation and settings.

The admin can edit, reorder, hide, or add any block. Featured projects, experience, certifications, and recommendations are pulled from their own systems. There are NO homepage-specific copies of entity data.

---

## 6. FILAMENT ADMIN PANEL

Use the installed Filament major version, or install the latest stable version compatible with the repository after Discovery. Follow that version's APIs exactly. Do not mix v3/v4/v5 syntax. Check the vendor source and official docs for the installed version when unsure.

### 6.1 Panel configuration
- Configurable, non-obvious admin path via `.env` (default `/admin`, documented).
- Branding from settings.
- Enable multi-factor authentication if the installed Filament version supports it natively. Otherwise use a vetted compatible package or document it as a follow-up.
- Login rate limiting, password reset, strong password rules.
- Dark mode support optional; the admin UI must remain usable.

### 6.2 Navigation groups
- **Content:** Pages, Projects (with case-study builder), Project Categories, Experience, Skills (with Skill Categories), Certifications, Articles (Article Categories, Tags, Authors), Recommendations.
- **Site Management:** Navigation Menus, Homepage shortcut, Footer, Site Settings, SEO Defaults, Booking, Contact Settings, CTA Settings, Analytics, Redirects, CV & Documents.
- **Inbox:** Contact Submissions, with an unread badge.
- **Media:** Media Library.
- **System:** Users, Roles & Permissions, Activity Log, Maintenance utilities (clear content cache, regenerate sitemap, rebuild image conversions), Super Admin only.

### 6.3 Standards for every resource
- Tables with search, sort, filters (status, category, featured, date), status badges (Draft / Scheduled / Published / Archived), and bulk actions (publish, unpublish, archive, delete) gated by permissions.
- Reorderable tables where `sort_order` exists.
- Forms organised in tabs: Content / Sections / Media / SEO / Publishing / Relationships. Reuse shared schema builders.
- Live slug generation from the title on create. Slug edits on published records warn that a 301 redirect will be created.
- Rich editor configured with a restricted toolbar matching what the sanitiser allows.
- Relationship selectors with search and preload.
- Actions: Preview (signed URL), Publish/Unpublish/Schedule (Action classes plus permission checks), Duplicate (for pages, projects, sections).
- Validation messages that are human-friendly.
- Inline admin warnings for PRD content rules: e.g. publishing a recommendation without consent is blocked; a certification marked as a placeholder; a project with a real organisation but not confidentiality-cleared; missing alt text blocks saving.
- Dashboard widgets: content counts by status, recent contact submissions, CV views/downloads (from local server-side events), and quick links. Keep it modest.

Keep the admin UX simple. Avoid wizards unless they genuinely help (e.g. "New Project" creation wizard: basics → card fields → case study).

---

## 7. MEDIA MANAGEMENT

1. Evaluate `spatie/laravel-medialibrary` with the matching official Filament Spatie Media Library plugin for the installed Filament version. If it is compatible, prefer it over a custom solution and document why. If the repository already has a working media system, evaluate reuse first.
2. Define media collections per model: `thumbnail`, `gallery`, `cover`, `logo`, `photo`, `cv`, `og_image`. Each has accepted MIME types, maximum size, and single/multiple file rules.
3. Custom properties stored per media item: `alt` (required for images; enforced), `caption` (optional).
4. Conversions (queued): responsive images and WebP (AVIF where the server supports it), with sizes appropriate for cards, detail heroes, OG images (1200×630), and thumbnails. Strip EXIF metadata. Validate dimensions where useful (e.g. OG image minimum).
5. Uploads:
   - validate MIME using server-side content inspection, not just the extension;
   - images: `jpg/jpeg/png/webp/avif`;
   - SVG only for super admin and only after sanitisation (or disallow it; decide and document);
   - CV: PDF only;
   - enforce size limits from config;
   - randomise and sanitise file names.
6. Disks: public disk for public imagery. The CV must be publicly *viewable*, but it is served through the `/cv` route (to enable analytics, versioning, and swap without changing URLs), so store it on a non-public disk and stream it with correct headers:
   - `/cv`: `Content-Disposition: inline`, `application/pdf`;
   - `/cv/download`: `Content-Disposition: attachment` with a clean file name from settings (e.g. `Godsfavour-Okpara-CV.pdf`).

   Old CV versions are never publicly reachable. Contact-related or private documents are never on the public disk.
7. A single Blade `<x-media.image>` component renders `srcset`/`sizes`, width/height attributes (to prevent CLS), `loading="lazy"` below the fold, `fetchpriority="high"` for hero/LCP images, and alt text. It degrades gracefully when the media item is missing.
8. Cloud storage (S3-compatible) must work purely through configuration.

---

## 8. SECURITY (FIRST-CLASS)

- CSRF on all state-changing routes. Eloquent and the query builder only, no raw interpolated SQL. Server-side validation via Form Requests or Action-level validators everywhere, including Filament saves for block payloads.
- **Rich HTML:** sanitise on save AND escape or sanitise at render. Use a maintained sanitiser (e.g. Symfony HtmlSanitizer or HTMLPurifier via a compatible Laravel wrapper) with an explicit allowlist that matches the editor toolbar. Never output CMS content with `{!! !!}` unless it has passed through the sanitiser. Centralise this in one `ContentSanitizer` service plus a Blade directive or component.
- **Embeds:** provider allowlist with URL parsing. Booking embed domains come from the selected provider configuration. No editor-supplied `<script>`.
- **Authorization:** policies for every model, enforced in Filament (resources, actions, bulk actions, relation managers) and in any non-Filament admin endpoint. Preview routes require auth, permission, AND a valid signature.
- **Rate limiting:** contact form (e.g. 5/min and 20/day per IP, configurable), admin login, CV endpoints (generous, to block abuse rather than real users), and preview.
- **Spam protection:**
  - honeypot plus minimum-submit-time check (e.g. `spatie/laravel-honeypot` if compatible);
  - pluggable optional CAPTCHA via a `CaptchaVerifier` contract with drivers: null, Cloudflare Turnstile, reCAPTCHA v3. Enabled by configuration; keys only in `.env`.
- **Security headers middleware:** `X-Content-Type-Options`, `Referrer-Policy`, `X-Frame-Options`/`frame-ancestors`, `Permissions-Policy`, and HSTS in production.
- **CSP:** nonce-based where feasible. Allowlist analytics and booking provider domains dynamically from settings. Ensure Filament/Livewire still work; the admin panel may use a separate, more permissive policy if required, documented. Never leave a CSP that silently breaks the booking embed: test it.
- **Sessions:** secure, httpOnly, sameSite cookies in production; session regeneration on login.
- **Data minimisation:** hash IPs on contact submissions; configurable retention; no personal data in analytics payloads.
- No stack traces in production (`APP_DEBUG=false` documented). `.env` never committed. Update `.env.example` with keys only.
- Run `composer audit` and `npm audit`. Address high/critical findings or document them.
- Do not expose the admin path in the sitemap, `robots.txt`, or public HTML.

---

## 9. ROLES AND PERMISSIONS

Use `spatie/laravel-permission`. Add a compatible Filament integration (e.g. Filament Shield) only if it fits the installed Filament version and keeps things simpler. Otherwise write policies directly against permissions. Document the choice.

| Capability | Super Admin | Content Manager | Editor |
|---|---|---|---|
| View/create/update content (all content types) | ✓ | ✓ | ✓ |
| Delete content | ✓ | ✓ | ✗ (own drafts only, optional) |
| Publish / unpublish / schedule / archive | ✓ | ✓ | ✗ (submits drafts) |
| Manage media | ✓ | ✓ | upload/attach only |
| Manage navigation, footer, homepage, CTAs, booking, CV | ✓ | ✓ | ✗ |
| Site/SEO/analytics/security settings, redirects | ✓ | SEO + redirects only | ✗ |
| Contact submissions | ✓ | ✓ | ✗ |
| Users, roles, permissions, activity log, maintenance | ✓ | ✗ | ✗ |
| Custom embed block, structured-data overrides | ✓ | ✗ | ✗ |

- Permissions are granular (`view`, `create`, `update`, `delete`, `publish`, `manage_settings`, `manage_users`, `manage_media`) and seeded idempotently.
- Super Admin is protected: the last super admin cannot be deleted or demoted.
- Provide an artisan command to create the first super admin interactively (`php artisan portfolio:create-admin`). Never seed a default password.

---

## 10. AUDITABILITY AND EDITORIAL WORKFLOW

- Use `spatie/laravel-activitylog` (if compatible) to log create, update (with changed attributes), delete, restore, publish, unpublish, schedule, archive, settings changes, CV replacement, user/role/permission changes, and section reorders. Expose a read-only Filament Activity Log for Super Admin.
- Never log secrets or full contact message bodies.
- Workflow: Draft → (Scheduled) → Published → Archived. Scheduled content goes live when `published_at` passes. Use a scheduled command that dispatches `ContentPublished` for cache invalidation, and document scheduler requirements.
- Preview of drafts through signed URLs.
- Revision history: do NOT build a full versioning system unless it is cheap. Ensure the architecture allows it later (e.g. activity log properties hold before/after snapshots; publishing goes through Actions that can later snapshot). Document this as a deliberate scope decision.

---

## 11. PUBLIC FRONTEND AND DESIGN SYSTEM

### 11.1 Rendering stack
- Blade with class-based/anonymous components, Tailwind CSS (version detected or installed per Discovery), Vite, and Alpine.js for small interactions: mobile nav, client-side filtering, carousel, expandable timeline, form states.
- No heavy SPA framework. No jQuery. Per-page Vite entry points or dynamic imports, so non-critical JS (carousel, filtering, booking loader) loads only where needed (§27 code-splitting).
- Progressive enhancement:
  - Project/article filters also work via query string without JS, while still filtering client-side without reload when JS is on.
  - Navigation works without JS.
  - The booking embed has a non-JS fallback link.
  - The contact form works as a normal POST when JS fails.
- If the contact form is built with Livewire instead (available via Filament), document why and keep the full-page fallback.

### 11.2 Visual direction (PRD §21–§22)
Structured-editorial: generous whitespace, strong typographic hierarchy, content in clear structured blocks, restrained accent use.

**Palette — Deep Navy + Warm Ivory + Electric Blue**, used intelligently:
- Warm Ivory as the primary page background (not stark white).
- Deep Navy for primary text, the header/footer, and occasional dark feature sections (e.g. Final CTA).
- Electric Blue as the SINGLE sparing accent: primary CTAs, focus rings, key highlights, links.
- Never a saturated blue page. Never the generic "blue-on-white SaaS" look the PRD warns against.

Define tokens as CSS custom properties consumed by Tailwind: e.g. `--color-navy-900`, `--color-ivory-50`, `--color-blue-500`, plus a neutral scale, success/error/warning, and surface/border tokens. Choose exact hex values yourself. VERIFY WCAG AA contrast for every text/background and interactive-state pairing, including Electric Blue on Ivory and on Navy (adjust shade for text use if needed), and document the contrast table in `docs/design/DESIGN-SYSTEM.md`.

**Typography:** one distinctive but professional heading face and one highly readable body face. Self-host the fonts (no third-party font CDN), subset them, use `font-display: swap`, and preload critical weights. Define a type scale (H1–H4, body, small, caption/label/eyebrow), line-heights, and a readable measure (~60–75ch).

**Spacing:** 4/8pt scale.

**Grid:** 12-column desktop collapsing for tablet/mobile; consistent max content width; breakpoints per §25.

**Motion:** purposeful only (subtle entrance on scroll, hover feedback). Everything respects `prefers-reduced-motion`. No parallax, no decorative animation.

**Icons:** one consistent line icon set (e.g. Heroicons or Lucide, via Blade icons), used for capabilities, process steps, and social/contact icons.

No stock-photo dependency. Layouts must look intentional with no images at all.

### 11.3 Component library
Every component reads data from view models/props, never from hardcoded content. Required:

- **Layout and navigation:** `layout.app`, `skip-link`, `header` (logo/name, primary nav, Download CV button, Contact action), `nav.primary`, `nav.mobile` (accessible hamburger with focus trap, Esc to close, `aria-expanded`), `nav.mobile-cta-bar` (persistent CV + Contact on mobile), `sticky-contact` (long pages), `footer` (full sitemap links from the footer menu, identity, social/contact, CV, copyright), `breadcrumbs` (project/article detail only, with BreadcrumbList schema).
- **Primitives:** `section-heading` (eyebrow/title/subtitle), `button` (primary filled / secondary outline-ghost / tertiary text-link; default/hover/focus/disabled/loading states; renders `<a>` or `<button>` correctly), `card` (one base card pattern with consistent radius/elevation/padding), `tag`/`badge`.
- **Content cards:** `project-card` (all §10.1 fields; hover elevation/scale and focus-visible; anonymised-aware), `article-card`, `experience.timeline` + `experience.card` (expandable, keyboard accessible, achievements emphasised), `skill-group`, `certification-card` (logo, issuer, date, credential ID, external verify link with `rel="noopener"` and analytics; placeholder treatment), `recommendation-card` and `recommendation-carousel` (accessible carousel: pause, keyboard and screen-reader controls; renders correctly with 1 or many; source attribution such as "via LinkedIn").
- **Engagement:** `cv-button` (location prop drives analytics and the `from` param; handles CV-unavailable fallback), `booking.embed` (lazy-loads the provider script on interaction or visibility; timeout/error detection triggers the fallback panel with hosted link + email), `contact.form` and form field components (`input`, `textarea`, `select`, `field-error`, `form-status` `aria-live`), `share-links` (LinkedIn first).
- **States:** `empty-state` (message + action, e.g. Clear filters), `error-state`, `loading-state`/skeleton, `pagination`/`load-more`.
- **Utilities:** `media.image`, `seo.head` (single component rendering all meta tags), `social-links`.

Write `docs/design/DESIGN-SYSTEM.md` documenting tokens, components, props, and states.

### 11.4 Page-specific behaviour
- `/projects`: featured first and visually distinguished; category/tag filters (client-side, no reload, URL-synced); sort by date (default) or category; empty state with Clear filters; "Load more" or pagination (the design must not assume a small fixed count).
- `/projects/{slug}`: per Section 4.3, plus a sticky Contact CTA on scroll and CV access.
- `/experience`: hybrid timeline (vertical chronology; each role expands into a structured card with achievements, linked projects, tools); Download CV.
- `/insights` and `/insights/{slug}`: listing with filters + empty state; detail with cover, meta (category, date, reading time, author), sanitised content with good typographic styles, tags, related articles, share links, breadcrumbs, sticky Contact.
- `/contact`: form + booking presented as parallel options + direct email + Download CV.
- `/book`: booking embed with meeting types, or a redirect/link-out when the embed is disabled.

---

## 12. CONTACT SYSTEM

- **Fields:** required Name, Email, Subject, Message. Optional Company, Role/Opportunity type (CMS-configurable options), LinkedIn URL (validated URL; must be on the linkedin.com domain).
- **Flow:** `StoreContactSubmissionRequest` → `SubmitContactMessage` Action. The Action:
  1. runs spam checks (honeypot, timing, optional CAPTCHA);
  2. persists the submission (if storage is enabled; default on);
  3. dispatches the `ContactSubmissionReceived` event;
  4. the event triggers queued mail notifications: owner notification to the configured recipients, and an optional acknowledgement to the sender (configurable; off by default to avoid abuse);
  5. records a server-side analytics event.

  Mail failures must not lose the submission.
- **Frontend states:** exactly per §19.2. Inline field errors on blur/submit; button disabled with a loading indicator; success replaces the form or shows a confirmation panel; error shows the fallback direct email link. Errors are announced via `aria-live`. Focus moves sensibly to the status message or first invalid field. Old input is preserved on server-side validation failure (non-JS path).
- **Privacy:** a short privacy note under the form (CMS-editable); hashed IP; retention pruning.
- **Analytics:** `contact_submit_attempt` and `contact_submit_success` (§29: successful vs attempted).

---

## 13. BOOKING, CV, AND ANALYTICS

### 13.1 Booking
- Fully configured from `BookingSettings`. A `BookingProvider` enum with a small provider-adapter strategy produces the embed markup/script URL and CSP domains per provider.
- Meeting types are listed with labels/durations; selecting one loads the corresponding provider URL.
- The embed loads lazily. A timeout or script error triggers the fallback (hosted booking link + direct email).
- When `embed_enabled` is false, show link-out buttons instead.
- When booking is disabled globally, all "Book a Call" CTAs hide automatically (handled centrally, not per template).
- Events: `booking_cta_click` (with location), `booking_widget_open`.

### 13.2 CV
- Current CV managed in **CV & Documents**. Replacing it requires no deployment and keeps the same public URLs.
- `cv-button` appears in all five locations (header, hero, experience, contact, footer). Each click opens `/cv?from={location}` in a new tab, viewed inline; a download option is available from there (`/cv/download?from=…`).
- Both endpoints record a server-side event (`cv_view` / `cv_download` with location, no personal data) and the client also fires `cv_download_{location}`, as named in §29, through the tracking layer.
- If no current CV exists or the file is missing, buttons render a fallback ("CV temporarily unavailable — email me") instead of a broken link, and the endpoints return a friendly page, not a 500.

### 13.3 Analytics abstraction
- **Server:** an `AnalyticsTracker` contract with drivers (`null`, `log`/`database` for first-party key events, and one or more providers such as Plausible, Umami, or GA4 Measurement Protocol where useful), bound via config/settings.
- **Client:** one tiny `track(eventName, props)` module, the ONLY place that talks to a provider. Provider adapters are selected by a data attribute or config rendered server-side from `AnalyticsSettings`. Components fire events through `data-track="event_key"` and `data-track-props` attributes handled by a single delegated listener. Components must not contain provider calls.
- **Event catalogue:** a PHP enum + generated JS constants file, so names stay consistent. It covers every §29 event:
  - page_view;
  - cv_download_{header,hero,experience,contact,footer};
  - contact_submit_attempt / contact_submit_success;
  - linkedin_click, social_click;
  - project_card_click (project slug + source: homepage/listing/related);
  - case_study_view (+ scroll depth milestones 25/50/75/100 and time-on-page if enabled);
  - external_link_click (credential URLs, article references);
  - cta_click (each CTA individually tagged);
  - booking_cta_click (by location), booking_widget_open;
  - recommendation_carousel_advance / recommendation_interaction.
- Respect a consent setting: if a cookie-based provider is chosen, do not load it before consent. Provide a minimal, accessible consent banner only when required by the selected driver. Privacy-friendly cookieless providers are preferred by default.
- Swapping providers must require only a settings/config change.

---

## 14. SEO AND PERFORMANCE

### 14.1 SEO
- `SeoResolver` service: entity SEO → page SEO → `SeoSettings` defaults, with a title template. One `<x-seo.head>` renders:
  - title, meta description;
  - canonical (auto from route, override allowed);
  - robots;
  - Open Graph (title, description, image with dimensions, type, url);
  - Twitter Card.
- Exactly one H1 per page, guaranteed by templates: the hero or page title owns the H1, and blocks use H2 and below. Test this.
- Structured data (JSON-LD), generated from real data only:
  - `Person` on Home/About (name, jobTitle, sameAs from social links, image if set);
  - `Article` on posts (headline, dates, author, image);
  - `BreadcrumbList` on detail pages;
  - `WebSite`.
  - Never emit fabricated ratings, reviews, or credentials. Never emit `Review` schema for recommendations unless it is legitimately applicable (default: do not).
- `sitemap.xml`: published, indexable pages, projects, and articles with `lastmod`. Regenerated on publish events (queued) and nightly via the scheduler. Excludes noindex, draft, admin, and preview URLs. Use `spatie/laravel-sitemap` if compatible.
- `robots.txt`: generated dynamically. Disallows everything on non-production environments. References the sitemap.
- Clean, human-readable, unique slugs, with automatic 301s on change.

### 14.2 Performance
- No N+1 queries. Enable `Model::preventLazyLoading()` outside production and make the test suite fail on lazy loading. Eager-load all listing relationships. Batch-resolve block references (Section 4.1).
- Indexes per Section 3.2. Inspect key queries with `EXPLAIN` during Phase 7.
- Caching ("ISR-style"): cache resolved page/section view models, navigation, settings, and listing queries using tagged cache where the driver supports it, or versioned keys otherwise. Invalidate precisely through model observers/events on save, publish, and delete. Full-page response caching only if it is safe with CSRF and forms; justify it or skip it.
- Production optimisation: `config:cache`, `route:cache` (catch-all route compatible), `view:cache`, `event:cache`, `icons:cache` if relevant, `filament:optimize` if available in the installed version, `composer install --no-dev --optimize-autoloader`, `npm run build`.
- Images per Section 7. Fonts per Section 11.2. Minimal, deferred JS. No render-blocking third-party scripts. The booking script loads only on demand.
- Targets: Core Web Vitals "Good" (LCP, CLS, INP) on Home, a Project detail page, and an Article detail page (§34).

---

## 15. ACCESSIBILITY (WCAG 2.1 AA)

Use semantic landmarks (`header`, `nav` with distinct `aria-label`s, `main`, `footer`), a correct heading hierarchy, a skip-to-content link, full keyboard operability with logical tab order, visible `:focus-visible` styles using the accent token, accessible names on icon-only buttons (hamburger, social icons, carousel controls), labelled form fields with `aria-describedby` errors and an `aria-live` status, `aria-expanded`/`aria-controls` on disclosure widgets, alt text required at the content-model level, AA contrast verified on all states, reduced-motion support, 44×44px touch targets, and no information conveyed by colour alone.

---

## 16. CONTENT SEEDING

Use separate seeders:

- **`EssentialSeeder`** (safe in all environments, idempotent): roles and permissions, default settings with PRD default copy (contact success/error strings, CTA labels), system pages with their default block structures and locked blocks, navigation menus (Primary: Home · About · Experience · Projects · Insights · Contact, matching §6.2; Footer: full sitemap), the default author record.
- **`DemoContentSeeder`** (local/staging ONLY; refuses to run in production): clearly labelled placeholder content using the PRD's bracket convention, e.g. `[Project Name]`, `[Professional Snapshot Content]`, `[Certification Name]`. Every demo record has a visible "Demo" indicator in the admin.
  - NEVER fabricate credentials, credential IDs/URLs, metrics, achievements, real employer or client names, or recommendations.
  - Seed ZERO recommendations, so the hidden-section behaviour is visible.
  - Use factories in tests to cover the one-recommendation and many-recommendations cases.
  - Placeholder certifications have `is_placeholder = true`.
  - Demo metrics are `is_verified = false`, so they do not render publicly.
- Provide `php artisan portfolio:purge-demo` to remove demo content safely, after confirmation.

---

## 17. TESTING

Use the repository's framework (Pest preferred if already present; otherwise follow the existing PHPUnit setup). Use factories for every model and an in-memory or test database per the existing configuration.

**Feature tests**
- **Public pages:** every required route returns 200 with published content; draft, scheduled-future, archived, and soft-deleted content returns 404 publicly; CMS-created pages resolve via the catch-all; reserved slugs are rejected.
- **Page builder:** sections render in order; hidden sections do not render; reordering persists; duplicating works; locked blocks cannot be deleted; unknown block types are skipped safely; block payload validation rejects invalid data.
- **Projects:**
  - CRUD and publish/schedule/unpublish via Actions and policies;
  - featured ordering; filtering query fallback; empty state;
  - previous/next navigation and related-projects logic;
  - anonymised mode never leaks the organisation name or logo in HTML, meta, JSON-LD, or alt text.
- **Case studies:** absent optional sections produce no empty headings; section navigation is built from visible sections; contribution callout renders.
- **CRUD + authorization:** Experience (current/date validation, ordering), Skills, Certifications (placeholder rendering, no link), Articles (reading time calculation and override, related articles, share links), Recommendations (consent required to publish; homepage preview with 0/1/many — hidden at 0).
- **Navigation/footer/settings:** changes reflect on the frontend without code changes; cache is invalidated on save.
- **Contact:**
  - validation rules; honeypot and timing rejection; rate limiting;
  - successful submission persists, queues mail (`Mail::fake()`/`Queue::fake()`), and records an analytics event;
  - mail failure still persists the submission;
  - success/error copy comes from settings.
- **CV:**
  - `/cv` streams inline with correct headers; `/cv/download` is an attachment;
  - replacing the CV changes the served file at the same URL;
  - missing CV shows the fallback;
  - old versions are not accessible;
  - CV button present in all five locations.
- **Booking:** enabled/disabled/embed-off rendering; fallback markup present; CTAs hidden when disabled.
- **SEO:** per-page unique titles/descriptions; canonical; OG tags; exactly one H1; JSON-LD validity for Person, Article, BreadcrumbList; sitemap excludes drafts/noindex; robots.txt per environment.
- **Redirects:** slug change creates a 301; redirect middleware works.
- **404:** custom page renders CMS content with Home/Projects/Contact links.
- **Security:** rich-text XSS payloads are sanitised on save and render; disallowed embeds are rejected; upload MIME spoofing is rejected; preview requires signature, auth, and permission; security headers are present.
- **Authorization matrix:** each role can and cannot do exactly what Section 9 states, in Filament (resource access, actions, bulk actions) and in policies. The last super admin is protected.
- **Audit:** publish, settings changes, and role changes create activity log entries.

**Unit tests**
Block registry and resolvers, SEO resolver precedence, publishing state logic, reading-time calculator, slug/redirect service, sanitiser allowlist, analytics event catalogue/driver selection, booking provider adapters, CV version selection.

**Browser/a11y (where tooling allows)**
If Node/Playwright can run in the environment, add a small Playwright suite with `@axe-core/playwright` covering Home, Projects listing (filtering without reload, empty state), Project detail, Article detail, Contact (all states), mobile nav, and the booking fallback. Otherwise document a manual axe/Lighthouse checklist and the commands to run.

Lazy-loading violations must fail tests.

---

## 18. CODE QUALITY

- Laravel Pint (repository config, or Laravel preset) on all new and changed code.
- Larastan/PHPStan: use the existing configuration. If absent, add it at a realistic level (e.g. 5–6) for new code and document the baseline. Never suppress real errors to pass.
- No dead code, no commented-out blocks, no debug statements (`dd`, `dump`, `ray`, `console.log`) left behind.
- Meaningful names and small classes. Comments explain *why*, not *what*.

---

## 19. ENVIRONMENT, DEPLOYMENT, DOCUMENTATION

### 19.1 Environment
- Inspect the actual environment before assuming anything (Section 2). Never hardcode environment values.
- Update `.env.example` with all new keys and comments: `APP_URL`, DB, `MAIL_*`, `FILESYSTEM_DISK`/S3, `QUEUE_CONNECTION`, `CACHE_STORE`, session, `ADMIN_PATH`, CAPTCHA keys, analytics driver/IDs, contact/CV limits, content retention days.

### 19.2 Deployment notes
Write `docs/DEPLOYMENT.md` covering:
- server requirements and PHP extensions;
- `storage:link`;
- queue worker (Supervisor example) for mail and image conversions;
- scheduler cron for scheduled publishing, sitemap regeneration, and pruning;
- cache driver recommendation (Redis if available, for tags);
- HTTPS/HSTS;
- CSP notes for booking/analytics domains;
- production optimisation commands (Section 14.2);
- zero-downtime considerations;
- backup notes for the database and media.

### 19.3 Documentation
- **README.md:** requirements; installation; environment configuration; database setup and seeding (essential vs demo); creating the first admin; Filament access; storage; queues; scheduler; testing (PHP + browser/a11y); build/deploy; and a CMS usage guide written for the non-technical owner (editing the homepage, using the page builder, adding a project and case study, anonymising a project, publishing/scheduling/previewing, replacing the CV, configuring booking, managing navigation/footer, SEO fields, media and alt text, roles/permissions, reading contact submissions, removing demo content).
- **docs/architecture/:** ARCHITECTURE.md, ERD, DECISIONS.md (ADRs), PAGE-BUILDER.md ("how to add a new block type"), ANALYTICS.md ("how to swap provider / add an event"), SECURITY.md.
- **docs/design/DESIGN-SYSTEM.md**

---

## 20. PHASED EXECUTION PLAN

At the end of every phase:
1. update `docs/implementation/PROGRESS.md`;
2. run the test suite and Pint;
3. summarise what was done, what remains, and any risks.

**PHASE 1 — DISCOVERY**
Section 2. Read-only. Deliver `01-DISCOVERY.md`.

**PHASE 2 — ARCHITECTURE**
Section 3 plus the block registry (Section 4), Filament structure (Section 6), frontend component architecture (Section 11), routing (Section 3.4), authorization (Section 9), media (Section 7), SEO (Section 14), analytics (Section 13.3), and caching strategy.

Deliver `ARCHITECTURE.md`, the ERD, `DECISIONS.md`, and a draft requirements traceability matrix (`docs/implementation/TRACEABILITY.md`) mapping every PRD FR-01…FR-14, NFR-01…NFR-08, every §34 acceptance criterion, every §33 state, and every invariant in Section 0.4 to the planned implementation.

**STOP for approval.**

**PHASE 3 — FOUNDATION**
- install/upgrade packages (with compatibility checks);
- base folder structure and shared infrastructure (enums, publishing trait/scopes, sanitiser, SEO value objects);
- migrations and models with factories;
- auth hardening and Filament panel configuration (MFA if supported);
- roles/permissions and policies;
- settings classes and pages;
- media configuration;
- SEO infrastructure;
- redirects;
- security headers/CSP baseline;
- design tokens, Tailwind/Vite configuration, base layout and primitive components;
- `EssentialSeeder`.

**PHASE 4 — CMS**
Pages + block registry + section builder + preview; navigation; footer; homepage; Projects + categories/tags + case-study builder + confidentiality rules; Experience; Skills; Certifications; Articles + categories/tags/authors; Recommendations (with consent rule); Booking settings; CV & Documents; Contact Submissions inbox; Activity Log; dashboard widgets; `DemoContentSeeder`.

**PHASE 5 — PUBLIC WEBSITE**
All routes and system pages; all blocks' Blade components; listings with client-side filtering and empty states; detail templates with section navigation, related, prev/next, breadcrumbs; experience timeline; 404/500/503; responsive and mobile CTA bar; reduced motion.

**PHASE 6 — FORMS AND INTEGRATIONS**
Contact flow (all states, spam, rate limits, queued mail); booking embed + fallback + CSP; CV endpoints + fallbacks; analytics abstraction (client + server) with the full event catalogue; consent handling; sitemap/robots; scheduler tasks.

**PHASE 7 — QUALITY**
Full test suite; Pint; Larastan; `composer audit` / `npm audit`; `route:list` review (no collisions, all named); migration rollback sanity on a disposable database; permission matrix verification; N+1/lazy-loading checks and `EXPLAIN` on key queries; accessibility checks (axe/Lighthouse or Playwright suite); SEO checks (titles, H1s, JSON-LD validation, sitemap); performance checks (Lighthouse on Home, Project detail, Article detail; asset sizes; image formats); responsive checks at 375/768/1024/1440 with no horizontal scroll; production build (`npm run build`, caching commands) succeeds.

**PHASE 8 — FINAL REVIEW**
Compare the implementation against the ENTIRE PRD and this prompt. Finalise `docs/implementation/TRACEABILITY.md` and produce `docs/implementation/FINAL-REPORT.md` with a checklist table:

| Requirement (PRD §/ID or prompt §) | Status (Completed / Partially completed / Not completed) | Evidence (files, tests, routes) | Reason / follow-up |

Include:
- all §34 acceptance criteria;
- all §35 Definition of Done items, noting that "approved by the site owner" is pending owner review, not claimed;
- every Section 0.4 invariant;
- outstanding TODOs;
- known limitations;
- recommended next steps.

Be honest. Never claim completion for anything not implemented and verified.

---

## 21. ACCEPTANCE CRITERIA (FINAL GATE)

The work is complete only when ALL of the following are verified, or are explicitly reported as not completed with reasons:

1. All sitemap pages (§6.1) exist and are reachable via primary, footer, and mobile navigation. Additional CMS pages can be created and are routable.
2. Every page's content, SEO, and sections are editable in Filament. Sections can be added, removed, reordered, duplicated, and hidden.
3. Homepage renders all §9 sections from CMS data with correct CTA behaviour (at most 3 hero CTAs, correct visual hierarchy).
4. Projects, case studies, Experience, Skills, Certifications, Articles, and Recommendations can be added, edited, published, and removed with no code changes. Case studies are flexible and section-driven, with ownership clarity and graceful absent sections.
5. Navigation, footer, site settings, CTAs, SEO defaults, booking configuration, and analytics configuration are CMS-managed.
6. The CV can be replaced without deployment, opens for viewing first from all five locations, offers optional download, has a fallback, and is tracked.
7. The booking embed works from every placement, with a working fallback when it fails or is disabled.
8. The contact form handles all §19.2 states, validation, spam protection, rate limiting, storage, and notifications.
9. Recommendations preview renders correctly with zero (hidden), one, and many.
10. SEO is CMS-driven: unique metadata, one H1, OG/Twitter tags, valid JSON-LD, sitemap.xml, robots.txt, clean slugs, 301 redirects on slug change.
11. WCAG 2.1 AA: no critical axe/Lighthouse violations.
12. Core Web Vitals "Good" in Lighthouse for Home, a Project detail page, and an Article detail page (or documented results with remediation).
13. All §29 analytics events fire through the abstraction and the provider is swappable via configuration.
14. Fully responsive at all §25 breakpoints, with no horizontal scroll and 44px touch targets.
15. 404 and every §33 empty/error state render as designed.
16. The security requirements in Section 8 are implemented and tested. RBAC matches Section 9. Audit logging covers Section 10.
17. Confidential/anonymised projects never leak protected information.
18. No fabricated credentials, metrics, achievements, or recommendations are presented as real. Demo content is clearly labelled and excluded from production.
19. The test suite passes, Pint is clean, and static analysis passes at the documented level.
20. README and architecture documentation allow another developer to maintain the system and the owner to run the CMS.

---

## 22. GUIDING PRINCIPLE

**Content must be decoupled from presentation.** The site owner changes *what the website says* (hero, about, projects, case studies, experience, articles, certifications, recommendations, CTAs, navigation, footer, CV, SEO) in Filament. A developer changes *how the website works*. If any piece of professional content can only be changed by editing Blade, PHP, JS, or CSS, the implementation is not done.

**Begin now with PHASE 1 — DISCOVERY. Do not modify any code until Discovery and Architecture are documented and approved.**
