= 1.8.11 =
A third "Why choose us" layout; square featured photographs shown whole; the filled button's label, its edge and its press.

* `includes/home/render.php`, `includes/home/admin.php`, `includes/home/cache.php`, `assets/css/home.css`, `assets/css/admin.css`: `why_style` takes a third value, `numbered` — "Numbered panels". Sanitiser, picker entry, `<select>` option, preset-allowed values and `section_why()`'s guard all accept it. No markup changed: the layout is a CSS variant over the same `<ol>` the other two styles render, so a site can move between the three without touching its reasons. The presets are untouched; all six still select `editorial`.
* The layout: the heading at the start of the section over the full width, then one outlined panel per reason, each led by a small numbered figure in the accent. It paints no ground of its own — the section stands on whatever the page's rhythm gives it, white or the warm paper, and the panels are a hairline over that rather than a fill, so one set of colours is at home on both. It invents no type size either: the heading takes the page's own `clamp(1.7rem, 3.2vw, 2.5rem)` title scale (the two older reason layouts set it a step larger, which suits the narrow column they put it in and not the full width this one uses), and the panel headings and copy take the scale every reason list already uses. The row is `repeat(auto-fit, minmax(min(280px, 100%), 1fr))`: two, three, four or five reasons each lay themselves out with no breakpoint named for them.
* Two traps met while writing it, both worth recording. A `max-width` in `ch` declared on the block *around* a heading is measured in that block's 16px text, not the heading's — 22ch came out at about 350 pixels and broke the title one word to a line; the measure now sits on the `h2`. And `.wptm-home__head` is a flex row with `justify-content: space-between` for the "see all" link at its far end, so the rule the heading carries as `::after` was being treated as that second item and parked against the opposite margin; the head is `display: block` in this layout.
* `.wptm-home__whynum` becomes the panel's first line at `clamp(1.05rem, 1.3vw, 1.25rem)`, its full stop drawn in CSS so the shared markup is untouched, and the pair isolated `ltr` so an Arabic page reads "01." and not ".01". An icon or an uploaded portrait keeps the far top corner, quietly, and the heading is padded clear of it. Hover raises the panel 3px, warms its border and draws a 2px accent rule along its top edge from the side the language starts on; both are dropped for reduced motion and coarse pointers.
* `assets/css/home.css`: `.wptm-home__section--featured-cards .wptm-card__inner` sets `--wptm-card-ratio: 1 / 1`, on the phone breakpoint as well. The library's tour photographs are square (800×800) and the card's 4:3 box with `object-fit: cover` was trimming a quarter of every one of them. Homepage grid only — the archive, hub, landing and activity cards keep 4:3, and the spotlight and ticket layouts keep their wide frames, since both are built around a wide picture.
* `assets/css/home.css`: `.wptm-home__btn--primary` is named twice (`a.wptm-home__btn--primary`) — `.wptm-home a { color: inherit }` is one class and one tag and was outranking it, so the label on a filled button took the page's ink instead of `--wptm-on-accent`: dark-on-gold by default, and near-black on a dark fill for any site with a deep accent. Same for `:hover`. The button also gains an inset hairline so its edge holds on a photograph, on the paper band and on a dark band; 2px more inline padding than block, set optically; a hover shadow mixed from the accent rather than grey; and a real pressed state (`translateY(1px)` with an inset shadow) in place of the previous return to zero. `.wptm-home__btn--ghost`: `1.5px` → one crisp pixel held at 74% of `currentColor`, firming to full on hover — a 1.5px rule resolves at 1x as one solid pixel and one half-lit one.
* `assets/css/home.css`: a featured card no longer lifts twice. The list item carried a 4px rise on `.25s` while `.wptm-card__inner` inside it was already rising 3px on `.35s`, so the same card travelled seven pixels on the homepage and three on the archive, on two timings. The item stays still; `card.css` does the moving. `.wptm-home__tilecount` is a small solid ink chip instead of a blurred translucent pill, which was leaving white numerals on a pale sky.
* `assets/css/admin.css`: a new miniature part pair, `headleft` and `figures3`, so the picker card shows what the layout actually is.
* `home.min.css` and `admin.min.css` regenerated from their sources and checked against them pixel for pixel in Chromium. `php -l` on every file. Verified in a static harness at 1440, 1024 and 390: LTR and RTL, Arabic copy, two/three/five reasons, a gold accent and a deep green one, no horizontal overflow at any width.

= 1.8.9 =
Card grids keep their own height; the listing card is placed against itself; the sidebar information cards resized.

* `assets/css/card.css`: `.wptm-cards` takes `align-content: start`. A grid with automatically sized rows stretches them to fill any height its box is given, and a theme whose content column is a grid or a flex column with `min-height` gives it one; the row grew, `.wptm-card__foot` (`margin-block-start: auto`) went to the bottom of it and a band of white sat between `.wptm-card__facts` and the price. Reproduced by giving `.wptm-cards` a height taller than its content and measured before and after in Chromium.
* `assets/css/card.css`: `.wptm-card__inner` is `position: relative` — `.wptm-card__peek` is `position: absolute` and had no positioned ancestor of its own, so on a theme with none it was placed against the initial containing block. `.wptm-card__inner` is also a flex column and `.wptm-card__link` a `flex: 1 1 auto` child of it, in place of the `height: 100%` chain, which needed a definite height on every box above it. New guard block: `.wptm-card__media` cannot grow, `.wptm-card__img` keeps `height: 100%` against a theme's `img { height: auto }`, and `::before`/`::after` on `.wptm-card`, `.wptm-card__inner`, `.wptm-card__link` and `.wptm-card__body` are cleared (the plugin draws none on those four).
* `assets/css/home.css`: the same `align-content: start` for `.wptm-home__tiles`, `.wptm-home__acts`, `.wptm-home__guides`, `.wptm-home__team`, `.wptm-home__why`, the homepage's `.wptm-cards` and `.wptm-ticket-list`.
* `assets/css/tour.css`: `.wptm-info__title` is `1.05rem` instead of `clamp( 18px, 2vw, 23px )` — a size that grew with the window while the sidebar did not, and read louder than the ticket's own heading. `.wptm-info__badge` 56px → 42px with its mark sized in CSS (`20px`), `.wptm-info__lead` `.875rem`, `.wptm-info__rows li` `.9rem` on 10px of padding, `.wptm-info__icon svg` 17px, the WhatsApp button's top margin 18px → 14px, and the card's padding written as `padding-block`/`padding-inline` so the badge's side is the start in Arabic as well. Under 520px the badge is 40px and the padding even.
* No PHP changed. `card.min.css`, `tour.min.css` and `home.min.css` regenerated from their sources; `php -l` on every file.

= 1.8.8 =
Good to know in the column, Need help redrawn, Book via WhatsApp, ticket reassurance row.

* `templates/sections/goodtoknow.php` (new), `includes/class-tour.php`: `goodtoknow` joins `Tour::sections()` after `bring`; the section prints `important_info` through `wpautop()` inside `.wptm-know` (a tinted panel, accent rule at the start, an info mark, a dash per list item) — styles in `tour.css`. Every design loops `sections()`, so all eleven layouts carry it. `templates/sections/sidebar.php` no longer prints the "Good to know" card.
* `templates/sections/sidebar.php`: the help card is `.wptm-info--help`; phone and e-mail stay rows, WhatsApp is `a.wptm-btn--whatsapp.wptm-info__whatsapp` outside the text column and spanning the card (`grid-column: 1 / -1`). Ticket WhatsApp buttons (both placements) and `book-now.php` use `.wptm-btn--whatsapp` with `icon( 'whatsapp' )` and the new `cta_whatsapp_book` label; `includes/tour/booking.php` uses it for the WhatsApp booking mode. `cta_whatsapp` ("Continue on WhatsApp") stays for the help card.
* `includes/icons.php`: `whatsapp` glyph (outline, same stroke family). `includes/i18n.php`: `sec_goodtoknow`, `nav_goodtoknow`, `cta_whatsapp_book` in EN/FR/ES/AR.
* `assets/css/tour.css`: `.wptm-btn--whatsapp` (WhatsApp green, white type, hover/focus states); the mosaic ticket's `.wptm-salma__assure` is a flex row with `flex: 1 1 0` items instead of `repeat(3, 1fr)`; `.wptm-tour--mosaic .wptm-tk__row dt` is `white-space: nowrap` with `flex: 0 0 auto` and the `dd` takes the remainder. `assets/css/salma.css`: the panel's own assure row is flex as well, stacking under 380px.
* Verified on a local WordPress 7.1 (SQLite) with demo content: homepage, tour page (mosaic and Salma), archive, admin screens; `php -l` on every file; WPCS security/escaping/i18n sniffs clean on the changed files.

= 1.8.7 =
Tour cards redrawn, with a strip of gallery photographs; related tours levelled; booking panel, itinerary and platform-strip fixes.

* `includes/card.php`, `assets/css/card.css`: the shared card (homepage featured grid, landing pages, hubs, `[wptm_tours]`, the Tours block, activity cards). The duration chip leaves the photograph for a `.wptm-card__facts` line (duration, next departure) under the lead; the pin icon before the place is gone and the place is one ellipsised line; `.wptm-card__title` clamps at two lines with a two-line minimum; the foot is price (lowercase "from" + amount + basis + group note) on the left and a `.wptm-card__cta` word with an underline that draws out on hover on the right, on one hairline; the badge is a square-cornered tag. The activity card prints the same structure. `.wptm-card__days` and `.wptm-arrow` are no longer printed (`.wptm-arrow` keeps a rule for third-party markup).
* Gallery strip: `Card\peek_images()` reads `$tour->gallery()` less the hero when Settings > `card_gallery` is on (default `1`), primes the attachments in one call, and `Card\tour()` prints `.wptm-card__peek` — a `<span hidden>` of `<button class="wptm-card__thumb" data-wptm-src data-wptm-srcset data-wptm-alt aria-pressed>` (the hero first, then up to three) and a `.wptm-card__more` count — as a sibling of the link, never inside it. The strip's box is the picture's box: both read `--wptm-card-ratio` on `.wptm-card__inner` (4/3; 16/10 under 600px). New `assets/js/card.js`, registered as `wptm-card` in `includes/assets.php` (footer) and enqueued by the first card that prints a strip: unhides the strip, swaps `src`/`srcset`/`alt` on `mouseenter` (fine pointers) and `click`, preloads the next picture and dims the current one meanwhile (`.is-switching`), wires cards added later through a MutationObserver. Filter `wptm_card_peek_images`; `tour()` argument `peek => false` turns it off per call. Reduced motion, forced colours, RTL and coarse pointers (strip always shown, 40px thumbs) handled in `card.css`.
* `includes/settings.php`: `card_gallery` sanitised as a checkbox and drawn under "Tour listings" as "Photos on tour cards".
* `includes/i18n.php`: `salma_min`, `card_photo_of`, `card_more_photos` in English, French, Spanish and Arabic, with translator notes.
* `templates/sections/related.php`, `assets/css/tour.css`: a `.wptm-related__body` column; `.wptm-related__place` on one line with an ellipsis; `.wptm-related__title` clamped to two with a two-line minimum; `.wptm-related__foot` is duration (clock mark) at the start and price (`.wptm-related__from` + amount) at the end on one hairline, the group note on a full-width line beneath; `.wptm-related__cta` takes the auto margin so it sits level across a row.
* `templates/salma-design/parts/booking.php`: `$wptm_min` from `group_min` (only when above one and not above the ceiling) sets the field's `min` and its starting value, and prints `salma_min` under the field; the `salma_max` line is no longer printed. The default reassurances are the two promises; stored `trust_items` are filtered through `Archive\is_confirmation_line()`.
* `includes/archive-sidebar.php`: `is_confirmation_line( $title, $note )` extracted from `side_trust()` and shared with the Salma panel; behaviour of the sidebar unchanged.
* `assets/css/tour.css`: `.wptm-timeline__content > .wptm-prose { max-width: none }` — the itinerary's copy fills its column; beside a photograph the column is narrow by construction.
* `assets/css/home.css`: the platform strip's scrollable-list form is keyed to `:has(.wptm-home__platformlink:focus-visible)` instead of `:focus-within`, so a mouse press no longer re-lays the strip under the pointer (which lost the click and left a scrollbar); `:hover` still pauses the animation.
* Verified in Chromium: cards at 1200px and under a coarse pointer, the thumbnail swap, the related row levelled with and without a group note; the strip pauses on hover, opens the link on click without a layout change, resumes when the pointer leaves, and becomes a stable list on keyboard focus. `php -l` on every file; the WordPress Coding Standards security, escaping, nonce, i18n, enqueue and prefix sniffs report zero errors on the changed files.

= 1.8.6 =
Homepage dialogs centred; coding-standards pass.

* `assets/css/home.css`: `.wptm-home__dialog` now declares `position: fixed`, `inset: 0`, `margin: auto`, `transform: none` and `box-sizing: border-box` itself (weighted to survive a theme reset such as `* { margin: 0 }` or `dialog { position: absolute }`), a `max-width` inside the viewport, `height: fit-content`, and `:not([open]) { display: none }`. The photo viewer keeps `width: fit-content` and gains a `max-height` of the viewport. No markup or script change: `showModal()`, Escape, focus trapping and the arrow keys are as before.
* `includes/admin/builder.php`: the `visual.previewFailed` string was declared twice; the second (served) wording is kept. Translator notes on `confirmDelete` and `undone`.
* `includes/cars/admin.php`, `includes/home/admin.php`: translator notes on the repeater row labels (`Vehicle %d`, `Trust item %d`, `Review %d`, `Person %d`).
* `includes/db.php`, `includes/departures.php`, `includes/quotes/quotes.php`, `includes/payments/payments.php`: the placeholder-list queries carry `phpcs:disable`/`enable` pairs naming both PreparedSQL sniffs, so a multi-line `prepare()` is annotated on the line the sniff reports.
* `includes/operations/manifest.php`, `includes/payments/receipt.php`, `includes/sections/editor.php`, `includes/partners/commissions.php`, `includes/tokens.php`, `includes/admin/departures.php`: sniff annotations with the reason on the `$GLOBALS['title']` and `$GLOBALS['post']` restores, the CSV `php://output` stream, the base64 transport encoding of keys and the return address read after each handler's own nonce check.
* Verified: `php -l` on every file under PHP 8.4; WordPress Coding Standards (security, escaping, nonces, sanitisation, prepared SQL, enqueues, i18n, prefixing) report zero errors.

= 1.8.5 =
Booking management and Guest Pass preparation, on the existing admin.

* Booking header (`includes/booking/admin.php`): reference, customer, travel date, tour thumbnail, saved status badge and one primary Save booking that submits WordPress's own `form#post`. The status select mirrors a chosen-but-unsaved status into the header as a dashed chip; the saved badge is dimmed while a change waits. `Booking\visibility_label()` prints the "Shown to guest" / "Internal only" pills, one per field group.
* Guest Pass details (`includes/guest-pass/admin.php`): `summaries()` reads `GuestPass\data()` so each closed disclosure shows the saved pickup, guide, vehicle, payment and emergency contact with the tour, crew, ledger and site fallbacks already applied, plus a note naming the fallback. `guest-pass-admin.js` marks a section "Unsaved changes" when a field differs from what was loaded and never redraws the saved text. `picker()` draws compact thumbnail pickers with empty-state lines; `admin-fields.js` now dispatches `change` on the hidden id and hides the empty line and Remove after a choice.
* Preview: `preview_controls()` offers "Preview saved Guest Pass" wherever a saved pass exists. The frame fills only on click, with the existing reference-only staff address (no key, existing capability and status checks in `GuestPass\current()`), and blanks on close; "Open in a new tab" stays for browsers without JavaScript. Regenerate and Revoke moved under "Manage access" with their existing nonces and single confirmation.
* Thumbnails: `Cars\thumbnail_url()` (library thumbnail or the external address as is) feeds the vehicle name field and the crew vehicle select (`data-image` per option; admin.js swaps the picture).
* Bookings list: Customer (name, e-mail, country), Travelers (total plus adults/children), `<time>` travel and submitted dates, "Open booking" as the first row action, column widths. Queries, sorting, filters and search untouched.
* History (`history_box()`): timeline with status-coloured dots, `<time>` stamps, "by operator", "Details updated" for trip edits, long notes behind a disclosure. Single source: `_wptm_history`.
* Styles scoped to `.post-type-wptm_booking` and plugin classes; `[hidden]` honoured on the new chips; mobile `.form-table` fixed width on booking screens; crew Save button no longer stretched by the panel grid.
* Homepage (`assets/css/home.css`, `includes/home/render.php`): the tile grids read the `wptm-home__tiles--count-N` class the renderer already printed — 2, 3/6/9, 4/8 columns, 3+2 for five, 4+3 for seven — above 1000px only; three-across destination tiles are 5:4 instead of 4:5; the gallery count class is printed for every count and `--6` draws three columns with an ordinary sixth tile (still big on a phone's two columns); the search bar's button track is `auto` with a one-line label instead of a 172px floor that wrapped it and pushed it past the bar's edge; trust-bar figures are larger than their captions.
* Tour page review (`templates/sections/*`, all eight designs): `booking-form.php` ids are suffixed for the second copy through `Booking\form_instance()` (a function static; the file-level `static` the Salma part used resets per include without an opcode cache, so that part now uses the same helper); `booking-modal.php` prints `hidden` and the script only clears it; `gallery.php` drops `fetchpriority=high`/eager on the stage when an Immersive hero is above it; `sidebar.php` resolves a library `ticket_bg` to an attachment and prints it at `medium_large` with `sizes`, cached per request; `features.php` returns before an empty list; `tour.css` gains `:focus-visible` on FAQ summaries and gallery thumbnails and an accent on a hovered question. No structured data, headings or field names changed.
* Verification: stub-WordPress harness under E_ALL for four booking states plus RTL, Playwright at 1280/782/390 (231 checks), the existing handler and form-ownership tests rerun. No real WordPress save, Polylang or Rank Math run was possible in the build environment.
* Homepage verification (`tools/tests/home-page/render-home.php`): every section rendered with the real home module against a fixture config under E_ALL, zero notices; Chromium at 1440/1280/1024/900/782/640/390 for the three rhythms and RTL — no overflow, one H1, no duplicate ids, alt on every image, balanced tile rows at every width, search button on one line inside the bar (28 checks); tile counts 5, 7 and 8 checked separately.
* Tour-page verification (`tools/tests/tour-page/`): the eight design templates rendered with the real sections against a mock Tour under E_ALL, zero notices; Chromium audit at 1280/782/390 plus RTL — one H1, no skipped heading levels, no duplicate ids, alt on every image, at most one fetch-first image, `loading` on every image, named links and buttons, resolving in-page anchors, `noopener` on new-tab links, labelled fields, no structured data, no horizontal overflow (171 checks).

= 1.8.0 =
Visual Builder 2.0 — edit the homepage, the car-hire page and every page made from a starter with a live preview.

* A two-panel editor on the three builder screens (`assets/js/builder-visual.js`, `assets/css/builder-visual.css`, `includes/admin/builder.php`): the settings on the left, a real preview of the page on the right rendered by the same PHP that renders the front end, refreshed a moment after each change through a per-user draft that is never written to the saved options or the page until you press Save.
* Builder Preview Mode (`includes/builder/preview.php`): the front end renders the draft only when a signed builder link is opened by a user allowed to edit that page; the preview is never cached, never indexed and never shown to visitors.
* Click a section in the preview to open its settings; a toolbar in the preview moves, hides or shows the section under the pointer.
* A section navigator with drag-and-drop reordering, arrows, duplicate (page sections), hide/show, delete with confirmation and a lightweight Undo (Ctrl+Z) for reorder, hide, duplicate, delete and field changes.
* Desktop / tablet (768px) / mobile (390px) preview widths, an "Edit visually" switch remembered per screen, a sticky header with the page title, Saved / Unsaved changes state, Undo, Preview and Save.
* "+ Add section" panel: categorised, searchable section cards on pages; for the homepage and the car-hire page the sections that are currently hidden.
* Every section has a name and a category in the registry (`includes/sections/editor.php`); hidden and empty sections show a placeholder in the preview only.
* Storage is unchanged: pages stay plain block content, the homepage and car-hire page keep their options; disabling visual mode leaves the classic forms exactly as in 1.7.7. Builder assets load only on the three builder screens.

= 1.7.7 =
Full-width pages, and search-engine tools in the page builder.

* A "Tripvance full-width page (no sidebar)" page template (`tripvance-page.php`, `templates/page-full.php`, `includes/site-page.php`): the theme's header and footer with the page's sections between them at full width, the hero and the closing band reaching both edges of the window and plain text kept at a readable width. Every page made from a starter now uses it; the page builder's first panel offers "Full width — no sidebar (recommended)" or "Theme layout" and warns when a page is still on the theme layout. A theme may override the template by shipping the file at its root or under /tripvance/.
* Starters set the hero title to h1, and the template prints a hidden h1 with the page title when the content has none, so every page has exactly one main heading.
* Page builder, first panel: the page's web address (slug) with the site address beside it, and the layout choice.
* Page builder, new "Search engines and sharing" panel: main phrase, title in search results, description, short summary (excerpt) and the picture shown when the page is shared (the featured image, chosen from the media library). When Rank Math, Yoast SEO or SEOPress is active these are written into that plugin's own fields so its checks and output use them; otherwise Tripvance prints the title tag and the description itself. A checklist updated as you type: phrase set, title and description lengths, phrase in the title / description / main heading / text, one H1, 300 words, pictures with descriptions, a questions section, links to tours or activities, a way to get in touch, a short address carrying the phrase, a sharing picture, full-width layout.
* Media picker: a `[data-wptm-media-id]` hidden input beside a picker receives the attachment id.

= 1.7.6 =
A page builder for every page made from a starter, and fixes on the homepage screen.

* Page builder (Site pages → Open the builder, or "Edit with Tripvance" on the Pages list). A page made from a starter — About us, a destination guide, a tour collection, Activities, Contact, Blank — opens as the same kind of form as the homepage: one numbered panel per section of the page, a label and help line on every field, a Save button and a Preview link in every panel, a side navigation, and Publish / Take off the site buttons pressed on purpose. Sections are added from a list (every Tripvance section, plus a heading, a paragraph and a picture), moved with arrows and removed. Filters that used to want slugs — destinations, tour types, categories, durations, departure points — are lists of boxes; specific tours are ticked by name; questions and trust items are rows to fill in; pictures come from the media library. Blocks the form does not know (a gallery, an embed, another plugin's block) are carried through untouched and can still be edited in the WordPress editor, one link away. The form reads and writes ordinary block markup, so both editors edit the same page.
* Creating a page from a starter now opens the page builder rather than the block editor.
* Homepage: the layout table is a proper table again (its class name collided with the builder's side-by-side grid, which split the header from the rows); the drag handles are gone in favour of the arrows, so `home-admin.js` is no longer shipped; the three-width preview is folded away until asked for.
* Status wording across the page screens: "Live on the site" and "Not published yet" instead of Published and Draft.
* Shared builder: `Builder\panel_open()` takes extra attributes; `media_field()` accepts a media library id where a section allows one; the script exposes its enhancers so a panel added later is enhanced like the rest.

= 1.7.5 =
The homepage and the Site pages screens get the same treatment the car hire screen got in 1.7.4, on one shared builder.

* One builder for both page screens. The panels, side navigation, per-section Save, repeaters, media pickers and icon lists that 1.7.4 built for the car hire screen now live in `includes/admin/builder.php` and `assets/js/builder-admin.js` and serve the homepage too; the car hire screen runs on the shared code unchanged in behaviour. `cars-admin.js` and the React field enhancer `section-admin.js` are gone.
* Homepage: fourteen numbered panels with a plain-language introduction each, a sticky side navigation showing which panels are filled in, empty, hidden or unsaved, and a Save button plus Preview link at the foot of every panel that returns the screen to that panel opened, with a "Saved" mark. The old "Save section" button in each heading is replaced by the footer one; the whole-form button remains for browsers without JavaScript.
* Homepage layout table: a description of each section, up/down arrows beside the existing drag handle, and an "Edit content" link to the panel that fills it. Each panel header carries "Shown on the page" switches mirrored to the table — the tours panel carries six, one per section it feeds.
* Homepage lists: trust items, team members and testimonials are cards with a label on every field; hero assurances, floating images, platform logos, reasons and questions are add/remove/reorder lists. Empty rows are hidden until added; the stored order follows the screen.
* Homepage pictures — hero photo and its description, video poster, platform logos, team photos, traveller photos, reason photos, floating images — are chosen from the media library with a thumbnail beside the address. Icons are picked from a named list with a preview instead of typed as keys; the hero assurances keep their allow-list.
* Homepage buttons: the hero and closing-band link fields accept `#section` jumps, as the settings text recommends; the WhatsApp switches show the number they will use and warn when none is set; a "How to set this page up" card ticks itself off; presets are folded under a disclosure; a multilingual note says which language to type in.
* Language tabs: the translation form is folded into the same panels, one per group, each with its own "Save the … words" button that returns to that group; groups with nothing to translate are not printed.
* Site pages: the create form reads as three numbered steps, starters that need a destination or a tour type say so and are disabled with an explanation when none exists yet, the page list shows the language on multilingual sites, coloured status pills, "Open the builder" / "Edit in the editor" / "Index settings" as the first action, Preview for drafts, and the sections library is folded away.
* The generic "On this page" row of links no longer duplicates the builder navigation on these screens.
* Storage, field names, sanitisation and front-end rendering unchanged for both pages.

= 1.7.4 =
Car hire settings, rebuilt for an owner with no technical background.

* Every section carries its own Save button (and a Preview link), and saving brings the screen back to that section, opened, with a "Saved" mark on it. No more scrolling to the bottom of a long page. A "Save all" button also sits in the side navigation.
* Fifteen numbered panels, one per section of the page, with a plain-language introduction and a worked example in each. A sticky side navigation on wide screens (a scrolling row on narrow ones) shows at a glance which panels are filled in, empty, hidden on the page or carrying unsaved changes. Panels remember which one was open last.
* Page layout is now a table with a description of each section, up/down arrows to reorder (the numbers still work), and an "Edit content" link to the section's panel. Each panel header also carries a "Shown on the page" switch mirrored to that table.
* The fleet, the trust items and the reviews are cards with a label on every field instead of ten-column tables that scrolled sideways. Rows in every list can be added, removed and moved up or down; the stored order follows the screen order.
* Pictures — hero background, vehicle photo, partner logo, passenger photo, closing band — are chosen from the media library with a thumbnail beside the address, which can still be pasted. Choosing the hero picture fills in its description from the library when that is empty.
* Icons are picked from a named list with a live preview instead of typing a key from memory. A key saved earlier that is not on the list is kept.
* The request form is picked from the forms already on the site — Contact Form 7, WPForms, Gravity Forms, Forminator, Fluent Forms — and the shortcode is filled in; pasting one by hand still works.
* The WhatsApp switches show the number they will use and link to the setting; when no number is set, they say so instead of silently printing nothing.
* A "How to set this page up" card with five steps that tick themselves off, and a note on multilingual sites saying which language to type in.
* Fixed: a hero or call-to-action button link could not be set to #wptm-cars-request, as the settings text recommended, because the field only accepted full addresses.
* The browser warns before leaving the screen with unsaved changes. Presets are folded away under a disclosure; the everyday control is the layout table.
* Storage, field names, sanitisation and the front-end rendering are unchanged; existing configurations load as they were.

= 1.7.3 =
* Refines the navigation language switcher so its globe, label and caret sit on the same optical baseline as neighbouring menu items and inherit the theme typography without an internal line-height mismatch.
* Makes the globe lighter and closer to the text size, and reduces the caret weight/size so the control reads like a native menu item rather than a separate widget.
* Keeps Polylang URLs, menu placement, mobile behaviour, accessibility states, keyboard controls and all existing settings unchanged.

= 1.7.2 =
* Refines the Car hire admin builder with a setup checklist, collapsible sections, quick navigation, compact add/remove repeaters, a visual media-library gallery adapter, and a sticky save action. Existing Car hire storage and frontend rendering remain compatible.

== Changelog ==

= 1.7.0 =
Activities become products: an activity can carry its own price and take booking requests directly, through the same engine tours use.

* Pricing panel on the activity editor: price, currency, per person / per group / on request, starting-price switch, an optional pre-promotion price shown struck through, and a price note. Stored under the tour meta keys the pricing engine already reads, so quotes, snapshots, seasons, extras and the currency formatter apply unchanged.
* Booking panel: an on/off switch (off for every existing activity, so nothing changes without a decision), booking mode (site setting, form, WhatsApp, external URL), custom button text, booking instructions, minimum and maximum participants, minimum notice.
* One booking engine. The booking handler, the date lookup and the live price preview accept any product type declared through the new `wptm_booking_post_types` / `wptm_booking_product` filters; a request from an activity is validated, priced, held in the reservation ledger, mailed and listed with the tour bookings, and a minimum-party rule is enforced server-side through `wptm_booking_validate`.
* A redesigned activity page: price and button in the header, a sticky booking panel beside the content on wide screens, a price bar on phones, highlights and "good to know" sections (two new optional fields), the confirmation section a tour shows after a request, and the same booking.js driving the form. An activity with no price and no booking renders as before, in one column.
* Site pages. A new Pages screen (Tripvance → Site pages) lists every page the plugin has a hand in and makes new ones from starters — About us, Destination guide, Tour collection, Activities, Contact and booking, Blank — opened straight in the WordPress editor as ordinary draft pages the owner writes the way they want. The sections they are built from are a new library, each a block (under "Tripvance" in the inserter, previewed live) and a shortcode for the classic editor or any page builder: `[wptm_hero]`, `[wptm_trust]`, `[wptm_cta]`, `[wptm_faq]`, `[wptm_contact]`, `[wptm_reviews]`, `[wptm_guides]`, `[wptm_destinations]`, `[wptm_tour_types]`, beside the existing `[wptm_tours]` and `[wptm_activities]`. Drawn on the plugin's own tokens, RTL-ready, with the site accent. Filters `wptm_sections`, `wptm_page_starters`, `wptm_page_starter_content`; action `wptm_page_created`.
* A calmer admin. One "Tripvance" menu with seven entries — Dashboard, Tours, Activities, Bookings, Operations, Site pages, Settings — instead of twenty-six entries under Tours plus a separate Activities menu. Every other screen keeps its address and lives in one of the groups: a row of tabs at the top of each Tripvance screen moves between the screens of its group, the sidebar stays on the group whichever screen is open, and the dashboard lists everything. Long settings screens gain an "On this page" row of section links. The classic menu is one setting away (Settings → Admin menu). New filters `wptm_admin_menu_groups`, `wptm_admin_menu_untabbed`, `wptm_compact_admin_menu`.
* `Card\activity()` — the shared card, for activities — and a `[wptm_activities]` shortcode (destination, place, type, ids, count, order, columns, bookable). The homepage activities section shows duration and price where set; the booking list filter offers bookable activities under the tours.
* Standards pass on the front end. Every small label the modules print now clears WCAG AA contrast: the muted grey tokens were darkened from 3:1 to over 4.5:1 on white across tokens.css, tour.css, home.css and sections.css, and eyebrows and kickers that were set in the bright accent take a new `--wptm-gold-text` shade — the same hue, darkened until it passes, derived from the site accent where the browser can mix colours. One shared focus ring and a reduced-motion switch for every module, a fuller print stylesheet for the activity page, and `content-visibility` on its gallery and tour cards so a long page paints its first screen sooner.
* An activity archive. With the archive switched on, `/activities/` and each activity type's page are drawn by the plugin — title, lead, count, a row of type links with the current one marked, the activity cards (h2 each, first row eager), pagination and an empty state — instead of the theme's blog listing. Theme overrides: `archive-wptm_activity.php`, `taxonomy-wptm_activity_type.php`, `tripvance/archive-activities.php`. Breadcrumbs on an activity now run Home → Activities → Type → Activity rather than through Tours; new filter `wptm_breadcrumb_trail`.
* SEO, without a competing schema. The page that follows a booking request (the one-use `?wptm_b=` address) is `noindex, follow` through `wp_robots` and Rank Math's filter, so a confirmation is never indexed as a copy of the tour. The activity hero is preloaded like the tour hero. The hero section defaults to an h2 title, since the theme usually prints the page title as the h1. Card images carry the alt text written in the media library when there is one. Guide and review dates are `<time>` elements.
* Faster pages. Every stylesheet and script ships with a minified copy (`tools/minify.sh` rebuilds them) and the plugin serves the `.min` file whenever it is current — 213 KB → 118 KB for the tour stylesheet before compression — falling back to the readable source under `SCRIPT_DEBUG`, when the copy is older than its source, or through `wptm_use_minified_assets`. `Card\prime()` warms posts, meta, terms and featured images for a row of cards in three queries and is used by the shortcode grids, the hub, landing and car-hire pages, the sections library and the activity page; the related-activities scorer reads candidate meta in one query instead of one per candidate.
* Activities list in the admin gains Price and Booking columns, read the way the card reads them.
* New `decimal` field type in the native form helper. New strings on the registry with French, Spanish and Arabic tables. No migration, no change to tours, URLs, relationships or existing data.

= 1.6.1 =
Hardening release for pricing, scheduled departures and the operations workflow.

* Persists unambiguous pricing choices and scheduled mode for tours that already have departures during an idempotent schema-v5 upgrade, while preserving legacy data.
* Keeps quote-only tours free of placeholder prices and uses the configured traveller rates consistently, including a neutral 100% child-rate default.
* Filters scheduled choices to departures that are genuinely bookable, applies inherited capacity consistently and prevents overselling in both public and staff booking paths.
* Serialises quote decisions and manual-ledger changes per booking, preserves one accepted offer and refuses mixed currencies or impossible refund totals.
* Expands guide, driver and vehicle conflict checks across the full trip and allows staff check-in only on confirmed bookings during the trip dates.
* Runs pending data migrations on activation and repairs missing tables without marking an incomplete upgrade as successful.

= 1.6.0 =
The operations release: a booking request now has a whole working life inside WordPress — quote, acceptance, payment tracking, confirmation, crew and vehicle assignment, the trip itself, completion and a review request.

No payment gateway is added, and none is planned. Money moves outside the website by bank transfer, a PayPal link, cash or whatever the agency has arranged; the plugin records what arrived. WhatsApp is a click-to-chat link and a copy button — no Business API, no automatic sending, no delivery tracking.

**Money the customer agreed to**

* Quotes. A versioned offer per booking with its own private page at /quote/{id}?k={key}, a public note, what is included and excluded, an expiry date, and Accept and Decline buttons in the customer's own language. The accepted quote is a frozen snapshot: editing a tour's prices afterwards never changes what somebody already agreed to.
* Manual payments. A ledger of what actually arrived — amount, currency, method, date, reference — with received, refunded and outstanding computed from the rows rather than typed. One currency per booking, enforced. A printable receipt.
* Server-side pricing. Adult and child rates, party-size bands, seasonal rates and optional extras are calculated once, on the server, and the booking form asks for the figure rather than doing arithmetic in the browser. Per-group tours are one price for the party, never multiplied per traveller.
* Payment instructions the agency writes once: bank transfer details, a PayPal.me link, cash on arrival, or anything else. Shown on the quote, the confirmation and the travel pass. No credentials of any kind are ever asked for or stored.

**Getting the trip out of the door**

* Scheduled departures with real capacity. Seats are held atomically, so simultaneous requests cannot oversell a departure; cancelling releases them.
* Guides, drivers and vehicles as records, with an assignment board that refuses to double-book somebody and warns before a licence expires.
* An operations board of exceptions — what has not been done yet — and a printable departure manifest.
* An outbox for every message the plugin sends: queued, retried, visible when it fails, and re-sendable by hand. A new request no longer carries a voucher or a pass; confirming the booking is what creates them, exactly once.

**Guest Pass 2.0**

* The meeting point as structured facts: landmark, address, map link, coordinates you paste yourself, a photograph, and an honest answer about whether a vehicle can reach it. Nothing calls a geocoding service.
* Two things the guest can press: "the pick-up is right" and "I have arrived", both timestamped and rate-limited.
* A clear notice when the meeting point moves after they confirmed it.
* Staff QR check-in from a scanner, a pasted link or a typed reference. A repeat scan of the same guest on the same day changes nothing, and staff without permission over that booking cannot check anybody in.

**Partners, customers and what is working**

* Referral partners — riads, concierges, drivers — each with a code, a link, and a fixed or percentage commission calculated from the accepted quote. Commissions are tracked as due, approved and paid, with a date, a note, a monthly report and a CSV export. Commission data is never public.
* A customer directory keyed by the normalised e-mail address, so one person is one row however they typed it: booking history, preferred language, an internal note and a review-request state. Built from existing bookings on demand.
* First-party attribution: the referring host, the five UTM values, a partner code and the landing tour, captured only when a link actually carries them. No third-party analytics script, no identifier that follows anybody. An Insights screen shows the funnel, conversion rates, agreed and received money, outstanding balances, top tours and top partners. `wptm_analytics_event` is there for a site that wants to connect GA4 or anything else itself.
* The WordPress privacy exporter and eraser now cover quotes, payments, the WhatsApp log, attribution and the customer record. Erasing closes every private link the person held.

**Upgrading**

* Schema version 4, applied idempotently under its own option. Five tables are added (outbox, quotes, payments, assignments, customers) and the departures table gains an end time. Nothing existing is renamed, moved or deleted, and no data is destroyed on upgrade, deactivation or uninstall.
* New capabilities `*_wptm_resources` and `*_wptm_partners`, granted to the roles that already manage bookings and filterable separately.
* New options wptm_payments, wptm_whatsapp, wptm_quote_route. New private post types wptm_guide, wptm_driver, wptm_vehicle and wptm_partner, all out of REST, search, archives and sitemaps.
* Plugin slug, text domain and every public URL are unchanged.

= 1.5.0 =
New: the Digital Guest Travel Pass.

* Every confirmed booking gets its own private, mobile-first page at /trip/{reference}?k={key}: pickup time and meeting point with directions, the assigned guide, the vehicle, the itinerary as a timeline, a voucher QR, a payment summary, what to bring, an emergency contact, a WhatsApp button and a weather link. A cancelled booking keeps its pass and says so, without travel actions. Reads the booking's language (Polylang) and mirrors for Arabic.
* Security: the reference in the address is for humans; access needs a 32-byte random key that is stored only as an HMAC under the site's AUTH_SALT and compared in constant time. A sealed copy (libsodium or OpenSSL, keyed from SECURE_AUTH_SALT) lets the booking screen re-send the same link; without either library the link is copyable once and can be regenerated. Regenerate and revoke from the booking; a deleted booking forgets its pass. Pending bookings are never served. Every pass answer is noindex, nofollow, no-store, private, DONOTCACHEPAGE.
* The voucher QR is drawn by the plugin's own encoder (byte mode, level M, versions 1–10), never by a third-party service.
* Tours → Guest Pass: enable, logo, company name, WhatsApp, emergency contact, footer, weather link template, and which sections to show. On each booking: a Guest Pass panel (status, view, copy link, send by e-mail, regenerate, revoke) and a Guest Pass details box (pickup, guide, vehicle, payment, emergency override).
* Hooks: wptm_guest_pass_created, wptm_guest_pass_regenerated, wptm_guest_pass_revoked, wptm_guest_pass_view, wptm_guest_pass_data, wptm_guest_pass_sections, wptm_guest_pass_qr_payload, wptm_guest_pass_url, wptm_guest_pass_enabled, wptm_guest_pass_servable_statuses.
* New option wptm_guest_pass; new booking meta _wptm_pass_hash, _wptm_pass_sealed, _wptm_pass_status, _wptm_pass_created, _wptm_pass_regenerated, _wptm_pass_details. Nothing existing is renamed or changed; the /trip/ rewrite rule is flushed once on the next wp-admin load.

= 1.4.7 =
Stability and performance release. No visual change, no new option, no change to stored data, URLs, slugs or settings.

* Bootstrap: the plugin's files are now grouped into modules. The admin group — menu pages, list-table columns, meta boxes, settings screens, importer, export, onboarding, setup, reconciliation and About — loads only on wp-admin requests (including admin-ajax and admin-post), so a visitor's page, a cron run, a REST call or a WP-CLI command no longer parses fourteen files it cannot use. Plugin activation and the REST importer load what they need explicitly, so `wp plugin activate` and the import endpoint keep working.
* Fixes the availability hint staying stale in other languages after a booking. Cached answers are stored once per language and once per translated tour; only the untranslated name was being dropped. A booking, a status change, a trashed, restored or deleted booking now retires every language variant of every translation, with the languages read from Polylang at the time rather than listed in the code. Saving a tour (capacity, blackout dates, minimum notice) also retires its cached answers at once. The final capacity check on submission is unchanged.
* Fixes the booking rate limiter, which was documented as a fixed window but slid: every submission reset the hour and the day, so a steady trickle could keep an address throttled indefinitely. Windows now close when they were opened plus their length, whatever happens in between; refunds keep the close in place; counters from 1.4.6 are read and upgraded in place. The date-lookup throttle uses the same helper. Limits and the filter are unchanged.
* The tour stylesheet is registered with the plugin's other asset handles instead of only by the term-index shortcode's hook.
* Homepage and car-hire settings screens load in wp-admin only; the one helper the front-end preview bar needs moved beside the preview code.
* A regression test suite (tests/, not shipped in the plugin ZIP) covering availability and overbooking, the lookup cache across languages and translations, rate limiting, nonce and capability enforcement, custom-HTML sanitisation, pricing and money arithmetic, and the itinerary/FAQ parser. Runs with PHPUnit and the WordPress test suite, or with `php tests/run.php` and nothing installed.

= 1.4.6 =
Travel Guides:

* Fixes the Editorial Journal layout showing article pictures at unsuitable sizes. Every picture now sits in a media frame with a fixed aspect ratio (16:10 lead, 4:3 compact cards, a full-width 16:10 lead on phones) and fills it with object-fit, so landscape, portrait, square, tiny and missing pictures all render at the same balanced size without stretching, distortion, over-tall rows or layout shift. WordPress srcset and intrinsic dimensions are kept and accurate sizes attributes are added, so browsers download smaller files.
* Adds five optional Travel Guides layouts beside Classic Article Cards and Editorial Journal: Magazine + Sidebar (magazine_rail), Panoramic Article Rows (panorama_rows), Compact News Grid (compact_grid), Editorial Thumbnail List (editorial_list) and Journey Timeline (timeline). Each has a miniature preview in the visual picker and appears in the display-style select.
* All layouts print the same queried articles, respect the article count, order, date visibility, blog URL, translated button label and section order, and keep the "All articles" button below the list. Classic Article Cards remains the default and is unchanged, so updating changes no existing site.
* Each optional layout has its own small stylesheet, loaded only while Travel Guides is active and that exact layout is saved. The classic layout loads nothing extra. No JavaScript, inline CSS, web fonts or third-party assets.
* The optional layouts use a real heading for each article title, logical CSS properties (verified in Arabic RTL), visible keyboard focus on the whole card link, lazy loading with asynchronous decoding, and remain scoped to the Travel Guides section so menus, other sections, tour cards, archives, posts and widgets are untouched.

= 1.4.5 =
Homepage and language switcher:

* Aligns and optically strengthens the language switcher's globe so it sits at the same visual size and centre line as the menu label, without changing the theme-owned typography or the switcher's behaviour.
* Adds a clear button below Travel Guides that opens the configured WordPress posts page, automatically selecting its Polylang translation. Its address and label can also be set per language in Homepage settings.
* Adds an optional Editorial Journal layout for Travel Guides: the first article becomes a wide lead story with its excerpt, followed by compact editorial cards. Classic Article Cards remains the default and unchanged. The additional stylesheet loads only when Travel Guides is active and Editorial Journal is selected.

= 1.4.4 =
Language switcher:

* Fixes the desktop switcher appearing in the browser's default font in Jannah and other classic themes. The switcher is now the menu item itself, leaving its trigger as the direct child link themes expect, so it receives exactly the same font, weight, height, spacing, colour and hover treatment as the neighbouring menu links. Shortcodes, the compact phone-header control, Polylang URLs and keyboard behaviour are unchanged.

= 1.4.3 =
Homepage builder:

* Adds a Custom HTML section with shortcode support. It shares the homepage container and spacing, can be translated per language, and is disabled by default so updating changes no existing page. Only trusted administrators may save unfiltered markup; WordPress filters it for other accounts. Its small, scoped stylesheet loads only while the section is enabled and contains code.
* The Sections table can now be reordered by dragging. The same handle supports the arrow keys, Home and End, and writes back to the existing Order fields; those fields remain editable as the no-JavaScript fallback.

= 1.4.2 =
Homepage:

* A sixth hero design, Certified Local Guide, under Tours > Homepage > Hero design: the words on the left — eyebrow, an italic editorial heading with one phrase set in the accent under a fine fading line, a short description, two buttons — and on the right a portrait in a white frame at 5:6 with the guide's name and credential on a card over its lower edge; a strip of the reassurance points closes the band on a warm paper ground with a hairline above. Two columns on a wide screen, one on a phone with full-width buttons and the strip two by two. It reads the same fields as every other hero (eyebrow, heading, description, image, the two buttons, the reassurance points) plus a few of its own: the highlighted phrase, the guide's name and role with a switch for the card, a primary-button mode (the address in its field, or WhatsApp through the number in Settings, drawn in WhatsApp green with the glyph), a small line under the primary label with a soft decorative dot, an open-in-new-tab switch and an arrow switch for the secondary button. A secondary address starting with # scrolls to that section. Nothing is pre-filled, no font is loaded, and the design is drawn from its own four tokens — navy, accent, gold, paper — which the Unified brand identity points at the site's colours, shapes and shadows in one rule; the WhatsApp button keeps its green. Loads its own small stylesheet only when it is the saved hero. Existing heroes, presets and settings are untouched.

= 1.4.1 =
SEO hardening: improved Schema defaults, filter URL handling, archive duplication protection, and safer taxonomy defaults.

Nothing in this release changes an existing site. Every default below applies only to a site whose first activation is on 1.4.1 or later; the profile is recorded once, at activation, and a site upgrading from an earlier version keeps every setting exactly as it saved it.

* Structured data is off by default on a new site — both the homepage graph (Tours > Homepage > Search engines) and the rating handed to Rank Math (Settings > Stars in search results). Neither feature is removed, both keep their switch, and the labels now say plainly that turning one on writes into the same graph an SEO plugin publishes. With the switch off Tripvance prints no JSON-LD and does not touch Rank Math's.
* Filtered archive addresses answer the status they should. A filter value that matches no term (`/tours/?destination=this-does-not-exist`) is a 404 with the theme's 404 page instead of the whole catalogue under a made-up address; an unsupported sort (`?sort=invalid`) is a 301 to the same page without it; a valid filter combination with no matching tours sends 404 in the header while still rendering the archive and its "no tours found" message. Valid filtered pages with results are untouched, as are their existing noindex and canonical. Query parameters that are not Tripvance's own are never read, validated or removed.
* A curated landing page can now replace the category archive it was built from, ending the split where `/sahara-desert/` and `/tours/category/sahara-desert/` competed for one search. Tick "This page replaces the category archive" in the page's own settings box and that archive redirects to it permanently. Off until ticked, never guessed from a similar name, and never applied to child categories, filtered archives, page two or a draft page. Polylang sites redirect to the page in the visitor's own language, and a redirected archive is dropped from both the core and the Rank Math sitemap.
* Departure and trip-length archives start switched off on a new site: an archive per departure point and one per trip length overlap the destination pages they are cut from. Both switches are where they always were, under Section pages. A duration archive that is switched off now registers no rewrite rules at all, so it exposes no addresses; the rules are rebuilt once on upgrade.
* Generated landing pages are created as drafts, and the assurances strip under the hero is left empty. The lines that used to be seeded there — "Local licensed guides", "No payment until it is confirmed", "Free cancellation" — are claims about how one business operates, and a generated page should not make them on its owner's behalf before anybody has read it. Existing pages are never touched, and nothing is deleted or overwritten on upgrade.
* Virtual taxonomy index pages remain off by default, unchanged.

= 1.4.0 =
Brand identity:

* Settings gains a Brand identity group with a mode: Original design palette (the default — every template keeps the colours, corners and shadows it was designed with, nothing changes) or Unified brand identity, one visual system across the tour pages, the homepage, the car page, the landing, destination and activity pages and every tour card. Unified mode adds a deep / secondary colour for the dark bands and the landing-page headings, a warm neutral for the paper bands, a heading style (theme, editorial serif, clean sans — system faces, no font is loaded), a button shape, a radius scale, a shadow intensity and a card border. The existing accent colour is the primary colour in both modes and every saved value is kept.
* The tokens are printed once on the module roots and a small brand.css maps them onto the names each stylesheet already reads, so no rule is duplicated; the file loads only in unified mode and only on pages that carry a Tripvance module. The landing, destination and activity pages' brighter gold and navy follow the brand in unified mode and stay as designed otherwise.
* Every module's button reads one button-radius token with its own shape as the fallback, so the shape setting reaches the tour page, the homepage, the car page, the landing and index pages and the two-step form.

Visual pickers:

* The pickers under the tour layout, hero, featured tours and gallery selects are redrawn as miniatures: a photograph, the title, the text, the price, the button, the card, the facts strip and the gallery composition are placed on a small grid per option, drawn in CSS with no image assets. Each card carries the name, one line, a usage label (Editorial, Photo-first, Multi-day tours, Conversion-focused, Luxury, Minimal, Adventure…) and, on a few general-purpose choices, a Recommended mark.
* Tour page layouts are grouped: Classic, Editorial, Booking-focused, Photo-led, Adventure, City / Activities, Moroccan Signature. A layout added through the wptm_layouts filter can name its group, label and miniature parts.
* The search style, destination style, reasons style, trust style and the new section rhythm gain pickers of their own; the selects stay the submitted controls.
* Keyboard: one card in the tab order, the arrow keys, Home and End move between them, Enter and Space choose. Rows that belong to one value of a select hide with it and keep their values.

Homepage presets:

* A preset is now two things taken separately: a section order, and a coordinated set of the visual choices the page already has — hero, featured tours, gallery, search, destinations, reasons, trust and section rhythm. Nothing is a new template. Six presets: Classic Travel, Editorial Morocco, Luxury Journey, Adventure Explorer, Minimal Modern and Storytelling (the three earlier keys keep their names, so a saved preset stays in use).
* "Apply visual preset" lists exactly which visual settings would change and asks before changing them; the section order, the content, the tours and every unrelated setting are untouched, and every value written is an ordinary setting on the same screen. "Use section order" keeps the earlier behaviour. An optional tick also writes the preset's brand suggestion (headings, corners, shadow, spacing) to Settings.
* The setup guide applies a preset's order and look together on a new site.

Homepage rhythm:

* A Section rhythm setting under Tours > Homepage > Sections: Alternating (white, soft warm band, white — worked out from the sections that are on; the hero, the platforms strip, the introduction video and the final call keep their own grounds, and two bands never touch), Fixed bands (the earlier arrangement: destinations, trust, reasons and gallery on the band) and Minimal (all white, hairlines between sections, more air). Alternating is the default; choose Fixed bands to keep the previous look exactly.
* A band reaches both edges of the window as a spread shadow clipped to the page's width, with its content in the same wrap as every other section, so no 100vw measure is involved. Sections are spaced on both sides so a band's edge never touches the content before it, and the pieces that carry their own paper — the signature tour, the spotlight, the ticket stubs, the question cards — stand on white when their section is on the paper.

= 1.3.0 =
Homepage:

* Featured tours gain a third layout, Journey Ticket, chosen under Tours > Homepage > Featured content (Classic Cards stays the default; Editorial Spotlight is unchanged). Every tour is one wide ticket, stacked: the picture with a small number on its corner, then the place, a serif title, a two-line description and up to three facts — duration, tour type, difficulty, each printed only when the tour has it — and past a perforated edge with two cut notches, the price with its From and per-person words and a "View journey" button. 34 / 44 / 22 above 1024px; the price stub drops under the picture and the text on a tablet; everything stacks on a phone with a full-width button. Same query, same order, same count and the same rule that keeps a chosen signature out of the list; price falls back to the existing "Price on request" line and a tour without a picture gets the plugin's drawn placeholder. Logical properties throughout for Arabic.
* The photo gallery gains a second layout, Editorial Mosaic, chosen under Tours > Homepage > Photo gallery (the classic grid stays the default and is unchanged). One dominant photo on the start side, up to four beside it, and a "View all N photographs" button in the corner of the last one; with fewer photos it becomes one wide photo, a large and a small, or a large one with two stacked. On a phone the main photo runs the full width and the rest sit in a strip that scrolls sideways and snaps. Same photos, same order, same captions and the same viewer: every photo opens the existing viewer at its own place, the button opens it at the first, and the photos past the fifth are in the viewer even though the wall does not show them. Responsive images with width and height, the main photo fetched first and the rest lazily, a name for every link, visible focus.
* Both layouts appear as visual picker cards beside the existing hero picker, and each carries its own small stylesheet, loaded only when that layout is the one saved and its section is on. Nothing changes for a site that keeps its current choices.

Fixes and refinement, homepage:

* Editorial Spotlight side cards on a phone showed only the picture: a rule written for the Journey Spotlight hero shares its class names and reached them, stretching the picture over the text. The hero's rules are now its own, and the spotlight cards pin their pictures themselves.
* The Journey Spotlight hero's card title took the page colour instead of white over its dark wash, and could not be read on some themes.
* Editorial Spotlight titles are clamped — two lines on the large card, three on a side card — as the grid's cards already were, so one long title no longer stretches a card and the card beside it. The duration pill on the large card sits in the top corner on every width instead of a fixed distance above the foot, where it could land on the title.
* A featured tour without a picture gets the same warm accent-tinted placeholder in every homepage layout; the grid's card used the plugin's navy one beside the others.

= 1.2.0 =
Homepage:

* Signature tour redesigned as one editorial card on warm paper: the picture on the start side with a small From / price badge, and beside it the eyebrow, a serif title, the lead, three highlights as plain accent bullets (no icons), a row of facts — duration, destination, from — each printed only when the tour has it, and one "View this tour" button. Two columns (54/46) on wide screens, one column with the picture first on phones; facts fall to two columns then one. Same data as before: featured image, title, lead, highlights, duration, destination, price.
* Featured tours gain a second layout, Editorial Spotlight, chosen under Tours > Homepage > Featured content (Classic Cards stays the default and is unchanged). The first tour is a large card with the title, place and a two-line description over the lower part of the image, and a row of facts and an "Explore journey" button beneath; the second and third stand beside it as horizontal cards with duration, place, title, description, price and an arrow; further tours follow in the ordinary grid. One tour is centred alone, two make the large card with one beside it. 58/42 above 1024px, the side cards two-up on tablets, one column on phones. Same query, same order, same count and the same rule that keeps a chosen signature out of the grid; price falls back to the existing "Contact us" line and a tour without a picture gets the plugin's drawn placeholder.
* Both use the homepage tokens, so a site accent recolours them; logical properties throughout for Arabic.

= 1.1.2 =
Language switcher:

* Inside a theme menu the trigger is now an anchor with role="button" rather than a button, so the theme's own "li > a" rules dress it — same font, size, weight, colour, padding, height and hover as the items beside it, on any theme, with nothing hardcoded. Space, Enter, Escape and the arrow keys still work.
* The site-search item added to the mobile menu in 1.1.1 is removed: themes carry their own search and it showed as a full form on tablet widths.
* Jannah is recognised: the compact phone-header switcher prints on the action the theme fires after the logo, pinned to the inner edge of the logo row where Jannah's own mobile search icon sits, and the fold breakpoint follows the theme's 992px. Switch the theme's mobile search icon off under Jannah > Header > Mobile Header Buttons; nothing to type in the hook field.
* Panel selectors carry enough weight to beat a theme's nested-list rules, so the open list is never styled as a sub-menu.

= 1.1.1 =
Language switcher, visual and mobile refinement (no change to the Polylang, URL, missing-translation or SEO behaviour):

* Classes now carry the `tripvance-language-switcher` prefix. The control is exactly one line tall with no vertical padding, so it sits on the same baseline as the menu items and inherits their font, size, weight, letter-spacing and colour; the globe is the plugin's own 1.5-stroke icon sized to the text, and the caret is a 0.4em hairline corner on the text baseline.
* Mobile header: a compact shape — the two-letter code and a caret, a 44px touch target, full names in the panel — printed on the action your theme fires inside its phone header (Settings > Language switcher > Mobile header). A phone header is not a menu and no hook is common to every theme, so the hook name is a setting; left empty, the phone header is untouched. `[wptm_language_switcher style="compact"]` for header builders.
* Mobile menu: the switcher is now a labelled block — the globe, "Language", then the full names in a column — and the site's own search form can be added as the first item of the mobile menu, so a search icon removed from the phone header stays one tap away. A menu shared with the desktop hides that search item on wide screens and folds the dropdown into the labelled column on phones.
* Footer lays the names in a row after the same label.

= 1.1.0 =
Language switcher (optional, off by default; Settings > Language switcher):

* A switcher inside the theme's own menus. Desktop header, mobile menu and footer can each be switched on and pointed at a registered menu location, or the theme's likely one is picked automatically; a `[wptm_language_switcher]` shortcode (with `style="inline"`) covers block themes, widgets and page builders. Nothing in the theme is edited and no header is rewritten.
* Every language links to the translation of the content on screen — page, post, tour, activity, term archive, index — through Polylang's own relationships and URLs. No address is guessed, no slug is rewritten, no language code is swapped inside a string. A missing translation is hidden (default) or shown greyed out, never linked to the homepage.
* Navigation only: no hreflang, no canonical, no head output and no query-string switching. Polylang keeps the relationships and the hreflang tags; Rank Math is untouched.
* Current language shown as a code (EN) or a name (English), with the plugin's own globe mark and no flags. Dropdown on wide screens, a flat list in the mobile menu and the footer; a menu shared by every screen folds from one to the other at a filterable breakpoint.
* Inherits the menu's font, size and colour; the open panel's colours, corner and shadow are custom properties a theme can override. Logical properties throughout for RTL. Keyboard: Enter, Escape, arrow keys, visible focus, `aria-expanded`, `aria-current`; links stay ordinary crawlable anchors.
* One small stylesheet and one small script, enqueued only when the switcher is on. Without Polylang, or with one language, nothing is printed and the settings screen says why.

Fixes:

* Destination, type, category, trip length and departure archives no longer answer 404 under a Polylang language directory (/fr/tours/destination/marrakech/): the explicit archive rules now also carry the language prefix, so they keep beating the tour post type's attachment rule as they do on a single-language site.
* A PHP 8.1 deprecation notice from the legacy address redirect on sites installed at the domain root.

= 1.0.0 =
First public release.

Visual refinement (stylesheets only; no template, script, setting, hook or data change):

* One shared foundation of design tokens (radius scale, two shadows, control heights, container and measure, eyebrow, one transition) declared identically in tokens.css, tour.css and home.css, so cards, buttons and fields are one family across the tour page, the archive, the homepage and the landing, hub and activity pages.
* Tour opening: the destination line is drawn as the eyebrow over the title, headings sit at one restrained weight, long titles wrap safely, the lead keeps a reading measure.
* Highlights on the warm paper, a quieter itinerary thread, a calmer ticket shadow, a lighter mobile bar whose price never truncates and whose button wraps before it overflows.
* Homepage buttons share the tour page's corner and lose the coloured glow; the "why" and closing bands are flat paper and flat ink rather than gradients.
* Settings: a Spacing choice (Compact / Balanced / Spacious) that scales the rhythm of the tour pages, homepage and car page through one custom property; visual pickers with a thumbnail and a short description for the tour page layout and the homepage hero, over the existing selects; a framed Desktop / Tablet / Mobile preview of the homepage and of the latest tour on their settings screens.
* The car page shares the same tokens, buttons and cards; the tour section navigation is a thin row with an underline on the current section; the introduction video band reaches the window edges without a 100vw measure, with a thinner frame and a still play ring.
* SEO under Rank Math: every noindex decision (filtered and sorted archives, empty terms, booking-state URLs, signed previews) now also reaches rank_math/frontend/robots; empty terms leave Rank Math's sitemap; the homepage graph drops its Organization, WebSite and WebPage nodes when Rank Math is active so no @id is defined twice; the term index title reaches rank_math/frontend/title; landing pages no longer print a second H1 under themes that print the page title; a page whose hero is switched off keeps its H1; sidebar info headings are h3; hub and car FAQ questions are headings. No schema is added anywhere.
* Every front-end string on the homepage, car page, landing pages, hubs, archive, activities and the term index now goes through the plugin's own translation layer with French, Spanish and Arabic wording, instead of English gettext strings the plugin ships no catalogue for.
* Fixes: the archive filter row no longer widens a phone (chip screen-reader spans were escaping the scroller); the editorial highlights stylesheet no longer narrows a SaraDesign page; the mobile bar's own inset now applies; facts and itinerary labels no longer break mid-word on phones.

* Tours post type with destinations, tour types, departure points, trip lengths and categories, each with an archive, filters and sorting.
* Eleven tour page designs on one foundation: sticky section navigation, booking ticket, route at a glance, day by day or step by step itinerary, group rates, FAQ, reviews with a rating under the title, related tours.
* Booking requests with a two-step form in the ticket, optional enforced seat capacity, a status workflow, notes, vouchers, reminders and an operations dashboard.
* Scheduled departures, with the next open date shown on the ticket and the cards.
* A Tours block and a shortcode for grids of tours on any page.
* Responsive visual refinements for tour grids, archive cards and the car fleet, including stable narrow-container layouts, consistent card palettes and touch-friendly controls.
* Five selectable, fully responsive homepage search designs: the original segmented bar and floating panel, plus Quick Search, Adventure Finder and the luxury Signature Search.
* Five selectable homepage hero systems: Classic Cover, Cinematic Journey video, Journey Spotlight with a responsive 800-pixel tour image and approved rating, card-free Floating Stories with owner-supplied images and decorative moving stars, and a naturally typeset animated Oasis Mesh with three travel palettes. Search can be shown or hidden in every design; video autoplay is muted, playback is controllable, and motion preferences are respected.
* Optional "Trusted platforms" homepage marquee with owner-supplied logos and profile links, three speeds, pause-on-interaction and a reduced-motion fallback.
* A homepage template and a car hire template, each assembled from ordered sections you switch on and off, with layout presets and a signed preview. The car hire page carries the fleet, the services, a route and price table, and reads your destinations and tours for the rest.
* WordPress.org-ready internationalization, with built-in French, Spanish and Arabic front-end fallbacks; RTL layouts; full Polylang support (free version), with optional per-language URLs.
* The homepage and the car hire page are created in every language at once and linked, so the language switcher and the hreflang tags are right from the start. The homepage's own wording gets a language tab per language: the same form, the source text above every box, and an untranslated box falls back to the source language instead of leaving a hole.
* No canonical or meta tags and no schema on tour pages: your SEO plugin stays in control. The homepage is the one exception -- a JSON-LD graph in the visitor's own language, describing only what the page shows, and switchable off. Optional Rank Math integration can enrich Rank Math's existing tour schema with approved review data.
* WooCommerce product import with a dry run.
