=== Partner Map Locator ===
Contributors:      desk9
Tags:              map, store locator, locations, google maps, openstreetmap
Requires at least: 6.2
Tested up to:      7.1
Requires PHP:      7.4
Stable tag:        1.4.12
License:           GPLv2 or later
License URI:       https://www.gnu.org/licenses/gpl-2.0.html

Display partners, retailers, locations or company branches on a responsive interactive map powered by Google Maps or OpenStreetMap.

== Description ==

Partner Map Locator is a lightweight WordPress plugin for showing your partner network, dealers, retail stores or company branches on a responsive interactive map. Locations are managed as a custom post type so they sit alongside the rest of your WordPress content.

**Using the settings:** you pick **one map type for the whole site** (Google Maps, OpenStreetMap or list-only). That controls which scripts load. You can still hide the map on a **single** page with the shortcode or block list-only option. Under **List & map visibility**, *Description* has **separate** checkboxes for the list and the popup. Under **Features**, **List card description (max. characters)** controls list excerpts (with optional “Read more”); map popups always show the **full** description. Under **Map defaults**, **Map popup width** sets how wide the location card is on the map.

Choose Google Maps as the map type and enter your Google Maps JavaScript API key, pick OpenStreetMap for a keyless map rendered with the bundled Leaflet library, or use the list-only mode for a map-free, filterable location list.

= Features =

* Custom post type "Locations" with proper capabilities and admin UI
* Optional category taxonomy to group locations (regions, types, brands, ...)
* `[partner_map_locator]` shortcode with optional category, height, zoom, coordinates, layout, design and map attributes
* Multiple maps on the same page are fully supported
* Accessible list rendering of all locations alongside the map
* Optional "Show my location" button (browser geolocation, off by default)
* Optional admin geocoder using the Google Geocoding API (off by default, with a clear opt-in toggle)
* Server-side cache so the location query only runs once per data change
* CSV import / export and a one-click import tool for locations from a previous installation
* Translation ready (text domain `partner-map-locator`)
* Map provider of your choice: Google Maps (incl. optional Map ID / Advanced Markers) or OpenStreetMap (bundled Leaflet, no account or API key needed)
* Four ready-made designs (Classic, Tab view, Mixed view, Stack view) plus per-page design/layout via shortcode or block
* Geolocation with radius search and "near me" out of the box
* Schema.org LocalBusiness markup and WPML/Polylang compatibility
* No statistics or marketing scripts are bundled; outbound traffic is limited to the selected map service and, if you turn it on, the optional geocoder

= Premium version =

[Partner Map Locator Pro](https://desk9design.com/plugins/partner-map-locator/) is a separate add-on that extends this plugin with:

* Marker clustering on OpenStreetMap and Google Maps
* Seven additional designs (Design 1–7) including the compact mini list and the Focus panel
* Pocket (a compact widget for sidebars) and Table (filter bar, map band, sortable table) designs
* Guided entry: Discover (search field with shortcut filters) or Guide (three questions, one tap each) as a first step in front of any design
* Floating map-popup overlays (mini / normal / mini-on-pin)
* Enquiry / lead form with anti-spam options
* Analytics dashboard (local data, no third-party tracking)
* Priority support

The free plugin is fully functional on its own and never checks licences or contacts our servers.

= Privacy =

The plugin does not load advertising or visitor-profiling scripts. By default the map type is set to "List only", so no external map service is contacted until the site owner opts in by choosing a map provider under Settings → Map. With the Google Maps provider, the visitor's browser loads the Google Maps JavaScript API and tiles from Google, which may transmit data such as the visitor's IP address to Google. With the OpenStreetMap provider, the Leaflet library is bundled and served from your own site; only the map tiles are loaded from the configured tile server — openstreetmap.org by default, or Carto (carto.com) when one of the Light / Dark / More-colors map color schemes is selected — which transmits the visitor's IP address to that tile provider. Use the built-in consent gate (Settings → Integration) if you need visitor consent before the map loads; it covers both providers. The browser geolocation feature, when enabled, only triggers after the visitor clicks the dedicated button and is handled entirely client-side by the browser. The optional admin geocoder, when enabled, sends address data server-side from your site to the Google Geocoding API to retrieve coordinates.

Web fonts are self-hosted by default — the optional Google fonts are bundled with the plugin and served from your own server, so no request is made to Google. Under Settings → Style → "Web font loading" an administrator may instead choose "Load from Google", which loads the selected font from `https://fonts.googleapis.com` and therefore sends the visitor's IP address to Google; this option is off by default.

== External services ==

This plugin can connect to the third-party services listed below. Every one of them is optional and off by default: the plugin ships with the map type set to "List only", so a fresh install loads no map tiles and contacts no external service at all. A map (and its tile service) is only loaded after the site owner explicitly selects a provider under Settings → Map; the Google API features additionally require you to enable the matching option and enter your own API key. A built-in map consent gate (Settings → Integration) can require visitor consent before any tiles load.

**OpenStreetMap tile server** (tile.openstreetmap.org)
Active only if the site owner selects "OpenStreetMap" as the map type (the plugin defaults to "List only", which loads no tiles). It is the keyless map option — no account or API key is required. When a visitor then opens a page that contains the map, their browser requests the map tile images from the OpenStreetMap tile server, which transmits the visitor's IP address (and the requested map area) to the OpenStreetMap Foundation. No other data is sent. You can avoid this by keeping the "List only" map type, or by enabling the built-in consent gate (Settings → Integration) so tiles load only after the visitor agrees.
Tile usage policy: https://operations.osmfoundation.org/policies/tiles/ — Privacy: https://wiki.osmfoundation.org/wiki/Privacy_Policy

**CARTO basemap tiles** (basemaps.cartocdn.com)
Used only if you pick one of the "Light", "Dark" or "More colors" map color schemes while using the OpenStreetMap provider. The visitor's browser then loads the map tiles from CARTO instead of OpenStreetMap, which transmits the visitor's IP address (and the requested map area) to CARTO. The default "Classic" scheme uses OpenStreetMap tiles and does not contact CARTO.
Terms: https://carto.com/legal/ — Privacy: https://carto.com/privacy/

**Google Maps JavaScript API** (maps.googleapis.com)
Used only if you set the map type to "Google Maps" (Settings → Map). When a visitor opens a page with the map, their browser loads the Google Maps script and map tiles using the API key you entered, which transmits data such as the visitor's IP address to Google. The same script is loaded in the location editor if you use the optional "Import from Google Maps" search.
Terms: https://cloud.google.com/maps-platform/terms — Privacy: https://policies.google.com/privacy

**Google Places API** (places.googleapis.com, maps.googleapis.com)
Used only if you enable Google ratings sync or use the Google business search / Maps-link import in the location editor, and have entered your own API key. The plugin sends, from your server, the place ID or the search text you provide (and never any visitor data) to retrieve business details such as name, address, opening hours, rating and review count. Requests are made when you save a location, when you run a search/import in the editor, or on the schedule you configure for ratings sync. In addition, if you paste a shortened Google Maps share link (e.g. maps.app.goo.gl or goo.gl) into the "Import from Google Maps link" field, the plugin follows that link server-side to Google to resolve the full map URL before extracting the location data. All of this happens in wp-admin and only with links/searches an administrator enters; no visitor data is involved.
Terms: https://cloud.google.com/maps-platform/terms — Privacy: https://policies.google.com/privacy

**Google Geocoding API** (maps.googleapis.com)
Used only if you enable the optional admin geocoder (Settings → General) and have entered an API key. When an administrator looks up coordinates while editing a location, the entered address is sent from your server to Google to obtain latitude/longitude. No visitor data is sent.
Terms: https://cloud.google.com/maps-platform/terms — Privacy: https://policies.google.com/privacy

**Google Fonts** (fonts.googleapis.com, fonts.gstatic.com)
Web fonts are self-hosted by default and no request is made to Google. Only if you change "Web font loading" to "Load from Google" (Settings → Style) does the visitor's browser load the selected font's stylesheet and font files from Google, which transmits the visitor's IP address to Google.
Terms: https://developers.google.com/fonts/faq — Privacy: https://policies.google.com/privacy

== Bundled libraries & source code ==

The plugin bundles the following third-party libraries in their official minified distribution form. The human-readable, unminified source code is publicly available in the projects' repositories:

* **Leaflet 1.9.4** (BSD-2-Clause) — `public/js/leaflet.js` — source: https://github.com/Leaflet/Leaflet
* **Leaflet.markercluster** (MIT) — `public/js/leaflet.markercluster.js` — source: https://github.com/Leaflet/Leaflet.markercluster
* **Lucide icons** (ISC) and **Heroicons** (MIT) — bundled as SVG data in `admin/data/icon-library.php` — sources: https://github.com/lucide-icons/lucide and https://github.com/tailwindlabs/heroicons

The plugin's own minified assets (`*.min.js` / `*.min.css`) ship alongside their readable source files in the same directories.

== Installation ==

1. Upload the `partner-map-locator` folder to `/wp-content/plugins/`.
2. Activate the plugin through the **Plugins** screen.
3. Add a few locations under **Partner Map &raquo; All Locations**.
4. Drop the shortcode on any page or post:
   `[partner_map_locator]`

== Frequently Asked Questions ==

= How do I display only one category? =

Use the `category` attribute with the slug of the term:
`[partner_map_locator category="region-north"]`

= Can I display several maps on one page? =

Yes. Each shortcode renders its own map with a unique container id. They do not share state.

= Can I show a different design per page? =

Yes. Add the `design` attribute to pick a design just for that map, independent of the global design under Partner Map → Settings:

`[partner_map_locator design="classic"]`
`[partner_map_locator design="design1"]`

Valid values are `classic`, `tab_view`, `mixed_view`, `stack_view`, `discover`, `guide` and `table` (plus `design1`–`design6` and `focus` when the Pro add-on is active). The design’s own layout is applied automatically (you can still override it with an explicit `layout` attribute). This is ideal for a showcase page where each subpage demonstrates one design. Note: the design’s **layout, toolbar and styling classes** switch per page, but the **colour scheme and marker palette** are emitted once site-wide, so all maps share the global colours configured under the Style tab.

= Where is the data stored? =

Each location is a regular WordPress post in the `pmloc_location` custom post type with metadata (address, coordinates, contact details) stored as post meta with the `pmloc_` prefix.

= Does it work without internet access? =

The plugin's PHP and the Leaflet library are local, but the map imagery always loads from the selected provider (Google Maps, or the configured tile server for OpenStreetMap). If your visitors cannot reach that service the map area stays empty, but the marker list still renders.

= Do I need a Google account? =

Only for the Google Maps provider and the optional Google integrations (geocoder, ratings sync, Maps-link import). Choose OpenStreetMap as the map type and the map renders with the bundled Leaflet library against openstreetmap.org tiles — no account, no API key.

= How do I move data from another plugin? =

The Tools page (**Partner Map &raquo; Tools**) includes CSV import and export plus a one-click option to copy locations that still exist in the database from an older store-locator setup into Partner Map Locator.

= Can one page show only the list while another shows the map? =

Yes in two ways: (1) Set **Map type** to “List only” under Partner Map → Settings so the whole site has no map. (2) Keep your map provider globally and add **`map="list"`** to the shortcode on specific pages, or choose **List only (no map)** under **Map for this block** in the block sidebar.

= How do list vs. map popup description settings relate? =

Under **List & map visibility**, the **Description** row has separate checkboxes for the list and the popup. Under **Features**, **List card description (max. characters)** sets list excerpts (optional “Read more”). When the popup shows the description, it always includes the **full** text (scrollable inside the card). Use **Map popup width** under **Map defaults** to change the card width.

= Which Google API keys do I need? =

Two scenarios:

1. **One key, browser-only (simplest)** — store your Maps JavaScript API key under **Google Maps API key**. Leave the **Google Places API key** field empty. In Google Cloud Console: enable **Maps JavaScript API** *and* **Places API (New)** for that key, and under HTTP-referrer restrictions add your domain plus `…/wp-admin/*` so the location editor can use it. The Maps-link import, search and Autocomplete all run in the browser; ratings sync (cron) is unavailable in this configuration.

2. **Two keys (recommended for ratings sync)** — keep the Maps key as in (1) for the browser, *and* add a second key with **IP restriction** (your server's IP) under **Google Places API key**. This second key powers server-side Place Details for the optional weekly “Sync Google ratings” cron job and never sends a referrer.

If you see “API key restricted to websites — use a separate IP-restricted key”, you are in scenario 1 and the message can be ignored — the browser flow handles everything except the cron sync.

= Can the importer read Google Business Profile data? =

Yes. Both the “Import from Google Maps link” and the live search use the **Places API (New)**, which exposes the same data Google shows on Business Profiles: name, address, phone, website, opening hours, average rating and review count. Individual review texts and Q&A are not exposed by the API. Pasting a `https://g.page/…` Business Profile share link or a `maps.app.goo.gl` link works the same as a regular Maps URL.

== Screenshots ==

1. The frontend map with markers and an accessible list of locations.
2. The location editor with address, coordinates and contact details.
3. Plugin settings: map defaults, designs and optional features.
4. Tools page with CSV import / export and migration assistant.

== Changelog ==

= 1.4.12 =
* Settings text: the Google map-type switcher is described as "Map / Satellite / Terrain" (the German UI labels had slipped into the English string).

= 1.4.11 =
* Fixed: the block editor's live preview rendered unstyled (plain selects, raw chips). The editor canvas is an iframe, and the plugin's stylesheets were enqueued on the hook that only reaches the surrounding admin page. They now load on the hook the canvas mirrors, together with the configured colours and the self-hosted web font, so the preview looks like the published page. Add-ons can join in through the new `pmloc_editor_preview_styles` action.

= 1.4.10 =
* The lead form's CAPTCHA settings (provider, site key, secret key) are removed together with the Pro add-on's CAPTCHA option (Pro 1.3.3); the built-in honeypot, time trap, rate limit and Akismet check remain.

= 1.4.9 =
* Layout > List & map visibility: a new table "Badges & fixed elements" under the Toolbar table, for elements that have a place of their own (set on the Style tab) and therefore are not part of the drag order, but can still be shown or hidden per surface. First row: the Featured badge, with its own List preview and Map popup checkboxes - list only, popup only, or both. The Style tab's "Featured badge" toggle stays the master switch.

= 1.4.7 =
* Fixed: the "After title" and "Inline" featured badges were drawn at the 28 px stamp size inside map popups (a rule meant for the corner stamps caught them); they keep their smaller title-row size in the popup again, as in the list.

= 1.4.6 =
* Fixed: the stamped badge over a popup photo sat 12 px too high (the photo's pulled-up margin moves the popup's own top edge); it is now level with the close button.

= 1.4.5 =
* Map popup with a photo: the stamped featured badge and the close button now share one line as well - both 28 px, top edges level 12 px below the popup's top, the top-right badge ending 8 px before the X. The X sits at the same spot with and without a photo, so it never jumps between partners.

= 1.4.4 =
* Fixed: the close button did not actually move down to the flowing featured badge. Leaflet keeps its X outside the content wrapper (the rule now targets the popup itself), and Google's X is positioned inline by the plugin's own script (which now knows about the badge line). The photo-case white pill behind Leaflet's X had the same wrong anchor and is fixed too.

= 1.4.3 =
* Map popup without a photo: the flowing featured badge and the close button share one line - both 28 px tall, top edges level, the top-right badge ending 8 px before the X. The X moves down to the badge's line in this case (over a photo it keeps its corner), so nothing is clipped by the popup frame.

= 1.4.1 =
* Fixed: a "Floating top-left / top-right corner" featured badge on a list row or map popup WITHOUT a photo no longer floats at the card edge above a 42 px reserve. Without a photo to stamp, the badge now flows as the first row of the content - left-aligned with the title, or at the content's right edge clear of the popup's close button - with the normal gap to the title. Photo cards keep the corner stamp.

= 1.4.0 =
* Pocket and Table move to the Pro add-on, alongside Design 1-7 and the Guided entry. Their stylesheet and script no longer ship with the free plugin; the free plugin keeps the four built-in designs (Classic, Tab view, Mixed view, Stack view). The design picker shows Pocket and Table as locked preview cards, and their layout options appear only with the add-on active. A site that had Pocket or Table selected without the add-on falls back to Classic.
* The free core now fires `pmloc_frontend_design_assets` with the rendered design id so an add-on can load its own assets per instance, honouring a per-shortcode `design` override.

= 1.3.14 =
* New Style option "Drop shadows" (on by default). Off renders every design flat: no shadow under the Table and Pocket frames, list cards, floating preview and overlay cards, map popups, toolbars, drawers, legend and map buttons. Keyboard focus rings and the map pins keep theirs.
* Table: the frame's corner radius now follows "Elements corner radius" (Style tab) instead of a fixed 10px - set it to 0 and the frame is square, like the map band already was.

= 1.3.13 =
* Table: two new options under the Table layout options - "Map band height" (desktop, 120-800px, default 220) and "Map band height on phones" (120-600px, default 180). The table keeps ignoring the global "Map height" on the Map tab; these two are its own.

= 1.3.12 =
* Guided entry (Discover / Guide in front of a design) is now part of the Pro add-on. The free plugin keeps the setting's stored value but ignores it, hides the finder options and no longer registers Discover and Guide as designs; the Designs tab shows a Pro note in their place. Sites with the add-on see no change.

= 1.3.11 =
* Release housekeeping for WordPress.org: tested up to WordPress 7.1, the unused "Domain Path" header is gone (translations come from translate.wordpress.org), and a translators note was added for the Guide's "within %s" string.

= 1.3.10 =
* Table: the "Export as CSV" button is gone, and so is the client-side CSV builder behind it. The footer keeps the page status and the pagination.

= 1.3.9 =
* Fixed: Table on phones - the core's 16 px mobile gutter was applied inside the table's own frame, so the filter bar and the map band sat inset while the frame border and the bar's background stayed at the edge. The frame is now the edge on phones; bar, map band and record cards line up with it.
* The "Outer safe-space margin" toggle (Style tab) now also controls the 16 px gutter on phones: off means edge-to-edge on every screen size, not only on desktop.

= 1.3.8 =
* Fixed: the Table "Filters" option had no effect on the page - it was applied to the search/filter block path only, not to the shortcode's main toolbar.
* Table: the option is now "Filter bar" and covers every item beside the search field, each on or off: Radius, Categories, Product types, Partner levels, Highlights and the live result line ("Showing 10 of 25 locations"). The search field follows the site-wide toolbar setting.

= 1.3.7 =
* Table filter bar: one type size (14 px, regular) for the search field, every select and the "Showing 10 of 25 locations - sorted by" line. An untouched select reads in the same grey as the search placeholder; only a chosen filter turns ink-dark (and keeps its accent frame).

= 1.3.6 =
* Table: new "Filters" option under the Table layout options - tick which of the four taxonomy filters (Categories, Product types, Partner levels, Highlights) the table's filter bar offers. It narrows the site-wide toolbar choice for this design only; search, radius and limit stay as set under "List & map visibility".

= 1.3.5 =
* Table: the action column no longer prints "Action button" as its header - the label belongs to the settings checkbox only.
* Table: with "Show result count" on, the live "Showing 10 of 25 locations" line takes the count's place in the filter bar and carries the "sorted by" note; the static "25 locations" no longer sits next to it.
* Table: the map canvas is sized to the 220 / 180 px band (the core's 280 px floor used to leave the map clipped with its centre off-screen), and the map library is told when the band changes size.

= 1.3.4 =
* Table: new "Columns" option under the Table layout options. Tick which columns the table shows beside the partner name: Region, Categories, Product types, Partner levels, Highlights, Distance, Rating and the action button - each on or off, in table order. The four taxonomy columns list every term of a location and carry the site's own taxonomy names in the header; a location without terms shows a dash. The default is the drawn table (region, products, level, distance, rating, action). On phones each ticked column is one line of the record card; the CSV export follows the same choice.

= 1.3.3 =
* Table: the row action is the plain button from the Style tab again - the record card had wrapped it in a second frame.
* Table filter bar as drawn: the labels live inside the fields. The search says "Filter by name, city or service" with a magnifier, an untouched select reads "All levels" / "All services", a chosen one "Region - North" and gets the accent frame. The radius and limit selects show "Radius - 50 km" / "Show - 25". Screen readers still get the original labels. Which filters appear, and in which order, is the existing Layout > "List & map visibility" > Toolbar table.

= 1.3.2 =
* Table design rebuilt to its drawing: one frame with the filter bar (search, selects, "N partners - sorted by distance, descending"), a 220 px map band, the table, and a footer with the page status, the pages and "Export as CSV" (the visible rows in their current order, as a semicolon-separated file that opens straight into Excel). Sorting is announced next to the count, the active column is underlined in the accent colour.
* Table on phones: a table becomes records, not a scroller. Each partner is a card in the page flow - number, name, distance in the first line, then Region / Level / Services / Rating as label and value, then one full-width action - and the selected one carries the accent border. The sheet over the map is gone for this design; the map stays in place as a 180 px band above the records.
* Table: clicking a row (not its action) pans the map to the partner's pin, opens its popup and scrolls the map band into view when it has left the screen - so the pin is always shown after a click, on desktop and on phones.
* Table with "Numbered list rows" off: the number column is dropped instead of left empty, on every screen size.

= 1.3.1 =
* Design 7 settings: "List height on phones" is shown only while "Panel on phones" is set to "Panel below the map" - the sheet keeps its drawn stops and "Full list" has no limit, so the row would have been meaningless there. (The Design 7 settings screen lives in this free plugin; the add-on only reads the values.)

= 1.3.0 =
* Pro: Design 7 gets "List height on phones" (percent of the screen) for its panel-below mode; the free core emits the value for the add-on's stylesheet.
* Layout options: the per-design option names no longer repeat the design ("Design 5 - chip strip cap" is "Chip strip cap" under the Design 5 heading) - 56 labels shortened. The heading of each group already names the design.
* Map fits now respect the instance's own overlay: the fullscreen rail on desktop and the Design 7 sheet on phones are read from the layout and folded into every fit-to-markers - initial and after each filter or search. Before, the fit used the whole map and parked the bottom or side strip of results under the panel; on a phone that was exactly the row of pins the visitor could not see.
* Fixed: Guided entry (Discover / Guide in front of a design) only took effect on grouped map/list blocks - the classes that start the finder were emitted by the partial renderer only. Ordinary shortcodes and single blocks now get them too.
* **Map popup - open on: Hover** now works for every design. It used to reach only the designs with a floating preview card; on the plain marker popup (Classic, Tab, Mixed, Stack - and therefore the whole free plugin) it did nothing. Hovering a list row now opens the marker popup on both map providers, without panning, zooming or scrolling, and never on touch screens. The Layout tab's shared options are also described without naming individual designs, since they apply to all of them.
* The Pro add-on is sold on desk9design.com - the "Go Pro" links and the readme now point at its product page there.
* Audit release. Every build since 1.2.0 was cut from a moving tree under the same version number; 1.3.0 draws a line under that so a site can tell which build it runs.
* Settings: the "Map popup style" description was a 744-character wall listing which design offers which variant - the picker already filters that, so it now says what each variant does and nothing else.
* Release hygiene per the wordpress.org review rules: the free ZIP no longer ships /languages/ (translations come from translate.wordpress.org); the shipped .pot is regenerated from source anyway (1,379 strings, German file merged: 1,266 translated, 76 open).

= 1.2.0 =
* The design picker now also shows **Design 7 — Focus** as a locked preview card, so the free plugin advertises every design the Pro add-on can unlock rather than only Designs 1–6.
* New design **Discover**: the page opens as a search field with shortcut filters and no map at all — the map is built only after the first search, so a visitor who never searches never loads a map tile. Shortcut counts are derived from the locations already on the page, so no extra query is made.
* New settings **Layout options → Discover — no-map-yet note**: switch the "no map has loaded yet" line on or off, and replace the wording with your own (links and emphasis allowed, translatable via WPML/Polylang). Off is the right choice for consumer audiences, where naming data transfers can raise a doubt the visitor did not have.
* New setting **Layout options → Discover — shortcut filters**: choose which taxonomies Discover offers as shortcuts under its search field — categories, product types, partner levels, highlights, any combination of them, or none for a search-only page. Each group is headed with your own taxonomy label, so a renamed taxonomy reads correctly on the frontend.
* New design **Guide**: a short guided finder — what do you need, where are you, how far will you travel — each question answerable in one tap, with a progress bar and a permanent "skip and show all". Every answer writes into the filter the plugin already owns, so the result state is the ordinary map and list with those filters applied. Three settings of its own under Layout options: which filter the first question asks about, up to four distance choices, and whether the skip link shows.
* New design **Pocket**: the locator as a component rather than a page - a sidebar on a product page, a footer block, a column beside an article. A heading, the search field, a small map band, the nearest few partners and a link into the full locator; badges, ratings, sorting and pagination are left out rather than shrunk, because they do not survive 400px. Four settings of its own under Layout options: the heading, how many partners to list, the height of the map band (0 leaves the map out entirely), and where the "all partners" link points.
* New design **Table**: a dense, sortable row layout under a thin map band, for networks that are compared rather than browsed. Eight columns — number, partner with its city, region, level, services, distance, rating and the partner's primary action. Click a column heading to sort; the marker numbers renumber to match. The distance cell adopts the element the core keeps up to date, so it still follows a "near me", and the action column sizes itself to the button rather than clipping it. On phones each row becomes a record card with label/value pairs.
* New **mobile bottom sheet**: on screens up to 782px the result list becomes a draggable panel over the map with three snap heights, operable by tap as well as by drag. Any design can opt in via the new `pmloc_layout_addon_designs` filter.
* New **marker density rules**: cluster bubbles now size in three steps instead of continuously, and list ordinals are hidden while the map is clustering — a numbered list beside a clustered map is not an index of what is on screen.
* Both new designs and the sheet are progressive enhancement: with JavaScript blocked they fall back to the ordinary toolbar, map and list.
* Pro: Focus always rides the card popup pipeline - with "Map popup style: On pin" the detail card stayed empty and selecting a partner blanked the panel. A new Pro setting chooses what selection does: the normal pin overlay (default, like every other design), or the partner in full inside the panel.
* Pro: **Design 7 (Focus) gets its own chrome settings** under Designs -> Layout options -> "Design 7 - Focus". Choose which taxonomies appear as filter chips under the search field (categories, product types, partner levels, highlights, any combination, or none for a search-only panel); switch the "Near me" button on or off; and switch the + / - group beside the search on or off - with it off, "Native zoom controls" (Map tab) decides whether the provider's own buttons appear on the map. The facet choice narrows what "List & map visibility" already allows - it cannot bring back a taxonomy the site has hidden. While Focus is the active design these three replace the generic "Zoom buttons" and "My location button" rows, which would otherwise be a second control for the same thing.
* **Layout options are sorted out.** The Designs tab used to be one flat table of seventy settings in registration order, where nothing distinguished an option that applies to every design from one that applies to Design 5 alone. Now: the four that really do apply to every design (numbered list rows, numbered map markers, map popup style, map popup trigger) have moved to the Layout tab under a new **Applies to every design** section, and the Designs tab shows only what belongs to the design you selected - grouped under headings that name the designs those options actually reach (Design 5, Discover, Pocket, "Design 1, Design 5 & Focus", "Shared by several designs", ...). Pick a design with no options of its own and the tab says so instead of showing an empty table. No setting changed its value or its meaning; they only moved.
* Fixed: the three **Numbered badge** colour pickers on the Style tab were permanently hidden. They follow the "Numbered list rows" checkbox, which sits on another tab, so the lookup found nothing and read it as "switched off".
* **The default palette is now the Signal Pin corporate identity**: near-white ground (#FCFCFD), near-black ink (#0C0D10), one hot signal red (#E5484D) reserved for actions - primary buttons, the active list row, and the ACTIVE map pin - while passive pins wear a calm blue (#3B71B8). Numbered badges go ink with a red active state. Sites that already saved their Style tab keep their own colors; the new defaults apply where nothing was ever saved.
* Pocket: selecting a partner now centres its marker in the small map band. Before, the popup auto-pan positioned the (hidden) popup instead, which parked the pin at the bottom edge.
* **Discover and Guide are now a "Guided entry", not designs.** A new section on the Designs tab - between the design picker and the layout options - asks whether the page should start with a finder: none, Discover (search field with shortcut filters) or Guide (three questions, one tap each). The chosen finder runs as a full first step in front of WHATEVER design is active; when the visitor answers or skips, that design appears with the answers already applied as ordinary filters. Their cards are gone from the design picker (a site that still has one active keeps its card until it switches); every Discover/Guide option under Layout options now follows the Guided-entry choice. One honest limit: the design underneath loads hidden behind the finder, so Discover's no-map-until-searched privacy behaviour applies only to a legacy Discover-as-design setup.
* New settings **Guide - background color** and **Guide - answer card color** (Layout options -> Guide): the stage behind the questions and the tappable cards on it. Empty keeps the shipped warm-paper look.
* Fixed: **List height (custom pixels)** did nothing whenever the value sat above the map height. The pixel value was only a cap on a column whose real height the map dictates - the list scrolls inside the row and never grows on its own, so a cap above the map height never engaged. In custom mode the value now IS the column's height (still capped at 85% of the viewport); the map column stretches along, keeping the side-by-side layout equal-height.
* Fixed: Pocket rows sat at ragged, varying indents when the list card style was "image-top" - that style stacks card children as a column, and Pocket never reset the direction, so its centring rule centred every child horizontally and pushed the distance under the text. One line: the row is explicitly a row.
* Fixed: **Marker color - active pin** never saved - its sanitising line had landed in the CSS-variable builder instead of the sanitizer, so every Style-tab save dropped the value again. It now runs through the optional-colour sanitiser, where empty ("no recolor") is a valid value.
* New setting **Style -> Marker color - active pin**: the pin of the currently selected location recolors on click (list row or the pin itself) and lifts above its neighbours, and reverts when the selection moves on. Works for the plain tinted pins of every layout, in both dot and shaped styles, on Google Maps and OpenStreetMap, clustered or not. Left empty (the default) nothing changes. Custom marker images cannot be recolored and are only lifted; Design 5 / Design 6 pins keep their own active styling.
* Fixed: three Pocket faults at once. Rows past the "results shown" cap stayed visible - the hiding rule lost a CSS specificity fight against the row rule, so all ten rows of a page sat above a "Nearest 3 of 10" footer. The core's pagination bar showed under the widget (the hide targeted a class the core does not use). And a theme that centres its cards centred Pocket's rows into ragged indents - alignment is now explicit.
* New setting **Pocket - pin click**: "Highlight the row only" (default) marks the matching row, scrolls it into view and keeps the provider's popup out of the small map band - a row past the cap shows as an extra row rather than staying invisibly highlighted. "Standard" follows the Map popup style above (with Pro that includes the mini overlay, which suits this format best).
* Fixed: **Numbered list rows** had no effect any more. Three things were wrong at once. The setting's checkbox was Pro-gated although the feature itself ships in the free core, so on a free site the numbers appeared with no way to switch them off. The Table and Pocket designs drew their own row numbers without ever asking the setting. And the "hide the ordinals while the map is clustering" rule covered only the new designs' badge, not the core's. All three now read the same switch; with numbering off, Table drops the number column entirely instead of leaving an empty one.
* Fixed: saving a settings tab while Partner Map Locator Pro was inactive silently reset every Pro-only setting on that tab - 37 of them, across Design, Map and General - because their fields are not rendered without the add-on and the tab-scoped save read "not submitted" as "switched off". Deactivating Pro for a moment was enough to lose the settings for good. Values whose field is not on screen are now preserved.
* Fixed: the Guide progress line printed its own placeholder ("Step %1\$d of %2\$d") instead of the step numbers. Every placeholder in the layout strings now also survives a translation that escapes the dollar sign, which is easy to do in a .po editor.

= 1.1.0 =
* The plugin is now the free core of a freemium pair: premium features (marker clustering, Designs 1–6 incl. mini list, floating map-popup overlays, enquiry/lead form, analytics dashboard) moved into the separate Partner Map Locator Pro add-on, which hooks in via the new `pmloc_is_premium` / `pmloc_designs` / `pmloc_settings_tabs` filters.
* Everything else is unchanged and stays free: unlimited locations, all four taxonomies with multi-select filters, geolocation + radius + near-me, per-page designs (Classic / Tab / Mixed / Stack), Schema.org markup, WPML/Polylang support, shortcode + Gutenberg block, Google Maps and OpenStreetMap rendering, Google business search and Maps-link import, ratings auto-sync, CSV import/export.
* Removed the "Import demo locations" tool and its bundled placeholder images.
* The free plugin performs no licence checks and never contacts our servers.

= 1.0.1 =
* New "List style" option (Settings → Design) for Design 1 and Design 5: choose between the normal list cards and a compact "Mini list" — slim two-line rows with the partner name and a "Level · ★ rating · review count" meta line.
* Mini rows follow the "Preview card heading size" / "Preview card text size" options from the Style tab and show the featured badge as a compact inline pill.
* New "Active row highlight" option for the mini list with four styles — accent bar, strong tint with a selection pulse, card, or a map-pin icon mirroring the marker — plus accent and background color pickers (defaults match the standard marker color and its light tint).
* Hardening: all generated HTML/SVG markup (map legend, filter toolbar, icon strips, list cards, design thumbnails, admin charts) is now escaped late via `wp_kses` with an explicit allowlist at the point of output.
* Prefixed the demo-image signature option key so every option name carries the plugin prefix.

= 1.0.0 =
* Initial public release.
* Custom post type `pmloc_location` with a single hierarchical category taxonomy.
* `[partner_map_locator]` shortcode with sanitised `category`, `height`, `zoom`, `lat`, `lng`, `list` attributes.
* Multiple shortcodes per page, each rendered with a unique container id.
* Accessible server-rendered list under the map and `<noscript>` fallback.
* Optional browser geolocation button, off by default.
* Optional opt-in Google Geocoding API lookup for admins, routed through a server-side proxy with a per-user rate limit.
* Renders maps with Google Maps or OpenStreetMap (bundled Leaflet, no API key required).
* Read-only public REST endpoint `/partner-map-locator/v1/locations`.
* CSV import / export and an optional importer for legacy dealer-location data still in the database.
* Versioned object cache for repeated map renders on the same request.
* Uninstall handler that removes plugin options and rate-limit transients only; user content is preserved.

== Upgrade Notice ==

= 1.2.0 =
Adds the Discover, Guide, Pocket and Table designs, a mobile bottom sheet and marker density rules. Existing designs and settings are untouched.

= 1.1.0 =
Splits the plugin into a free core and an optional Pro add-on; removes the demo-location importer. Existing settings are preserved.

= 1.0.1 =
Adds the optional "Mini list" style for Design 1 / Design 5.

= 1.0.0 =
First public release.
