# Development Plan — Premium Taxi, Chauffeur & Airport Transfer Platform

**Brand:** MCR AeroLink (logo, name and colours are global — see section 6.6)  
**Project domain:** `cars.ronnieridge.com`  
**Plan date:** 12 October 2026  
**Status:** In development — phases are delivered one at a time after approval (section 35).  
**Source requirements:** [`doc/sts-cars-website-modules-features.md`](sts-cars-website-modules-features.md) (feature-parity baseline taken from <https://www.stscars.co.uk/>).

This document is the single source of truth for how the platform is built: stack, environment, architecture, design system, every module, database design, SEO, media library, performance, security, testing, deployment, and a phase-by-phase delivery plan. Development will proceed one phase at a time, and each phase is only started after explicit approval.

---

## Table of contents

1. [Goals and non-negotiable targets](#1-goals-and-non-negotiable-targets)
2. [Technology stack](#2-technology-stack)
3. [Server environment audit and required changes](#3-server-environment-audit-and-required-changes)
4. [Configuration, secrets, and database](#4-configuration-secrets-and-database)
5. [Application architecture](#5-application-architecture)
6. [Design system and UX direction](#6-design-system-and-ux-direction)
7. [Typography: Segoe UI and Selawik](#7-typography-segoe-ui-and-selawik)
8. [Mobile-first strategy](#8-mobile-first-strategy)
9. [Public website](#9-public-website)
10. [Quote, pricing, and booking engine](#10-quote-pricing-and-booking-engine)
11. [Payments and finance](#11-payments-and-finance)
12. [Customer panel](#12-customer-panel)
13. [Corporate accounts](#13-corporate-accounts)
14. [Admin panel](#14-admin-panel)
15. [Operations: fleet, drivers, dispatch, flights, driver app](#15-operations-fleet-drivers-dispatch-flights-driver-app)
16. [Notifications and communications](#16-notifications-and-communications)
17. [Support desk and CRM](#17-support-desk-and-crm)
18. [CMS and content modules](#18-cms-and-content-modules)
19. [Media Library (WordPress parity)](#19-media-library-wordpress-parity)
20. [SEO module (Rank Math Pro + Yoast Premium parity)](#20-seo-module-rank-math-pro--yoast-premium-parity)
21. [Performance engineering (98+ desktop / 95+ mobile)](#21-performance-engineering-98-desktop--95-mobile)
22. [Accessibility](#22-accessibility)
23. [Internationalization](#23-internationalization)
24. [Analytics, consent, and conversion tracking](#24-analytics-consent-and-conversion-tracking)
25. [Security and compliance](#25-security-and-compliance)
26. [Database schema](#26-database-schema)
27. [Routes and URL structure](#27-routes-and-url-structure)
28. [Third-party integrations](#28-third-party-integrations)
29. [Background jobs and scheduler](#29-background-jobs-and-scheduler)
30. [Testing and quality assurance](#30-testing-and-quality-assurance)
31. [Coding standards and workflow](#31-coding-standards-and-workflow)
32. [Deployment, backups, and monitoring](#32-deployment-backups-and-monitoring)
33. [Content and legal considerations](#33-content-and-legal-considerations)
34. [Decisions to confirm before or during development](#34-decisions-to-confirm-before-or-during-development)
35. [Development phases](#35-development-phases)
36. [Requirements traceability](#36-requirements-traceability)

---

## 1. Goals and non-negotiable targets

### 1.1 Product goal

Build a taxi, chauffeur, airport-transfer, minibus, courier, and corporate-travel platform that keeps **every** module, feature, and function listed in the features inventory, and is substantially better in backend capability (pricing, dispatch, finance, CRM, SEO, media) and frontend experience (premium, interactive, fast, mobile-first).

### 1.2 Hard targets

| Target | Requirement | How it is verified |
| --- | --- | --- |
| Lighthouse Performance, desktop | 98 or higher on every public template | Lighthouse CI, median of 5 runs |
| Lighthouse Performance, mobile | 95 or higher on every public template | Lighthouse CI, median of 5 runs, Moto G Power / slow 4G profile |
| Lighthouse SEO | 100 on every public page | Lighthouse CI gate |
| Internal SEO audit score | 100 on every published page | Built-in SEO audit engine (Rank Math/Yoast-style) |
| Lighthouse Accessibility | 100 target, never below 95 | Lighthouse CI + axe-core |
| Lighthouse Best Practices | 100 | Lighthouse CI |
| Core Web Vitals (field) | LCP < 2.0 s, INP < 150 ms, CLS < 0.05 | CrUX / RUM via `web-vitals` |
| Mobile first | Designed at 360 px first, enhanced upwards | Design review + browser tests at 360/390/768/1024/1440 |
| Fonts | Primary Segoe UI, secondary Selawik | Design tokens + font loading audit |
| Brand | MCR AeroLink name, supplied logo and logo colour scheme applied globally | `config/brand.php` + design tokens; brand sync test (section 6.6) |
| Media library | WordPress-equivalent in admin and frontend | Parity checklist in section 19 |
| SEO module | Rank Math Pro + Yoast Premium union | Parity checklist in section 20 |

Lighthouse lab scores fluctuate with network, CPU, and third-party script timing. The targets are therefore enforced as a **median of five runs** against a fixed profile in CI, and every public template ships with a performance budget (section 21.2) so regressions fail the build before they reach production.

### 1.3 Experience goal

The public site, customer panel, corporate panel, driver app, and admin panel must feel highly interactive, modern, premium, creative, and effortless — without sacrificing speed. Interactivity is achieved primarily with native browser capabilities (CSS animations, scroll-driven animations, View Transitions, Popover API, `<dialog>`) and small Alpine.js islands, so the visual ambition does not cost Lighthouse points.

---

## 2. Technology stack

### 2.1 Core

| Layer | Choice | Notes |
| --- | --- | --- |
| Language | PHP 8.5 | Installed on server: 8.5.11 (`ea-php85`) |
| Framework | Laravel 13 | Modular monolith |
| Database | MySQL-compatible — server runs **MariaDB 10.11** | Use Laravel's `mariadb` driver; schema stays MySQL-compatible |
| Cache / sessions / queues | Redis (server responding to `PING`) | Requires PHP `redis` extension (section 3) |
| Search (admin global search) | Laravel Scout, database engine | Upgrade path to Meilisearch if volume grows |
| Real-time | Laravel Reverb (WebSockets) + Laravel Echo | Polling fallback; see decision D-6 |
| PDF | `barryvdh/laravel-dompdf` | Invoices, receipts, statements |
| Images | `intervention/image` v3 (GD driver; Imagick when available) | GD on server supports WebP and AVIF |

### 2.2 Frontend

| Area | Choice | Reason |
| --- | --- | --- |
| Public website | Blade server-side rendering + Tailwind CSS v4 + Alpine.js islands | Minimal JavaScript, best Lighthouse results, fully crawlable HTML |
| Booking wizard | Livewire 4 component, lazy-loaded only on booking pages | Rich, stateful flow without a heavy SPA |
| Admin, customer, corporate panels | Livewire 4 + Alpine.js + Tailwind CSS v4, custom design system | Highly interactive, real-time, one coherent PHP/Blade codebase |
| Driver app | Mobile-first PWA built on the same Livewire stack, installable, offline shell | No app-store dependency |
| Rich text | TipTap editor (admin only) | Block content, SEO analysis hooks |
| Drag and drop | SortableJS (admin only) | Media ordering, menus, dashboards, dispatch board |
| Maps | Leaflet + vector/raster tiles, lazy-loaded | Admin dispatch map, customer live tracking; public pages use static route previews |
| Charts | Lightweight chart library (admin only), loaded per page | Dashboards and reports |
| Build | Vite with per-surface entry points | `public.js`, `booking.js`, `admin.js`, `account.js`, `driver.js` |
| Icons | Inline SVG sprite (Lucide/Tabler-style set) | No icon fonts |

Admin-only libraries are never shipped to public pages.

### 2.3 Key Laravel packages (latest versions compatible with Laravel 13)

| Purpose | Package |
| --- | --- |
| Authentication backend, 2FA, password reset | `laravel/fortify` |
| API tokens for driver PWA / integrations | `laravel/sanctum` |
| Roles and permissions | `spatie/laravel-permission` |
| Audit log | `spatie/laravel-activitylog` |
| Backups | `spatie/laravel-backup` |
| Honeypot spam protection | `spatie/laravel-honeypot` |
| Translations | `spatie/laravel-translatable` |
| Phone validation and formatting | `propaganistas/laravel-phone` |
| Stripe | `stripe/stripe-php` (Payment Intents, not Cashier) |
| CSV/Excel import and export | `spatie/simple-excel` |
| Monitoring | `laravel/pulse` (production), `laravel/telescope` (local/staging only) |
| Testing | Pest 4 (+ browser testing plugin), Larastan, Laravel Pint, Rector |

Final package versions are pinned in `composer.lock` during Phase 0 after confirming Laravel 13 compatibility. Any package that is not compatible is replaced by a first-party module rather than downgraded.

---

## 3. Server environment audit and required changes

### 3.1 Current state (audited 12 Oct 2026)

| Item | Found | Action |
| --- | --- | --- |
| Web server | Apache (cPanel, EasyApache 4) | Keep; tune `.htaccess`, enable Brotli/gzip and HTTP/2 |
| PHP CLI / handler | 8.5.11, `ea-php85` set for this domain | Keep |
| Composer | 2.9.3 | Keep |
| Node / npm | 25.9.0 / 11.12.1 | Keep for Vite builds |
| Database | MariaDB 10.11.19 | Use `mariadb` driver |
| Redis server | Running (`PONG`) | Use for cache, sessions, queues |
| PHP `redis` extension | **Missing** | Enable via EasyApache/MultiPHP (or use `predis/predis` as fallback) |
| PHP `intl` | **Missing** | Required (Laravel number/currency formatting, i18n). Enable |
| PHP `exif` | **Missing** | Required for media library (orientation, metadata stripping). Enable |
| PHP `imagick` | **Missing** | Recommended for higher-quality resizing/AVIF; GD fallback works. Enable if possible |
| GD | WebP + AVIF supported | Used by default image pipeline |
| ImageMagick CLI | 6.9.13 present | Optional fallback |
| `memory_limit` | 128M | Raise to 512M |
| `upload_max_filesize` / `post_max_size` | 2M / 8M | Raise to 64M / 72M (media library) |
| OPcache | Enabled, JIT disabled | Enable JIT (`tracing`), tune memory and file limits |
| Process supervisor | **Not available** | Queue workers via cron (existing pattern on this account); Reverb via watchdog or hosted alternative (D-6) |
| Cron | Available for user `ronnie` | Add scheduler + queue entries |
| Git | Project is not yet a repository | Initialise in Phase 0 |

### 3.2 Required directory layout

The domain document root is currently `/home/ronnie/public_html/cars.ronnieridge.com`. Laravel must only expose its `public/` folder.

- Laravel is installed at `/home/ronnie/public_html/cars.ronnieridge.com` (project root).
- The cPanel document root for `cars.ronnieridge.com` is changed to `/home/ronnie/public_html/cars.ronnieridge.com/public`.
- Until the document root is changed, everything in this folder — including `doc/`, `.env`, and `vendor/` — is web-reachable. This change is the first task of Phase 0.
- Existing files to preserve when installing: `.well-known/` (SSL validation), `doc/`, cPanel `.user.ini` / `php.ini` directives (moved into `public/` as required by the handler).
- All files must be owned by `ronnie:ronnie` (the current `doc/` folder is owned by `root` and will be corrected).

### 3.3 PHP configuration targets (`public/.user.ini`)

```ini
memory_limit = 512M
upload_max_filesize = 64M
post_max_size = 72M
max_input_vars = 5000
max_execution_time = 60
opcache.enable = 1
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 32
opcache.max_accelerated_files = 50000
opcache.jit = tracing
opcache.jit_buffer_size = 64M
realpath_cache_size = 4096K
realpath_cache_ttl = 600
expose_php = Off
```

### 3.4 Cron entries (user `ronnie`)

```cron
* * * * * cd /home/ronnie/public_html/cars.ronnieridge.com && /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1
* * * * * cd /home/ronnie/public_html/cars.ronnieridge.com && /usr/local/bin/php artisan queue:work redis --queue=critical,default,notifications,media,seo --stop-when-empty --max-time=55 >> /dev/null 2>&1
```

The exact PHP binary path is confirmed in Phase 0 so cron runs PHP 8.5 rather than another installed version.

---

## 4. Configuration, secrets, and database

### 4.1 Database

| Setting | Value |
| --- | --- |
| Driver | `mariadb` |
| Host | `127.0.0.1` |
| Port | `3306` |
| Database | `ronnie_cars` |
| Username | `ronnie_cars` |
| Password | Provided by the project owner; stored **only** in `.env` |
| Charset / collation | `utf8mb4` / `utf8mb4_unicode_ci` |
| Engine | InnoDB |
| Timezone | All timestamps stored in UTC; displayed in `Europe/London` |
| Money | Stored as integer minor units (pence) with a `currency` column (default `GBP`) |

The database password is intentionally **not** written into this document. `doc/` currently lives inside the public web root, and documentation is often committed to Git; writing credentials here would expose them. The password is entered once into `.env` during Phase 0, `.env` is excluded from Git, and the password should be rotated if it has been shared in any other plain-text location.

### 4.2 `.env` structure (no secret values)

```dotenv
APP_NAME="MCR AeroLink"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://cars.ronnieridge.com
APP_TIMEZONE=UTC
APP_DISPLAY_TIMEZONE=Europe/London
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_CURRENCY=GBP
SITE_INDEXABLE=false

DB_CONNECTION=mariadb
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ronnie_cars
DB_USERNAME=ronnie_cars
DB_PASSWORD=

CACHE_STORE=redis
SESSION_DRIVER=redis
SESSION_SECURE_COOKIE=true
QUEUE_CONNECTION=redis
REDIS_CLIENT=phpredis
BROADCAST_CONNECTION=reverb

MAIL_MAILER=
MAIL_FROM_ADDRESS=
STRIPE_KEY=
STRIPE_SECRET=
STRIPE_WEBHOOK_SECRET=
SMS_PROVIDER=
WHATSAPP_CLOUD_TOKEN=
FLIGHT_PROVIDER=
FLIGHT_API_KEY=
ADDRESS_PROVIDER=
ADDRESS_API_KEY=
ROUTING_PROVIDER=
ROUTING_API_KEY=
TURNSTILE_SITE_KEY=
TURNSTILE_SECRET_KEY=
GTM_ID=
GA4_MEASUREMENT_ID=
SEO_AI_API_KEY=
SEO_AI_API_URL=
SEO_AI_MODEL=
SEO_INDEXNOW_KEY=
SEO_GSC_ENABLED=false
SEO_DEFAULT_LOCALE=en-GB
SEO_TITLE_SEPARATOR=|
MEDIA_DISK=public
```

`SITE_INDEXABLE=false` forces `X-Robots-Tag: noindex, nofollow` and a disallow-all `robots.txt` on the live subdomain until launch, so unfinished pages are never indexed.

### 4.3 Settings stored in the database

Business values that staff must change without a deployment live in a typed `settings` table, cached in Redis: company details (NAP), contact numbers, emails, WhatsApp, opening hours, minimum online lead time, free airport waiting minutes, waiting rate, cancellation policy, festive surcharge dates, booking reference format, tax/VAT, payment methods, Stripe credentials (encrypted), finance numbering/bank details, notification toggles, social links, trust badges, cookie categories, and integration on/off switches. Prefer `.env` for production secrets when the host injects them; Admin → Settings → Payments may store encrypted Stripe secrets as an alternative.

---

## 5. Application architecture

### 5.1 Modular monolith

```
app/
  Core/                 Shared kernel: base models, value objects (Money, Address, GeoPoint), enums, traits, helpers
  Modules/
    Identity/           Users, auth, roles, permissions, sessions, 2FA, passkeys
    Settings/           Typed settings, company profile, feature flags
    Media/              WordPress-style media library
    Cms/                Pages, blocks, menus, notices, services, fleet content, FAQs, reviews, clients, awards, offers
    Blog/               Travel guide / blog posts, categories, tags, authors
    Seo/                Rank Math + Yoast parity module
    Geo/                Addresses, airports, terminals, zones, postcodes, routing, autocomplete
    Fleet/              Vehicle categories, vehicles, equipment, documents, maintenance
    Pricing/            Tariffs, rules, fixed routes, hourly packages, surcharges, promotions, quote engine
    Booking/            Quotes, bookings, legs, stops, passengers, extras, lifecycle, amendments, cancellations
    Payments/           Stripe, cash, PayPal, account billing, refunds, webhooks
    Finance/            Invoices, receipts, statements, credit control, reconciliation, VAT
    Customers/          Customer profiles, saved places, preferences
    Corporate/          Companies, members, cost centres, approvals, account managers
    Drivers/            Driver profiles, compliance, shifts, availability, earnings/settlement
    Dispatch/           Allocation, job board, live status, tracking, flight monitoring
    Notifications/      Templates, channels (mail, SMS, WhatsApp, push, in-app), delivery logs
    Support/            Enquiries, complicated journeys, cases, complaints, lost property
    Reports/            Dashboards, reports, exports
    Consent/            Cookie consent, marketing consent, legal document versions
    Integrations/       Provider adapters, webhook inbox, health checks
```

Each module contains its own `Models`, `Actions`, `Data` (DTOs), `Enums`, `Events`, `Listeners`, `Jobs`, `Policies`, `Http` (controllers, requests, resources), `Livewire`, `Views`, `Database/Migrations`, `Database/Factories`, `Database/Seeders`, `Routes`, and `Tests`. A module service provider registers its routes, views, migrations, policies, permissions, scheduled tasks, and admin navigation items.

### 5.2 Architectural rules

- **Actions** hold business logic (`CreateBooking`, `CalculateQuote`, `AssignDriver`, `RefundPayment`). Controllers and Livewire components stay thin.
- **DTOs** carry validated input between layers; **Form Requests** validate HTTP input.
- **Enums** for all statuses and types (booking status, payment status, vehicle class, notification channel).
- **State machine** for booking and payment lifecycles with explicit allowed transitions and an append-only status history.
- **Domain events** (`BookingConfirmed`, `DriverAssigned`, `PaymentSucceeded`, `FlightDelayed`) drive notifications, cache invalidation, SEO pings, and audit logs.
- **Provider adapters** (interfaces) for every third party: address lookup, routing, flights, SMS, WhatsApp, payments, email. Swapping providers never touches business code.
- **Policies** for every model; every admin route is permission-gated.
- **Idempotency keys** on booking creation, payment intents, and webhooks.
- **Money** value object prevents floating-point arithmetic.
- **Soft deletes** on content and customer-facing records; hard delete only through GDPR erasure workflow.
- **UUIDs/ULIDs** for public identifiers; auto-increment IDs never appear in URLs.

### 5.3 Application surfaces

| Surface | URL | Audience | Layout |
| --- | --- | --- | --- |
| Public website | `/` | Visitors | `layouts.public` |
| Booking flow | `/book/*` | Visitors and customers | `layouts.booking` |
| Booking lookup | `/my-booking` | Guests | `layouts.public` |
| Customer panel | `/account/*` | Registered passengers | `layouts.account` |
| Corporate panel | `/business/*` | Corporate members and admins | `layouts.account` (business mode) |
| Driver app (PWA) | `/driver/*` | Drivers | `layouts.driver` |
| Admin panel | `/admin/*` (path configurable) | Staff | `layouts.admin` |
| Public API | `/api/v1/*` | Quote widget, driver PWA, integrations | JSON |
| Webhooks | `/webhooks/{provider}` | Stripe, SMS, WhatsApp, flights | Signature-verified |

---

## 6. Design system and UX direction

### 6.1 Creative direction — MCR AeroLink "Flight Path"

The identity is taken from the MCR AeroLink logo: deep **navy** (wings, "MCR", hexagon), bright **sky blue** ("AeroLink", the flight arc) and a **teal** highlight (the swoosh). Surfaces are navy-tinted, typography is crisp and white on dark bands, and sky blue carries every primary action. The signature visual motif is the **route line** — an animated path connecting pickup and destination, echoing the logo's flight arc — which appears in the hero, quote results, booking confirmation, live trip tracking, and dispatch board, giving every surface the same recognisable identity.

Principles:

1. **Speed is a feature** — nothing decorative may delay the main content.
2. **One primary action per screen**, always within thumb reach on mobile.
3. **Show, don't tell** — live price, live route, live status instead of static text.
4. **Calm confidence** — generous whitespace, restrained motion, high contrast.
5. **Progressive disclosure** — simple first, power features one tap away.
6. **Every state designed** — empty, loading (skeleton), success, error, offline, and no-permission states.

### 6.2 Design tokens

Tokens live in Tailwind v4 `@theme` CSS variables and are shared by all surfaces. Light and dark themes are supported in all panels; the public site ships in light mode with a dark hero band (dark mode follows the user's system preference).

| Token group | Values (initial — refined in Phase 1) |
| --- | --- |
| Navy (brand primary, from logo) | `#132B52` (navy-900); 50–950 scale; dark surfaces `#050D1C`, `#0A1730`, `#10203F` |
| Sky (brand secondary, from logo) | `#4A8DCF` (sky-500); primary buttons use sky-600 `#3074B4` (hover `#1C5D98`) so white text meets AA; dark theme uses sky-400 `#6CA9E7` with navy text |
| Teal (brand highlight, from logo) | `#4FC2BF` (teal-400); decorative accents only |
| Ink (text) | `#0B1A33` (light theme), `#F2F6FC` (dark theme), muted `#4A5873` / `#A9B6CC` |
| Links, focus, info | Light: link `#1C5D98`, focus `#4A8DCF`; dark: link `#93C3F5`, focus `#6CA9E7` |
| Success / Warning / Danger | `#12B76A` / `#F79009` / `#F04438` |
| Neutral scale | 50–950, 11 steps |
| Radius | 6, 10, 14, 20, 28 px, full |
| Elevation | 5 shadow levels + coloured glow for primary CTA |
| Spacing | 4 px base grid |
| Breakpoints | 360 (base), 480, 640, 768, 1024, 1280, 1536 |
| Motion durations | 120 ms (micro), 200 ms (standard), 320 ms (emphasis), 480 ms (page) |
| Motion easing | `cubic-bezier(.2,.8,.2,1)` standard, spring-like emphasis curve |
| Z-index scale | base, dropdown, sticky, overlay, modal, toast, tooltip |

All colour pairs must meet WCAG 2.2 AA contrast (4.5:1 for body text, 3:1 for large text and UI components); 27 token pairs are tested in both themes. Brand colours are confirmed (decision D-1).

### 6.3 Signature interactions (performance-safe)

| Interaction | Where | Technique |
| --- | --- | --- |
| Animated route line drawing between pickup and destination | Hero, quote results, confirmation, tracking | Inline SVG `stroke-dashoffset` CSS animation |
| Live price "count-up" as options change | Quote results, booking summary | Alpine + `requestAnimationFrame`, respects reduced motion |
| Smart vehicle recommender (passengers + luggage steppers highlight the best-fit vehicle) | Homepage, fleet page, booking step 2 | Alpine island, no network calls |
| Seamless page transitions | Public site | Cross-document View Transitions API (zero JS, progressive enhancement) |
| Scroll-revealed sections and parallax depth | Public site | CSS scroll-driven animations (`animation-timeline: view()`), disabled for reduced motion |
| Mobile booking bottom sheet with snap points | Mobile public pages | `<dialog>` + CSS scroll snap |
| Sticky mobile "Get a quote" bar | All public pages on mobile | CSS `position: sticky`, appears after hero |
| Command palette (`Ctrl/⌘ + K`) | Admin, customer, corporate panels | Livewire + Alpine, global search and quick actions |
| Drag-and-drop dispatch board with live updates | Admin dispatch | SortableJS + Reverb events |
| Live trip timeline with driver ETA and map | Customer panel, booking lookup | Livewire + Echo + lazy Leaflet |
| Inline editing, optimistic UI, and undo toasts | Admin tables | Livewire + Alpine |
| Customisable dashboard widgets (drag, resize, hide) | Admin and corporate dashboards | SortableJS + per-user layout storage |
| Skeleton loaders and shimmer | All async UI | CSS only |
| Haptic-style micro-feedback (button press scale, success check animation) | All surfaces | CSS transforms |
| Guided onboarding tours | First login to admin/customer/driver panels | Lightweight tour component |
| Keyboard shortcuts with on-screen cheat sheet (`?`) | Admin | Alpine |

### 6.4 Component library

Built once as Blade components (with Alpine/Livewire behaviour where needed) and documented on an internal style guide page (`/admin/styleguide`):

- **Foundations:** container, grid, stack, cluster, section, divider, visually-hidden.
- **Typography:** display, heading (h1–h6), lead, body, caption, overline, code, prose wrapper.
- **Actions:** button (primary, secondary, ghost, danger, link, icon; sizes; loading state), button group, split button, floating action button.
- **Forms:** input, textarea, select, combobox/autocomplete, address autocomplete, date picker, time picker, date-time range, number stepper, phone input with country selector, checkbox, radio cards, toggle switch, segmented control, slider, file/media picker, colour picker, rich-text, tag input, OTP input, password with strength meter, form section, field error summary.
- **Navigation:** header, mega menu, mobile drawer, bottom tab bar, breadcrumbs, tabs, stepper/wizard progress, pagination, sidebar, command palette.
- **Feedback:** alert, banner/notice, toast, inline validation, progress bar, spinner, skeleton, empty state, error state, offline state.
- **Overlays:** modal (`<dialog>`), drawer/slide-over, bottom sheet, popover (Popover API), tooltip, dropdown menu, context menu, lightbox.
- **Data display:** card, stat card, badge, status pill, avatar, avatar group, timeline, description list, table (sortable, filterable, selectable, sticky header, column chooser, saved views, inline edit, bulk actions, export), kanban board, calendar (day/week/month), map, chart, key-value grid, accordion, carousel, logo marquee, rating stars, price display, route summary, vehicle card.
- **Booking-specific:** quote widget, journey type tabs, via-stop list (add/remove/reorder), airport direction toggle, vehicle comparison card, extras selector, passenger/luggage steppers, price breakdown, booking summary, trip status tracker.

### 6.5 Surface-specific UX

**Public website:** cinematic dark hero with quote widget above the fold, trust strip, interactive vehicle recommender, animated "how it works" route, rich route landing pages, instant FAQ search.

**Customer panel:** bottom tab bar on mobile (Trips, Book, Wallet, Profile), trip cards with live status, one-tap "Book again", saved places (Home, Work, favourite airports), passenger address book, wallet of invoices and receipts, add-to-calendar, share-trip link.

**Corporate panel:** company dashboard with spend charts, team management, cost centres, approval inbox, bulk booking (CSV upload), statements, invoice payment.

**Driver app:** large-touch-target job cards, swipe to accept/advance status, one-tap navigation hand-off (Google Maps/Waze/Apple Maps), waiting timer, offline-tolerant status queue, dark mode by default for night driving.

**Admin panel:** collapsible sidebar with module groups, command palette, global search, notifications centre, live dispatch board and map, split-view booking detail, saved filters, bulk actions, inline editing, keyboard shortcuts, activity timelines on every record, light/dark theme, density toggle (comfortable/compact).

### 6.6 Brand: MCR AeroLink (global)

| Item | Value |
| --- | --- |
| Brand name | **MCR AeroLink** (`APP_NAME`); short name "AeroLink" |
| Logo | Supplied artwork (`resources/images/brand/mcr-aerolink-logo.png`, transparent PNG) |
| Colours | Navy `#132B52`, Sky `#4A8DCF`, Teal `#4FC2BF` — sampled from the logo |
| Fonts | Segoe UI primary, Selawik secondary (section 7) |

Rules:

- **One source of truth.** The brand name, logo files, icons and colours are defined once in `config/brand.php`, and the colour palette once in `resources/css/core/theme.css`. A test fails if the two drift. Every page title, logo, email header, browser theme colour, favicon and the web app manifest read from these sources; no view hard-codes the name, a colour or a logo path.
- **Logo artwork.** "Light" artwork for light backgrounds; "dark" artwork (navy recoloured to near-white, sky and teal kept) for dark mode and dark sections. The `<x-logo>` component picks the right artwork automatically from the nearest theme and only downloads the visible one. Sizes: 160–640 px WebP (`srcset`), a PNG for email.
- **Mark and icons.** The mark (emblem with plane, no lettering) is used where space is tight (collapsed admin sidebar, mobile panel top bar). Favicons, the Apple touch icon and manifest icons use the compact emblem on white.
- **Company details** (legal entity, registration number, VAT number, address, phone, email, social links) are edited in Admin → Settings → Company and Contact, and are shown everywhere the brand appears.
- **Changing the logo later:** replace the source PNG, regenerate the assets, keep the same file names; uploading a logo from the Media Library becomes possible in Phase 3.

---

## 7. Typography: Segoe UI and Selawik

### 7.1 Font stack

```css
--font-primary: "Segoe UI", "Selawik", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
--font-secondary: "Selawik", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
```

- **Segoe UI (primary)** is a Microsoft system font. Its licence does not permit serving it as a web font, so it is referenced through `local()` / the font stack and renders natively on Windows devices (and wherever installed).
- **Selawik (secondary)** is Microsoft's open-source (SIL Open Font License) font, metric-compatible with Segoe UI. It is **self-hosted** as WOFF2 and acts as the visual fallback on macOS, iOS, Android, and Linux, so the design looks consistent everywhere.
- Secondary usage: Selawik is used directly for selected UI labels, numerals in price displays, and captions, as defined in the type scale.

### 7.2 Loading strategy (zero layout shift)

```css
@font-face {
  font-family: "Selawik";
  src: local("Selawik"), url("/fonts/selawik-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
```

- Only weights actually used are shipped (Light 300, Regular 400, Semibold 600, Bold 700), subset to Latin, WOFF2 only.
- Only the Regular weight is preloaded (`<link rel="preload" as="font" crossorigin>`), and only on templates where it is above the fold.
- A metric-adjusted fallback face (`size-adjust`, `ascent-override`, `descent-override`) prevents layout shift while fonts load.
- Fonts are served from the same origin with `Cache-Control: public, max-age=31536000, immutable`.

### 7.3 Type scale (fluid, mobile first)

| Token | Mobile | Desktop | Usage |
| --- | --- | --- | --- |
| `display` | 40 px | 72 px | Hero headline |
| `h1` | 32 px | 52 px | Page titles |
| `h2` | 26 px | 38 px | Section titles |
| `h3` | 21 px | 28 px | Card titles |
| `h4` | 18 px | 22 px | Sub-sections |
| `body-lg` | 18 px | 20 px | Lead text |
| `body` | 16 px | 17 px | Body text (never below 16 px on mobile) |
| `small` | 14 px | 14 px | Meta, captions |
| `micro` | 12 px | 12 px | Badges, overlines |

Sizes use `clamp()`; line-height 1.5 for body, 1.1–1.25 for headings; maximum line length about 70 characters.

---

## 8. Mobile-first strategy

- Every component is designed and built at **360 px** first; larger breakpoints only add layout enhancements (`min-width` media queries exclusively).
- Touch targets at least 44 × 44 px, with 8 px spacing between adjacent targets.
- Primary actions placed in the thumb zone (bottom of screen) on mobile: sticky quote bar, bottom sheets, bottom tab bars in panels.
- Inputs use correct `type`, `inputmode`, and `autocomplete` attributes (tel, email, postal-code, one-time-code, name).
- Native date/time pickers on mobile when they give a better experience; custom pickers on desktop.
- No hover-only functionality; every hover effect has a tap/focus equivalent.
- Images are responsive (`srcset`/`sizes`) with art direction for mobile heroes.
- Tables in panels collapse into card lists on small screens.
- Customer, corporate, and driver panels are installable PWAs (manifest, icons, service worker with offline shell).
- Browser tests run at 360, 390, 414, 768, 1024, 1280, and 1440 px widths.

---

## 9. Public website

### 9.1 Global shell (all public pages)

| Element | Requirements |
| --- | --- |
| Important notice | Admin-managed notices (message, severity, schedule, audience, target pages, linked page, dismiss mode: session/permanent/not dismissible). Rendered without layout shift (reserved space or overlay). |
| Cookie consent | Granular categories (necessary, preferences, analytics, marketing), accept all / reject all / customise, consent log, re-open from footer, scripts blocked until consent. |
| Top contact bar | Click-to-call, email, WhatsApp deep link with prefilled message, language selector, Login/Register, My Booking. Condensed on mobile. |
| Header | Logo, mega menu (Services, Airports & Locations, Fleet, Business, Help), primary CTA "Get a quote", account entry. Sticky with shrink-on-scroll. |
| Mobile navigation | Full-screen drawer with grouped links, contact shortcuts, account actions; bottom sticky quote bar. |
| Breadcrumbs | All inner pages, with BreadcrumbList schema. |
| Footer | Brand block, payment method icons, link groups (Services, Locations & Airports, Company, Help, Legal), contact block (NAP, phones, emails, WhatsApp), social links, trust badges, newsletter (optional), copyright, legal links, cookie settings link. |
| WhatsApp chat | Lightweight first-party floating button (no third-party widget script), opens WhatsApp deep link; optional pre-chat topic selector. |
| 404 / 410 / 500 / 503 pages | Branded, helpful search and links, logged for SEO 404 monitor. |

### 9.2 Homepage sections

1. Hero with headline, value proposition, and the full quote widget (Instant Quote / Hourly Hire tabs).
2. Trust strip (review platforms, awards, accreditations) — static optimised images, outbound links.
3. "How it works" — 5-step animated route (check price, choose vehicle, add details, pay securely, get confirmation and driver details).
4. Primary benefits grid (reputation, modern fleet, licensed drivers, fixed prices, meet & greet, 60 min free airport waiting, SMS updates, safety, no card fees).
5. Company introduction with internal links.
6. Fleet showcase with interactive vehicle recommender.
7. Airports and popular routes with "from £X" fixed prices.
8. Secondary benefits (cancellation policy summary, 24/7 support, child seats, flight tracking, tolls included, pay online or cash).
9. Reviews carousel (single instance — the reference site's duplicate section is not copied).
10. Commitment statement.
11. Clients served logo marquee.
12. Contact callout ("Talk to us") with quick enquiry and contact channels.
13. FAQ preview (top questions, FAQPage schema).
14. Final CTA band.

All sections are CMS blocks, so order, content, and visibility are editable.

### 9.3 Quote widget (homepage and landing pages)

**Instant quote tab**

- Pickup address (autocomplete, clear button, "use my location", recent/saved places for logged-in users, airport quick-picks).
- Airport direction toggle (from airport / to airport) that adapts labels and asks for flight details later.
- Via stops: add up to 5 (configurable), remove, drag to reorder, each with autocomplete.
- Destination (autocomplete, clear button, swap pickup/destination button).
- Pickup date and time (future-only, respects minimum online lead time).
- Journey type: one way / return; return date and time enabled only for return.
- Passengers and luggage steppers (optional at this stage, pre-filters vehicles).
- "Get prices" CTA.
- "Complicated journey?" link to manual-quote form.
- Lead-time guard: if pickup is inside the configured window (default 12 hours) or on a flagged event date, show call/WhatsApp CTAs or route to a "request booking" flow instead of instant booking.

**Hourly hire tab**

- Pickup address, date, time.
- Package selector (2 h / 25 miles … 24 h / 300 miles, admin-managed).
- Optional notes/itinerary.
- "Get prices" CTA.

**Technical behaviour**

- Works on fully cached HTML: no CSRF token is embedded. Quote requests go to a rate-limited, stateless JSON endpoint protected by Turnstile/honeypot and signed quote tokens.
- Address autocomplete script and requests load only on first focus of an address field.
- Widget state persists in `sessionStorage`, so navigating back keeps the user's input.

### 9.4 Public pages

| Page | Key content and features |
| --- | --- |
| Home | Section 9.2 |
| About Us | Story timeline (founded, growth, milestones), animated statistics counters, values, team/drivers highlight, reviews, CTA |
| Drivers & Fleet | Driver standards (experience, training, DBS, licensing, insurance), fleet categories with capacities, luggage guide with visual size comparison, fleet age policy, equipment (child seats), accessibility information |
| Fleet category detail (`/fleet/{slug}`) | Gallery, capacity, luggage, ideal-for, example prices, FAQ, book CTA |
| Services index | Service cards |
| Service detail (open an account, meet & greet, chip & pin, courier, match day, event/group, pay via card, corporate travel, student transfers) | Hero, benefits, how it works, pricing guidance, FAQs, related services, CTA, Service schema |
| Airport pages (Manchester, Liverpool, others) | Terminal guides, meet & greet points, waiting policy, flight tracking explanation, fixed fares table, popular routes, FAQ |
| Location pages (Manchester taxis, minibuses, executive) | Local content, coverage map, prices, FAQs |
| Route pages (`/routes/{from}-to-{to}`) | Distance, typical duration, fixed prices by vehicle, route map preview, travel tips, FAQs; replaces "travel guide" route articles with richer, programmatic-but-unique content |
| Travel Guide / Blog index, category, tag, author, post | Featured posts, search, related posts, reading time, table of contents, Article schema |
| Offers | Active offers, promo codes, terms, expiry countdown |
| FAQ | Categorised, instant search, deep-linkable answers, FAQPage schema |
| Online Booking Procedure | Step guide with screenshots, HowTo schema |
| Pick Up & Drop Off Procedure | Airport-specific instructions, terminal maps |
| Accreditations | Licences, certifications, awards with verification links |
| Contact Us | Departmental contacts, contact form, map (static image, interactive on tap), opening hours |
| Complicated Journey | Manual-quote form (name, email, phone, journey description, optional structured details: dates, passengers, luggage, stops, event) |
| Open an Account | Personal and business account comparison, application form |
| Login / Register / Forgot password / Reset password / Verify email | Section 12.1 |
| My Booking (lookup) | Reference + email lookup, secure magic link option |
| Booking flow | Section 10 |
| Legal: Terms, Privacy & Cookies, GDPR, Cancellation Policy, Accessibility Statement | Versioned legal documents with effective dates |
| HTML sitemap | All public pages grouped |
| Search results | Site search across pages, services, routes, FAQs, posts |

---

## 10. Quote, pricing, and booking engine

### 10.1 Journey types

Point-to-point, airport pickup, airport drop-off, one way, return, multi-stop (up to 5 vias, configurable), hourly hire, manual quote (complicated journey), event/group (multi-vehicle), match day, courier, and corporate account journeys.

### 10.2 Geo services

- **Address autocomplete** via provider adapter (UK-focused: Google Places API (New), Ideal Postcodes, getAddress.io, or Loqate), returning structured address, postcode, lat/lng, and place type.
- **Airport recognition** from an `airports` table (IATA code, terminals, geofence polygon/radius, meet & greet instructions, parking fees).
- **Routing** via provider adapter (Google Routes API, Mapbox, or OpenRouteService) returning distance, duration, and polyline; results cached by rounded coordinates.
- **Zones** (postcode district lists or polygons) for zone-to-zone and fixed fares.
- **Congestion/clean-air/toll zones** detected by polygon intersection with the route polyline.
- **Manual override** for unmapped addresses (admin can enter distance/price).

### 10.3 Pricing engine pipeline

Each vehicle category is priced through an ordered, configurable pipeline. Every step writes a line to the price breakdown so staff and customers can see exactly how a fare was built.

1. **Fixed route match** — airport/zone fixed fares override calculation when matched.
2. **Base fare** — booking fee per vehicle category.
3. **Distance charge** — tiered per-mile bands (e.g. 0–10, 10–50, 50+ miles).
4. **Time charge** — optional per-minute component.
5. **Vehicle class multiplier** — saloon, estate, executive, people carrier, 8-seat, 16-seat.
6. **Via stop charge** — per stop and/or extra distance.
7. **Airport charges** — drop-off/pickup fees, parking, meet & greet.
8. **Extras** — child seats, extra luggage, name board with logo, pets (if enabled).
9. **Time-of-day surcharge** — night/weekend rules.
10. **Date surcharge** — festive dates (default +50% Christmas/New Year), bank holidays.
11. **Event surcharge / availability rule** — event calendar entries (concerts, match days) can add surcharges or force "request" mode.
12. **Tolls and congestion** — included or added per configuration.
13. **Return journey** — second leg priced independently, optional return discount.
14. **Corporate tariff** — account-specific discounts or rate cards.
15. **Promotion** — promo codes and automatic offers with eligibility rules.
16. **Minimum fare and rounding** — rounding rule (e.g. nearest £1 / £0.50).
17. **Tax/VAT** — inclusive or exclusive display per settings.

**Hourly hire:** priced from hourly packages (hours, included miles, price per category, excess mileage rate, excess hour rate).

**Quote output:** fixed total per available vehicle, breakdown lines, included items, exclusions, availability state (instant / request / unavailable), quote expiry (default 30 minutes), signed quote token, cancellation policy summary.

**Admin tools:** pricing simulator (enter any journey, see full breakdown for all vehicles), rule priority and effective dates, change history with diff, and "test before publish" mode.

### 10.4 Booking wizard

| Step | Content |
| --- | --- |
| 1. Journey | Pre-filled from quote widget; editable route, date, time, return, vias |
| 2. Vehicle | Vehicle comparison cards (image, capacity, luggage, class, price, inclusions); passengers/luggage fit warning; smart recommendation badge |
| 3. Details | Lead passenger (title, first/last name, email, country + mobile), passenger count, luggage (suitcases, hand luggage, oversized items), flight number + airline + terminal (airport pickups), name-board text or logo upload, child seats (infant/child/booster with quantities), special requests, notes for driver |
| 4. Account | Continue as guest, log in, or create an account (password or magic link); corporate members choose cost centre, PO/reference, passenger from team list |
| 5. Payment | Pay now (card via Stripe with 3-D Secure, Apple Pay / Google Pay via Payment Request), pay driver (cash/card in car), or bill to account (corporate); promo code; Terms/Privacy consent; marketing opt-in (unticked) |
| 6. Confirmation | Reference, animated route summary, booking details, add to calendar (.ics), manage booking link, account creation prompt for guests |

Wizard features:

- Draft booking persisted server-side with a resumable token (refresh-safe, cross-device via email link).
- Abandoned-booking reminder email (only with consent).
- Price re-validated server-side at every step and at payment; the client never decides the price.
- Live summary sidebar (bottom sheet on mobile) with price count-up.
- Accessible step indicator with keyboard navigation and error summaries.

### 10.5 Booking references

Configurable prefix plus non-sequential, unambiguous code (e.g. `RC-7K4M2Q`, Crockford Base32). Lookup requires reference + email, is rate-limited, and can issue a time-limited magic link instead of exposing details directly.

### 10.6 Booking lifecycle (state machine)

`draft → quoted → pending_payment → confirmed → awaiting_allocation → allocated → driver_accepted → en_route → arrived → passenger_on_board → completed`

Side states: `request_pending` (manual approval), `amended`, `cancelled_by_customer`, `cancelled_by_operator`, `no_show`, `payment_failed`, `refunded`, `partially_refunded`.

Every transition records actor, timestamp, reason, and metadata in `booking_status_histories`, and emits a domain event.

### 10.7 Amendments and cancellations

- **Cancellation policy engine** with configurable tiers (for example: more than 48 hours before pickup free; 48–24 hours a percentage fee; less than 24 hours non-refundable). This resolves the reference site's contradictory messaging by making one policy the source for the website, emails, and calculations.
- Customer self-service amend/cancel within policy windows (from My Booking or customer panel); outside the window, an amendment request goes to the support queue.
- Price difference handling for amendments (additional charge or partial refund).
- SMS cancellation not accepted (parity); cancellations via portal, email, or phone (staff).
- Full audit trail and notifications for every amendment or cancellation.

### 10.8 Business rules (all configurable)

- Minimum online lead time (default 12 hours); inside the window, show contact CTAs or accept as request.
- Event dates require availability confirmation (request mode).
- Free airport waiting minutes (default 60) and waiting rate (pro rata per hour).
- Flight delays tracked; no delay surcharge when flight tracking applies.
- No online card fees.
- Tolls included in fixed fares.
- Child seats subject to inventory availability.
- Accessibility needs captured as structured data; vehicle capabilities modelled explicitly.

---

## 11. Payments and finance

### 11.1 Payment methods

| Method | Implementation |
| --- | --- |
| Card online | Stripe Payment Intents + Payment Element, Strong Customer Authentication / 3-D Secure |
| Apple Pay / Google Pay | Stripe Payment Request / Express Checkout Element |
| Saved cards (customer panel) | Stripe Customer + SetupIntent; card data never stored locally |
| Pay driver (cash or in-car card) | Marked as `pay_on_journey`; driver records settlement in driver app |
| PayPal | Optional (manual "request PayPal link" as on the reference site, or Stripe-supported wallet if enabled) |
| Corporate account | Invoice billing; BACS/bank transfer; Direct Debit via Stripe Bacs Direct Debit (optional) |
| Payment links | Staff can send a secure payment link for phone bookings or amendments |

### 11.2 Payment workflow

1. Create Payment Intent with idempotency key tied to the booking draft.
2. Confirm on client with 3-D Secure challenge if required.
3. Booking confirmed only after the `payment_intent.succeeded` webhook (or synchronous confirmation verified server-side).
4. Handle `requires_action`, failure, cancellation, timeout, and retry states with clear UI.
5. Webhook inbox stores every event (signature verified), processed idempotently by a queued job.
6. Refunds (full/partial) initiated from admin with reason, synced back via webhooks.

### 11.3 Finance module

- Invoices (numbered sequences, PDF, email, download, print), receipts after completion, credit notes.
- Corporate statements, aged debt, credit limits, payment terms, reminders/dunning.
- Driver settlement for cash jobs and commission (if applicable).
- Reconciliation reports by method, date, and Stripe payout.
- VAT configuration and VAT-compliant invoice fields.
- Exports (CSV, and accounting-system friendly formats).

---

## 12. Customer panel

### 12.1 Authentication

- Register: title, first name, last name, email, password (strength meter, breached-password check), country, auto-derived calling code, mobile, Terms/Privacy acceptance, optional marketing consent.
- Login with email + password, "remember me", rate limiting, lockout notifications.
- Optional passwordless magic link and passkeys (WebAuthn).
- Optional two-factor authentication (TOTP + recovery codes).
- Email verification, forgot/reset password (non-enumerating, single-use, expiring tokens), session list with "log out other devices".
- Social login (Google/Apple) — optional, decision D-9.

### 12.2 Features

| Area | Features |
| --- | --- |
| Dashboard | Next trip hero card with live status, quick book, recent trips, notifications |
| Trips | Upcoming / past / cancelled tabs, trip detail with route map, status timeline, driver and vehicle (when assigned), live tracking on the day, amend, cancel, rebook, add to calendar, share status link, download receipt |
| Book | Full booking wizard with saved data pre-filled |
| Saved places | Home, Work, custom labels, favourite airports |
| Passengers | Saved passengers (family, guests) for quick booking |
| Preferences | Default vehicle, child seats, favourite driver request, special requirements, communication channel preferences |
| Wallet | Saved cards, invoices, receipts, payment history, outstanding payments (pay now) |
| Support | Raise a request (booking query, lost property, complaint), view case status and replies |
| Profile and security | Personal details, password, 2FA, passkeys, sessions, marketing consent, download my data, delete account request |

---

## 13. Corporate accounts

- **Application:** public form (company, registration number, VAT, billing address, contact person, expected volume, payment preference) → admin approval workflow → welcome email and account manager assignment.
- **Company structure:** company admins, bookers, travellers; invitations; role permissions.
- **Billing:** credit limit, payment terms, invoice frequency (per trip / weekly / monthly), statements, online invoice payment.
- **Booking controls:** cost centres, mandatory PO/reference fields, approval rules (e.g. above amount or outside hours), allowed vehicle classes, priority booking flag, business-class upgrade when available.
- **Bulk booking:** CSV upload with validation preview.
- **Reporting:** spend by traveller, cost centre, period; exportable.
- **Account manager:** dedicated contact displayed in the panel, with direct messaging.

---

## 14. Admin panel

### 14.1 Shell and global features

- Collapsible sidebar grouped by module, favourites, recent items.
- Command palette (`Ctrl/⌘ + K`): search bookings, customers, drivers, pages; quick actions (new booking, new page, upload media).
- Global notifications centre (real-time) and activity feed.
- Light/dark theme, density toggle, personalised dashboard.
- Every list: search, filters, saved views, column chooser, sorting, bulk actions, export (CSV/XLSX), inline editing where safe, pagination or infinite scroll.
- Every record: activity timeline (audit log), notes, attachments (media library), related records.
- Impersonation of customers/drivers for support (permission-gated, fully logged, visible banner).

### 14.2 Navigation (information architecture)

1. **Dashboard** — today's pickups and returns, unallocated jobs, delayed flights, failed payments, revenue today/week/month, enquiry backlog, driver availability, conversion funnel.
2. **Dispatch** — live board, map, calendar, allocation queue.
3. **Bookings** — all bookings, requests pending approval, quotes, abandoned drafts, amendments, cancellations.
4. **Customers** — passengers, corporate accounts, applications.
5. **Drivers** — profiles, compliance, shifts, availability, settlements.
6. **Fleet** — vehicle categories, vehicles, equipment, documents, maintenance.
7. **Pricing** — tariffs, fixed routes, zones, hourly packages, surcharges, event calendar, promotions, simulator.
8. **Finance** — payments, refunds, invoices, statements, credit control, reconciliation, VAT.
9. **Support** — enquiries, complicated journeys, cases, complaints, lost property.
10. **Content** — pages, homepage builder, services, airports, locations, routes, fleet content, FAQs, reviews, clients, awards, offers, notices, menus, footer, legal documents.
11. **Blog / Travel Guide** — posts, categories, tags, authors.
12. **Media** — media library.
13. **SEO** — hub (overview, settings, audits, 404 monitor, redirects, keywords, bulk editor, workouts, schema, sitemaps, robots/llms.txt, reports).
14. **Communications** — templates (email/SMS/WhatsApp/push), delivery logs, broadcast messages.
15. **Reports** — section 14.4.
16. **Settings** — company, booking rules, cancellation policy, notifications, integrations, consent, appearance, localisation, users, roles & permissions, API keys, backups, system health.

### 14.3 Roles (default)

Super Admin, Admin, Operations Manager, Dispatcher, Booking Agent, Finance, Content Editor, SEO Manager, Support Agent, Account Manager, Driver (driver app only), Customer, Corporate Admin, Corporate Booker, Corporate Traveller. Permissions are granular (`module.action`), and roles are editable in the UI.

### 14.4 Reports

Booking volume and conversion funnel, quote-to-booking rate, abandoned bookings, revenue and average booking value, routes/airports/service types, vehicle category mix, fleet utilisation, driver performance and punctuality, cancellations and no-shows, payment methods/failures/refunds, corporate spend, support response/resolution times, marketing attribution (UTM/source), SEO traffic (when GSC connected). All reports filterable by date range and exportable; scheduled email delivery of reports.

---

## 15. Operations: fleet, drivers, dispatch, flights, driver app

### 15.1 Fleet

- Vehicle categories: name, slug, class (economy/executive), passenger capacity, suitcase and hand-luggage capacity, images (media library), description, features, accessibility capabilities, display order, pricing link.
- Vehicles: registration, make, model, year, colour, category, capacity, status (active/maintenance/retired), assigned driver(s), photos.
- Compliance documents with expiry and reminders: insurance, MOT, council licence plate, V5, inspection.
- Maintenance schedule and history.
- Fleet age policy checks (economy ≤ 7 years, executive ≤ 4 years — configurable warnings).
- Equipment inventory: infant/child/booster seats by vehicle/base; availability used by booking engine.

### 15.2 Drivers

- Profile: personal details, photo, contact, languages, base location, vehicle(s), rating.
- Compliance: driving licence, private hire licence (council), enhanced DBS (date, recheck due), right to work, insurance, training records — with expiry tracking, reminders, and auto-suspension on expiry.
- Shifts and availability calendar; time off.
- Job history, performance (acceptance rate, punctuality, complaints, ratings), notes.
- Settlement: cash collected, commission, payouts (if applicable).

### 15.3 Dispatch

- **Live board** (kanban by status: unallocated, allocated, accepted, en route, arrived, on board, completed) with drag-and-drop allocation.
- **Map view** with drivers' last-known locations and active jobs.
- **Calendar/timeline** view per driver and vehicle.
- **Allocation:** manual, suggested (nearest available driver with correct vehicle and compliance), and optional auto-offer to drivers with acceptance timeout.
- **Conflict detection:** overlapping jobs, insufficient capacity, expired compliance, missing child seats.
- **Alerts:** unallocated jobs near pickup time, flight delays/early arrivals, driver not en route, waiting time running, no-show.
- **Group/event coordination:** multi-vehicle bookings linked under one group.
- **Waiting time and no-show** handling with automatic charge calculation per policy.

### 15.4 Flight tracking

- Flight number validated at booking; provider adapter (e.g. FlightAware AeroAPI, Aviationstack, Cirium/OAG).
- Scheduled polling for flights within a time window (e.g. from departure until landing), with webhooks where the provider supports them.
- Pickup time automatically adjusted to actual landing time + configured buffer; dispatcher and driver notified of changes.
- Flight status shown on booking, dispatch board, and driver job.

### 15.5 Driver app (PWA)

- Login with 2FA, installable, dark mode default.
- Today/upcoming job list; job detail with passenger name, phone (masked number option), flight status, name board text/logo, notes, child seats, luggage, payment method, price (if configured).
- Accept/decline offers with countdown.
- Status buttons: en route → arrived → passenger on board → completed (with confirmation), no-show reporting with evidence (photo/time).
- Waiting timer; extra stops/expenses entry (parking, tolls) with receipt photo.
- Navigation hand-off to Google Maps / Waze / Apple Maps.
- Location sharing while on an active job (foreground geolocation, battery-aware intervals).
- Cash collection recording, end-of-shift summary, earnings.
- Offline queue: status updates stored locally and synced when connectivity returns.
- Push notifications for new offers and changes.

### 15.6 Real-time

Laravel Reverb broadcasts booking status, driver location, dispatch updates, and notifications on private channels with authorisation. If WebSockets are unavailable, Livewire polling (configurable interval) provides the same data with higher latency.

---

## 16. Notifications and communications

### 16.1 Channels

Email (transactional provider such as Postmark, Amazon SES, or Mailgun), SMS (Twilio, Vonage, or a UK aggregator), WhatsApp Business Cloud API (approved templates), web push (PWA), and in-app (database) notifications.

### 16.2 Notification catalogue

| Event | Customer | Staff | Driver |
| --- | --- | --- | --- |
| Account registered / verify email / password reset / new device login | Email | — | — |
| Quote saved / abandoned booking reminder (consent) | Email | — | — |
| Booking confirmed | Email + SMS | In-app | — |
| Booking request received / approved / declined | Email | In-app + email | — |
| Booking amended / cancelled | Email + SMS | In-app | Push (if assigned) |
| Payment succeeded / failed / refund issued / payment link | Email | In-app (failures) | — |
| Driver assigned (approx. 2 hours before pickup, configurable) | SMS + email (+ WhatsApp opt-in) | — | Push |
| Driver en route / arrived | SMS / push | — | — |
| Flight delayed / pickup time adjusted | SMS | In-app | Push |
| Journey completed + receipt + review request | Email | — | — |
| Invoice / statement / overdue reminder | Email | — | — |
| Enquiry received / replied | Email | In-app + email | — |
| Corporate application received / approved | Email | In-app | — |
| Compliance document expiring | — | Email + in-app | Push + email |
| Job offered / changed / cancelled | — | — | Push + SMS fallback |

### 16.3 Template management

- Editable templates per channel and locale with variables (`{{ booking.reference }}`, etc.), preview with sample data, test-send, version history.
- Branded responsive email layout (dark-mode friendly).
- Trigger configuration and per-event channel toggles.
- Delivery logs with status (queued, sent, delivered, failed, bounced), provider message IDs, retries, and resend.
- Quiet hours and customer channel preferences; marketing vs transactional separation; unsubscribe handling.

---

## 17. Support desk and CRM

- **Inbox** for contact form, complicated-journey enquiries, corporate applications, lost property, complaints, amendment requests, and email-to-ticket (optional inbound parsing).
- Each case: reference, type, priority, status (new, open, awaiting customer, resolved, closed), assignee, SLA timers, linked booking/customer, internal notes, public replies (email), attachments.
- **Convert enquiry to quote/booking** with prefilled data.
- **Canned replies** and templates.
- **Lost property register:** item, booking, found by, storage location, status, return method.
- **Complaints/feedback:** category, severity, resolution, linked driver for performance tracking.
- **Customer 360 view:** bookings, payments, cases, notes, communications, consent.
- **Review requests** after completed journeys, with internal rating and optional redirect to public review platforms.

---

## 18. CMS and content modules

### 18.1 Page builder

- Block-based pages (stored as structured JSON + rendered HTML cache): hero, rich text, image, gallery, video (lite embed facade), feature grid, steps, stats counters, fleet cards, vehicle recommender, price table, route list, airport cards, testimonials, logo marquee, FAQ (with schema), CTA band, contact block, quote widget, map preview, tabs, accordion, columns, spacer, custom HTML (restricted permission).
- Drag-and-drop block ordering, live preview at mobile/tablet/desktop widths, reusable/global blocks, page templates.
- Draft, scheduled publishing, published, archived; revision history with compare and restore; autosave; preview links for unpublished content.
- Every block renders semantic, accessible, performance-budgeted HTML.

### 18.2 Content types

| Type | Fields (beyond title, slug, status, SEO, timestamps) |
| --- | --- |
| Page | Blocks, template, parent (hierarchy), featured image |
| Service | Summary, icon, hero, blocks, FAQs, related services, schema type |
| Airport | IATA, terminals, meet & greet points, maps, fees, fixed fares, FAQs |
| Location | Area, coverage, blocks, featured routes, FAQs |
| Route | From/to places, distance, duration, price table (auto from pricing), unique content blocks, FAQs |
| Fleet category content | Linked to Fleet module category; gallery, features |
| Post (Travel Guide / Blog) | Excerpt, content (TipTap), categories, tags, author, featured image, reading time, table of contents, related posts, cornerstone flag |
| FAQ | Question, answer, category, order, show on pages |
| Review / testimonial | Name, rating, text, source, date, photo, moderation status, order |
| Client logo | Name, logo, URL, order, active |
| Award / accreditation | Name, issuer, year, image, verification URL |
| Offer | Title, description, promo code link, validity, terms, banner image |
| Notice | Message, severity, schedule, audience, pages, dismiss mode |
| Legal document | Type, version, effective date, content, change summary; acceptance tracked |
| Team member (optional) | Name, role, photo, bio |

### 18.3 Menus and footer

Visual menu builder (drag-and-drop, nesting, mega-menu columns, icons, badges), multiple menu locations (header, mobile, footer columns, legal), footer widget areas, social links, payment icons.

### 18.4 Forms

Contact, complicated journey, corporate application, lost property, feedback — all with server-side validation, honeypot + Turnstile (loaded on interaction), rate limiting, consent capture, auto-acknowledgement email, routing to the support inbox, and spam log.

---

## 19. Media Library (WordPress parity)

A first-party module modelled on the WordPress Media Library (`upload.php`, the `wp.media` modal, attachment details, and the image editor), extended with modern image delivery.

### 19.1 Admin: library screen

- **Views:** grid view (thumbnails) and list view (table), toggled and remembered per user.
- **Filters:** media type (images, audio, video, documents, spreadsheets, archives, unattached, mine), date (month/year dropdown, as in WordPress), uploaded by, folder, tags.
- **Search** by title, filename, alt text, caption, description.
- **Infinite scroll** in grid view; pagination in list view.
- **Upload:** "Add New" button, drag-and-drop onto the whole page, multi-file upload, per-file progress bars, chunked uploads for large files, client-side pre-validation (type, size), server-side MIME sniffing.
- **Bulk select mode:** select multiple, delete permanently or move to trash, move to folder, tag, download as ZIP.
- **Attachment details modal** (grid view): large preview, previous/next navigation with arrow keys, editable title, caption, alt text, description; read-only uploaded on, uploaded by, uploaded to (linked parent), file name, file type, file size, dimensions, file URL with "Copy URL to clipboard"; links to view attachment page, edit more details, download file, delete permanently.
- **Edit media screen** (full page) with the same fields plus usage list.
- **Folders** (virtual, nested, drag items into folders) — a common WordPress plugin feature, included natively.
- **Trash** with restore and auto-empty after a configurable number of days.
- **Usage tracking:** "Used in" list showing every page, post, service, vehicle, etc. that references the file; warning before deleting media that is in use.
- **Replace media:** upload a new file keeping the same ID/URL (or new URL with automatic redirect), regenerating all sizes.
- **Duplicate detection** by file hash on upload, offering to reuse the existing item.

### 19.2 Admin: image editor

WordPress-equivalent "Edit Image" plus enhancements:

- Crop (free, preset aspect ratios such as 1:1, 4:3, 16:9, 3:2, custom), rotate left/right, flip horizontal/vertical, scale (with proportional dimensions), undo/redo.
- "Apply changes to": all image sizes, thumbnail only, all sizes except thumbnail (as in WordPress).
- Restore original image (originals are always kept).
- **Focal point** picker used for automatic art-directed crops.
- Regenerate thumbnails (single, bulk, all) via queued jobs with progress.

### 19.3 Media modal (picker) used everywhere

Equivalent to `wp.media` frames:

- Tabs: **Upload files**, **Media Library**, **Insert from URL**.
- Single and multiple selection modes; selection limit; allowed types per field.
- Sidebar attachment details editable inside the modal.
- **Featured image** frame for content types.
- **Gallery** frame: create/edit gallery, drag to reorder, columns, link to (none / media file / attachment page), image size, captions, random order.
- **Insert into content** from the rich-text editor with alignment, size, link, and caption options.
- Reusable Blade/Livewire component: `<x-media-picker name="featured_image_id" :multiple="false" accept="image/*" />`.

### 19.4 Image processing pipeline

- Registered image sizes (configurable in settings, like `add_image_size`): thumbnail 150×150 (crop), medium 300, medium_large 768, large 1024, xl 1600, 2xl 2048, plus named sizes (`hero`, `card`, `og` 1200×630, `logo`).
- Every size generated in **AVIF and WebP** plus the original format as fallback.
- EXIF orientation applied and metadata stripped (GPS removed for privacy), colour profile converted to sRGB.
- Blurred low-quality placeholder (LQIP) or dominant colour computed and stored.
- Large originals capped (e.g. 2560 px "big image" threshold, as in WordPress) while keeping the true original.
- Processing runs on the `media` queue; UI shows processing status.
- **SVG** uploads allowed only for permitted roles and sanitised.
- Video: poster frame upload, optional transcoding if a provider/ffmpeg becomes available; video metadata for VideoObject schema.
- Documents (PDF, DOCX, XLSX) supported with icons and optional PDF first-page preview.
- Storage via Laravel filesystem disks (local `public` by default; S3-compatible/CDN offload configurable).

### 19.5 Frontend delivery

- `<x-image>` component renders `<picture>` with AVIF/WebP sources, `srcset`, `sizes`, explicit `width`/`height`, `alt`, `decoding="async"`, and `loading="lazy"` (or `fetchpriority="high"` and eager loading for the LCP image).
- Focal-point-aware cropping via CSS `object-position`.
- Placeholder background (LQIP/dominant colour) to avoid visual pop-in.
- Lightbox for galleries (lightweight, keyboard accessible).
- **Attachment pages** (`/media/{slug}`) as in WordPress, with the Yoast/Rank Math option to redirect attachment URLs to the file or parent (default: redirect, to avoid thin content).
- Image sitemap entries with title/caption.
- Long-lived immutable caching with content-hashed filenames.

### 19.6 Frontend (customer-facing) uploads

Like WordPress's frontend `wp.media` for logged-in users, but restricted:

- Customers/corporate users: upload name-board logos, profile photos, corporate logos, documents for support cases.
- Drivers: compliance documents, receipts, no-show evidence photos.
- Each user sees only their own uploads ("Mine" library); private files stored on a non-public disk and served via signed, expiring URLs.
- Per-role type and size limits, virus/malware scanning hook (ClamAV if available), and quota per user.

---

## 20. SEO module (Rank Math Pro + Yoast Premium parity)

Built as `app/Modules/Seo`, implementing the union of Rank Math Pro/Business and Yoast SEO Premium (including Local, News, and Video patterns), adapted from WordPress to Laravel.

### 20.1 Editor experience (every content create/edit form)

- Tabs: **General | Social | Schema | Advanced**.
- Focus keyphrase, up to 4 synonyms, related/secondary keyphrases, word-form and variant matching in analysis.
- Live **Google SERP preview** (desktop and mobile, with title/description pixel-width meters), **Facebook preview**, **X/Twitter preview**.
- SEO title and meta description with `%%variable%%` templates (`%%title%%`, `%%sep%%`, `%%sitename%%`, `%%category%%`, `%%from%%`, `%%to%%`, `%%price_from%%`, etc.).
- Live **SEO score** (0–100) with pass/warning/fail checks and one-click "fix" guidance; **readability score** (Flesch reading ease, sentence length, paragraph length, passive voice, transition words, subheading distribution, consecutive sentences).
- Content checks: keyphrase in title, slug, first paragraph, subheadings, image alt; keyphrase density; content length; internal and external links; image presence; unique keyphrase across site; title and description length; single H1; heading hierarchy.
- Internal link suggestions while editing; orphan warning.
- Cornerstone content flag; breadcrumb title override.
- Canonical URL, robots directives (index/noindex, follow/nofollow, noarchive, nosnippet, max-snippet, max-image-preview, max-video-preview), include in sitemap, sitemap priority/changefreq.
- Social title, description, image (media picker) per network.
- Schema type picker and custom JSON-LD override with validation.
- News and video fields where relevant.
- **AI assistance** (when `SEO_AI_API_KEY` is set): generate titles, meta descriptions, FAQs, alt text; content optimisation suggestions; offline rule-based fallbacks when AI is not configured.
- **Publish gate:** in strict mode (default on), a page cannot be published until its SEO audit scores 100 and its Lighthouse-SEO checks pass in pre-flight; editors see exactly which checks remain.

### 20.2 Technical SEO runtime

- `SeoMetaResolver` renders title, description, canonical, robots, hreflang, Open Graph, Twitter cards, and JSON-LD on every public page, with sensible auto-generated defaults so no page is ever missing metadata.
- Title templates per content type with variables; separator setting.
- **XML sitemaps:** index + per type (pages, services, airports, locations, routes, fleet, posts, categories, tags, authors, images, video, news); honours noindex/exclude; `lastmod` from real updates; cached and invalidated on content events; XSL stylesheet for human viewing.
- **robots.txt** editor with validation; AI crawler toggles (GPTBot, CCBot, Google-Extended, etc.).
- **`/llms.txt`** generator and editor.
- **IndexNow** pings on publish/update/delete (when key set); Google Search Console connector (env-ready).
- Hreflang for every locale; `x-default`.
- Pagination handled with self-referencing canonicals.
- Trailing-slash and lowercase URL normalisation; duplicate-content protection (query-string canonicalisation, UTM stripping in canonical).
- Breadcrumbs (UI + schema) with configurable labels.
- Attachment URL redirection option (section 19.5).
- RSS feed for blog with SEO-safe settings.

### 20.3 Schema (JSON-LD graph)

Organization / `TaxiService` / `LocalBusiness` (NAP, geo, opening hours, area served, price range, sameAs), WebSite (+ SearchAction), WebPage, BreadcrumbList, Service (+ Offer / AggregateOffer for fixed fares), FAQPage, HowTo, Article / BlogPosting / NewsArticle, Person (authors), ImageObject, VideoObject, ItemList, Review / AggregateRating (only where eligible under Google guidelines — self-serving LocalBusiness ratings do not produce rich results), Event (match days/events where relevant), Speakable, Airport references. Schema templates table, per-content type defaults, per-page overrides, validation against schema.org types.

### 20.4 Redirects and 404 monitor

- Redirect CRUD with 301, 302, 307, 308, 410, and 451; exact, prefix, and regex matching with capture group replacement (`$1`); hit counts and last-hit date; enable/disable; notes.
- **Automatic redirect on slug change** and on content deletion (choose target or 410).
- CSV import/export.
- **404 monitor** logging URL, referrer, user agent, hits, first/last seen; ignore rules; one-click "create redirect".
- **Legacy URL map** preloaded for all reference-site URL variants (section 27.3).
- Redirect chain and loop detection.

### 20.5 Site structure and workouts

- Internal linking suggestions and link counts (inbound/outbound) per page.
- Orphaned content workout, cornerstone workout, stale content workout, missing meta workout, thin content workout.
- Broken link checker (internal and external, queued).
- Duplicate title/description detector.

### 20.6 Keywords and reporting

- Keyword rank tracker (CRUD, manual entry, history, daily snapshot job, winners/losers reports).
- GSC-connected reports (clicks, impressions, CTR, position) when enabled.
- White-label PDF/email SEO reports.

### 20.7 Local SEO

- Business locations (NAP, geo, opening hours, service area, phone) with consistency audit across the site and schema.
- Area/route landing page generator with uniqueness checks (required unique content blocks so programmatic pages are not thin).

### 20.8 Bulk tools and permissions

- Bulk SEO editor (inline edit titles, descriptions, focus keyphrases, robots across all content types).
- SEO meta CSV import/export.
- Audit-all job across all content types, scheduled nightly; site-wide SEO health dashboard.
- Permissions: `seo.view`, `seo.create`, `seo.update`, `seo.delete`, `seo.settings`, `seo.redirects`.

### 20.9 SEO admin hub tabs

Overview | Settings | Audits | 404 Monitor | Redirects | Keywords | Bulk Editor | Workouts | Schema | Sitemaps | Robots & llms.txt | Reports.

### 20.10 Lighthouse SEO 100 checklist (enforced per template)

- `<title>` present and unique; meta description present and unique.
- Valid `lang` attribute; viewport meta tag; legible font sizes.
- HTTP 200 for indexable pages; correct 301/404/410 elsewhere.
- Indexable unless intentionally noindex; `robots.txt` valid; no blocked critical resources.
- Valid canonical (absolute, self or intended target); valid `hreflang`.
- All links crawlable (`<a href>`), descriptive link text (no "click here").
- All images have meaningful `alt` (empty only for decorative).
- Structured data validates.
- Tap targets sized and spaced appropriately.

---

## 21. Performance engineering (98+ desktop / 95+ mobile)

### 21.1 Strategy

1. **Full-page static cache for anonymous visitors.** Public pages are rendered once and written to `public/page-cache/…html`; Apache rewrite rules serve them directly when no session/auth cookie is present (time to first byte in the tens of milliseconds, PHP never boots). Cache is invalidated precisely by domain events (content, pricing, settings, menus, notices). Forms on cached pages use stateless signed endpoints or fetch CSRF tokens on interaction.
2. **Critical CSS inlined** per template; remaining CSS loaded non-blocking. Tailwind v4 output purged per surface.
3. **Minimal JavaScript:** public pages ship only Alpine core plus the islands present on the page, loaded with `type="module"` and deferred; Livewire loads only on booking pages and panels.
4. **LCP optimisation:** hero image (or CSS gradient hero) preloaded with `fetchpriority="high"`, AVIF/WebP, correct dimensions, no lazy-loading above the fold, LCP element present in initial HTML.
5. **Zero CLS:** explicit image dimensions, reserved space for notices/banners/embeds, metric-matched font fallbacks, no late-injected content above the fold.
6. **Low INP:** small event handlers, no long tasks, heavy work deferred with `requestIdleCallback`/`scheduler.yield()`.
7. **Third-party discipline:** every third-party script is consent-gated and/or interaction-gated:
   - GTM/GA4 load after consent and after first interaction or idle.
   - Address autocomplete loads on first focus of an address field.
   - Turnstile/reCAPTCHA load on form focus, never on page load.
   - WhatsApp: first-party button (no widget script).
   - Maps: static image preview; interactive map on tap ("facade").
   - Video: lite-embed facade.
   - Trust widgets (Trustpilot etc.): static badges with links; live widgets only on dedicated reviews page, lazy-loaded.
8. **HTTP layer:** HTTP/2 (HTTP/3 via CDN), Brotli/gzip, `immutable` caching for hashed assets, `stale-while-revalidate` for HTML at CDN, early hints/preconnect only where needed, no redirects on primary entry URLs.
9. **Backend:** OPcache + JIT, `config:cache`, `route:cache`, `view:cache`, `event:cache`, Redis cache for queries/settings/menus, eager loading enforced (`Model::preventLazyLoading()` in non-production), indexed queries, queued heavy work (images, emails, SEO audits, sitemaps).
10. **CDN (recommended):** Cloudflare in front of the domain for edge caching, Brotli, HTTP/3, and image caching (decision D-5).

### 21.2 Performance budgets (per public template, mobile)

| Metric | Budget |
| --- | --- |
| HTML (compressed) | ≤ 35 KB |
| Critical CSS inlined | ≤ 14 KB |
| Total CSS (compressed) | ≤ 30 KB |
| Total JS on initial load (compressed) | ≤ 45 KB (≤ 90 KB on booking pages) |
| Fonts | ≤ 2 files above the fold, ≤ 60 KB total |
| LCP image | ≤ 90 KB (AVIF) |
| Total page weight on initial load | ≤ 500 KB |
| Requests on initial load | ≤ 25 |
| Main-thread long tasks | 0 over 50 ms during load |
| TTFB (cached) | ≤ 200 ms |
| Lab LCP (mobile, simulated slow 4G) | ≤ 2.0 s |
| Total Blocking Time | ≤ 100 ms |
| CLS | ≤ 0.02 |

Budgets are enforced by Lighthouse CI (`lighthouserc` assertions) and bundle-size checks; a build that breaks a budget fails.

### 21.3 Measurement

- Lighthouse CI on every public template (home, service, airport, location, route, fleet, post, FAQ, contact, booking step 1, 404) for mobile and desktop.
- Real-user monitoring with the `web-vitals` library posting to a first-party endpoint (consent-aware), shown in an admin dashboard.
- PageSpeed Insights checks before each release.

---

## 22. Accessibility

- Target WCAG 2.2 AA across all surfaces; Lighthouse Accessibility 100 target.
- Semantic landmarks, logical heading order, skip links.
- Fully keyboard-operable navigation, menus, tabs, accordions, dialogs, date/time pickers, autocomplete (ARIA combobox pattern), carousels (pause controls), drag-and-drop (keyboard alternatives).
- Visible focus styles that meet contrast requirements.
- Form labels, descriptions, inline errors, and error summaries linked via `aria-describedby`; ARIA live regions for price updates, validation, and toasts.
- `prefers-reduced-motion` disables non-essential animation; `prefers-contrast` support.
- Text resizable to 200% without loss; reflow at 320 px.
- Meaningful alt text required by the media library and SEO checks.
- Accessibility statement page; accessibility needs captured as structured booking data.
- Automated axe-core checks in browser tests plus manual screen-reader passes (NVDA, VoiceOver) before launch.

---

## 23. Internationalization

- English (UK) default; architecture ready for additional languages from day one.
- Human-managed translations for interface strings (Laravel lang files), content (translatable fields), and notification templates; machine translation (optional provider) only as an editor aid, never auto-published for legal, booking, or payment content.
- Locale-prefixed URLs for non-default languages (`/fr/…`), `hreflang` + `x-default`, translated slugs and SEO meta per locale.
- Language switcher (replaces the reference site's Google Translate widget) with persistent preference.
- Locale-aware formatting for dates, times, numbers, currency (requires PHP `intl`), and international phone numbers.
- Right-to-left layout support via logical CSS properties.

---

## 24. Analytics, consent, and conversion tracking

- Consent Mode v2 compatible implementation; nothing non-essential loads before consent.
- GA4 via GTM (or direct gtag) loaded after consent and on idle/interaction.
- Data layer events: `quote_started`, `quote_submitted`, `address_selected`, `vehicle_selected`, `begin_checkout`, `add_payment_info`, `purchase` (booking completed, with value), `payment_failed`, `sign_up`, `login`, `booking_lookup`, `generate_lead` (enquiry), `click_to_call`, `click_email`, `click_whatsapp`, `promo_applied`, `booking_cancelled`.
- Server-side conversion recording (booking value, source, UTM) stored with the booking for reliable attribution and optional Measurement Protocol / Google Ads offline conversion upload.
- UTM and referrer capture on first visit (first-party cookie, consent-aware).
- Admin marketing attribution report.

---

## 25. Security and compliance

### 25.1 Application security

- Argon2id password hashing; breached-password check; strong password policy.
- MFA optional for customers, **mandatory for all staff and drivers**; passkeys supported.
- Role-based access control with policies on every model and action; principle of least privilege.
- Session security: secure, HTTP-only, SameSite cookies; session rotation on login; idle and absolute timeouts for admin; "log out all devices".
- Rate limiting: login, registration, password reset, booking lookup, quote API, contact forms, webhooks.
- CSRF protection on all stateful forms; signed endpoints for stateless quote API.
- Output escaping by default; HTML sanitisation for rich text and SVG; strict server-side validation.
- SQL injection prevention (Eloquent/query builder bindings only); SSRF protection on "insert from URL" and link checker (block private IP ranges).
- File upload security: MIME sniffing, extension allow-list, size limits, randomised storage names, private disks for sensitive files, malware scan hook.
- Security headers: Content-Security-Policy (nonce-based), Strict-Transport-Security (preload-ready), X-Content-Type-Options, Referrer-Policy, Permissions-Policy, frame-ancestors.
- Secrets only in `.env`/server environment; no private API keys in browser code; browser-exposed keys restricted by domain/referrer.
- Webhook signature verification and replay protection.
- Admin path configurable; optional IP allow-list for admin.
- Audit log for every staff, finance, pricing, permission, and settings change (who, what, before/after, IP, user agent).
- Dependency auditing (`composer audit`, `npm audit`) in CI; Larastan at a high level; periodic OWASP ZAP baseline scan.

### 25.2 Payments (PCI DSS)

Card data is handled exclusively by Stripe Elements/Payment Element (SAQ A scope). The platform stores only Stripe IDs, card brand, last four digits, and expiry.

### 25.3 Privacy (UK GDPR / PECR)

- Granular cookie consent with logged consent records and easy withdrawal.
- Privacy notice, cookie policy, and GDPR page as versioned legal documents; acceptance recorded at registration/booking.
- Data subject requests: export my data (JSON/CSV), rectification, erasure (anonymise bookings needed for financial records while removing personal data), restriction.
- Data retention policies per data type with scheduled purge/anonymisation (e.g. driver location history, logs, abandoned drafts).
- Marketing consent separate from transactional communication; unsubscribe on every marketing message.
- Records of processing and third-party processor list maintained in documentation.

---

## 26. Database schema

All tables use `utf8mb4`, InnoDB, ULID or UUID public identifiers where exposed, `created_at`/`updated_at`, soft deletes where noted (SD), and foreign keys with appropriate indexes. Money columns are unsigned integers in minor units with a `currency` char(3).

### 26.1 Identity and settings

| Table | Key columns |
| --- | --- |
| `users` | id, ulid, title, first_name, last_name, email (unique), email_verified_at, password, phone_country, phone, country_code, locale, timezone, type (staff/customer/driver), status, last_login_at, two_factor_secret, two_factor_recovery_codes, two_factor_confirmed_at, marketing_opt_in_at, SD |
| `passkeys` | id, user_id, credential_id, public_key, name, last_used_at |
| `password_reset_tokens`, `sessions` | Laravel defaults |
| `roles`, `permissions`, `model_has_roles`, `model_has_permissions`, `role_has_permissions` | spatie/laravel-permission |
| `settings` | id, group, key, value (JSON), type, is_public, unique(group, key) |
| `activity_log` | spatie/laravel-activitylog (subject, causer, event, properties before/after, IP, user agent) |
| `feature_flags` | key, enabled, rules (JSON) |
| `api_clients` | name, token hash, abilities, last_used_at |

### 26.2 Media

| Table | Key columns |
| --- | --- |
| `media` | id, ulid, disk, directory, filename, original_filename, extension, mime_type, type (image/video/audio/document/archive/other), size_bytes, width, height, duration_seconds, hash (sha256, indexed), title, alt, caption, description, focal_x, focal_y, dominant_color, lqip, meta (JSON: EXIF subset, colour profile), folder_id, uploaded_by, visibility (public/private), status (processing/ready/failed), trashed_at, SD |
| `media_variants` | id, media_id, size_name, format (avif/webp/jpg/png), width, height, size_bytes, path |
| `media_folders` | id, parent_id, name, slug, owner_id (null = shared), order |
| `media_tags`, `media_taggables` | Tagging |
| `mediables` | media_id, mediable_type, mediable_id, role (featured/gallery/inline/og/logo/document), order |
| `media_edits` | id, media_id, operations (JSON), applied_to, user_id (image editor history) |

### 26.3 CMS and blog

| Table | Key columns |
| --- | --- |
| `pages` | id, ulid, parent_id, title, slug, template, blocks (JSON), rendered_html (cache), status, published_at, author_id, featured_media_id, is_homepage, SD |
| `page_revisions` | id, revisionable_type, revisionable_id, data (JSON), user_id, note |
| `reusable_blocks` | id, name, type, data (JSON) |
| `services` | id, title, slug, summary, icon, blocks, status, order, schema_type, featured_media_id, SD |
| `airports` | id, iata, name, slug, lat, lng, geofence (JSON), meet_greet_info, parking_fee, pickup_fee, dropoff_fee, blocks, status |
| `airport_terminals` | id, airport_id, name, code, meeting_point, map_media_id |
| `locations` | id, name, slug, area, blocks, status |
| `routes_content` | id, from_place_id, to_place_id, slug, distance_miles, duration_minutes, blocks, status |
| `places` | id, name, slug, type (city/town/airport/station/venue), lat, lng, postcode |
| `posts` | id, ulid, title, slug, excerpt, content (JSON + HTML), author_id, status, published_at, is_cornerstone, reading_minutes, featured_media_id, SD |
| `categories`, `tags`, `post_category`, `post_tag` | Blog taxonomy |
| `authors` (profile on users) | user_id, bio, avatar_media_id, social links |
| `faqs` | id, question, answer, faq_category_id, order, status |
| `faq_categories` | id, name, slug, order |
| `faqables` | faq_id, faqable_type, faqable_id, order |
| `reviews` | id, name, rating, body, source, reviewed_at, photo_media_id, booking_id, status (pending/approved/rejected), order |
| `client_logos` | id, name, logo_media_id, url, order, is_active |
| `awards` | id, name, issuer, year, media_id, verify_url, order |
| `offers` | id, title, slug, description, promotion_id, starts_at, ends_at, banner_media_id, terms, status |
| `notices` | id, message, severity, starts_at, ends_at, audience, page_rules (JSON), link_url, dismiss_mode, is_active |
| `menus`, `menu_items` | id, location, name; item: menu_id, parent_id, label, url / linkable_type+id, icon, badge, order, column, is_mega |
| `legal_documents` | id, type, version, effective_at, content, summary, status |
| `legal_acceptances` | id, user_id / booking_id, legal_document_id, accepted_at, ip |
| `form_submissions` | id, form, data (JSON), ip, user_agent, spam_score, status |

### 26.4 SEO

| Table | Key columns |
| --- | --- |
| `seo_meta` | id, seoable_type, seoable_id, locale, title, description, focus_keyphrase, synonyms (JSON), related_keyphrases (JSON), canonical, robots (JSON), og_title, og_description, og_media_id, twitter_title, twitter_description, twitter_media_id, schema_type, schema_custom (JSON), breadcrumb_title, is_cornerstone, include_in_sitemap, sitemap_priority, sitemap_changefreq, is_news, video_meta (JSON), seo_score, readability_score, last_audited_at |
| `seo_audits` | id, seoable_type, seoable_id, score, readability_score, checks (JSON), lighthouse (JSON), run_at |
| `seo_title_templates` | id, content_type, title_template, description_template |
| `seo_schema_templates` | id, name, type, template (JSON), content_type |
| `redirects` | id, source, target, match_type (exact/prefix/regex), status_code, hits, last_hit_at, is_active, is_auto, notes |
| `seo_404_logs` | id, url, referrer, user_agent, hits, first_seen_at, last_seen_at, is_ignored |
| `seo_keywords` | id, keyword, target_url, locale, device, is_active |
| `seo_keyword_rankings` | id, seo_keyword_id, position, url, recorded_on, source |
| `seo_internal_links` | id, source_type, source_id, target_type, target_id, anchor |
| `seo_broken_links` | id, source_type, source_id, url, status_code, checked_at |
| `seo_settings` | Stored in `settings` (group `seo`) |

### 26.5 Geo, fleet, pricing

| Table | Key columns |
| --- | --- |
| `zones` | id, name, type (postcode_list/polygon/radius), definition (JSON), priority |
| `special_areas` | id, name, type (congestion/clean_air/toll), polygon (JSON), charge, applies_to |
| `vehicle_categories` | id, name, slug, class (economy/executive), passengers, suitcases, hand_luggage, accessibility (JSON), features (JSON), order, is_active, media gallery via `mediables` |
| `vehicles` | id, vehicle_category_id, registration, make, model, year, colour, plate_number, status, SD |
| `vehicle_documents` | id, vehicle_id, type, number, issued_at, expires_at, media_id, status |
| `vehicle_maintenances` | id, vehicle_id, type, due_at, completed_at, cost, notes |
| `equipment_types`, `equipment_stock` | child seat types; stock per vehicle/base |
| `tariffs` | id, name, is_default, valid_from, valid_to, status |
| `tariff_vehicle_rates` | id, tariff_id, vehicle_category_id, base_fare, per_minute, minimum_fare, multiplier, via_stop_fee |
| `tariff_distance_bands` | id, tariff_vehicle_rate_id, from_miles, to_miles, per_mile |
| `fixed_fares` | id, from_zone_id / from_place_id, to_zone_id / to_place_id, vehicle_category_id, price, bidirectional, valid_from, valid_to |
| `hourly_packages` | id, hours, included_miles, vehicle_category_id, price, excess_mile_rate, excess_hour_rate, order |
| `surcharges` | id, name, type (date/time/event/airport/extra), rule (JSON), amount_type (fixed/percent), amount, priority, valid_from, valid_to, is_active |
| `calendar_events` | id, name, starts_at, ends_at, location_place_id, radius, mode (surcharge/request_only/block), surcharge_id |
| `extras` | id, name, code, price, vehicle_category_rules (JSON), requires_stock, is_active |
| `promotions` | id, code, name, type (percent/fixed), value, min_spend, max_discount, usage_limit, per_user_limit, eligibility (JSON), starts_at, ends_at, is_active |
| `promotion_redemptions` | id, promotion_id, booking_id, user_id, discount |
| `pricing_rule_changes` | id, user_id, model_type, model_id, before (JSON), after (JSON) |
| `route_cache` | id, hash, distance_meters, duration_seconds, polyline, provider, expires_at |

### 26.6 Booking

| Table | Key columns |
| --- | --- |
| `quotes` | id, ulid, token, journey_type, payload (JSON input), results (JSON per vehicle with breakdown), expires_at, user_id, ip, utm (JSON) |
| `booking_drafts` | id, ulid, resume_token, step, data (JSON), quote_id, user_id, email, reminder_sent_at, expires_at |
| `bookings` | id, ulid, reference (unique), group_id, customer_id, company_id, cost_centre_id, created_by, channel (web/phone/email/admin/api), journey_type, status, vehicle_category_id, passengers, suitcases, hand_luggage, oversized_items, child_seats (JSON), special_requests, driver_notes, name_board_text, name_board_media_id, flight_number, airline, terminal_id, airport_direction, pickup_at, payment_method, payment_status, currency, subtotal, discount, surcharges_total, tax, total, price_breakdown (JSON), cancellation_policy_snapshot (JSON), po_reference, utm (JSON), confirmed_at, completed_at, cancelled_at, SD |
| `booking_legs` | id, booking_id, leg (outbound/return), pickup_at, distance_meters, duration_seconds, polyline, driver_id, vehicle_id, status |
| `booking_stops` | id, booking_leg_id, sequence, type (pickup/via/dropoff), address_line, postcode, lat, lng, place_id, airport_id |
| `booking_passengers` | id, booking_id, title, first_name, last_name, email, phone_country, phone, is_lead |
| `booking_extras` | id, booking_id, extra_id, quantity, unit_price, total |
| `booking_status_histories` | id, booking_id, booking_leg_id, from_status, to_status, actor_type, actor_id, reason, meta (JSON) |
| `booking_amendments` | id, booking_id, requested_by, changes (JSON), price_difference, status, processed_by |
| `booking_cancellations` | id, booking_id, cancelled_by, reason, fee, refund_amount, policy_tier |
| `booking_groups` | id, name, company_id, event_name, notes |
| `waiting_time_records` | id, booking_leg_id, started_at, ended_at, free_minutes, charged_minutes, charge |
| `flights` | id, booking_id, flight_number, scheduled_departure, scheduled_arrival, estimated_arrival, actual_arrival, status, terminal, provider_ref, last_polled_at |
| `flight_status_snapshots` | id, flight_id, status, payload (JSON), recorded_at |

### 26.7 Payments and finance

| Table | Key columns |
| --- | --- |
| `payments` | id, ulid, booking_id, invoice_id, customer_id, method, provider, provider_payment_id, amount, currency, status, failure_code, failure_message, paid_at, idempotency_key, meta (JSON) |
| `refunds` | id, payment_id, amount, reason, provider_refund_id, status, issued_by |
| `payment_methods` | id, user_id, provider, provider_method_id, brand, last4, exp_month, exp_year, is_default |
| `payment_links` | id, booking_id, amount, token, expires_at, paid_at, sent_by |
| `invoices` | id, number (unique sequence), customer_id, company_id, status, issue_date, due_date, subtotal, tax, total, paid_total, pdf_path, sent_at |
| `invoice_lines` | id, invoice_id, booking_id, description, quantity, unit_amount, tax_rate, total |
| `credit_notes` | id, number, invoice_id, amount, reason |
| `statements` | id, company_id, period_start, period_end, opening_balance, closing_balance, pdf_path |
| `driver_settlements` | id, driver_id, period, cash_collected, commission, payout, status |
| `webhook_events` | id, provider, event_id (unique), type, payload (JSON), signature_valid, processed_at, attempts, error |

### 26.8 Customers, corporate, drivers, dispatch

| Table | Key columns |
| --- | --- |
| `customer_profiles` | id, user_id, default_vehicle_category_id, preferences (JSON), favourite_driver_id, stripe_customer_id, notes |
| `saved_places` | id, user_id, label, address_line, postcode, lat, lng, place_id |
| `saved_passengers` | id, user_id, first_name, last_name, email, phone |
| `companies` | id, ulid, name, registration_number, vat_number, billing_address (JSON), status (applied/approved/suspended), credit_limit, payment_terms_days, invoice_frequency, account_manager_id, priority, tariff_id, SD |
| `company_members` | id, company_id, user_id, role (admin/booker/traveller), cost_centre_id |
| `cost_centres` | id, company_id, code, name |
| `company_approval_rules` | id, company_id, rule (JSON), approver_user_id |
| `booking_approvals` | id, booking_id, approver_id, status, decided_at, note |
| `drivers` | id, user_id, driver_number, licence_number, licence_expires_at, phv_licence_number, phv_licence_council, phv_licence_expires_at, dbs_number, dbs_issued_at, dbs_recheck_at, status, rating, base_place_id, SD |
| `driver_documents` | id, driver_id, type, number, expires_at, media_id, status, verified_by |
| `driver_shifts` | id, driver_id, starts_at, ends_at, status |
| `driver_availability` | id, driver_id, date, available_from, available_to, reason |
| `driver_vehicle` | driver_id, vehicle_id, is_primary |
| `driver_locations` | id, driver_id, lat, lng, heading, speed, accuracy, recorded_at (retention-limited) |
| `job_offers` | id, booking_leg_id, driver_id, offered_at, expires_at, response, responded_at |
| `driver_expenses` | id, booking_leg_id, type, amount, receipt_media_id, status |

### 26.9 Notifications, support, consent

| Table | Key columns |
| --- | --- |
| `notification_templates` | id, event, channel, locale, subject, body, variables (JSON), is_active, version |
| `notification_template_versions` | id, notification_template_id, body, subject, user_id |
| `notification_logs` | id, notifiable_type, notifiable_id, event, channel, to, provider, provider_message_id, status, error, sent_at, delivered_at |
| `notifications` | Laravel database notifications |
| `push_subscriptions` | id, user_id, endpoint, keys (JSON) |
| `support_cases` | id, reference, type, priority, status, subject, customer_id, email, phone, booking_id, assignee_id, sla_due_at, source, data (JSON), resolved_at |
| `support_messages` | id, support_case_id, author_type, author_id, body, is_internal, attachments via `mediables` |
| `canned_replies` | id, title, body, category |
| `lost_property_items` | id, support_case_id, booking_id, description, found_by_driver_id, storage_location, status, returned_at |
| `consent_records` | id, user_id, anonymous_id, categories (JSON), version, ip, user_agent, recorded_at |
| `data_requests` | id, user_id, type (export/erasure/rectification), status, completed_at, file_path |
| `rum_metrics` | id, page_template, metric, value, rating, device, recorded_at (aggregated nightly) |

---

## 27. Routes and URL structure

### 27.1 Public routes (canonical)

| URL | Purpose |
| --- | --- |
| `/` | Home |
| `/about-us` | About |
| `/drivers-fleet` | Drivers & fleet overview |
| `/fleet/{category}` | Vehicle category detail |
| `/services` | Services index |
| `/services/{service}` | Service detail |
| `/airports` and `/airports/{airport}` | Airport index and detail |
| `/locations/{location}` | Location pages (e.g. Manchester taxis, minibuses, executive) |
| `/routes` and `/routes/{route}` | Route index and route detail |
| `/travel-guide`, `/travel-guide/{post}`, `/travel-guide/category/{category}`, `/travel-guide/tag/{tag}`, `/travel-guide/author/{author}` | Blog / travel guide |
| `/offers` | Offers |
| `/faqs` | FAQ |
| `/online-booking-procedure`, `/pickup-dropoff-procedure` | Guides |
| `/accreditations` | Accreditations |
| `/contact-us` | Contact |
| `/complicated-journey` | Manual quote |
| `/open-an-account` | Account comparison and corporate application |
| `/corporate-travel`, `/student-airport-transfers` | Landing pages (CMS) |
| `/terms-conditions`, `/privacy-and-cookies`, `/gdpr-general-data-protection-regulation`, `/cancellation-policy`, `/accessibility` | Legal |
| `/search` | Site search |
| `/sitemap` | HTML sitemap |
| `/book`, `/book/vehicle`, `/book/details`, `/book/payment`, `/book/confirmation/{ulid}` | Booking wizard |
| `/my-booking` | Guest booking lookup |
| `/login`, `/register`, `/forgot-password`, `/reset-password/{token}`, `/verify-email` | Authentication |
| `/media/{slug}` | Attachment page (redirect by default) |
| `/sitemap.xml`, `/sitemaps/{type}.xml`, `/robots.txt`, `/llms.txt`, `/feed` | SEO endpoints |

### 27.2 Panel and system routes

`/account/*` (customer), `/business/*` (corporate), `/driver/*` (driver PWA), `/admin/*` (staff; path configurable), `/api/v1/*` (quote, autocomplete proxy, driver app, RUM), `/webhooks/stripe`, `/webhooks/sms/{provider}`, `/webhooks/whatsapp`, `/webhooks/flights`, `/up` (health check).

### 27.3 Legacy URL redirects (from the reference URL patterns)

| Legacy | Redirect to |
| --- | --- |
| `/index.php` | `/` |
| `/faq` | `/faqs` |
| `/contact`, `/contact.php` | `/contact-us` |
| `/authentication` | `/my-booking` |
| `/passenger-registration.php` | `/register` |
| `/reset-password.php` | `/forgot-password` |
| `/privacy-policy` | `/privacy-and-cookies` |
| `/open-an-account`, `/services/open-an-account` | `/open-an-account` (canonical) |
| `/meet-and-greet-service`, `/chip-and-pin`, `/courier-service`, `/match-day-transfers`, `/event-group-transfers`, `/pay-via-card` | `/services/{same-slug}` |
| `/manchester-airport-transfers` | `/airports/manchester` |
| `/liverpool-airport-transfers` | `/airports/liverpool` |
| `/manchester-taxis`, `/manchester-minibuses`, `/manchester-executive-taxi` | `/locations/{slug}` |
| `/travel-guide/taxi-from-manchester-to-{city}`, `/travel-guide/{city}-to-manchester-taxi`, `/travel-guide/manchester-to-{city}-taxi` | `/routes/manchester-to-{city}` or `/routes/{city}-to-manchester` |

These redirects are seeded into the SEO redirects table (301) so they are editable and tracked. Final canonical slugs are confirmed in decision D-8.

---

## 28. Third-party integrations

| Capability | Default choice | Alternatives | Adapter interface |
| --- | --- | --- | --- |
| Payments | Stripe | — | `PaymentGateway` |
| Address autocomplete (UK) | Google Places API (New) | Ideal Postcodes, getAddress.io, Loqate | `AddressLookup` |
| Routing / distance | Google Routes API | Mapbox, OpenRouteService | `RouteCalculator` |
| Map tiles | OpenStreetMap-based tiles via provider | Mapbox, MapTiler | (frontend config) |
| Flight data | FlightAware AeroAPI | Aviationstack, Cirium/OAG | `FlightTracker` |
| SMS | Twilio | Vonage, UK aggregators | `SmsSender` |
| WhatsApp | WhatsApp Business Cloud API | Twilio WhatsApp | `WhatsAppSender` |
| Email | Postmark | Amazon SES, Mailgun, SMTP | Laravel mailer |
| Bot protection | Cloudflare Turnstile | Google reCAPTCHA v3 | `HumanVerifier` |
| Analytics | GA4 via GTM | — | Consent-gated loader |
| AI (SEO assist) | OpenAI-compatible API | Any compatible endpoint | `SeoAiService` |
| Search indexing | IndexNow, Google Search Console | — | `IndexNowService` |
| Reviews | Trustpilot / TripAdvisor / Feefo links & static badges | Live widgets on reviews page | — |
| Monitoring | Laravel Pulse + error tracking (Sentry/Flare) | — | — |

Every integration has sandbox/production configuration, health checks shown in **Settings → System health**, retry/backoff, logged errors, and an on/off switch.

---

## 29. Background jobs and scheduler

| Job / schedule | Frequency |
| --- | --- |
| Process webhook inbox | On receipt (queue `critical`) |
| Send notifications | On event (queue `notifications`) |
| Image processing / regenerate sizes | On upload (queue `media`) |
| Driver details notification | Every minute — finds legs at T-minus configured time (default 2 h) |
| Flight polling | Every 5 minutes for flights in tracking window |
| Unallocated/at-risk job alerts | Every minute |
| Abandoned booking reminders | Every 15 minutes (consent only) |
| Quote and draft cleanup | Hourly |
| Compliance expiry reminders (drivers/vehicles) | Daily |
| Invoice generation for corporate billing cycles | Daily |
| Overdue invoice reminders | Daily |
| Receipt emails after completion | On completion + daily catch-up |
| SEO audit-all | Nightly (queue `seo`) |
| Sitemap regeneration | On content events + nightly |
| Broken link checker | Weekly |
| Keyword rank snapshot | Daily |
| RUM aggregation | Nightly |
| Data retention purge/anonymisation | Daily |
| Backups (database daily, files weekly) + cleanup | Daily |
| Trash auto-empty (media/content) | Daily |
| Pulse/health checks | Every minute |

---

## 30. Testing and quality assurance

| Layer | Tooling | Scope |
| --- | --- | --- |
| Static analysis | Larastan (high level), Rector, Laravel Pint | Every commit |
| Unit tests | Pest 4 | Pricing pipeline, money, policies, state machine, SEO analysers, reference generator |
| Feature tests | Pest 4 | HTTP endpoints, Livewire components, auth, permissions, webhooks, notifications, jobs |
| Browser tests | Pest browser testing (Playwright) | Booking flow end-to-end (guest, customer, corporate), payments with Stripe test cards incl. 3-D Secure, admin dispatch, media library, SEO editor, responsive widths |
| Accessibility | axe-core in browser tests + manual screen readers | All templates |
| Performance | Lighthouse CI (mobile + desktop), bundle-size checks | All public templates, budgets enforced |
| SEO | Lighthouse SEO, internal SEO audit, structured-data validation, sitemap/robots validation, redirect map test | All public pages |
| Visual regression | Screenshot comparison of key templates | Before releases |
| Load testing | k6 | Quote API, booking flow, cached pages |
| Security | `composer audit`, `npm audit`, OWASP ZAP baseline, permission matrix tests | CI + pre-release |

Coverage target: 90%+ for Pricing, Booking, Payments, and Finance modules; 75%+ overall. Every bug fix includes a regression test.

---

## 31. Coding standards and workflow

- PSR-12 via Laravel Pint; strict types in all PHP files; typed properties, return types, and readonly where appropriate.
- Conventional Commits; `main` (production), `develop` (integration), feature branches `feature/<phase>-<topic>`.
- Every phase delivered through reviewed commits with passing CI.
- Database changes only through migrations; seeders for reference data (roles, permissions, settings, vehicle categories, airports, legacy redirects, demo content for staging).
- No business logic in Blade views or controllers.
- Translations for every user-facing string from the start (`__()`).
- Configuration over hard-coding for every business rule listed in section 10.8.
- Documentation updated alongside code: module READMEs, `doc/` decisions log, API reference for `/api/v1`.

---

## 32. Deployment, backups, and monitoring

### 32.1 Environments

- **Local/dev** (optional) for developers.
- **Staging** — recommended `staging.cars.ronnieridge.com` with a separate database, `SITE_INDEXABLE=false`, Stripe test mode.
- **Production** — `cars.ronnieridge.com`.

### 32.2 Deployment steps (scripted)

1. `git pull` (or CI artefact upload) into the project root.
2. `composer install --no-dev --optimize-autoloader`.
3. `npm ci && npm run build` (or upload built assets from CI).
4. `php artisan down --render="errors::503" --retry=15` (only for breaking migrations).
5. `php artisan migrate --force`.
6. `php artisan optimize` (config, routes, views, events cache) and page-cache purge.
7. `php artisan queue:restart`, Reverb restart if used.
8. `php artisan up`; smoke tests on key URLs; Lighthouse spot check.

Zero-downtime release folders with symlink switching can be introduced if the hosting setup permits.

### 32.3 Backups

`spatie/laravel-backup`: daily database backups, weekly full file backups (including media), retained per policy (e.g. 7 daily, 4 weekly, 6 monthly), stored off-server (S3-compatible or remote storage), with monthly restore tests.

### 32.4 Monitoring

Laravel Pulse (slow queries, jobs, exceptions, cache), error tracking (Sentry or Flare), uptime monitoring, queue health, failed-job alerts, webhook failure alerts, integration health checks, disk space alerts, and RUM Core Web Vitals dashboard.

---

## 33. Content and legal considerations

- The reference website is used **only** as a functional baseline. Text, images, logos, reviews, and client logos from the reference site must **not** be copied; all content, imagery, and testimonials will be original or properly licensed.
- Brand name (MCR AeroLink) and logo are supplied; contact details, licences, and legal entity details for the new platform are supplied by the project owner (decision D-1).
- Client logos and awards are displayed only with permission and genuine entitlement.
- Reviews must be genuine and moderated; review schema follows Google guidelines.
- Legal documents (terms, privacy, cookies, cancellation) should be reviewed by a legal professional before launch.
- Private hire operator licensing details are displayed as required by the relevant licensing authority.

---

## 34. Decisions to confirm before or during development

These have sensible defaults in this plan, but should be confirmed by the project owner. None block Phase 0.

| ID | Decision | Default in this plan |
| --- | --- | --- |
| D-1 | Brand name, logo, final colour palette, contact details, legal entity | **Confirmed 12 Oct 2026:** brand name MCR AeroLink, supplied logo, logo colour scheme (section 6.6). Contact details and legal entity: placeholders, editable in Admin → Settings |
| D-2 | Admin panel approach | Fully custom Livewire admin (premium, bespoke) rather than an off-the-shelf admin kit |
| D-3 | Address and routing provider | Google Places (New) + Google Routes |
| D-4 | SMS, email, WhatsApp, flight data providers | Twilio, Postmark, WhatsApp Cloud API, FlightAware AeroAPI |
| D-5 | Use Cloudflare (CDN, HTTP/3, edge cache) | Recommended yes |
| D-6 | Real-time transport on cPanel without a process supervisor | Reverb with cron watchdog; fallback to Livewire polling; hosted Pusher-compatible service as alternative |
| D-7 | Cancellation policy tiers | >48 h free, 48–24 h 20% fee, <24 h non-refundable (editable) |
| D-8 | Final canonical URL structure (section 27) and whether to keep the reference URL slugs exactly | New structure + 301 redirect map |
| D-9 | Social login (Google/Apple) for customers | Off at launch, ready to enable |
| D-10 | Additional languages at launch | English only at launch, architecture ready |
| D-11 | Driver commission/settlement model | Cash settlement tracking; commission optional |
| D-12 | Staging subdomain | `staging.cars.ronnieridge.com` |
| D-13 | Hosting upgrade path (VPS with Supervisor for Horizon/Reverb) if real-time load grows | Stay on current cPanel server initially |

---

## 35. Development phases

Each phase ends with a demo, acceptance checklist sign-off, and approval before the next phase starts. Phases are ordered so every phase builds on stable foundations, and the public site, booking engine, and operations come together progressively.

### Phase 0 — Environment and project foundation

**Goal:** a secure, correctly configured Laravel 13 skeleton on the server.

- Change the cPanel document root to `/public`; fix file ownership to `ronnie:ronnie`; protect `doc/`.
- Enable PHP extensions: `redis`, `intl`, `exif` (and `imagick` if possible); apply PHP ini targets (section 3.3).
- Install Laravel 13 (preserving `.well-known`, `doc/`, cPanel ini files); configure `.env` with the `ronnie_cars` database and credentials; verify MariaDB connection with `utf8mb4`.
- Configure Redis for cache, sessions, queues; cron for scheduler and queue worker.
- Initialise Git repository, `.gitignore`, branch strategy; optional remote.
- Install tooling: Pest 4, Larastan, Pint, Rector, Vite, Tailwind CSS v4, Alpine.js, Livewire 4.
- Create modular structure (`app/Core`, `app/Modules/*`) with module service provider loader.
- `SITE_INDEXABLE=false` guard (noindex headers + disallow robots) while in development.
- Base security headers middleware, HTTPS enforcement, health check route.
- CI pipeline skeleton (lint, static analysis, tests, build, Lighthouse CI placeholder).

**Acceptance:** site serves Laravel from `/public`; `.env` and `doc/` are not web-accessible; database migrations run; queue and scheduler execute via cron; CI passes.

### Phase 1 — Design system and UI foundation

**Goal:** the complete visual language and component library used by every surface.

- Design tokens (colour, type, spacing, radius, elevation, motion) in Tailwind v4 theme; light/dark themes.
- Typography: Segoe UI via `local()`, self-hosted Selawik WOFF2 subsets, metric-adjusted fallbacks, fluid type scale.
- SVG icon sprite system.
- Blade component library (section 6.4) with accessibility built in.
- Layouts: public, booking, account (customer/corporate), driver, admin, auth, error pages.
- Motion system with reduced-motion support; View Transitions; scroll-driven animations.
- Internal style guide page with every component and state.
- Performance baseline: critical CSS pipeline, per-surface Vite entries, bundle-size checks.

**Acceptance:** style guide complete; components keyboard and screen-reader accessible; empty layout pages score 100 in Lighthouse (all categories) on mobile and desktop.

### Phase 2 — Identity, roles, security baseline, and admin shell

**Goal:** authentication for all user types and the admin panel framework.

- Fortify authentication: register (all reference fields), login, logout, email verification, forgot/reset password, 2FA (TOTP + recovery codes), passkeys, session management, magic links.
- Roles and permissions (section 14.3) with UI to manage roles and users.
- Mandatory MFA for staff and drivers; rate limiting; lockout notifications.
- Audit log foundation (activity log on all models).
- Admin shell: sidebar, top bar, command palette, global search (Scout database engine), notifications centre, theme/density toggles, dashboard placeholder, impersonation.
- Customer and driver panel shells (navigation, bottom tab bars, profile and security pages).
- Settings module (typed settings with admin UI: company, contact, social, booking rules placeholders).
- Security headers/CSP with nonces, cookie hardening.

**Acceptance:** all auth flows tested end-to-end; permission matrix tests pass; admin shell usable on mobile and desktop; audit log records changes.

### Phase 3 — Media Library (WordPress parity)

**Goal:** the full media library used by all later modules.

- Media, variants, folders, tags, mediables, edits tables.
- Library screen (grid/list, filters, search, infinite scroll, drag-and-drop/chunked upload, progress, bulk actions, trash, folders).
- Attachment details modal and edit screen; copy URL; usage tracking; replace media; duplicate detection.
- Image editor (crop with ratios, rotate, flip, scale, apply-to options, restore original, focal point); regenerate thumbnails.
- Processing pipeline: registered sizes, AVIF/WebP/fallback, EXIF orientation and stripping, LQIP/dominant colour, big-image threshold, SVG sanitisation, queued processing.
- Media modal picker component (upload, library, insert from URL, featured image, gallery frame, insert into editor).
- Frontend `<x-image>` responsive component, lightbox, attachment page with redirect option.
- Frontend uploads for customers/drivers with private disk and signed URLs.

**Acceptance:** every item in section 19 verified against the parity checklist; images render as AVIF/WebP with correct `srcset`; uploads secure; tests pass.

### Phase 4 — CMS and content modules

**Goal:** staff can build and manage all public content.

- Page builder with all blocks (section 18.1), revisions, scheduling, autosave, previews, reusable blocks, templates.
- Content types: pages, services, airports, terminals, locations, places, routes, fleet category content, FAQs, reviews, client logos, awards, offers, notices, legal documents (versioned), team members.
- Blog / Travel Guide: posts (TipTap), categories, tags, authors, reading time, table of contents, related posts.
- Menus and footer builder; social links; payment icons.
- Notices and cookie consent module (granular categories, consent logs).
- Forms engine (contact, complicated journey, corporate application, lost property, feedback) with honeypot, Turnstile on interaction, rate limits, acknowledgements.

**Acceptance:** all reference-site content structures can be created and edited without code; revisions and scheduling work; forms deliver submissions to the database and email.

**Implemented (13 Oct 2026):** `app/Modules/Cms` — block registry/renderer (25 block types), publishable + revisions traits, admin hub and CRUD for every content type, Alpine page builder + TipTap blog editor, menus, notices, cookie consent, forms + submissions, `cms:publish-scheduled`, public page/post/FAQ show routes, `CmsSeeder`. TipTap is admin-only (`@tiptap/*`). Full public templates and original seed copy land in Phase 6.

### Phase 5 — SEO module (Rank Math Pro + Yoast Premium parity)

**Goal:** complete SEO system integrated into every content type.

- SEO meta, audits, templates, schema templates, redirects, 404 logs, keywords, rankings, internal/broken links tables.
- Editor panel (General / Social / Schema / Advanced) on every content form with live SERP/social previews, SEO and readability analysis, internal link suggestions, AI assist with offline fallback, publish gate (strict mode).
- Runtime: meta resolver, title templates, canonical, robots, hreflang, OG/Twitter, JSON-LD graph, breadcrumbs.
- Sitemaps (index + all types + images + video + news), robots.txt editor with AI-bot toggles, `llms.txt`, IndexNow, GSC-ready connector, RSS.
- Redirect manager (regex, 410, CSV, auto slug redirects, chain detection) and 404 monitor; legacy redirect map seeded.
- Workouts (orphan, cornerstone, stale, thin, missing meta), broken link checker, duplicate detection.
- Keyword tracker, reports, bulk editor, CSV import/export, audit-all job, SEO hub tabs.
- Local SEO (locations/NAP audit).

**Acceptance:** every item in section 20 checked off; sample content achieves internal audit 100; `/sitemap.xml`, `/robots.txt`, `/llms.txt` valid; slug change creates redirect; 404s logged.

**Implemented (13 Oct 2026):** `app/Modules/Seo` — meta/audits/templates/redirects/404/keywords tables; `SeoMetaResolver` + OG/Twitter/JSON-LD on public layout; sitemaps, robots, llms.txt, RSS; redirect + 404 fallback middleware; admin SEO hub tabs; editor panel on pages/posts; audit engine + nightly `seo:audit-all`; `SeoSeeder`; premium CMS public page + block styling (`resources/css/cms-pages.css`).

### Phase 6 — Public website build

**Goal:** the complete, premium, fast public website (quote widget visually complete, wired to the pricing API in Phase 7).

- Global shell: notices, cookie consent, top bar, header + mega menu, mobile drawer, sticky mobile quote bar, breadcrumbs, footer, first-party WhatsApp button, error pages.
- Homepage (all sections in 9.2) and every page in 9.4: about, drivers & fleet, fleet categories, services, airports, locations, routes, travel guide/blog, offers, FAQ, guides, accreditations, contact, complicated journey, open an account, legal, search, HTML sitemap.
- Quote widget UI (instant + hourly tabs, vias, airport toggle, return, steppers, lead-time guard) with session persistence.
- Signature interactions: route line animation, vehicle recommender, View Transitions, scroll animations, bottom sheets.
- Full-page static cache with event-based invalidation; critical CSS; third-party facades; consent-gated analytics.
- Original seed content for all pages (no copied reference content).

**Acceptance:** every public template scores Performance ≥ 98 desktop / ≥ 95 mobile, SEO 100, Accessibility ≥ 95 (target 100), Best Practices 100 in Lighthouse CI; internal SEO audit 100 on every page; fully responsive from 360 px.

**Implemented (13 Oct 2026):** `app/Modules/Site` — top contact bar; premium homepage (all §9.2 sections) with Alpine quote widget (`sessionStorage`, instant/hourly, vias, airport toggle, lead-time guard); marketing routes for about, fleet, services, airports, routes, travel guide, offers, accreditations, contact, complicated journey, open account, guides, legal, search, HTML sitemap; CMS content preferred when published. Quote pricing API + full-page cache remain Phase 7 / later hardening.

### Phase 7 — Geo, fleet catalogue, and pricing engine

**Goal:** accurate, configurable, explainable pricing.

- Address autocomplete adapter (lazy-loaded) and proxy endpoint; airport recognition; routing adapter with caching; zones and special areas.
- Vehicle categories (capacities, luggage, accessibility, media); equipment types and stock.
- Tariffs, distance bands, fixed fares, hourly packages, extras, surcharges, calendar events, promotions.
- Pricing pipeline (section 10.3) with breakdown lines; hourly pricing; return pricing; corporate tariffs; rounding and minimums; VAT.
- Quote API (stateless, signed tokens, rate-limited, bot-protected) wired to the public quote widget; vehicle results UI.
- Admin pricing management UI, pricing simulator, change history, test-before-publish.

**Acceptance:** pricing unit tests cover every rule and combination; simulator matches API; quote widget returns prices for point-to-point, airport, multi-stop, return, and hourly journeys; lead-time and event rules enforced.

**Implemented (13 Oct 2026):** `app/Modules/Geo` (airports, zones, special areas, route cache, NullAddressLookup + HaversineRouteCalculator, autocomplete API); `app/Modules/Fleet` (vehicle categories, equipment types, admin CRUD); `app/Modules/Pricing` (tariffs/bands, fixed fares, hourly packages, extras, surcharges, promotions, calendar events, quotes, pricing rule change audit, PricingEngine §10.3 pipeline, CalculateQuote, public `POST /quote` + `POST /api/quotes`, admin hub + simulator); public quote widget wired to the pricing API with vehicle results UI; `PricingSeeder` in `DatabaseSeeder`.

**Geo address providers (updated):** `GEO_ADDRESS_PROVIDER=auto` uses free OpenStreetMap Nominatim by default, or Google Places API (New) when `GOOGLE_MAPS_API_KEY` is set; quote widget + booking wizard show live place suggestions and an OpenStreetMap embed after selection (`/api/geo/autocomplete`, `/api/geo/resolve`).

### Phase 8 — Booking engine and checkout

**Goal:** end-to-end booking from quote to confirmation (payments in test mode).

- Booking wizard (journey, vehicle, details, account, payment, confirmation) as a lazy-loaded Livewire component with resumable drafts.
- Guest and registered booking; corporate fields placeholders (completed in Phase 11).
- Booking, legs, stops, passengers, extras, status history; state machine; reference generator.
- Request-mode bookings (inside lead time / event dates) with admin approval.
- Confirmation page, `.ics` calendar file, basic transactional emails (confirmation, request received/approved/declined).
- My Booking lookup (reference + email, rate-limited, magic link) with booking detail view.
- Amendment and cancellation flows with the cancellation policy engine.
- Admin booking management: list with saved views, detail split-view, create on behalf of customer (phone bookings), edit, cancel, resend emails, timeline.
- Abandoned booking reminders (consent-based).

**Acceptance:** browser tests complete bookings for all journey types as guest and registered user; price re-validated server-side; amendments and cancellations apply policy correctly; admin can manage every booking.

**Implemented (13 Oct 2026):** `app/Modules/Booking` — bookings/legs/stops/passengers/extras/status history/drafts/amendments/cancellations; state machine; Crockford reference + manage/resume tokens; cancellation policy engine; `PlaceBooking` with server-side reprice; Livewire `BookingWizard` (journey → vehicle → details → account → payment) with resumable drafts; quote-widget “Continue to book” hand-off; confirmation page + `.ics`; lookup (`/my-booking`) + manage amend/cancel; admin list/detail/create/approve/decline/cancel/resend; abandoned-draft reminder job; pay_driver / pay_now_test until Phase 9 Stripe.

### Phase 9 — Payments and finance

**Goal:** secure payments and complete financial records.

- Stripe Payment Element with 3-D Secure, Apple Pay/Google Pay, saved cards (SetupIntent), payment links.
- Webhook inbox with signature verification and idempotent processing; payment state machine.
- Pay-on-journey (cash/in-car card) handling; PayPal option per decision.
- Refunds (full/partial) from admin; cancellation fees.
- Invoices (numbering, PDF, email), receipts after completion, credit notes, VAT configuration.
- Reconciliation and payment reports; finance exports.

**Acceptance:** Stripe test suite (success, 3-D Secure challenge, decline, insufficient funds, webhook retries) passes; no duplicate charges under retry; invoices/receipts generated correctly; refunds sync.

**Implemented (13 Oct 2026):** `app/Modules/Payments` — Payment / PaymentRefund / PaymentLink / PaymentWebhookEvent; `PaymentGateway` contract with Stripe + Fake drivers; PaymentIntent checkout (`/booking/{ulid}/pay`) with Stripe Payment Element (Apple Pay / Google Pay via automatic payment methods); staff payment links; signed Stripe webhook inbox + queued idempotent processor; payment state machine; admin refunds. `app/Modules/Finance` — sequential invoices/receipts/credit notes, VAT snapshot, admin finance hub + CSV export, print-ready invoice/receipt/credit-note views. Booking wizard `pay_now` creates `PendingPayment` then confirms via checkout/webhook; `pay_driver` issues invoice on place. **Admin → Settings → Payments / Finance** controls enabled payment methods, currency, link/checkout lifetimes, demo gateway, Stripe keys (secrets encrypted at rest; blank keeps saved / falls back to `.env`), VAT, document prefixes, payment terms, and bank details on invoices. Saved-card SetupIntent wallet UI remains for Phase 11 customer panel.

### Phase 10 — Notifications and communications

**Goal:** reliable, branded, multi-channel communication.

- Notification template system (per event/channel/locale, variables, preview, test-send, versions).
- Channels: email provider, SMS adapter, WhatsApp Cloud API adapter, web push, in-app notifications.
- Full notification catalogue (section 16.2), including driver details at T-minus 2 hours and flight-change alerts.
- Delivery logs, provider webhooks (delivery status), retries, resend, quiet hours, preferences, unsubscribe.

**Acceptance:** every catalogue event triggers the right channels with correct content; delivery statuses recorded; preferences respected.

**Implemented (13 Oct 2026):** `app/Modules/Notifications` — template catalogue with version history, `DispatchNotification` / `DeliverNotification` pipeline on the `notifications` queue, channel drivers (mail + log/fake SMS/WhatsApp/push + database in-app), delivery logs with resend, quiet hours, preference opt-outs, marketing unsubscribe, driver-details minute job, Admin → Communications hub (preview/test-send/seed), Admin → Settings → Notifications. Booking place/cancel/approve/resend, abandoned drafts and payment success route through `NotifyBooking`. Live Twilio / WhatsApp Cloud / VAPID adapters remain credential-gated (log drivers until keys are set).

### Phase 11 — Customer panel and corporate accounts

**Goal:** full self-service for passengers and businesses.

- Customer panel: dashboard, trips (upcoming/past/cancelled, detail, amend, cancel, rebook, calendar, share link, receipts), saved places, saved passengers, preferences (favourite driver, defaults), wallet (cards, invoices, receipts, outstanding payments), support requests, profile/security, data export, account deletion request.
- PWA install, offline shell, push notifications.
- Corporate: application and approval workflow, company members and roles, invitations, cost centres, PO/reference rules, approval rules and approval inbox, allowed vehicles, priority booking, credit limits and terms, invoicing cycles and statements, online invoice payment, bulk CSV booking, spend reports, account manager contact.

**Acceptance:** customers and corporate users can complete every self-service task on mobile; approval workflows and billing cycles verified by tests.

**Implemented (13 Oct 2026):** `app/Modules/Customers` — account dashboard (next trip + recent activity), trips list/detail with ownership checks, saved places and passengers (policies), wallet (outstanding bookings, invoices, receipts, payments), support cases (`SUP-` references), preferences/profile defaults; full `/account/*` routes wired to navigation. `app/Modules/Corporate` — public `/business/apply`, admin companies queue (approve/decline → Corporate Admin user + membership), `/business` panel (`panel.business` middleware) with dashboard, bookings, approvals, team invites, cost centres, statements, reports and settings; `company_id` / `cost_centre_id` on bookings. Pest: `CustomerPanelTest`, `CorporatePanelTest`. PWA, saved cards, data export/deletion, bulk CSV and full billing cycles remain for later hardening.

### Phase 12 — Operations: drivers, fleet, dispatch, flight tracking, driver app

**Goal:** run day-to-day operations from the platform.

- Vehicles, documents, maintenance, fleet-age warnings, equipment stock.
- Drivers: profiles, compliance documents with expiry reminders and auto-suspension, shifts, availability, vehicle assignment, performance, settlements.
- Dispatch: live kanban board, map, calendar/timeline, manual/suggested/auto-offer allocation, conflict detection, alerts, group coordination, waiting time and no-show handling.
- Flight tracking adapter, polling/webhooks, automatic pickup adjustments, alerts.
- Driver PWA: jobs, offers with countdown, status flow, waiting timer, expenses with receipts, navigation hand-off, location sharing, cash recording, offline queue, push.
- Real-time via Reverb (or fallback polling); customer live tracking and trip timeline.

**Acceptance:** a booking can be allocated, accepted, tracked live, and completed through the driver app with all status notifications; flight delay simulation adjusts pickup; compliance expiry blocks allocation.

**Implemented (14 Oct 2026):** Vehicles, compliance documents, maintenance, equipment stock and fleet-age warnings under Admin → Fleet. Child-seat stock is checked before a vehicle can be allocated. Driver profiles, documents, shifts, offer acceptance, punctuality, complaint counts and monthly settlements. Daily `operations:suspend-expired-compliance`. Dispatch board with manual allocation, drag-and-drop onto a driver’s usual vehicle, auto-offer, alerts, multi-vehicle groups, a same-day timeline, and an OpenStreetMap embed that refreshes on a configurable poll (`operations.board_poll_seconds`). Flight delays come from a tracker (simulated delays in config) via `operations:poll-flights`, applied once from the original pickup. Driver app: installable manifest, service worker, offline POST queue, offer notification, waiting timer, no-show fee, expenses with receipt photos, location sharing, schedule, earnings and profile. Status changes notify the passenger. Customer trip pages show the latest shared location. Live updates use polling rather than a new WebSocket dependency.

### Phase 13 — Support desk and CRM

**Goal:** structured handling of every enquiry and case.

- Unified support inbox (forms, complicated journeys, corporate applications, lost property, complaints, amendment requests); optional inbound email to ticket.
- Case management: priorities, statuses, assignment, SLA timers, internal notes, email replies, attachments, canned replies.
- Convert enquiry to quote/booking.
- Lost property register; complaints linked to drivers; review requests and moderation.
- Customer 360 view.

**Acceptance:** every form submission becomes a trackable case; SLA timers and notifications work; enquiries convert to bookings with prefilled data.

**Implemented (14 Oct 2026):** `app/Modules/Support` — admin inbox at `/admin/support` (priority, status, assignee, SLA, internal notes, public email replies, canned replies, attachments, complaint category and resolution). Public forms and booking amendment requests open a case unless a honeypot trips. Lost property records the item, who found it, storage location and return method. Review requests record an internal rating and can be marked sent when `SUPPORT_REVIEW_URL` is set. Customer 360 shows trips, payments, cases, notes, communications and marketing/terms consent. Optional email-to-ticket is `POST /support/inbound` with `X-Support-Token` once `SUPPORT_INBOUND_TOKEN` is set. `support:notify-sla-breaches` runs every five minutes. Pest: `SupportDeskTest`.

### Phase 14 — Reports, analytics, and conversion tracking

**Goal:** insight for operations, finance, marketing, and SEO.

- Admin dashboard widgets (customisable layout) and all reports in section 14.4 with filters, charts, exports, and scheduled email delivery.
- Consent Mode v2 analytics, data layer events (section 24), server-side conversion capture, UTM attribution.
- RUM Core Web Vitals collection and dashboard.
- SEO reports integration (GSC when enabled).

**Acceptance:** reports reconcile with raw data; analytics events fire only with consent; attribution stored on bookings.

### Phase 15 — Internationalization

**Goal:** multi-language readiness (and any launch languages from decision D-10).

- Locale routing, language switcher, translatable content fields, translated slugs and SEO meta, hreflang/x-default, translated notification templates, locale-aware formatting, RTL readiness.
- Translation management UI (with optional machine-translation draft aid).

**Acceptance:** a second test locale works end-to-end (pages, booking flow, emails, SEO tags) with valid hreflang and no performance regression.

### Phase 16 — Hardening, certification, content, and launch

**Goal:** production-ready, verified, and launched.

- Full regression suite; load testing (k6); OWASP ZAP baseline; permission audit; dependency audit; backup and restore test.
- Lighthouse certification of every public URL (not just templates): Performance ≥ 98 desktop / ≥ 95 mobile, SEO 100, Accessibility ≥ 95 (target 100), Best Practices 100; internal SEO audit 100 for every page.
- WCAG 2.2 AA manual audit with screen readers.
- Final original content, imagery, legal documents (reviewed), seed data (airports, routes, fares, FAQs).
- Production configuration: live API keys, Stripe live mode, email/SMS sender verification, DNS/CDN (Cloudflare), HSTS, monitoring and alerts.
- Switch `SITE_INDEXABLE=true`; submit sitemaps to Google Search Console and Bing; IndexNow ping.
- Staff training material (admin guide, dispatch guide, driver guide); handover documentation.
- Post-launch hypercare: monitoring of errors, Core Web Vitals, 404 log, conversion funnel; first optimisation iteration.

**Acceptance:** all checklists in sections 1.2, 19, 20, 21, 22, 25, and the feature-parity checklist (features inventory section 24) are signed off; site is live and indexable.

### Phase summary

| Phase | Name | Depends on |
| --- | --- | --- |
| 0 | Environment and project foundation | — |
| 1 | Design system and UI foundation | 0 |
| 2 | Identity, roles, security baseline, admin shell | 1 |
| 3 | Media Library (WordPress parity) | 2 |
| 4 | CMS and content modules | 3 |
| 5 | SEO module (Rank Math + Yoast parity) | 4 |
| 6 | Public website build | 5 |
| 7 | Geo, fleet catalogue, pricing engine | 2 (and 6 for widget wiring) |
| 8 | Booking engine and checkout | 7 |
| 9 | Payments and finance | 8 |
| 10 | Notifications and communications | 8 |
| 11 | Customer panel and corporate accounts | 9, 10 |
| 12 | Operations: drivers, fleet, dispatch, flights, driver app | 8, 10 |
| 13 | Support desk and CRM | 4, 10 |
| 14 | Reports, analytics, conversion tracking | 9, 12 |
| 15 | Internationalization | 6, 10 |
| 16 | Hardening, certification, content, launch | All |

---

## 36. Requirements traceability

Every section of the features inventory maps to the phase(s) that deliver it.

| Features inventory section | Delivered in |
| --- | --- |
| 2. User roles | Phase 2 (roles), 11 (customer/corporate), 12 (driver/dispatcher), 13 (support) |
| 3. Global website shell (notice, cookies, top bar, header, footer, shared UI, 404) | Phases 1, 4, 6 |
| 4. Homepage modules (hero, quote/hourly tabs, trust strip, how to book, benefits, fleet, reviews, clients, WhatsApp) | Phases 4, 6, 7 |
| 5. Quote, pricing, booking engine | Phases 7, 8 |
| 6. Payments, billing, invoices, receipts | Phase 9 |
| 7. Authentication and customer accounts (login, registration, reset, booking lookup, portal, corporate) | Phases 2, 8, 11 |
| 8. Enquiry and support modules | Phases 4 (forms), 13 (support desk) |
| 9. Service catalogue | Phases 4, 6 |
| 10. Fleet and driver modules | Phases 7 (categories), 12 (operations) |
| 11. Dispatch and journey operations | Phase 12 |
| 12. Notifications and communications | Phases 8 (basic), 10 (complete) |
| 13. Content, marketing, trust (about, reviews, logos, awards, FAQ, travel guide, info pages, CMS) | Phases 4, 6 |
| 14. Internationalization | Phase 15 |
| 15. Third-party integrations | Phases 7, 9, 10, 12, 14 |
| 16. SEO and discoverability | Phase 5 (+ Phase 6 implementation, Phase 16 certification) |
| 17. Analytics and conversion tracking | Phase 14 |
| 18. Security, privacy, compliance | Phases 0, 2, 9, 16 |
| 19. Accessibility and responsive behaviour | Phases 1, 6, 16 |
| 20. Administration modules | Phases 2, 7–14 |
| 21. Data entities | Section 26, created across phases |
| 22. Public route inventory | Section 27, Phase 5 (redirects), Phase 6 (pages) |
| 23. Known inconsistencies not to copy | Resolved by design: single reviews section, unified cancellation policy, consistent lead time, canonical URLs + redirects, no exposed credentials |
| 24. Feature-parity acceptance checklist | Phase 16 sign-off |

---

*End of development plan. Development starts only on explicit instruction, beginning with Phase 0.*
