=== SMiLE Basic Web ===
Contributors: smilecomunicacion
Tags: contact, reCAPTCHA, SMTP, sitemaps, svg
Requires at least: 6.3
Tested up to: 7.1
Stable tag: 1.3.11
Requires PHP: 7.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

SMiLE Basic Web is a versatile plugin combining features like Contact Form, sitemap generation, and configuration tools, all from unified interface.

== Description ==
SMiLE Basic Web is a modular WordPress plugin that includes a flexible Contact Form system with real-time email preview via the Customizer, a dynamic Sitemap Generator supporting various formats, and a configurable Cookie Consent Manager. Its brand-adaptable interfaces let visitors choose which registered optional scripts may run. Review your site's configuration and third-party integrations before relying on its consent behavior.

**Key Features:**

- Customizable contact form with advanced SMTP configuration.
- Support for multiple custom fields, including new types: single/multi-select, user email, and textarea.
- Required field indicators and placeholders.
- Field reordering with drag-and-drop interface.
- Google reCAPTCHA v3 integration for spam protection.
- Built-in honeypot, configurable per-IP attempt limits, and a signed minimum form completion time.
- Send a copy of the form to the user, with a customizable message.
- Insert logo and company link in user copy emails.
- Privacy policy and legal notice checkbox fields with linked pages.
- Optional marketing opt-in field with customizable text.
- Explanation field to describe the purpose of the form.
- Real-time preview of the user email using the WordPress Customizer.
- Optional SEO metadata editor for posts and pages (title, description, and indexation).
- Modular tab interface supporting additional tools.
- Dynamic generation of:
  - `llms.txt` (Markdown content guide), with an additional `llms.json` export.
  - `sitemap.xml`
  - `sitemap-images.xml`
  - `robots.txt`
- New “General” tab that centralises global options.
- Optional SVG / SVGZ upload sanitization using an isolated, bundled structural sanitizer, with bounded input and decompression.
- Automatic image Alt-Text: copies IPTC/XMP “Alt Text Accessibility” (fallback to Title).
-Cookie Consent Panel:
    - Clean, responsive, and customizable cookie banner.
    - Three display sizes: Small, Large, or Fullscreen.
    - Consent tab with position options: Left, Center, or Right.
    - Auto-hide on Accept or Deny with full keyboard accessibility.
    - Multilingual-ready and fully translatable (.pot included).
    - Preferences panel for per-script consent using `<details>`.
    - Add unlimited scripts with name, description, and JS code.
    - Registered scripts run only for accepted categories, once per document. Withdrawing an active category saves the choice and reloads the page to stop its running code.
    - Consent stored in LocalStorage. Optional scripts stay disabled if acceptance cannot be saved.
    - Backend settings for texts, styles, legal pages, and tab behavior.
    - Controls only scripts registered in this plugin, not scripts independently loaded by themes or other plugins.
    - Built with vanilla JavaScript – no jQuery dependency.


Use the shortcode `[smile_contact_form]` to embed the form on any page or post.

== Installation ==
1. Upload the `smile-basic-web` plugin folder to your `/wp-content/plugins/` directory.
2. Activate the plugin through the "Plugins" menu in your WordPress admin panel.
3. Go to the "SMiLE Basic Web" settings page in the admin menu.
4. Configure the Contact Form and other tools in their respective tabs.
5. Insert the form anywhere using the `[smile_contact_form]` shortcode.
6. In the “Sitemaps” tab, activate the dynamic generation of llms.txt, sitemap.xml, sitemap-images.xml and robots.txt.
7. Enable the Cookie Notice and configure the appearance and behavior of the panel.
7. Optionally add third-party tracking scripts and descriptions in the Preferences section.


== External services ==
This plugin integrates Google reCAPTCHA v3 to protect the form from spam and abuse.

*What the service is and what it is used for:*
Google reCAPTCHA v3 analyzes user behavior to block automated spam submissions.

*What data is sent and when:*
When reCAPTCHA is enabled, pages containing the form load Google's browser script directly. Those browser requests expose the visitor's IP address to Google. On a valid form submission, server-side verification sends:
- reCAPTCHA token
- Your reCAPTCHA secret key

*Links to terms and privacy policy:*
- [Google Terms of Service](https://policies.google.com/terms)
- [Google Privacy Policy](https://policies.google.com/privacy)

== Frequently Asked Questions ==

= What is SMiLE Basic Web? =
A feature-rich plugin with a modular system for Contact Form, sitemaps, and email customization.

= How can I add or reorder custom fields? =
Navigate to the "Custom Fields" section under the "Contact Form" tab. Use the admin interface to add, edit, delete, and reorder fields.

= How does the user copy feature work? =
If enabled, users receive an HTML email containing their submitted data, a custom message, your logo, and a link to your company.

If the internal message is accepted but the user copy fails, the form reports a partial-success warning and does not request a resubmission. Its JSON response keeps `success: true` and a string `data`, with additional `warning: true` and `delivery` statuses. The form resets because the internal request was accepted. The `sbwscf_contact_form_delivery_result` action exposes internal/copy statuses without addresses or message content; no automatic retry or persistent mail log is added. Transport acceptance does not guarantee inbox delivery.

= How are submitted values validated? =
The server validates the configured fields independently of browser constraints. Text, textarea, email, number, URL and phone limits are 250, 1000, 70, 100, 250 and 30 characters respectively, matching the existing HTML limits. Length uses HTML UTF-16 units, including emoji, and textarea line endings are normalized. Required zero values are valid; required values empty after cleaning, unexpected arrays and excessive input are rejected. Selection values must exactly match the configured options; nested arrays, duplicates and unknown choices are rejected. Unconfigured POST keys are not emailed.

Email addresses must be valid, numbers must be finite, URLs must use HTTP/HTTPS, and phone values must use digits with common international/extension punctuation rather than arbitrary text. Privacy/legal/terms checkboxes must submit their actual checked value. No new country-specific telephone rules or business number ranges are introduced.

= What does reCAPTCHA v3 verify? =
When enabled, incomplete keys or malformed/failed verification never bypass protection. A successful HTTP/JSON response must have boolean success, action `submit`, the canonical `home_url()` hostname and a numeric score from 0.5 to 1. Browser code waits for `grecaptcha.ready`; unavailable/rejected CAPTCHA does not submit an unprotected fallback request. Invalid fields are rejected before calling Google.

Sites intentionally serving multiple verified hostnames may use the `sbwscf_recaptcha_allowed_hostnames` filter to explicitly add trusted aliases. Incoming Host or forwarded headers never extend that allowlist automatically. Keep Google's key/domain configuration aligned with the public site URL. Verification uses HTTPS with a 10-second timeout, no redirects and a bounded response; Google handles token expiry and reuse.

= How can I adjust contact form protection? =
Open Contact Form > Form protection to set the maximum attempts per IP address (1 to 100), the period (1 to 1440 minutes), and the minimum completion time (1 to 60 seconds). Defaults are 5 attempts per 60 minutes and 3 seconds. These protections and the hidden honeypot are always applied. Attempts with a valid nonce count even when validation or delivery fails; loading the form does not count. Rate limiting uses a salted IP hash and temporary counters rather than storing raw IP addresses.

If the site is behind a proxy, configure the server to provide the actual visitor IP address. Forwarded headers are not automatically trusted. An integration for a specifically trusted proxy can use the `sbwscf_contact_form_client_ip` filter. Clear page and asset caches after updating so cached forms load the current script.

= What format does llms.txt use? =
The public `/llms.txt` endpoint always returns Markdown with a site-name H1, a short summary, and grouped Markdown links. `/llms.json` provides the existing structured metadata format. Select the JSON export in Sitemaps to also show its generated URL. Integrations that previously read JSON from `/llms.txt` must switch to `/llms.json`. This content guide helps agents find information; it does not enforce crawling or training permissions.

= Which content is included in the generated feeds? =
Only selected content types are queried. Empty or taxonomy-only selections do not implicitly include posts. The XML sitemap retains support for explicitly selected public products; the image and LLM feeds support posts/pages as before. Password-protected, non-published and explicitly `_sbwscf_meta_index=noindex` publications are excluded from all three content listings, including image discovery and the XML static homepage. Stored noindex exclusions are honored even if the metadata editor is disabled. This does not replace access control or integrate every third-party SEO exclusion.

Object-cache entries use a versioned generation invalidated by changes to posts, attachments, post metadata, terms, relationships and relevant site/plugin options. The invalidation hooks load on AJAX and REST writes too. Old generation entries expire within one hour without a global cache flush. robots.txt is built on every request so native `robots_txt` and existing plugin extension filters remain current. When WordPress Reading visibility is disabled, the plugin emits a generic Disallow: / without its own Allow exceptions or sitemap advertisements. Trusted native filters can still customize that output. This is crawler guidance, not authentication; the public feed URLs are not made private. Physical files and external page/CDN caches can override dynamic endpoints; purge those caches after updating.

= How is sitemap.xml paginated? =
The existing `/sitemap.xml` URL now returns a sitemap index instead of one unbounded URL list. It links to selected, nonempty post/page/product and category/tag children, plus the homepage when explicitly selected and eligible. Each child holds up to 500 entries by default. The `sbwscf_sitemap_page_size` filter may change this within 1–2000. Post and term IDs provide stable ordering; counts retrieve only a single post ID or a term aggregate, never all content. Counts and individual child lists use the existing content/settings cache generation. Changes during a crawl can move page boundaries; this is not a frozen export.

Children reuse `/sitemap.xml?sbwscf_sitemap_type=post&sbwscf_sitemap_page=1` (or the root query route with `sbwscf_sitemap=1` for plain permalinks). No new rewrite rules, activation or permalink reset is required. Keep submitting the original index URL. Caches/CDNs must preserve these query parameters or bypass sitemap caching. Public robots output has specific permissions for these routes, without removing the generic block for other query URLs. Reading visibility still takes precedence.

The static homepage is not repeated in the pages child. Terms use native non-hierarchical SQL pagination with hide_empty: empty parent categories are not added solely because a descendant contains posts. Unknown/disabled sections, missing or invalid child parameters, out-of-range pages, empty indexes and children with no valid URLs return 404. A failed term query, index beyond 50000 children, or an index exceeding the 50 MB protocol limit or the available PHP memory budget returns 503 without a partial document. The index size is conservatively checked before allocation, leaving room for string copies and shutdown hooks. Post/static-home timestamps remain real modification dates; term archives and the index do not invent lastmod dates.

This change paginates the normal content sitemap only. The image sitemap and LLM exports retain their existing format and batching. Bounded responses do not eliminate expensive database counts or deep OFFSET queries: benchmark very large real sites and persistent caches before publishing.

= What happens when there are no eligible images? =
The image sitemap endpoint returns HTTP 404 instead of advertising an empty sitemap as successful. robots.txt only lists the image sitemap when an eligible public post/page has a resolvable HTTP(S) image URL. Invalid or missing attachment URLs do not create empty entries. The availability probe stops after the first valid entry and caches its result for up to one hour, including on sites without a persistent object cache. Content, attachment, metadata and relevant settings changes invalidate this per-site transient; uninstall removes it. On an empty large site, the first probe after invalidation may still scan all eligible batches.

Enabled feed endpoints run before WordPress canonical redirects, keeping the advertised sitemap child URLs directly accessible without an added trailing slash that would evade their specific robots Allow rule. Ordinary pages and disabled feed routes retain WordPress behavior. No permalink reset or reactivation is needed; purge external page/XML/robots caches after updating.

= Can I preview the email sent to users? =
Yes, go to "Customize" > "Email Preview" in the WordPress Customizer to see how the email will look.
= Are privacy and marketing consents included? =
Yes. You can enable privacy and legal notice checkboxes and add a marketing opt-in field with custom text.

= What are the new features in 1.2.0? =
This version introduces a dedicated “Sitemaps” tab where you can generate llms.txt, sitemap.xml, robots.txt, and images sitemap dynamically.

= Does this plugin block scripts until consent is given? =
Yes. Scripts are injected **only after the user gives consent**, and only those matching accepted categories.

= Is it GDPR-compliant? =
The plugin provides configurable consent controls, not a compliance certification. Its behavior depends on your site's settings and integrations; it does not automatically control scripts loaded outside this plugin or erase arbitrary third-party cookies.

= What happens when a visitor withdraws consent? =
If an affected category has already run, the plugin saves the new choice and reloads the current page. This stops that document's timers and listeners; the new document loads only still-accepted categories. Saving unchanged preferences does not reinitialize scripts or reload the page. Choices withdrawn in another tab are also applied. A reload can discard unsaved page input, so consider this when integrating checkout or other interactive workflows.

Provider cookies and other persistent identifiers are not automatically erased: implement provider-specific cleanup where needed. The `sbwscf:consentchange` document event exposes `detail.status` and `detail.prefs` for integrations. Code loaded independently by other plugins is outside this manager's control.

= What are the SVG upload requirements? =
SVG uploads use bundled enshrined/svg-sanitize 1.1.1 under a plugin-specific namespace. PHP DOM/libxml are required; SVGZ also requires zlib. Both the uploaded file and the decompressed document are limited to 2 MB. Uploads and sideloads are sanitized without trusting the client MIME, and rejected if parsing, reading or writing fails. Remote references are removed; a logo depending on external resources may need a self-contained export.

The library is included in the plugin package; site administrators do not need Composer. Already uploaded SVG files are not rewritten automatically: review and, where appropriate, re-upload them. Large JPEG/PNG scaling is not disabled by enabling SVG support.

= Can I add my own scripts or analytics tools? =
Users with both `manage_options` and `unfiltered_html` can register code with a name and purpose. WordPress multisite and DISALLOW_UNFILTERED_HTML restrictions are respected; users without executable-content permission cannot change the saved list, including while saving other cookie settings. Script names must be unique; Functional is reserved. Invalid submissions preserve the previous list atomically.

Use either plain JavaScript or an HTML fragment containing script tags, up to 128 KB per entry. Do not mix bare JavaScript with HTML. Code is preserved without HTML filtering or a second unslash pass, while names/descriptions remain restricted HTML and admin textareas/configuration are escaped. Classic external scripts without async finish before subsequent inline code. Explicit async, modules, nested scripts and loader attributes are preserved; data scripts such as application/ld+json remain inert. A failed blocking loader stops dependent code in its category and emits `sbwscf:scripterror` with `detail.category`.

Snippets must support insertion after page load; legacy document.write integrations are not supported. Only approved categories are loaded, and unchanged preferences do not reload SDKs. Snippets previously stripped by older sanitization cannot be reconstructed automatically: paste the complete provider snippet again with an authorized account. Existing data is not migrated or rewritten by this update.

= Can I style cookie accordions independently of the panel? =
Yes. In Cookies > Accordion Colors, configure the header background, content background, title color, and text color. The title color also applies to enable controls on the header. Existing installations receive light accordion backgrounds with dark text even when the main panel uses a dark background and light text.

= Can I repeat the contact form shortcode? =
Yes. Each rendered instance has independent field IDs, labels, textarea counter descriptions and live feedback. POST field names and the nonce action are unchanged. The first instance retains legacy IDs; use `.sbwscf-contact-form`, `form.sbwscf-form`, field names or classes for custom CSS/JavaScript that must target every instance. The updated script also accepts old cached markup; refresh page and asset caches together so repeated forms receive the current script.

= Do the appearance editors check color contrast? =
The Cookies editor warns about nine text/background combinations, including buttons, links, minimized label and accordions. The form Customizer warns about submit button text against normal and hover backgrounds. Checks update as colors change, use the unrounded sRGB contrast ratio against a conservative 4.5:1 normal-text threshold, and never change colors or block saving. They require JavaScript and do not certify the whole page: theme styles, gradients, opacity, font sizes and other content still require accessibility review.

New/unset defaults use darker blue submit backgrounds and darker green/red cookie buttons with readable white text; existing saved colors are retained. Cookie hover underlines the label instead of reducing opacity, preserving the chosen contrast. Keyboard focus indicators are added without changing dimensions. Existing low-contrast saved colors must be adjusted explicitly by the site owner.

= What is removed when I uninstall? =
Deactivation does not run this cleanup. Deleting the plugin through WordPress removes its registered configuration, its keys inside current/inactive theme-mod options, temporary database counters/locks and the known cron hook on each site. Networks are processed in batches of 100 sites. Only the saved preview page that still has the expected identity, exact preview shortcode and draft/trash status is permanently removed. Published, edited, missing or unrelated targets are retained.

No global object-cache flush is performed. Known keys and supported plugin-only cache groups are cleared; feed generations are invalidated. Unenumerable expiring entries in persistent caches expire naturally (feed entries within one hour, rate counters within their configured period). Uploaded images, legal/privacy pages, other content, unrelated theme settings and editorial SEO metadata are retained. Browser consent records on visitors' devices and third-party provider data cannot be deleted by server-side uninstall. Back up configuration before uninstalling; reinstalling does not restore deleted settings or the preview draft.

== Changelog ==

= 1.3.11 =
* NEW: Added a honeypot and server-side contact form protection with configurable maximum attempts per IP, rate limit period, and minimum completion time.
* UPDATED: Initialize signed form tokens outside the page cache and show a waiting time when the attempt limit is reached. Existing installations use defaults of 5 attempts per 60 minutes and a minimum completion time of 3 seconds.
* FIXED: llms.txt now always returns Markdown with a site-name H1, summary, and grouped links. The additional llms.json endpoint preserves structured metadata independently of the selected export preference.
* FIXED: Excluded password-protected posts from LLM exports, honored taxonomy-only selections, and stopped advertising llms.txt as an XML sitemap in robots.txt.
* FIXED: Linked the cookie consent dialog to its visible, configurable title so screen readers can identify its accessible name.
* NEW: Added independent header background, content background, title, and text colors for cookie accordions, including their enable controls. Light backgrounds and dark text remain the defaults independently of the panel colors.
* UPDATED: Grouped dialog, minimized label, and accordion colors into separate sections, followed by the panel layout controls.
* SECURITY: Replaced regex-only SVG handling with bundled, namespace-isolated enshrined/svg-sanitize 1.1.1. Validate SVG/SVGZ content on uploads and sideloads, bound decompression and reject processing failures.
* FIXED: Enabling SVG uploads no longer disables WordPress large-bitmap scaling.
* FIXED: Removed global DOM observation and mutation API patching from cookie script management so unrelated dynamic site content is not marked or deleted.
* FIXED: Initialize each allowed category only once per document. Revoking a running category persists the choice and reloads the page, including revocation from another tab, with fail-closed behavior when storage is unavailable.
* SECURITY: Validate contact fields server-side for type, required values after cleaning, existing length limits, allowed selections and strict consent checkbox values. Accept required zero values and normalize textarea line endings.
* SECURITY: Fail closed on incomplete reCAPTCHA configuration and invalid HTTP/JSON, action, hostname or score. Wait for grecaptcha.ready in the frontend and remove unprotected fallback submission.
* FIXED: Report a failed user email copy as a partial success without duplicating the accepted internal request. Added a transport-status action without message data.
* FIXED: Preserve authorized tracking JavaScript/HTML and escapes through Settings API saves; require manage_options plus unfiltered_html. Load external/inline snippets in order while retaining explicit async and consent gating.
* FIXED: Honor explicit feed selections without implicit posts, exclude protected/noindex content from XML/image/LLM listings, and validate static homepage eligibility.
* FIXED: Version and invalidate feed caches for content, metadata, taxonomy and settings changes, including AJAX/REST writes. Bound filtered LLM/image batch sizes.
* FIXED: Remove the redundant Googlebot group, respect Reading visibility and integrate the native robots_txt filter without caching request-dependent filtered output. Advertise sitemap URLs relative to the public home URL.
* FIXED: Give repeated contact shortcodes independent IDs/labels/counter descriptions and accessible live messages while preserving POST names and first-instance legacy IDs.
* ACCESSIBILITY: Improve unset button color defaults, preserve saved colors, add advisory live contrast checks in appearance editors and visible keyboard focus. Cookie button hover no longer reduces opacity.
* FIXED: Read and verify the preview draft before uninstalling, cover all registered options and own theme mods, paginate network cleanup and remove the global object-cache flush.
* MAINTENANCE: Document narrowly scoped coding-standard exceptions for published file paths, PHPMailer property names and WordPress callback signatures. No executable behavior changes.
* PERFORMANCE: Replace the unbounded content sitemap with a cached-count index and paginated post/page/product and taxonomy children, keeping the original sitemap.xml entry point and exclusion policy.
* FIXED: Validate child parameters and URL/XML limits, preflight index memory allocation, avoid duplicate static homepages and allow public sitemap query routes in robots.txt. No rewrite flush or settings migration required.
* FIXED: Return 404 for empty image sitemaps, omit them from robots.txt and skip entries with missing or invalid image URLs. Cache image availability per site, invalidate on edits and remove it on uninstall.
* FIXED: Serve enabled sitemap, image, robots and LLM endpoints before canonical redirects without changing ordinary page redirects or disabled routes.

= 1.3.10 =
* NEW: Added per-category consent summaries so visitors can review stored cookie choices before changing their preferences.
* UPDATED: Improved cookie preference persistence to keep granular selections intact when banner layouts or labels are updated.
* FIXED: Addressed a race condition that occasionally skipped script cleanup when users denied cookies on slow networks.

= 1.3.9 =
* UPDATED: Documented compatibility with WordPress 6.8 and modern PHP versions.
* FIXED: Clarified consent-instructions workflow to prevent confusing field duplication in multilingual installs.
* FIXED: Hardened sanitization around Customizer previews so live form styles render reliably.

= 1.3.8 =
* NEW: Upgraded the Form Explanation setting to use the WordPress editor for bold text, separators, and other formatting.
* UPDATED: Store formatted explanations with `wp_kses_post()` so only trusted HTML is saved.
* UPDATED: Render explanation content with `wpautop()` to preserve administrator formatting on the front end.

= 1.3.7 =
* NEW: Added a consent instructions field so site owners can clarify data usage directly in the form.
* UPDATED: Introduced style controls in the WordPress Customizer to fine-tune consent panel typography and colors.

= 1.3.6 =
* FIXED: SVG preview now tolerates missing or incomplete metadata when integrating with the Media Library.

= 1.3.5 =
* NEW: Added the `sbwscf_meta_description_priority` filter so site owners can control when the plugin prints the SEO meta description, improving compatibility with third-party themes and SEO suites.
* UPDATED: Detect and remove conflicting theme callbacks before output to ensure SMiLE Basic Web remains the single source of truth for meta descriptions.
* FIXED: Resolved cases where recent changes to custom meta descriptions failed to override the site tagline or appear on the front end.

= 1.3.4 =
* NEW: Added toggleable SEO metadata editor for posts and pages, including custom title, description, and noindex nofollow controls.
* NEW: Outputs custom metadata on the front end through the document title, `<meta name="description">`, and `wp_robots` hooks.
* UPDATED: General tab now loads earlier so metadata features register before other tabs.
* UPDATED: Removed deprecated text-domain loader to align with modern WordPress translation handling.

= 1.3.3 =
* FIXED: allow pasting hexadecimal color values in the colour picker input.
* FIXED: added translation support for minimized label positions (Left, Center, Right).

= 1.3.2 =
* FIXED: JavaScript translations now load correctly by registering `wp_set_script_translations()` for **sbwscf-cookies-panel**, ensuring all cookie-banner strings are translatable.

= 1.3.1 =
* FIXED: Links to Cookies Policy, Privacy Policy and Legal Notice open in a new tab.
* FIXED: “Accept Preferences” button was not displayed on first page load.

= 1.3.0 =
* NEW: Added “General” tab; now loaded first and order overridable via filters.
* NEW: Safe SVG / SVGZ upload support with sanitisation, dedicated checkbox in General tab.
* NEW: Auto-populate image Alternative-Text from embedded XMP AltTextAccessibility or IPTC Title.
* UPDATED: Uninstall script now deletes preview page, options, transients and cache keys across all sites.
* Initial public release of Cookie Consent functionality.
* Includes responsive and accessible cookie panel.


= 1.2.1 =
* FIXED: Resolved settings conflicts between tabs by properly separating `option_group` and `option_page` in `register_setting()`.
* FIXED: Fixed issue that prevented the "SMiLE Basic Web Form Appearance" section from appearing in the WordPress Customizer.
* UPDATED: Fully implemented modular tab architecture (`SBWSCF_Tab_Manager` and `SBWSCF_Tab_Interface`), allowing new features to be added without altering the plugin core.
* UPDATED: Refactored script and style loading system to ensure assets are enqueued only when their corresponding tab is active.
* UPDATED: Integrated JavaScript internationalization using `wp.i18n.__()` and connected it with `wp_set_script_translations()` to enable translations via `.po` files.
* FIXED: Backend reCAPTCHA field validation improved for dynamic required fields.
* NEW: Full Multisite support added to `uninstall.php`, cleaning up options, transients, cron jobs, and custom pages network-wide.
* FIXED: Ensured the email preview page (`sbwscf-customizer-email-preview`) is created and properly linked to the Customizer for live email preview.
* FIXED: Removed duplicate URL entries in `sitemap.xml` to prevent confusion and ensure each canonical URL appears only once.
* UPDATED: `<lastmod>` timestamps in both `sitemap.xml` and `sitemap-images.xml` now include full ISO-8601 date and time for greater precision.
* FIXED: Included images embedded in pages so they now appear correctly in sitemap-images.xml.

= 1.2.0 =
* NEW: Added "Sitemaps" tab with dynamic generation of llms.txt, sitemap.xml, sitemap-images.xml, and robots.txt.
* NEW: Choose between TXT or JSON format for llms.txt.
* NEW: Filter by content types and set priority category in sitemap output.


= 1.1.0 =
* NEW: Added support for select (single/multiple) and "user email" field types.
* NEW: Added drag-and-drop field reordering.
* NEW: Added form explanation field.
* NEW: Added Legal Notice checkbox with link to a specific page.
* NEW: Added optional Marketing Opt-In checkbox with custom label.
* NEW: Introduced real-time email preview with WordPress Customizer.
* NEW: Added validation to prevent duplicate field names.
* UPDATED: Improved sanitization constants and structure.
* UPDATED: Modularized codebase for maintainability.

= 1.0.0 =
* Initial release of SMiLE Basic Web.
* Integrated Contact Form with SMTP settings.
* Supported custom fields with placeholders and required markers.
* Enabled user copy email with company logo and link.
* Added privacy policy checkbox.
* Integrated Google reCAPTCHA v3.

== Upgrade Notice ==

= 1.3.11 =
Clear page/asset caches. Review form protection. llms.txt uses Markdown; use llms.json for JSON. SVG/SVGZ uploads require DOM/libxml (plus zlib for SVGZ), max 2 MB; review older files. Withdrawing an active script category now reloads the page.

= 1.3.10 =
Update to deliver clearer consent summaries, stronger persistence of banner preferences, and reliable script cleanup when visitors refuse cookies.

= 1.3.9 =
Install this update to keep Customizer styling previews in sync across languages and ensure the consent instructions remain consistent on multilingual sites.

= 1.3.8 =
Upgrade to let administrators format the form explanation with headings, lists, and other safe HTML while ensuring the public view matches their layout.

= 1.3.7 =
Customize the consent experience with new instructions and appearance controls directly in the Customizer, improving clarity for visitors.

= 1.3.6 =
Avoids PHP notices triggered when SVG metadata is empty during file uploads by improving metadata handling.

= 1.3.5 =
Update to guarantee custom SEO meta descriptions override theme output consistently and to adjust the rendering priority via the new `sbwscf_meta_description_priority` filter when required.

= 1.3.4 =
Enable the new SEO metadata editor from the General tab to customise titles, descriptions, and search engine indexing per post.

= 1.3.3 =
Please update to benefit from improved HEX input support in the colour picker and full translation readiness for label positions.

= 1.3.2 =
Adds missing JS translation loader; update to see translated cookie-banner labels.

= 1.3.1 =
* Links to Cookies Policy, Privacy Policy and Legal Notice now open in a new tab.
* Fixed issue where the “Accept Preferences” button did not appear on initial visit.


= 1.3.0 =
Major update introducing a new General tab with options for safe SVG uploads and automatic Alt-Text population from EXIF metadata.
Also includes full Cookie Consent functionality. Please review your tracking scripts and cookie settings after upgrading.

= 1.2.1 =
Internal plugin reorganization for improved modularity and future scalability. Review your settings in each tab after updating.

= 1.2.0 =
This version introduces a new “Sitemaps” tab with multiple output formats and dynamic endpoints. Please review your sitemap settings after upgrading.


== Screenshots ==
1. Screenshot-1.png: Admin interface showing SMTP, reCAPTCHA, and custom field configuration.
2. Screenshot-2.png: Appearance customization for the contact form using WordPress Customizer.
3. Screenshot-3.png: Live preview of the email sent to users via the WordPress Customizer.
4. Screenshot-4.png: Sitemaps settings tab for configuring llms.txt, sitemap.xml, and robots.txt.
5. Screenshot-5.png: Enable secure SVG/SVGZ uploads, auto-fill image ALT text from EXIF metadata, and toggle the SEO metadata editor for posts and pages.
6. Screenshot-6.png: Backend settings panel for customizing colors, text and scripts.
7. Screenshot-7.png: Cookie banner with preferences toggle, script injection only after consent is given.

== Other Notes ==
SMiLE Basic Web is actively maintained. If you find it helpful, consider supporting development through donations. Contributions and feedback are always welcome!
