/*
Theme Name: swimbooker Holidays
Theme URI: https://holidays.swimbooker.com
Author: Mark Compton
Author URI: https://holidays.swimbooker.com
Description: Premium carp fishing holiday booking website for French and European lakes. SEO-optimised, fast, and professional. Powered by swimbooker booking technology.
Version: 1.0.116
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: swimbookerholidays
Tags: fishing, carp, holidays, france, europe, booking, lakes, accommodation

swimbooker Holidays WordPress Theme

== Changelog ==

Theme developed by Mark Compton for swimbooker.

v1.0.116 (2026-05-08)
- Fix: Master iCal admin Refresh button now also clears and re-fetches
  the lake post transient for any lake whose swimbooker iCal field points
  to that entry's Master iCal URL. Previously, refreshing in the iCal
  Master Feeds page updated the source feed transients but the lakes
  dashboard and lake page still showed the old event count until the
  LiteSpeed cache expired or a manual purge was performed.

v1.0.115 (2026-05-05)
- Feature: Master iCal Normaliser. New inc/master-ical.php fetches a
  lake's swimbooker iCal feed plus up to 3 third-party iCal feeds
  (Airbnb, Booking.com, Pinpoint, Google Sheet etc.), normalises all
  third-party events to the lake's configured changeover day boundaries
  (arrive 13:00, depart 11:00), merges and deduplicates all events into
  one clean timeline, and outputs a valid .ics file at:
  /master-ical/[lake-slug]/
  The Master iCal URL is pasted into the lake's swimbooker iCal field
  so the rest of the theme continues to work unchanged.
- Feature: New third-party iCal URL fields (_lake_3p_url_1-3) and
  changeover day selector (_lake_changeover_day) added to the iCal
  meta box on each lake admin screen. Master iCal URL displayed with
  one-click copy button once third-party URLs are configured.
- Feature: New admin submenu Lakes > iCal Master Feeds showing all
  lakes with third-party feeds configured, their feed status, and
  Master iCal URLs with copy buttons and View .ics links.
- Feature: Hourly cron (francebooker_ical_refresh_all) extended to
  also refresh all third-party iCal URL transients.
- Feature: Rewrite rule /master-ical/[slug]/ registered. Flush
  permalinks (Settings > Permalinks > Save) after installing.

v1.0.114 (2026-05-01)
- Improvement: Lake gallery expanded from 10 to 20 images. The first 10
  images are used in the hero slider and lake card slider as before. All
  20 images are displayed in the Gallery tab. Admin description text
  updated to reflect the new limits.

v1.0.113 (2026-05-01)
- Improvement: Active tab on lake page now shows gradient blue background
  with white text (matching site-wide button/header style) for clearer
  visibility, particularly on mobile when scrolling between sections.
  Previously the active tab was only indicated by a blue underline.

v1.0.112 (2026-04-28)
- Fix: Removed automatic-feed-links theme support to prevent WordPress
  generating RSS feed URLs for lake_category taxonomy terms (e.g.
  /lake-category/individual-anglers/feed/). These auto-generated URLs
  were being crawled by Google and flagged as validation failures in
  Search Console. The site does not use RSS feeds.

v1.0.111 (2026-04-25)
- Feature: Lakes with "Date Search Not Available" tick box now appear
  on the Last Minute Availability page with a neutral badge reading
  "Dates may be available — check lake page" instead of the green
  date-range badge. Uses a navy blue pill style (lm-badge--neutral)
  to distinguish from standard availability badges. Lakes are added
  to results with sort_date PHP_INT_MAX so they appear at the end
  of the Last Minute list after all dated results.

v1.0.110 (2026-04-25)
- Feature: "Date Search Not Available — Always Show in Results" tick box
  added to the Lake Details meta box (highlighted in amber when ticked).
  When ticked: the lake always appears in search results regardless of
  any date filter entered — identical behaviour to lakes with an External
  Booking URL (e.g. Exotic Fishing Thailand). The availability section
  (2026/2027 Availability box) is hidden from the lake page sidebar.
  All other existing logic is unchanged — Taking Bookings Until still
  displays if an iCal URL is set, the booking widget works normally,
  and the card overlay feature works independently as before.
  Meta key: _lake_no_date_search. Saves in francebooker_save_lake_meta().
  Bypass added at top of francebooker_has_availability() in ical.php.
  Availability section condition updated in single-lake.php.

v1.0.109 (2026-04-23)
- Change: Rolling date window extended from 3 years to 4 years
  (current year + 3). Date inputs on all filter bars — homepage,
  /lakes/, category pages, and country pages — now cap at 31 December
  of current year + 3 instead of + 2. Voice search year clamping and
  "Dates searchable until" hint text updated to match. Allows anglers
  to search and book venues with calendars open to end of 2029.
  Updated in: functions.php (dateMax), front-page.php,
  archive-lake.php, taxonomy-lake_category.php.

v1.0.108 (2026-04-23)
- Fix: Lakes admin list columns now sit tightly to the left matching
  the mockup layout. Column widths tightened: Title 28%, Visibility
  80px, iCal/Bookings 130px, Last Refreshed 100px, Open Until 80px,
  Date 14%. Date column width also constrained. iCal column content
  uses word-break to prevent long text forcing the column wider.

v1.0.107 (2026-04-23)
- Fix: Lakes admin list columns now sit correctly to the left. Title
  column was stretching to fill all available space and pushing the
  custom columns to the right. Title column now constrained to 22%
  width with !important to override WordPress default behaviour.
  All custom column widths also use !important for reliability.

v1.0.106 (2026-04-23)
- Feature: Lakes admin list column order updated. Visibility and iCal /
  Bookings columns now appear immediately after Title. Two new columns
  added after iCal / Bookings:
  "Last Refreshed" — shows how long ago the iCal feed was last fetched
  (e.g. "32 minutes ago"), with the full date/time on hover. Reads from
  a new fb_ical_fetched_at_ transient (primary) or fb_ical_url_fetched_
  at_ transient (multi-lake). Shows "Not yet fetched" if no timestamp
  exists yet — use Refresh All iCal Feeds to populate.
  "Open Until" — shows the effective calendar open-until date in green
  (valid) or red (expired). Reads from iCal header when master toggle
  is enabled, falls back to manual date. Both primary and multi-lake
  venues supported. Shows "—" if no date is set.
  Timestamp transients stored with 2hr TTL (longer than feed cache so
  the refresh time persists even after the events cache expires).

v1.0.105 (2026-04-23)
- Fix: Multi-lake venues (e.g. Clay Lakes) now correctly respect the
  X-SWIMBOOKER-BOOKINGS-OPEN-UNTIL iCal header for date filtering.
  Previously the open-until check was bypassed entirely for venues
  with no primary iCal URL, allowing searches beyond the booking
  window to incorrectly return availability.
  fb_fetch_ical_by_url() now also caches the open-until date in a
  separate transient (fb_ical_url_open_until_{md5}). New helper
  fb_get_url_open_until() retrieves it. The multi-lake block in
  francebooker_has_availability() now reads open-until from the first
  multi-lake URL with a valid header, falling back to manual date.
- Fix: "Taking bookings until..." line now displays correctly on
  multi-lake venue cards and lake pages. Previously fb_get_bookings_
  open_until_display() only checked the primary iCal transient which
  is empty for multi-lake venues. Now falls back to checking each
  multi-lake URL's open-until transient before trying the manual date.
  After installing, use Refresh All iCal Feeds to populate the new
  open-until transients for multi-lake URLs.

v1.0.104 (2026-04-22)
- Feature: iCal / Bookings column added to lake admin list. Shows a
  green/amber/red status dot, number of slots loaded from the feed
  (including multi-lake feeds), and a per-lake "Refresh feed" link.
  Refresh link triggers a nonce-verified admin_init handler that clears
  and re-fetches all iCal feeds for that lake, then redirects back to
  the list with a success notice. Column width 160px.

v1.0.103 (2026-04-22)
- Cleanup: Removed dead overseas lake exclusion logic from partner cards.
  AP Crossings card now shows on all lakes including Thailand/overseas.
  Removed $is_overseas_lake variable declaration from single-lake.php
  desktop block and removed if(!$is_overseas_lake) conditional wrapper
  from template-parts/partner-cards.php.

v1.0.102 (2026-04-22)
- Fix: Account card "Create Free Account" button hover now uses a solid
  dark navy (#0e3352) instead of undefined --primary-dark variable which
  was causing the button to appear washed out on hover.
- Feature: AP Crossings card now supports a background image. Set the
  image URL via Settings -> swimbooker Holidays Settings -> "AP Crossings
  Card Image URL". When an image is set, a dark gradient overlay is applied
  over it to keep the text legible. Falls back to the dark green gradient
  if no image is set.

v1.0.101 (2026-04-22)
- Fix: Partner cards now use two instances with CSS show/hide.
  .partner-cards--desktop sits inside lake-main (visible >=1025px only)
  filling the void alongside the sidebar as originally intended.
  .partner-cards--mobile sits after lake-content-wrap (visible <=1024px
  only) so booking box and availability show before the cards on mobile.
  Card markup extracted to template-parts/partner-cards.php to avoid
  duplication. Breakpoint: 1024px matches lake-content-wrap grid collapse.

v1.0.100 (2026-04-22)
- Fix: Partner cards moved outside lake-content-wrap entirely, sitting
  as a direct child of .container below the grid. Removes all need for
  CSS order tricks or grid-column spanning. Desktop shows tabs+sidebar
  in the grid as normal, with partner cards full-width below. Mobile
  shows tabs, then sidebar, then partner cards — correct on all screens.
  App+Plus cards sit in a 2-col sub-grid on desktop, single col on mobile.

v1.0.99 (2026-04-22)
- Fix: Partner cards moved to a third sibling div (lake-partner-cards)
  inside lake-content-wrap, after lake-sidebar. On desktop the div spans
  both grid columns so cards sit below the tabs+sidebar area. On mobile
  lake-content-wrap switches to flex column with lake-main order:1,
  lake-sidebar order:2 and lake-partner-cards order:3 — giving the
  correct sequence: tab content first, booking sidebar second, partner
  cards last.

v1.0.98 (2026-04-22)
- Fix: Partner cards correctly placed inside lake-main column so they
  fill the space alongside the sidebar on desktop as intended. On mobile
  (<=1024px) lake-content-wrap switches to flex column with lake-sidebar
  order:1 and lake-main order:2 so the booking box always appears before
  the partner cards.

v1.0.97 (2026-04-22)
- Fix: Partner cards moved from inside lake-main to a full-width section
  after lake-content-wrap closes. On mobile they now correctly appear
  below the booking sidebar rather than above it.
- Fix: swimbooker App card now shows QR code only (via Google Charts API)
  with "Scan the QR code with your device camera" prompt, matching the
  swimbooker.com design. App Store/Google Play buttons removed.
- Layout: Cards use a 2-column grid on desktop, single column on mobile.
  AP Crossings spans full width as a banner.

v1.0.96 (2026-04-22)
- Feature: Four partner/promotional cards added below the tab content on
  lake pages (lake-main column), filling the dynamic space alongside the
  sidebar on desktop. Cards stack full-width on mobile.
  1. AP Crossings ferry card — dark styled banner linking to
     apcrossings.com/enquiry-form. Only shown on French/European lakes,
     hidden on overseas venues (Thailand etc).
  2. swimbooker App card — features list with App Store and Google Play
     buttons plus QR code (hidden on mobile).
  3. swimbooker+ card — primary blue card with 6 feature benefits and
     CTA linking to plus.swimbooker.com/sign-up.
  4. swimbooker Angler Account card — compact card linking to
     swimbooker.com/user/signup.

v1.0.95 (2026-04-22)
- Fix: Country taxonomy pages (e.g. /country/france/) were falling back to
  a generic WordPress template, outputting raw unstyled lake content.
  Added taxonomy-lake_country.php which delegates to the existing
  taxonomy-lake_category.php — that template already handles lake_country
  in its $is_tax check. Country pages now display with full filter bar,
  lake cards, pagination and related category cluster.

v1.0.94 (2026-04-21)
- Fix: Form success/error scroll now correctly accounts for the sticky
  header height. Removed the #form-message URL fragment approach (which
  caused the browser to scroll past the message). Replaced with a JS
  DOMContentLoaded handler in each template that calculates the correct
  scroll position with header offset, matching the same pattern used for
  the search results scroll.

v1.0.93 (2026-04-21)
- UX: Contact form and List Your Lake form now scroll to the success or
  error message after submission. Both form redirects in functions.php
  now append a #form-message URL fragment, and both success/error divs
  in contact.php and list-your-lake.php now have id="form-message".
  Browser scrolls automatically on page load — no JS needed.

v1.0.92 (2026-04-21)
- Fix: Post-search scroll now targets the results count ("X lakes found")
  instead of the filter bar. Works correctly on all screen sizes including
  mobile where the filter bar fills the viewport. Removed device-specific
  tolerance logic — no longer needed with this approach.
- Fix: Homepage now shows "X lakes found" result count after a search.
  Added resultCount element above the lakes grid in front-page.php so it
  appears and updates consistently with archive and category pages.

v1.0.91 (2026-04-21)
- Fix: Post-search scroll now works on mobile. The scroll tolerance was too
  tight (20px) — on mobile the stacked filter bar is much taller so the
  position check was passing incorrectly. Tolerance now set to 80px on
  mobile (<768px) and 20px on desktop.

v1.0.90 (2026-04-21)
- UX: After AJAX search/filter completes, page now smoothly scrolls so the
  filter bar sits just below the sticky header. Gives clear visual feedback
  that the search has run, preventing the static feeling when results update
  below the fold. Scroll only triggers if the page is not already at the
  correct position (avoids unnecessary movement). Applied to all filter bars
  — /lakes/, category pages, country pages and homepage.

v1.0.89 (2026-04-20)
- Fix: Facilities checkboxes updated. Removed: Keepnets Banned, CCTV Security,
  Photos Encouraged. Added: Food Package Available, Bait Fridge/Freezer,
  Airport Transfers Available. Updated in all three locations: admin checkbox
  grid, front-end display map, and JSON-LD schema amenities array.

v1.0.88 (2026-04-20)
Contact form: updated response time copy to 'as soon as possible, usually within one working day (Mon-Fri)'
Contact form: added Phone Number field
Contact form: added conditional Booking ID field when Booking Query selected
Contact form: updated success message with Monday to Friday note
List Your Lake: updated response time copy and subtitle
List Your Lake: updated success message with Monday to Friday note
functions.php: contact form handler now captures and emails phone + booking ID fields
v1.0.87 (2026-04-20)
- Fix: Last Minute Availability page now checks multi-lake pairs for
  venues with no primary iCal URL (e.g. Clay Lakes). Uses OR logic —
  lake appears if any sub-lake has a slot in the 6-week window. The
  card slot dates shown are the earliest available slot across all
  sub-lakes. Slot-finding logic extracted into a $find_lm_slot closure
  to avoid duplication between single and multi-lake paths.

v1.0.86 (2026-04-20)
- Fix: AJAX filter posts_per_page changed from 12 to -1. Previously only
  the first 12 lakes were checked for availability, causing valid lakes
  to be missing from search results entirely.
- Fix: Pagination is now hidden on the /lakes/ page when a date or filter
  search is active (AJAX returns all matching results so pagination is
  misleading). Pagination is restored when the Reset Search button is
  clicked. This prevents page 2+ showing unfiltered results alongside
  AJAX-filtered page 1 results.

v1.0.85 (2026-04-20)
- Fix: Master iCal refresh success notice now correctly counts multi-lake
  venues. Previously only counted lakes with a primary _lake_ical_url,
  missing multi-lake-only venues entirely. Notice now shows lakes refreshed
  and total iCal feeds when they differ (e.g. "14 lakes refreshed across
  16 iCal feeds").

v1.0.84 (2026-04-20)
- Fix: francebooker_ical_refresh_all() no longer skips multi-lake URL
  transients for lakes that have no primary iCal URL set. Previously the
  early continue meant multi-lake-only venues (like Clay Lakes) were never
  included in the master Refresh All or hourly cron refresh.

v1.0.83 (2026-04-20)
- Fix: Multi-lake meta box now shows per-URL Refresh/Fetch/Try again links,
  matched to the single iCal refresh UX. Handled by a new admin_init hook
  for fb_refresh_ical_url with per-index nonce verification.
- Fix: Single lake page now renders per-lake availability boxes for multi-lake
  venues (when no primary iCal URL is set). Each lake gets its own avail-box
  headed with the lake name, using identical three-tier logic to single-lake
  availability (Tier 1: current year, Tier 2: next year with notice,
  Tier 3: no availability message). Uses fb_fetch_ical_by_url() directly.

v1.0.82 (2026-04-20)
- Feature: Multi-Lake Venue support added. Venues with more than one
  bookable lake (e.g. Lake 1 & Lake 2) can register up to 3 lake
  name/iCal URL pairs in the swimbooker iCal Feed meta box. The lake
  card overlay dynamically shows which lakes have availability after a
  date search, or a default "N lakes available to book / Search dates
  above to check individual lake availability" message when browsing
  without dates. Availability uses OR logic — the lake card appears in
  search results if any one of its sub-lakes has a slot. If no sub-lakes
  have availability, the card is correctly excluded. The Refresh All
  iCal Feeds button also refreshes multi-lake URL transients. Functions
  added to inc/ical.php: fb_fetch_ical_by_url(), fb_url_has_availability(),
  fb_get_multi_lake_pairs(), fb_get_multi_lake_overlay(). Meta keys:
  _lake_multi_name_1/_lake_multi_url_1 ... _3.

v1.0.81 (2026-04-20)
- Fix: fb_ical_open_until_ transient now correctly cleared in two places
  in inc/ical.php: (1) when the iCal URL is changed or removed on save,
  alongside the existing fb_ical_ clear; (2) in the no-URL early return
  of francebooker_fetch_and_cache_ical(). Previously the stale open-until
  date persisted for up to 30 minutes after removing the iCal URL, causing
  the iCal date to continue showing on the card instead of the manual date.

v1.0.80 (2026-04-20)
- Fix: iCal open-until header now accepts both YYYYMMDD and YYYY-MM-DD
  date formats. The parser normalises dashes out on read so both formats
  produce identical internal storage. Resolves mismatch with swimbooker
  feed sending X-SWIMBOOKER-BOOKINGS-OPEN-UNTIL:2028-06-28 (dashed)
  rather than the originally spec'd YYYYMMDD format.

v1.0.79 (2026-04-20)
- Fix: Lake archive posts_per_page set to 9 (3x3 grid) via pre_get_posts.
  Previously falling back to WordPress default of 10.
- Fix: Pagination page numbers now display horizontally. Added flex layout
  to .pagination ul.page-numbers and li to override default block stacking.

v1.0.78 (2026-04-19)
- Fix: Restored margin-top:auto on .card-footer so Last Minute Availability
  cards (which have no .card-lower wrapper) anchor the footer correctly.
  Regular lake cards continue to use .card-lower for pill/bookings alignment.

v1.0.77 (2026-04-19)
- Feature: Card Image Overlay Note added to lake admin (SEO & Schema box).
  Tick box enables a custom overlay on the card image — icon (emoji),
  title line, and optional sub-line. Reuses the existing card-direct-overlay
  style. Independent of External Booking URL; both can coexist.
  Meta keys: _lake_card_note_enabled, _lake_card_note_icon,
  _lake_card_note_title, _lake_card_note_sub.

v1.0.76 (2026-04-19)
- Fix: .card-features now has matching min-height and max-height so the
  "Taking bookings until..." line always sits at a consistent position
  across all lake cards regardless of pill count.
- Fix: Voice search hint text — "Dates open until..." now appears on its
  own line (via <br>) in both archive-lake.php and front-page.php.

v1.0.75 (2026-04-18)
- Fix: Review structured data schema corrected on homepage testimonials.
  Added missing itemReviewed property (Organization: swimbooker Holidays)
  to each Review item — resolves Google Search Console critical issue
  "Missing field itemReviewed". Fixed author field from plain text to
  properly typed Person schema — resolves non-critical issue "Invalid
  object type for field author". Added bestRating meta to Rating schema.

v1.0.74 (2026-04-17)
- Feature: Browse Lakes page (/lakes/) SEO description field added.
  Set via Settings → swimbooker Holidays Settings → Browse Lakes Description.
  Displayed below the hero, above the filter bar, matching the style of
  category page descriptions. Supports multiple paragraphs. Leave blank
  to show nothing. Not displayed on individual category pages.

v1.0.73 (2026-04-17)
- Update: Brand name changed from "swimbookerHolidays" to "swimbooker Holidays"
  (with a space) throughout all visitor-facing and SEO output. Updated across
  19 files: 109 replacements covering meta titles, descriptions, OG/schema tags,
  page text, email subjects, admin labels, footer, header alt text, README, and
  SETUP-NOTES. Internal PHP function names (francebooker_, fb_), class names,
  hooks, CSS classes, and theme folder name are unchanged.

v1.0.72 (2026-04-17)
- Feature: "Taking bookings until [date]" display added to lake page
  booking sidebar and lake cards. Uses the same priority logic as the
  availability filter: iCal header X-SWIMBOOKER-BOOKINGS-OPEN-UNTIL
  takes precedence when master toggle is ON, falls back to manual
  _lake_calendar_open_until date field. Shows nothing if neither is set.
  New helper function fb_get_bookings_open_until_display() added to
  inc/ical.php. Date formatted as "31 December 2028".

v1.0.69 (2026-04-15)
- Fix: XML sitemap cleanup — users sitemap removed (exposed WordPress
  usernames; irrelevant to SEO). Standard WordPress blog categories
  taxonomy sitemap removed (not applicable to this site).
- Config: /wp-sitemap added to LiteSpeed Cache URI excludes.

v1.0.68 (2026-04-15)
- Version bump only (replaces earlier session build).

v1.0.67 (2026-04-15)
- Version bump only (replaces earlier session build).

v1.0.66 (2026-04-15)
- Feature: Swimbooker account booking tip panel added above the "Check
  Availability & Book" button on every lake page. Informs anglers that
  booking with their account email automatically adds the booking to their
  Swimbooker profile. Lightbulb icon, muted mist background panel.

v1.0.65 (2026-04-13)
- Change: "List Your Lake" CTA button removed from header right-hand side —
  already present in the navigation menu, duplicate not needed.
- Feature: "Fishing News & Tips" added to main navigation menu between
  About Us and Contact. Links to /fishing-news-tips/.

v1.0.64 (2026-04-13)
- Fix: Last Minute Availability page lake card facility pills now use the same
  logic as standard lake cards — specific ordered set of tackle_hire,
  showers_toilets, electric_points only. Previously showed first 3 facilities
  in random order from the stored array.

v1.0.63 (2026-04-13)
- Fix: Direct booking card overlay text repositioned to top-left. Gradient
  direction flipped to fade from top to transparent bottom.

v1.0.62 (2026-04-13)
- Fix: Direct booking card overlay redesigned — both badge and note now display
  as a single always-visible overlay covering the card image (gradient dark blue,
  text bottom-left). Overlay fades out on hover/touch so the image and gallery
  controls are fully accessible. Below-excerpt note removed — no longer needed.

v1.0.61 (2026-04-13)
- Feature: Direct booking lake card badge added — lakes with _lake_booking_url
  set show an "✈️ Book direct — no date filter" overlay badge on the card image
  (top-left, --primary-light blue, matches lm-badge position pattern).
- Feature: Below-excerpt note added to direct booking lake cards —
  "ℹ️ Availability is not date-searchable — use the Check Availability & Book
  button on the lake page." shown in italic below the excerpt.
- Both features driven by _lake_booking_url meta — no extra admin config needed.

v1.0.60 (2026-04-13)
- Change: Airport subtitle notes updated — both now read "International Airport"
  (HKT — International Airport / KBV — International Airport).

v1.0.59 (2026-04-13)
- Fix: Airport driving times order corrected — Phuket now listed first as
  closest airport (HKT — closest airport). Krabi note updated to
  KBV — regional airport.

v1.0.58 (2026-04-13)
- Feature: Airport driving times added for overseas lakes. If a lake has an
  External Booking URL set AND airport drive times populated, the Location tab
  shows "Approximate Driving Times from Airports" (Krabi, Phuket) instead of
  ferry port times. All standard lakes continue to show ferry port times.
- Feature: _drive_krabi and _drive_phuket meta fields added to lake editor
  (Lake Details meta box, below ferry port times section).

v1.0.57 (2026-04-13)
- Fix: External Booking URL link now opens in the same tab (removed target="_blank")
  so the browser back button returns the visitor to the lake page as expected.

v1.0.56 (2026-04-13)
- Feature: External Booking URL field added to lake editor (SEO & Schema sidebar
  box). If populated, the Check Availability & Book button on that lake's page
  becomes an <a> link to the URL (opens in new tab) instead of triggering the
  swimbooker widget. All other lakes unaffected. Designed for lakes with bespoke
  booking pages (e.g. Exotic Fishing Thailand). Field saved as _lake_booking_url.

v1.0.55 (2026-04-13)
- Change: "France" removed from Europe & Overseas homepage category card
  description — France is covered by all other categories.
- Remove: "Cookie Policy" link removed from footer Help & Legal section —
  no longer required since custom cookie consent banner handles compliance.

v1.0.54 (2026-04-13)
- Change: "Europe & Overseas" category descriptions updated to reflect actual
  country list (Croatia, France, Italy, Portugal, Thailand). Homepage card desc,
  and seo_desc in archive-lake.php, single-lake.php, taxonomy-lake_category.php
  all updated. Long-form category intro text is set via WordPress admin
  (Lake Categories → Europe & Overseas → Description) — see SETUP-NOTES.txt.

v1.0.53 (2026-04-13)
- Change: "Rest of Europe" lake category renamed to "Europe & Overseas" across
  all theme files to accommodate overseas fisheries (e.g. Exotic Fishing Thailand).
  Display label updated in: header.php nav dropdown, front-page.php category cards,
  footer.php links, list-your-lake.php checkbox, archive-lake.php SEO desc,
  single-lake.php SEO desc, taxonomy-lake_category.php SEO desc, README.md.
  WordPress taxonomy slug (rest-of-europe) intentionally unchanged to preserve
  existing URLs and avoid permalink/database conflicts.

v1.0.52 (2026-04-13)
- Feature: Custom GDPR-compliant cookie consent banner added — no third-party
  dependency (replaces CookieYes / Cookiebot). Banner matches theme design
  (Gilroy font, --primary blue palette, --radius-lg card style).
- Feature: Three consent categories: Necessary (always on), Analytics, Marketing.
  Consent stored in first-party cookie sbh_cookie_consent (JSON, 365-day expiry).
- Feature: GA4 Consent Mode v2 initialisation block injected at top of <head>
  via sbh_gtag_consent_init(). Defaults analytics_storage to denied until
  visitor consents. Compatible with Google Site Kit and WPCode GA snippets.
- Feature: sbh_analytics_allowed() PHP helper — returns true only if visitor
  cookie confirms analytics consent. Use to gate any server-side GA enqueue.
- Feature: "Cookie Settings" link added to footer-bottom links bar. Reopens
  banner in Customise view pre-populated with saved preferences.
- Remove: Old full-width cookie banner (footer.php HTML, style.css .cookie-banner
  block, main.js initCookieBanner function and fb_cookie_consent helpers) removed.
  Replaced entirely by the new sbh-cookie-consent system.
- Style: Cookie consent CSS block appended to style.css (no separate file).

v1.0.51 (2026-04-12)
- Fix: Blog category archive pages (/category/fishing-tips/ etc.) were
  falling back to index.php and displaying posts unstyled one after
  another. Added category.php template using the same blog card grid
  layout as home.php. Category filter pills highlight the active
  category. Pagination and Browse Lakes CTA included.

v1.0.50 (2026-04-12)
- Feature: Blog post SEO meta box added to post editor sidebar.
  Custom Meta Title and Meta Description fields per post, identical
  pattern to the lake SEO box. Custom fields take priority; if left
  blank the theme auto-generates: title from post title + site name,
  description from post excerpt or first 30 words of content.
  Saves as _post_meta_title and _post_meta_desc post meta.
  Green confirmation shown when custom fields are set.
  SEO output in functions.php updated to read custom fields first,
  falling back to auto-generation — og:type, og:image, canonical,
  and JSON-LD Article schema all fully wired up.

v1.0.49 (2026-04-12) Used when a static page is
  set as the Posts page in Settings -> Reading. Displays a page hero,
  category filter pills, a responsive card grid (post image, category
  badge, date, read time, excerpt, Read Article button), pagination,
  and a Browse Lakes CTA strip. Falls back gracefully if no featured
  image is set on a post.

- Feature: Single post template (single.php). Full-width featured image
  hero with overlay and title. Breadcrumb navigation. Two-column layout
  with article content and sidebar (Back to News, Browse Lakes CTA,
  Categories widget). Rich article typography (h2/h3 styles, lists,
  links). Previous/Next post navigation. Related posts grid (same
  category, up to 3). Browse Lakes CTA strip. Fully responsive —
  collapses to single column on tablet and mobile.

- Feature: Blog CSS added to style.css — card grid, category filter
  pills, image hover effects, sidebar, pagination, article typography,
  single post hero, breadcrumb, and post navigation.

v1.0.48 (2026-04-12)
- Update: Homepage angler reviews replaced with three real verified
  Trustpilot reviews (Laura, Ian Ramsbottom, Chris Collins). Each
  review attributed as "Verified Review via Trustpilot" with date.
  Chris Collins review lightly corrected for spelling (grate->great,
  useing->using) while preserving the authentic voice.
  Trustpilot credibility line added below section heading showing
  4.8 star rating and 2,076 reviews, linking to Trustpilot profile.

v1.0.47 (2026-04-12)
- Fix: Mobile stats bar (.hero-stats-mobile) was appearing 6 times on
  the homepage — once in the correct position (below the hero section)
  and 5 duplicate times throughout the page after every section close.
  Root cause: the block was incorrectly inserted after every </section>
  tag when added in v1.0.44. All 5 duplicates removed. Single instance
  retained in the correct position immediately below the hero section.

v1.0.46 (2026-04-12)
- Performance: Font Awesome CDN replaced with self-hosted SVG sprite.
  Previous: full Font Awesome 6.5.0 loaded from Cloudflare CDN —
  288 KiB transfer, 900 ms render-blocking, two large woff2 files.
  New: 13 KiB SVG sprite containing only the 24 icons used by the
  theme, inlined in header.php (zero network requests). JS renderer
  in main.js replaces <i class="fas/fab fa-X"> elements with inline
  <svg><use href="#fa-X"/></svg> references. CSS in style.css handles
  sizing and fill. Cloudflare DNS prefetch removed from wp_head.

- Performance: Theme images converted from JPG to WebP format.
  hero-bg.jpg -> hero-bg.webp (CSS background, style.css updated)
  lake-placeholder.jpg -> lake-placeholder.webp (3 PHP files updated)
  lakes-hero.jpg -> lakes-hero.webp (archive-lake.php updated)
  og-default.jpg stays JPG (Facebook/social crawlers require JPG/PNG)
  swimbooker-logo.png stays PNG (transparency required)
  IMAGES-NEEDED.txt updated with WebP format guidance.

- Performance: Preload hint added for hero-bg.webp on the homepage.
  <link rel="preload" as="image" fetchpriority="high"> emitted in
  wp_head (priority 0) when is_front_page() is true. Tells the browser
  to fetch the LCP image immediately rather than waiting for CSS parse.
  Addresses the "LCP request discovery" and "fetchpriority=high should
  be applied" warnings from PageSpeed Insights.

- Update: Hero stat label changed from "Top Fish Weight" to
  "Big Fish Category" (already done in v1.0.45, noted here for
  completeness as this version supersedes v1.0.45).

v1.0.45 (2026-04-12)
- Feature: Homepage & About page stat figures now managed from Settings
  -> swimbooker Holidays Settings. Four fields added: Lakes Listed,
  Lake Categories, Countries, Big Fish Category. Values stored as
  WordPress options (fb_stat_lakes, fb_stat_categories, fb_stat_countries,
  fb_stat_bigfish) with helper functions. front-page.php hero stats bar
  and mobile stats block both updated to pull from helpers. about.php
  stats strip updated for Lake Categories and Biggest Fish Category.
  Remaining about.php stats (Instant, 100%, £0, 24/7) stay hardcoded
  as they are marketing claims not counted figures.
- Update: Homepage hero stat label changed from "Top Fish Weight" to
  "Big Fish Category" to match the site category naming convention.
- Fix: Settings page section separators now have increased top margin
  (24px instead of 8px) so each section has more breathing room and
  is easier to read.

v1.0.44 (2026-04-12)
- Feature: Homepage stats bar (50+ Lakes, 8 Categories, 6+ Countries,
  60lb+ Top Fish Weight) now visible on mobile. On desktop/tablet the
  stats remain overlaid at the bottom of the hero image as before.
  On mobile the hero overlay version stays hidden (avoids covering the
  hero image), and a new .hero-stats-mobile block is rendered immediately
  below the hero section in the document flow, styled with the same
  primary background colour and matching typography. All 4 stats shown
  on mobile. 480px partial-hide rule removed (no longer needed).

v1.0.43 (2026-04-12)
- Feature: Primary Category picker added to lake editor (sidebar meta box).
  Select which category is used for SEO titles, OG/Facebook share tags,
  and JSON-LD breadcrumb schema. Defaults to first alphabetically if not
  set. Saves as _lake_primary_category post meta (term_id).
  Helper function fb_get_primary_cat() added to functions.php.
  SEO title generation and JSON-LD breadcrumb both updated to use it.

- Feature: Referring category breadcrumb on lake pages. When a visitor
  clicks a lake card from a category page (e.g. Lake Exclusive), the URL
  gains ?from_cat=lake-exclusive. The lake page reads this, validates the
  slug against the lake's actual assigned categories (preventing spoofing),
  and shows that category in the hero breadcrumb for correct navigation
  context. Defaults to primary category when no from_cat param is present.
  SEO titles and OG tags are unaffected — they always use primary_cat.

v1.0.42 (2026-04-11)
- Update: Brand name capitalisation corrected throughout all theme files.
  "Swimbooker" and "swimbooker Holidays" in all visitor-facing output,
  admin labels, email subjects, SEO strings, changelog, and documentation
  updated to "swimbooker" and "swimbooker Holidays" to match the official
  lowercase brand name. PHP class identifiers (SwimbookerHolidays_ICS_Parser),
  WordPress @package tags, and internal file docblocks preserved unchanged
  as these are code identifiers not visible to visitors or users.
  18 files updated, 186 display-text instances corrected.

v1.0.41 (2026-04-11)
- Fix: Parse error in functions.php line 773. An unescaped apostrophe in
  "this lake's booking calendar" inside a single-quoted PHP string caused
  a syntax error (E_PARSE) on every page load. Replaced with the HTML
  entity &#039; which renders correctly and is safe inside PHP strings.

v1.0.40 (2026-04-11)
- Fix: Critical error caused by unguarded define() in inc/ical.php. If
  WordPress loaded ical.php more than once (which can occur in certain
  admin or cron contexts), the bare define() call for FB_ICAL_OPEN_UNTIL_HEADER
  threw a fatal "Constant already defined" error. Wrapped in
  if ( ! defined(...) ) guard. Also replaced $open_until ?? '' with an
  explicit ternary for maximum PHP version compatibility.

v1.0.39 (2026-04-11)
- Fix: Second critical error — fb_show_no_ical_lakes() also called inside
  francebooker_has_availability() in inc/ical.php but defined later in
  functions.php. Same load-order issue as v1.0.38. Both helper functions
  that francebooker_has_availability() calls directly (fb_show_no_ical_lakes
  and fb_ical_open_until_enabled) are now defined in inc/ical.php itself,
  wrapped in function_exists() guards, before francebooker_has_availability().
  Both duplicates removed from functions.php and replaced with comments.
  Rule established: any function called directly (not via a hook) from
  inc/ical.php must be defined in inc/ical.php, not functions.php.

v1.0.38 (2026-04-11)
- Fix: Critical error caused by fb_ical_open_until_enabled() being called
  in inc/ical.php before it was defined. Root cause: ical.php is loaded
  via require_once on line 14 of functions.php, so any function it calls
  must already exist at that point. fb_ical_open_until_enabled() was
  defined later in functions.php, causing a fatal "call to undefined
  function" error on every page load. Fixed by moving the function
  definition into inc/ical.php itself (wrapped in function_exists() guard),
  before francebooker_has_availability() which calls it. The duplicate
  definition in functions.php replaced with a comment.

v1.0.37 (2026-04-11)
- Fix: About page pillar cards (Verified Lakes, Real-Time Availability,
  Secure Instant Booking) and values cards now stack vertically on mobile.
  Previously used inline grid-template-columns:repeat(3,1fr) with no
  responsive breakpoint, causing all three cards to render side by side
  and overflow off screen on mobile. Replaced with CSS classes
  .about-pillar-grid and .about-values-grid with a max-width:767px
  breakpoint that switches both grids to a single column layout.

v1.0.36 (2026-04-11)
- Feature: Booking calendar period controls to prevent false availability
  being shown beyond a lake's actual booking window.

  CONSTANT: FB_ICAL_OPEN_UNTIL_HEADER defined in inc/ical.php as
  'X-SWIMBOOKER-BOOKINGS-OPEN-UNTIL'. Update this one constant if
  swimbooker use different wording — no other changes needed.

  PER-LAKE FIELD: "Calendar Open Until" date picker added to the
  SEO & Schema meta box on each lake's edit screen. Set the last date
  the lake's booking calendar is open. Any date search extending beyond
  this date will exclude the lake from results.

  MASTER TOGGLE: "Use iCal Open-Until Header" added to Settings ->
  swimbooker Holidays Settings. When enabled: the iCal feed is checked
  for the FB_ICAL_OPEN_UNTIL_HEADER line first; if absent, falls back
  to the manual per-lake date. When disabled: manual date only.

  PRIORITY LOGIC in francebooker_has_availability():
    Toggle ON  + iCal header present  -> use iCal header date
    Toggle ON  + iCal header absent   -> use manual per-lake date
    Toggle OFF + manual date set      -> use manual per-lake date
    Neither set                       -> no restriction (existing behaviour)

  iCal parser updated to capture the open-until header (calendar-level,
  outside VEVENT blocks). Cached in a separate transient alongside events.

v1.0.35 (2026-04-11)
- Fix: About page three feature pillar cards (Verified Lakes, Real-Time
  Availability, Secure Instant Booking) now centred on screen. Switched
  from auto-fill grid to a fixed 3-column grid with max-width:900px and
  margin:0 auto so the group of three cards sits centrally rather than
  anchoring to the left of the container.

v1.0.34 (2026-04-11)
- Update: About page headline changed from "swimbooker's Window on French
  & European Fishing" to "The Home of swimbooker's French & European Lakes".

v1.0.29 (2026-04-11)
- Update: About Us page fully rewritten. Content now correctly positions
  swimbooker Holidays as built and operated by swimbooker to showcase
  French and European partner fisheries. Previous copy implied a third-
  party relationship rather than swimbooker's own platform. New page
  structure: Who We Are, Why swimbooker Holidays Exists (with three
  feature pillars), About swimbooker (mission/platform/Bite Conditions),
  Mission & Values grid (6 cards from swimbooker ethos), stats strip,
  and a Browse All Lakes CTA. All WordPress editor content still renders
  below if added via the page editor.

v1.0.28 (2026-04-11)
- Fix: Hero category pills hidden on mobile (<768px). On small screens,
  the pills were pushing the lake title upward and covering a large
  portion of the hero image — increasingly problematic as more categories
  are assigned to a lake. Wrapped the pills in a .hero-cat-pills span,
  hidden via CSS on mobile (display:none) and restored on tablet/desktop
  (display:contents so children render inline without adding a box).
  Categories remain fully accessible on mobile via the "Listed In"
  section further down the lake page. Location text is retained in the
  hero on all screen sizes.

v1.0.27 (2026-04-11)
- Feature: Lake page hero redesigned for desktop and tablet.
  On screens >= 768px (tablet + desktop): a full-width static hero image
  is shown using the lake's WordPress Featured Image. If no Featured Image
  is set, the first gallery image is used as a fallback. The image covers
  the full width at 500px height with background-size:cover and
  background-position:center so it always fills cleanly regardless of
  image orientation or crop.
  On mobile (< 768px): the existing gallery slider is retained unchanged
  — 1 image at a time, touch swipe, arrow buttons, auto-advance.
  The slider perView() function updated to always return 1 (was
  conditionally returning 2 on desktop — now redundant as slider is
  CSS-hidden on desktop). Hero slider buttons also hidden >= 768px via CSS.
  Setting the Featured Image: Appearance → Set Featured Image on the lake
  edit screen. Recommended: a wide landscape shot of the lake. Falls back
  to first gallery image automatically if not set.

v1.0.26 (2026-04-10)
- Update: Scrolling banner speed options revised. Added Very Slow (60s);
  removed Fast (15s). Options are now: Very Slow (60s), Slow (40s),
  Medium (25s).
- Update: Scrolling banner text size options expanded from 3 to 6:
  Extra Small (0.7rem), Small (0.8rem), Medium (0.9rem), Large (1rem),
  Extra Large (1.15rem), XX Large (1.3rem).

v1.0.25 (2026-04-10)
- Fix: Scrolling banner message spacing improved. A wide 120px gap is now
  inserted between each repetition of the message, giving clear breathing
  room so the text is easy to read as it loops. Previously used a narrow
  bullet separator which made repetitions feel crowded.
- Feature: Banner text size control added to Settings → swimbooker Holidays
  Settings. Three options: Small (0.75rem), Medium (0.875rem, default),
  Large (1rem). Applied via CSS custom property so the banner height
  adjusts automatically.

v1.0.24 (2026-04-10)
- Feature: Scrolling announcement banner. Controlled via Settings →
  swimbooker Holidays Settings. Options: Enable/Disable toggle, Message
  text field, optional Link URL (makes banner clickable), and Scroll
  Speed selector (Slow 40s / Medium 25s / Fast 15s). Banner appears
  at the top of every page above the top bar, in the site teal colour
  with white text. Message repeats seamlessly in a continuous loop.
  Pauses on hover for accessibility. When disabled, outputs nothing
  and has zero effect on page layout. Implemented in header.php with
  CSS keyframe animation; all settings stored as WordPress options.

v1.0.23 (2026-04-10)
- Fix: Excess white space below pill row on lake cards removed. Previous
  approach used min-height on .card-features to force 3 lines of space,
  which left a large gap when fewer pills were present. Replaced with a
  proper flex column layout: .lake-card and .card-body are now flex
  columns, .card-footer uses margin-top:auto to anchor to the bottom of
  the card, and .card-features has only max-height (3-line safety cap)
  with no min-height. Cards in the same grid row align by the tallest
  card naturally, with no artificial dead space in the pill area.

v1.0.22 (2026-04-10)
- Fix: Lake card pill row changed from 2-line to 3-line fixed height.
  min-height and max-height both set to 3 pill rows so cards with fewer
  pills always occupy the same vertical space, keeping the grid visually
  consistent. align-content: flex-start added so pills pack to the top
  of the reserved space rather than stretching. Pills beyond 3 lines
  are clipped cleanly via overflow: hidden.

v1.0.21 (2026-04-10)
- Update: Lake card feature pills rationalised. Pills now shown in a fixed
  defined order: Lake Size, Biggest Carp, Max Anglers, Swims, Tackle Hire,
  Showers & Toilets, Electric Points. Any pill with no data for that lake
  is omitted automatically. Previously showed random facilities from the
  full facilities list. CSS updated to enforce a two-line maximum on the
  pill row (overflow hidden) so all cards have a consistent height
  regardless of how many pills are populated.

v1.0.20 (2026-04-10)
- Feature: Weekly seed ordering applied to all lake card listings.
  Lakes on the homepage (featured 6), Browse Lakes archive, category
  pages, and AJAX filter results are now ordered by a weekly rotating
  seed derived from the ISO week number (date('YW')). The order changes
  automatically every Monday with no manual action needed, ensuring no
  lake owner perceives permanent favouritism from fixed ordering. The
  order is stable within a week (cache-friendly, no mid-session jumping).
  Implemented via fb_weekly_seed() and fb_seed_shuffle() helpers in
  functions.php. Homepage fetches all visible IDs, shuffles, then shows
  the first 6. Archive/category pages use pre_get_posts with post__in.
  AJAX filter shuffles the ID array before availability filtering.

v1.0.19 (2026-04-08)
- Feature: "Lakes Without iCal in Date Search" toggle added to Settings →
  swimbooker Holidays Settings. When enabled (default): lakes without an iCal
  URL are always included in date-filtered search results (previous hardcoded
  behaviour). When disabled: lakes without an iCal URL are excluded from any
  search that uses date filtering, while still appearing in non-date searches.
  Controlled by the fb_show_no_ical_lakes option; read in
  francebooker_has_availability() in inc/ical.php.

v1.0.18 (2026-04-07)
- Update: Facilities & Features checkboxes updated.
  Added: Showers & Toilets (showers_toilets), Electric Points (electric_points).
  Changed: "Boats Available" renamed to "Bait Boat Hire" (key unchanged: boats,
  so existing lakes with this facility ticked are unaffected).
  Removed: "Exclusive Hire Available" checkbox. All three changes applied
  consistently across the admin meta box, the francebooker_get_facilities()
  display map, and the JSON-LD schema facility_labels map.

v1.0.17 (2026-03-30)
- Feature: Three-tier availability display on individual lake pages.
  Tier 1 (current year has slots): heading shows current year, slots
  display as before, no extra notice.
  Tier 2 (current year empty, next year has slots): heading switches to
  next year, italic notice reads "No availability for YYYY — showing
  availability for YYYY+1.", next year slots displayed Jan–Dec.
  Tier 3 (neither year has slots): heading stays current year, message
  reads "No availability found — please browse our other lakes." with a
  link to the lakes archive. All years calculated server-side on every
  page load via $this_year = (int)date('Y') — rolls forward automatically
  on 1st January each year with no code changes required.

v1.0.16 (2026-03-30)
- Fix: Contact page "Send Us a Message" form now matches the full width of
  the List Your Lake form on mobile. Root cause: the contact page uses a
  two-column grid layout; on mobile when the grid collapses to a single
  column the grid cell was constraining the form width. Fixed by adding
  width:100% and box-sizing:border-box to .contact-form-wrap, wrapping the
  form column in a min-width:0 container to prevent grid blowout, and
  enforcing max-width:100% on the form within the mobile breakpoint.

v1.0.15 (2026-03-29)
- Cleanup: Removed debug console.log statements from visibility toggle JS
  now that the feature is confirmed working in production.

v1.0.14 (2026-03-29)
- Fix: Visibility toggle script was never outputting on the Lakes list screen.
  admin_footer passes no $hook argument (unlike admin_enqueue_scripts), so the
  $hook !== 'edit.php' guard was always true and the function returned immediately
  — meaning no <script> tag was ever written and no click handlers were attached.
  Fixed by switching the page/post-type check to the $pagenow and $typenow globals,
  which are reliably set at admin_footer time.

v1.0.13 (2026-03-29)
- Fix: Visibility toggle AJAX handler rewritten. Previous version used
  check_ajax_referer() which dies with a plain "-1" string (not JSON) on
  nonce failure, causing the JS to receive an unparseable response and
  abort silently. Replaced with wp_verify_nonce() + explicit wp_send_json_error()
  + wp_die() so all failure paths return valid JSON. Handler now also returns
  a fresh nonce in the success response, eliminating the separate nonce-refresh
  request. JS updated to read response as text before JSON.parse() so any
  unexpected output is logged to the browser console rather than silently dropped.

v1.0.12 (2026-03-29)
- Fix: Visibility toggle AJAX on Lakes → All Lakes now correctly updates
  the hidden status. Root cause: POST body was sent as URLSearchParams
  without a matching Content-Type; switched to FormData which WordPress
  admin-ajax.php parses reliably. Button label and dot colour now update
  via named child elements (.fb-vis-dot, .fb-vis-label) instead of
  lastChild, eliminating text-node targeting failures.
- Update: Theme screenshot replaced with branded swimbooker Holidays image.

v1.0.11 (2026-03-29)
- Feature: Lake Visibility column added to Lakes → All Lakes admin list.
  Each lake shows a green Active or orange Hidden status badge. Clicking
  the badge instantly toggles the lake's hidden status via AJAX without
  leaving the list screen. A nonce refresh ensures repeated toggling works
  safely in a single session.
- Feature: Date & Availability Search toggle added to Settings →
  swimbooker Holidays Settings. When disabled: date inputs and voice search
  are hidden from all filter bars (homepage, Browse Lakes, category pages);
  the Availability section is hidden on individual lake pages; the Last
  Minute Availability page shows a "not currently available" maintenance
  message with a Browse All Lakes fallback link. Re-enabling restores full
  functionality instantly.
- Feature: Theme screenshot added (screenshot.png). WordPress now shows a
  preview image of the theme layout in Appearance → Themes.

v1.0.10 (2026-03-29)
- Fix: Hidden lakes now fully excluded from individual lake pages.
  (a) "Listed In" category pills, sidebar category links, and related category
      cards were using WordPress raw $term->count (includes hidden lakes).
      All three now use fb_visible_lake_count_for_term() instead.
  (b) "More Lakes Like This" related lakes query lacked a meta_query exclusion,
      allowing hidden lakes to appear as related results. Fixed by adding the
      standard _lake_hidden meta_query to $related_args in single-lake.php.

v1.0.9 (2026-03-29)
- Fix: Hidden lakes correctly excluded from all lake counts site-wide.
  WordPress's built-in $term->count includes hidden lakes; all listing
  contexts now use fb_visible_lake_count() / fb_visible_lake_count_for_term().
  Affects: homepage category cards, homepage filter dropdown, "View All X
  Lakes" button, Browse Lakes filter dropdown and related category cards,
  category page filter dropdown and related category cards.

v1.0.8 (2026-03-29)
- Fix: Hidden lakes excluded from category counts across all listing pages.
  Affects: homepage category cards, homepage filter dropdown, "View All X
  Lakes" button, Browse Lakes filter/related category cards, category page
  filter/related category cards.

v1.0.7 (2026-03-29)
- Revert: Date inputs restored to native type=date fields with min/max
  attributes baked in via PHP. Custom month+year dropdowns introduced in
  v1.0.6 removed — past years in the year dropdown is a known Chrome
  browser behaviour and will be revisited in a future version.
- Retained: Category description alignment feature from v1.0.6.
- Retained: All other fixes from v1.0.3-v1.0.5.

v1.0.6 (2026-03-29)
- Fix: Calendar year dropdown no longer shows past years (2024, 2025).
  Native date inputs replaced with custom Month + Year dropdown pickers
  on all three filter bars. Year range is strictly set by PHP server-side
  (current year to current year+2) so only valid years appear. Hidden
  inputs carry the date value for the existing AJAX filter chain — no
  change to search behaviour. Voice input and Reset Search both update
  the custom selects correctly.
- Feature: Category description text alignment — a new "Description
  Alignment" radio button (Left / Centre / Right) now appears on the
  Lake Category edit screen in WordPress admin, below the image field.
  Selection is saved as term meta and applied to the description block
  on the category page automatically.

v1.0.5 (2026-03-29)
- Fix: Calendar dropdown showing past years (2024, 2025) — min attribute
  now set directly in PHP as HTML attributes on all date inputs across all
  three filter bars. JS setAttribute alone was not sufficient as Chrome's
  native date picker initialises before deferred JS runs.
- Fix: Voice date clamping defaulting to 2013 — caused by new Date()
  parsing ISO date strings as UTC midnight which shifts to the previous
  day/year in local timezones. Fixed by parsing dateMax as integer parts
  (year, month, day) using new Date(y, m, d) local time constructor.

v1.0.4 (2026-03-29)
- Fix: Date calendar now shows only from current year — min attribute
  (1 Jan current year) applied to all date inputs so past years (2024,
  2025) no longer appear in the calendar dropdown.
- Fix: Hidden lakes now also excluded from homepage Featured Lakes section.
  Previously the pre_get_posts filter did not cover this direct WP_Query.
- Feature: "Dates open until 31 December [year]" note added to the voice
  hint on all three filter bars (homepage, Browse Lakes, category pages).
  Year updates automatically each January — no code change needed.
- Feature: Voice date input now shows a message when a spoken date exceeds
  the maximum search date — e.g. "Note: one or both dates exceeded the
  maximum search date (31 Dec 2028) and have been adjusted."

v1.0.3 (2026-03-26)
- Feature: Hide Lake — checkbox in SEO & Schema meta box. Hidden lakes
  stay published (URL and SEO value preserved, no 404) but are excluded
  from all archive pages, category pages, AJAX search results, and the
  Last Minute Availability page. Direct URL still accessible. Box
  highlights amber when active as a visual reminder.
- Feature: Rolling 3-year date search window — date inputs now have a
  max attribute set to 31 Dec (current year + 2). In 2026 the max is
  2028-12-31, in 2027 it rolls to 2029-12-31 etc. Calculated server-side
  on every page load so it updates automatically on 1st January each year.
  Voice date input also clamped to the same maximum.

v1.0.2 (2026-03-25)
- Fix: Category page SEO description moved above the filter bar and
  restyled to match the Last Minute Availability page — plain text,
  muted colour, no border box. Removed the bordered block style.

v1.0.1 (2026-03-22)
- Feature: Extended SEO description block on category pages — appears
  between the filter bar and lake cards, pulling from the WordPress
  category Description field. Supports full HTML/paragraphs via
  wp_kses_post(). Styled with a left primary-colour border for visual
  prominence. Edit per category in Lakes → Lake Categories → Description.
- Change: Category page hero now shows a short fixed tagline rather than
  the full description — full text appears in the body where Google
  weights it more heavily.
- SEO: Each category page can now carry 150-300 words of keyword-rich
  body content above the results, directly targeting terms like
  "exclusive carp lake hire France", "big carp lakes 60lb France" etc.

v1.0.0 (2026-03-21)
- Initial swimbooker Holidays release — rebranded from FranceBooker V36.1.21
- Theme name, display text, URLs updated to swimbooker Holidays /
  holidays.swimbooker.com throughout all templates, README, SETUP-NOTES

v36.1.21 (2026-03-21)
- Change: Homepage "List Your Lake" section button updated to match List
  Your Lake page — now reads "Get Started — Apply to List Your Lake"
- Change: All hardcoded email addresses updated from info@francebooker.co.uk
  to support@swimbooker.com throughout header, footer, contact page,
  and form error message fallbacks. Admin settings placeholders also updated.
  Form destination emails are unaffected (managed via Settings → swimbooker Holidays Emails)

v36.1.20 (2026-03-20)
- Feature: Social media links updated in header and footer — Facebook,
  Instagram, TikTok, YouTube all now point to swimbooker accounts and
  open in new windows
- Change: Footer Help & Legal FAQs renamed to "Help & FAQs" and now
  links to swimbooker support hub (opens in new window)
- Fix: README.md and SETUP-NOTES.txt updated to version 36.1.20

v36.1.19 (2026-03-20)
- Change: Text page padding (FAQs, T&Cs, Privacy Policy etc) updated from
  5px to 24px to match the site-wide container padding consistently.

v36.1.18 (2026-03-20)
- Fix: V36.1.17 mobile padding regression — the .container padding override
  at max-width:480px was removing the 24px side padding from filter bars,
  lake cards, and all other sections on mobile. Removed the .container
  rule. The page.php inline padding:0 5px on .entry-content is sufficient
  to pad text pages (FAQs, T&Cs etc) without affecting anything else.

v36.1.17 (2026-03-20)
- Fix: Standard page content (FAQs, How to Book, Travel Guide, Privacy
  Policy, Terms & Conditions, Cookie Policy etc) now has 5px left/right
  padding on mobile so text no longer runs edge-to-edge on small screens.
  Applied via page.php inline style and a max-width:480px CSS rule on
  .container and .entry-content.

v36.1.16 (2026-03-20)
- Change: List Your Lake hero button updated from "Get Started — It's Free"
  to "Get Started — Apply to List Your Lake" — removes misleading "free"
  implication since commission is taken on bookings.

v36.1.15 (2026-03-20)
- Fix: Category pages (Lake Exclusive, Big Carp etc) now have the same full
  filter bar as the homepage and lakes archive — No. of Anglers input,
  Speak Dates voice button, Reset Search button, and voice hint text.
  Previously had a basic bar with a text search field and no voice or
  reset functionality.

v36.1.14 (2026-03-20)
- Feature: Gallery lightbox now full-screen with prev/next arrow navigation
- Feature: Lightbox shows full-size uncropped images (fetches 'full' size
  from WordPress media library, falls back to lake-hero if unavailable)
- Feature: Image counter (e.g. "2 / 6") displayed at top of lightbox
- Feature: Keyboard navigation — left/right arrow keys, Escape to close
- Feature: Touch swipe support for mobile (swipe left/right to navigate)
- Feature: Click backdrop closes lightbox, arrows hidden if only one image
- Fix: Gallery grid thumbnails still use lake-hero crop for fast loading;
  full-size only loads when image is opened in lightbox

v36.1.13 (2026-03-20)
- Fix: All ferry port driving directions now start from the actual terminal
  rather than town centre — Caen now starts from Brittany Ferries Terminal
  Ouistreham, Dieppe from the ferry terminal, Cherbourg from Gare Maritime
  Transatlantique, Le Havre from Terminal de la Citadelle, St Malo from
  Gare Maritime du Naye. Calais/Eurotunnel was already correct.

v36.1.12 (2026-03-19)
- Feature: Gilroy font family (Light, Regular, Medium, SemiBold, Bold)
  self-hosted in /fonts/ directory — matches swimbooker.com branding
- Change: Playfair Display and Nunito (Google Fonts) replaced with Gilroy
  across all --font-heading, --font-body, and --font-nav variables
- Remove: Google Fonts external request and preconnect hints removed —
  fonts now load entirely from the theme with no external dependency
- Font files: Gilroy-Light.otf, Gilroy-Regular.otf, Gilroy-Medium.otf,
  Gilroy-SemiBold.otf, Gilroy-Bold.otf

v36.1.11 (2026-03-19)
- Feature: Refresh All iCal Feeds button added to swimbooker Lakes admin
  list page. Appears above the lake list, triggers the same refresh
  function as the hourly cron in one click. Confirmation dialog prevents
  accidental clicks. Success notice shows how many lakes were updated.
  Only visible to administrators.

v36.1.10 (2026-03-19)
- Fix: Last Minute page intro text was constrained to 780px centered column
  causing excessive vertical height. Now uses full container width with
  5px left/right padding.

v36.1.9 (2026-03-19)
- Feature: Last Minute Availability added to footer Quick Links for SEO
  discoverability and internal linking
- Feature: Last Minute Availability page template now outputs any content
  written in the WordPress editor — add keyword-rich intro text there
  to help Google rank the page for last minute search terms

v36.1.8 (2026-03-19)
- Feature: SEO meta title and description added specifically for the Last
  Minute Availability page, targeting search terms such as "last minute
  carp fishing lakes France", "last minute availability France", etc.
  Title: "Last Minute Carp Fishing Lakes | France & Europe Availability |
  swimbooker Holidays". OG/Twitter tags also updated for social sharing.

v36.1.7 (2026-03-19)
- Change: Hero CTA reduced to two buttons — Browse All Lakes and Last Minute
  Availability. Exclusive Hire and Big Carp category buttons removed.
- Change: Last Minute button renamed to Last Minute Availability and given
  btn-lg class to match Browse All Lakes button size.

v36.1.6 (2026-03-19)
- Fix: Last Minute page showing duplicate cards for the same lake — break
  from booking loop was not preventing the tail-end gap check from adding
  a second result. Added $slot_found flag to guard against this.
- Fix: Lakes without an iCal URL were appearing on the Last Minute page
  with a spurious full 6-week window. Now skipped entirely — only lakes
  with a connected iCal feed appear, ensuring all results are based on
  real booking data.

v36.1.5 (2026-03-19)
- Feature: Last Minute Availability page (page-templates/last-minute.php)
  Shows all lakes with 7+ day availability within the next 6 weeks, sorted
  by earliest available date. Reads from iCal transient cache — no live
  fetches on page load. Lakes without iCal URLs shown as always available.
  Fail-open on iCal errors. Empty state shown if no availability found.
- Feature: Availability window badge on each card showing exact dates and
  number of days available (e.g. "2 Apr → 16 Apr  14 days")
- Feature: Last Minute hero button added to homepage CTA group
- Remove: Hero search text box removed (duplicated filter search below)
- Remove: Exclusive Hire Lakes button renamed to Exclusive Hire (shorter)
- Remove: initHeroSearch JS function removed (no longer needed)

v36.1.4 (2026-03-16)
- Feature: Search text field replaced with Number of Anglers input on both
  homepage and lakes archive filter bars
- Feature: Entering a number filters results to lakes with max_anglers >= that
  number using a numeric meta_query on _lake_max_anglers
- Feature: Anglers field cleared correctly by Reset Search on both pages
- Remove: Generic text search field (lake name/region search removed)

v36.1.3 (2026-03-16)
- Fix: iOS Safari date input empty state renders with minimal height until a
  value is entered — added min-height: 44px and display: flex with
  align-items: center so empty and populated states match visually

v36.1.2 (2026-03-16)
- Fix: Homepage search not working on first load on desktop after fresh theme
  install — LiteSpeed was delaying the wp_localize_script inline fbData block
  independently of main.js, causing initFilters() to bail silently as fbData
  was undefined at DOMContentLoaded. Added a 100ms poll (max 30 attempts /
  3 seconds) so initFilters() retries until fbData is available.

v36.1.1 (2026-03-16)
- Fix: iOS Safari filter bar overflow — date inputs, voice button, Search Lakes
  and Reset Search buttons now render correctly at full width on iPhone
- Fix: Added -webkit-appearance: none and box-sizing: border-box to date inputs
  to resolve iOS Safari width calculation bug
- Fix: All filter bar child elements constrained to 100% width on mobile via
  max-width: 768px media query — desktop and Android unaffected

v36.1 (2026-03-16)
- Feature: Native email form handlers for Contact and List Your Lake forms
  (no plugin required — uses wp_mail() routed through SMTP plugin)
- Feature: Settings → swimbooker Holidays Emails admin page with independent To, CC,
  BCC, From Name, From Email for each form
- Feature: Success and error messages on both form pages after submission
- Fix: List Your Lake form field name prefix mismatch (ll_ vs l_) causing
  "required fields" error on all submissions
- Fix: Lake Category multi-select replaced with checkboxes — no Ctrl key needed
- Fix: Rest of Europe category added to List Your Lake form
- Fix: Lake Size label changed from hectares to acres (form and email)
- Theme version now displays correctly in WordPress Theme Details

v36 (2026-03-16)
- Feature: Voice date search using Web Speech API — no external libraries
- Feature: Natural language date parser supporting ordinals, word-numbers,
  2-digit and 4-digit years, and connector-free date pairs
- Feature: Reset Search button added to both homepage and lakes archive
- Feature: Speak Dates button on both homepage and lakes archive filter bars
- Fix: Archive lake page card layout restored after voice button insertion
- Fix: Date parser word-number lookup (first, second etc → digits)
- Fix: 2-digit year support (27 → 2027) and ordinal year handling (27th → 2027)
- Fix: No-connector date pair parsing (dates spoken without "to" between them)

v35 (2026-03-11)
- Feature: SEO meta title and description fields wired up and outputting correctly
- Feature: Rich auto-generated meta descriptions from lake data fields
- Feature: Schema upgraded to SportsActivityLocation with amenityFeature,
  priceRange, and parentOrganization (swimbooker)
- Feature: lakes-hero.jpg drop-in hero image support on archive page
- Fix: AJAX search nonce removed — LiteSpeed cached pages no longer serve
  stale nonces causing silent search failure
- Fix: lakes-hero.jpg file_exists() check removed — browser handles gracefully
*/

/* ============================================================
   GILROY FONT FACES
   Self-hosted — files in /fonts/ directory
   ============================================================ */
@font-face {
  font-family: 'Gilroy';
  src: url('fonts/Gilroy-Light.otf') format('opentype');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Gilroy';
  src: url('fonts/Gilroy-Regular.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Gilroy';
  src: url('fonts/Gilroy-Medium.otf') format('opentype');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Gilroy';
  src: url('fonts/Gilroy-SemiBold.otf') format('opentype');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Gilroy';
  src: url('fonts/Gilroy-Bold.otf') format('opentype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ============================================================
   CSS VARIABLES - COLOUR PALETTE
   Inspired by swimbooker blues + pastoral/rural French tones
   ============================================================ */
:root {
  /* Primary Brand Colours */
  --primary:       #1a5276;   /* Deep lake blue */
  --primary-light: #2e86c1;   /* Mid blue */
  --primary-bright:#3498db;   /* swimbooker accent blue */
  --accent:        #27ae60;   /* Reed/pastoral green */
  --accent-light:  #a9dfbf;   /* Pale meadow green */

  /* Pastels & Rural */
  --cream:         #faf6f0;   /* Warm parchment */
  --sand:          #f0e8d8;   /* Sandy bank */
  --mist:          #e8eff5;   /* Morning mist blue */
  --bark:          #7d6045;   /* Bark brown */

  /* Neutrals */
  --white:         #ffffff;
  --off-white:     #f8f9fa;
  --light-grey:    #e9ecef;
  --mid-grey:      #6c757d;
  --dark-grey:     #343a40;
  --text:          #2c3e50;
  --text-light:    #5d6d7e;

  /* UI */
  --border:        #dce6f0;
  --shadow:        0 4px 20px rgba(26,82,118,0.12);
  --shadow-hover:  0 8px 32px rgba(26,82,118,0.22);
  --radius:        12px;
  --radius-lg:     20px;
  --transition:    all 0.3s ease;

  /* Typography */
  --font-heading:  'Gilroy', 'Segoe UI', sans-serif;
  --font-body:     'Gilroy', 'Segoe UI', sans-serif;
  --font-nav:      'Gilroy', sans-serif;
}

/* ============================================================
   FONT AWESOME SVG ICON STYLES
   Self-hosted SVG sprite replaces CDN Font Awesome.
   Icons are rendered as inline SVG via JS (main.js initSvgIcons).
   ============================================================ */
.fa-svg-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  fill: currentColor;
  overflow: visible;
}
/* Size variants */
.fa-xs   { font-size: 0.75em; }
.fa-sm   { font-size: 0.875em; }
.fa-lg   { font-size: 1.25em; }
.fa-xl   { font-size: 1.5em; }
.fa-2x   { font-size: 2em; }
.fa-fw   { width: 1.25em; text-align: center; }

/* ============================================================
   RESET & BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; font-size: 16px; }

body {
  font-family: var(--font-body);
  color: var(--text);
  background: var(--white);
  line-height: 1.7;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }
a { color: var(--primary-light); text-decoration: none; transition: var(--transition); }
a:hover { color: var(--primary); }

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  color: var(--primary);
  line-height: 1.25;
  font-weight: 700;
}

h1 { font-size: clamp(2rem, 5vw, 3.5rem); }
h2 { font-size: clamp(1.6rem, 3.5vw, 2.5rem); }
h3 { font-size: clamp(1.2rem, 2.5vw, 1.75rem); }
h4 { font-size: 1.25rem; }

p { margin-bottom: 1rem; color: var(--text-light); }

ul { list-style: none; }

/* ============================================================
   UTILITY CLASSES
   ============================================================ */
.container { max-width: 1240px; margin: 0 auto; padding: 0 24px; }
.container-wide { max-width: 1440px; margin: 0 auto; padding: 0 32px; }

.section-pad { padding: 80px 0; }
.section-pad-sm { padding: 48px 0; }

.text-center { text-align: center; }
.text-white { color: var(--white) !important; }

.section-tag {
  display: inline-block;
  background: var(--mist);
  color: var(--primary);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 2px;
  text-transform: uppercase;
  padding: 6px 16px;
  border-radius: 50px;
  margin-bottom: 16px;
}

.section-title {
  font-family: var(--font-heading);
  color: var(--primary);
  margin-bottom: 16px;
}

.section-subtitle {
  color: var(--text-light);
  font-size: 1.1rem;
  max-width: 580px;
  margin: 0 auto 48px;
}

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 28px;
  border-radius: 8px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.95rem;
  cursor: pointer;
  border: 2px solid transparent;
  transition: var(--transition);
  text-decoration: none;
  white-space: nowrap;
}

.btn-primary {
  background: var(--primary);
  color: var(--white);
  border-color: var(--primary);
}
.btn-primary:hover {
  background: var(--primary-light);
  border-color: var(--primary-light);
  color: var(--white);
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(26,82,118,0.3);
}

.btn-accent {
  background: var(--accent);
  color: var(--white);
  border-color: var(--accent);
}
.btn-accent:hover {
  background: #1e8449;
  border-color: #1e8449;
  color: var(--white);
  transform: translateY(-2px);
}

.btn-outline {
  background: transparent;
  color: var(--primary);
  border-color: var(--primary);
}
.btn-outline:hover {
  background: var(--primary);
  color: var(--white);
}

.btn-white {
  background: var(--white);
  color: var(--primary);
  border-color: var(--white);
}
.btn-white:hover {
  background: var(--mist);
  transform: translateY(-2px);
}

.btn-lg { padding: 18px 36px; font-size: 1.05rem; }
.btn-sm { padding: 10px 20px; font-size: 0.85rem; }

/* ── swimbooker "Book Now" trigger button ──────────────────────
   Calls SBWidget('openWidget') — handled entirely by the
   swimbooker WordPress plugin. Theme only provides styling.
   ─────────────────────────────────────────────────────────── */
.btn-book {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: linear-gradient(135deg, var(--accent) 0%, #1e8449 100%);
  color: var(--white);
  border: none;
  padding: 16px 36px;
  border-radius: 8px;
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0.3px;
  cursor: pointer;
  transition: var(--transition);
  box-shadow: 0 4px 18px rgba(39,174,96,0.35);
  text-decoration: none;
  white-space: nowrap;
}
.btn-book:hover,
.btn-book:focus-visible {
  background: linear-gradient(135deg, #1e8449 0%, #196f3d 100%);
  transform: translateY(-3px);
  box-shadow: 0 8px 28px rgba(39,174,96,0.45);
  color: var(--white);
  outline: none;
}
.btn-book:active {
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(39,174,96,0.35);
}

/* Full-width variant inside the sidebar booking box */
.booking-box-body .btn-book {
  width: 100%;
  font-size: 1.1rem;
  padding: 18px 24px;
}

/* Larger hero variant */
.btn-book-lg {
  padding: 20px 48px;
  font-size: 1.15rem;
  border-radius: 10px;
}

/* ============================================================
   SCROLLING ANNOUNCEMENT BANNER
   ============================================================ */

@keyframes fb-scroll {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

.fb-banner {
  background: var(--primary);
  color: var(--white);
  width: 100%;
  overflow: hidden;
  position: relative;
  z-index: 200;
  height: 36px;
  display: flex;
  align-items: center;
}

.fb-banner__track {
  display: flex;
  white-space: nowrap;
  animation: fb-scroll var(--fb-banner-duration, 25s) linear infinite;
  will-change: transform;
}

.fb-banner__text,
.fb-banner__link {
  display: inline-flex;
  align-items: center;
  font-size: var(--fb-banner-size, 0.875rem);
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--white);
  text-decoration: none;
  white-space: nowrap;
}

.fb-banner__link:hover {
  color: var(--accent);
  text-decoration: underline;
}

/* Wide gap between message repetitions */
.fb-banner__gap {
  display: inline-block;
  width: 120px;
}

/* Pause on hover for accessibility */
.fb-banner:hover .fb-banner__track {
  animation-play-state: paused;
}

/* ============================================================
   TOP BAR
   ============================================================ */
.top-bar {
  background: var(--primary);
  color: rgba(255,255,255,0.85);
  font-size: 0.8rem;
  padding: 8px 0;
}
.top-bar .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.top-bar a { color: rgba(255,255,255,0.85); }
.top-bar a:hover { color: var(--white); }
.top-bar-left { display: flex; align-items: center; gap: 20px; }
.top-bar-right { display: flex; align-items: center; gap: 20px; }
.top-bar .social-links { display: flex; gap: 12px; }
.top-bar .social-links a { font-size: 0.9rem; }
.powered-by { display: flex; align-items: center; gap: 6px; font-weight: 600; }
.powered-by span { color: var(--primary-bright); }

/* ============================================================
   HEADER / NAVIGATION
   ============================================================ */
#site-header {
  background: var(--white);
  box-shadow: 0 2px 12px rgba(26,82,118,0.08);
  position: sticky;
  top: 0;
  z-index: 1000;
  transition: var(--transition);
}

#site-header.scrolled {
  box-shadow: 0 4px 24px rgba(26,82,118,0.15);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 0;
  gap: 24px;
}

.site-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
.site-logo .logo-mark {
  width: 44px;
  height: 44px;
  background: var(--primary);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
}
.site-logo .logo-text {
  font-family: var(--font-heading);
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--primary);
  line-height: 1;
}
.site-logo .logo-text span { color: var(--accent); }
.site-logo .logo-tagline {
  font-size: 0.65rem;
  color: var(--text-light);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  display: block;
  margin-top: 2px;
}

/* Main nav */
.main-nav { display: flex; align-items: center; }

.nav-menu {
  display: flex;
  align-items: center;
  gap: 4px;
  list-style: none;
}

.nav-menu > li { position: relative; }

.nav-menu > li > a {
  display: block;
  padding: 8px 14px;
  color: var(--text);
  font-weight: 600;
  font-size: 0.9rem;
  border-radius: 6px;
  transition: var(--transition);
}
.nav-menu > li > a:hover,
.nav-menu > li.current-menu-item > a {
  color: var(--primary);
  background: var(--mist);
}

/* Dropdown */
.nav-menu .dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-hover);
  min-width: 260px;
  padding: 8px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: var(--transition);
  z-index: 100;
}
.nav-menu li:hover > .dropdown-menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.dropdown-menu li a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  color: var(--text);
  font-size: 0.875rem;
  border-radius: 8px;
  font-weight: 600;
}
.dropdown-menu li a:hover {
  background: var(--mist);
  color: var(--primary);
}
.dropdown-menu .cat-icon {
  width: 30px;
  height: 30px;
  background: var(--mist);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.9rem;
  flex-shrink: 0;
}

/* Hide mobile arrow indicator on desktop */
.has-dropdown > a::after { content: none; }

.header-cta {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Mobile menu toggle */
.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  cursor: pointer;
  padding: 8px;
  background: none;
  border: none;
}
.nav-toggle span {
  display: block;
  width: 24px;
  height: 2px;
  background: var(--primary);
  transition: var(--transition);
  border-radius: 2px;
}

/* ============================================================
   HERO - HOMEPAGE
   ============================================================ */
.hero {
  position: relative;
  min-height: 88vh;
  display: flex;
  align-items: center;
  overflow: hidden;
  background: var(--primary);
  padding-bottom: 96px; /* reserve space for the absolutely-positioned stats bar */
}

.hero-bg {
  position: absolute;
  inset: 0;
  background-image: url('images/hero-bg.webp');
  background-size: cover;
  background-position: center;
  filter: brightness(0.85);
}

.hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    rgba(26,82,118,0.35) 0%,
    rgba(26,82,118,0.1) 60%,
    rgba(39,174,96,0.05) 100%
  );
}

.hero-content {
  position: relative;
  z-index: 2;
  max-width: 780px;
  padding: 80px 0;
}

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: rgba(255,255,255,0.15);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,0.2);
  color: var(--white);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  padding: 8px 18px;
  border-radius: 50px;
  margin-bottom: 24px;
}

.hero h1 {
  color: var(--white);
  font-size: clamp(2.4rem, 6vw, 4.2rem);
  margin-bottom: 20px;
  text-shadow: 0 2px 20px rgba(0,0,0,0.3);
}
.hero h1 em {
  font-style: normal;
  color: var(--accent-light);
}

.hero-subtitle {
  color: rgba(255,255,255,0.88);
  font-size: clamp(1rem, 2vw, 1.2rem);
  margin-bottom: 36px;
  max-width: 580px;
  line-height: 1.7;
}

.hero-search {
  background: var(--white);
  border-radius: 12px;
  padding: 8px 8px 8px 20px;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: 580px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.2);
  margin-bottom: 32px;
}
.hero-search input {
  flex: 1;
  border: none;
  outline: none;
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--text);
  background: transparent;
}
.hero-search input::placeholder { color: var(--mid-grey); }

.hero-cta-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
}

.hero-stats {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 2;
  background: rgba(255,255,255,0.1);
  backdrop-filter: blur(12px);
  border-top: 1px solid rgba(255,255,255,0.15);
}
.hero-stats .container {
  display: flex;
  align-items: stretch;
  justify-content: center;
}
.stat-item {
  flex: 1;
  max-width: 220px;
  text-align: center;
  padding: 20px 16px;
  border-right: 1px solid rgba(255,255,255,0.1);
}
.stat-item:last-child { border-right: none; }
.stat-number {
  font-family: var(--font-heading);
  font-size: 2rem;
  font-weight: 700;
  color: var(--white);
  line-height: 1;
}
.stat-label {
  font-size: 0.75rem;
  color: rgba(255,255,255,0.75);
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-top: 4px;
}

/* Mobile stats bar — shown below hero on small screens only.
   Uses the same visual style as the desktop hero stats bar but
   sits in the document flow rather than overlaying the hero image. */
.hero-stats-mobile {
  display: none; /* hidden on desktop — desktop uses .hero-stats overlay */
  background: var(--primary);
  padding: 0;
}
.stat-item-mobile {
  flex: 1;
  text-align: center;
  padding: 16px 8px;
  border-right: 1px solid rgba(255,255,255,0.1);
}
.stat-item-mobile:last-child { border-right: none; }
.stat-number-mobile {
  font-family: var(--font-heading);
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--white);
  line-height: 1;
}
.stat-label-mobile {
  font-size: 0.65rem;
  color: rgba(255,255,255,0.75);
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-top: 4px;
}

/* ============================================================
   CATEGORIES / BROWSE SECTION
   ============================================================ */
.categories-section { background: var(--cream); }

.category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 20px;
  margin-top: 48px;
}

.category-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 28px 24px;
  text-align: center;
  transition: var(--transition);
  cursor: pointer;
  text-decoration: none;
  display: block;
}
.category-card:hover {
  transform: translateY(-5px);
  box-shadow: var(--shadow-hover);
  border-color: var(--primary-light);
}
.cat-emoji {
  font-size: 2.5rem;
  margin-bottom: 14px;
  display: block;
}
.category-card h3 {
  font-size: 1rem;
  color: var(--primary);
  margin-bottom: 8px;
}
.category-card p {
  font-size: 0.82rem;
  color: var(--text-light);
  margin: 0;
  line-height: 1.5;
}
.cat-count {
  display: inline-block;
  background: var(--mist);
  color: var(--primary);
  font-size: 0.72rem;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 50px;
  margin-top: 10px;
}

/* ============================================================
   LAKE CARDS (LISTING)
   ============================================================ */
.lakes-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 28px;
}

.lake-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow);
  transition: var(--transition);
  border: 1px solid var(--border);
  display: flex;
  flex-direction: column;
}
.lake-card:hover {
  transform: translateY(-6px);
  box-shadow: var(--shadow-hover);
}

/* Last minute availability badge */
.lm-card { position: relative; }
.lm-badge {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 10;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--accent);
  color: var(--white);
  font-size: 0.78rem;
  font-weight: 700;
  padding: 6px 12px;
  border-radius: 20px;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0,0,0,0.25);
  letter-spacing: 0.2px;
}
.lm-badge .fa-bolt { font-size: 0.72rem; }
.lm-badge--neutral {
  background: rgba(26,82,118,0.82);
  white-space: normal;
  max-width: 180px;
  line-height: 1.3;
}
.lm-badge--neutral .fa-calendar-check { font-size: 0.72rem; }
.lm-arrow { opacity: 0.8; font-weight: 400; }
.lm-days {
  background: rgba(255,255,255,0.2);
  padding: 1px 7px;
  border-radius: 10px;
  font-size: 0.72rem;
}

/* Image carousel on card */
.card-gallery {
  position: relative;
  height: 220px;
  overflow: hidden;
}
.card-gallery .slides {
  display: flex;
  height: 100%;
  transition: transform 0.5s ease;
}
.card-gallery .slide {
  min-width: 100%;
  height: 100%;
  object-fit: cover;
  flex-shrink: 0;
}
.card-gallery .gallery-nav {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 10px;
  opacity: 0;
  transition: var(--transition);
}
.card-gallery:hover .gallery-nav { opacity: 1; }
.gallery-btn {
  width: 32px;
  height: 32px;
  background: rgba(255,255,255,0.9);
  border: none;
  border-radius: 50%;
  cursor: pointer;
  font-size: 0.9rem;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: var(--transition);
}
.gallery-btn:hover { background: var(--white); }
.gallery-dots {
  position: absolute;
  bottom: 10px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  gap: 5px;
}
.gallery-dot {
  width: 6px;
  height: 6px;
  background: rgba(255,255,255,0.6);
  border-radius: 50%;
  cursor: pointer;
  transition: var(--transition);
}
.gallery-dot.active { background: var(--white); width: 18px; border-radius: 3px; }

.card-badge {
  position: absolute;
  top: 12px;
  left: 12px;
  background: var(--primary);
  color: var(--white);
  font-size: 0.7rem;
  font-weight: 700;
  padding: 4px 12px;
  border-radius: 50px;
  letter-spacing: 0.5px;
}
.card-badge.featured { background: var(--accent); }
.card-badge.exclusive { background: var(--bark); }

/* Direct booking overlay — always visible, fades on hover/touch */
.card-direct-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(
    to bottom,
    rgba(26, 82, 118, 0.82) 0%,
    rgba(26, 82, 118, 0.55) 50%,
    rgba(26, 82, 118, 0.15) 100%
  );
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  padding: 14px 16px;
  gap: 6px;
  z-index: 9;
  border-radius: 12px 12px 0 0;
  opacity: 1;
  transition: opacity 0.3s ease;
  pointer-events: none;
}
.lake-card:hover .card-direct-overlay,
.lake-card:focus-within .card-direct-overlay,
.card-gallery:hover .card-direct-overlay {
  opacity: 0;
}
.card-direct-title {
  color: var(--white);
  font-family: var(--font-heading);
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1.3;
  text-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.card-direct-sub {
  color: rgba(255, 255, 255, 0.88);
  font-size: 0.75rem;
  font-weight: 400;
  line-height: 1.4;
  text-shadow: 0 1px 2px rgba(0,0,0,0.3);
}

.card-body {
  padding: 20px;
  display: flex;
  flex-direction: column;
  flex: 1;
}

.card-location {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 0.78rem;
  color: var(--text-light);
  margin-bottom: 8px;
  font-weight: 600;
}

.card-title {
  font-family: var(--font-heading);
  font-size: 1.2rem;
  color: var(--primary);
  margin-bottom: 8px;
  line-height: 1.3;
}

.card-desc {
  font-size: 0.85rem;
  color: var(--text-light);
  margin-bottom: 14px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card-features {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 6px;
  margin-bottom: 16px;
  /* Cap at 3 lines — pills beyond that are clipped cleanly */
  max-height: calc((1.6em + 8px) * 3 + 12px);
  overflow: hidden;
}
.feature-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--mist);
  color: var(--primary);
  font-size: 0.72rem;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 50px;
  line-height: 1.6;
  white-space: nowrap;
}

.card-lower {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  /* Reserve 3 pill rows + bookings line so pills always start at same position */
  min-height: calc((1.6em + 8px) * 3 + 12px + 36px);
}
.card-lower .card-open-until-wrap {
  margin-top: auto;
}

.card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  margin-top: auto;
}
.card-price {
  font-size: 0.8rem;
  color: var(--text-light);
}
.card-price strong {
  display: block;
  font-size: 1.05rem;
  color: var(--primary);
  font-family: var(--font-heading);
}
.card-open-until-wrap {
  padding: 8px 20px;
  border-top: 1px solid var(--border);
}
.card-open-until {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 0.72rem;
  color: var(--text-light);
  font-style: italic;
}

/* ============================================================
   PARTNER & PROMOTIONAL CARDS — single lake page
   Cards sit inside lake-main filling the space alongside the
   sidebar on desktop. On mobile, lake-sidebar gets order:1 and
   lake-main gets order:2 so booking box always appears first.
   ============================================================ */

/* Base card — stacks vertically inside lake-main */
.partner-card {
  display: block;
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-top: 20px;
  text-decoration: none;
  color: inherit;
  transition: var(--transition);
  border: 1px solid var(--border);
}
.partner-card:hover { transform: translateY(-2px); box-shadow: var(--shadow); }

/* AP Crossings */
.partner-card--ap {
  background: linear-gradient(90deg, #1a2a1a 0%, #2c3e2c 40%, #4a5a3a 70%, #6b7a5a 100%);
  border-color: #2e5235;
}
/* When a background image is set — darken overlay so text stays readable */
.partner-card--ap-has-image {
  background: none;
  position: relative;
}
.partner-card--ap-has-image .partner-card__overlay {
  background: linear-gradient(90deg, rgba(0,0,0,0.65) 0%, rgba(0,0,0,0.35) 60%, rgba(0,0,0,0.15) 100%);
  border-radius: inherit;
}
.partner-card__overlay { display: flex; align-items: center; justify-content: space-between; padding: 24px 24px; gap: 16px; }
.partner-card__left { flex: 1; }
.partner-card__tag {
  display: inline-block;
  background: rgba(255,255,255,0.15);
  color: rgba(255,255,255,0.8);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 3px;
  margin-bottom: 6px;
}
.partner-card__headline { color: #fff; font-size: 1rem; line-height: 1.3; margin: 0 0 4px; }
.partner-card__sub { color: rgba(255,255,255,0.75); font-size: 0.8rem; margin: 0; }
.partner-card__arrow { color: rgba(255,255,255,0.7); font-size: 1.2rem; flex-shrink: 0; }

/* swimbooker App */
.partner-card--app { background: var(--white); cursor: default; }
.partner-card--app:hover { transform: none; box-shadow: none; }
.partner-card__app-inner { display: flex; align-items: center; gap: 20px; padding: 18px 20px; }
.partner-card__app-text { flex: 1; }
.partner-card__app-title { font-size: 0.95rem; color: var(--text); margin: 0 0 10px; line-height: 1.3; }
.partner-card__app-list { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.partner-card__app-list li { font-size: 0.8rem; color: var(--text-light); display: flex; align-items: center; gap: 7px; }
.partner-card__app-list li .fa-check-circle { color: #27ae60; font-size: 0.85rem; }
.partner-card__app-scan { font-size: 0.78rem; color: var(--text-light); margin: 0; font-weight: 600; }
.partner-card__app-qr { flex-shrink: 0; }
.partner-card__app-qr img { display: block; border-radius: 6px; }

/* swimbooker+ */
.partner-card--plus { background: var(--primary); }
.partner-card__plus-inner { padding: 18px 20px; }
.partner-card__plus-title { color: #fff; font-size: 0.95rem; margin: 0 0 12px; line-height: 1.3; }
.partner-card__plus-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; margin-bottom: 14px; }
.partner-card__plus-grid span { display: flex; align-items: center; gap: 6px; font-size: 0.78rem; color: rgba(255,255,255,0.88); }
.partner-card__plus-grid .fa-check-circle { color: #5dde8a; font-size: 0.82rem; flex-shrink: 0; }
.partner-card__plus-cta {
  display: inline-block;
  background: rgba(255,255,255,0.15);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 600;
  padding: 7px 16px;
  border-radius: 6px;
  border: 1px solid rgba(255,255,255,0.25);
}
.partner-card--plus:hover .partner-card__plus-cta { background: rgba(255,255,255,0.25); }

/* swimbooker Account */
.partner-card--account { background: var(--mist); }
.partner-card__account-inner { display: flex; align-items: center; gap: 14px; padding: 14px 18px; }
.partner-card__account-icon { font-size: 2rem; color: var(--primary); flex-shrink: 0; }
.partner-card__account-text { flex: 1; }
.partner-card__account-title { font-size: 0.88rem; color: var(--text); margin: 0 0 3px; }
.partner-card__account-sub { font-size: 0.78rem; color: var(--text-light); margin: 0; line-height: 1.5; }
.partner-card__account-btn { flex-shrink: 0; }
.partner-card__account-btn span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--primary);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: 6px;
  white-space: nowrap;
}
.partner-card--account:hover .partner-card__account-btn span { background: #0e3352; }

/* Partner cards — two instances, toggled by screen size
   .partner-cards--desktop: inside lake-main, visible on desktop only
   .partner-cards--mobile:  after sidebar, visible on mobile only       */
.lake-partner-cards { padding-bottom: 20px; }
.lake-partner-cards .partner-card { margin-top: 20px; }
.lake-partner-cards__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  margin-top: 20px;
}

/* Desktop: show inside lake-main, hide the mobile copy */
.partner-cards--desktop { display: block; }
.partner-cards--mobile  { display: none; }

/* Mobile: hide the desktop copy, show after sidebar */
@media (max-width: 1024px) {
  .partner-cards--desktop { display: none; }
  .partner-cards--mobile  { display: block; padding-bottom: 40px; }
  .lake-partner-cards__grid { grid-template-columns: 1fr; }
  .partner-card__plus-grid { grid-template-columns: 1fr; }
  .partner-card__account-inner { flex-wrap: wrap; }
  .partner-card__account-btn { width: 100%; }
  .partner-card__account-btn span { width: 100%; justify-content: center; }
}

/* ============================================================
   SINGLE LAKE PAGE
   ============================================================ */
.lake-hero {
  position: relative;
  background: #0d1f2d;
  overflow: hidden;
}

.lake-hero-inner {
  position: relative;
}

/* ── Static hero (tablet + desktop, >=768px) ─────────────────
   Full-width background image using the lake Featured Image.
   Hidden on mobile — slider shown instead.
   ──────────────────────────────────────────────────────────── */
.lake-hero-static {
  display: none; /* hidden on mobile */
  width: 100%;
  height: 500px;
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
}

@media (min-width: 768px) {
  .lake-hero-static {
    display: block;
  }
}

/* ── Slider (mobile only, <768px) ───────────────────────────── */
.lake-hero-slider {
  width: 100%;
  overflow: hidden;
}

@media (min-width: 768px) {
  .lake-hero-slider { display: none; }
  .hero-slider-btn  { display: none; }
}

.lake-hero-slider .slides {
  display: flex;
  transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Mobile: 1 image, full width */
.lake-hero-slider .slide {
  flex-shrink: 0;
  display: block;
  width: 100vw;
  height: 320px;
  object-fit: cover;
  object-position: center 20%;
}

.lake-hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(13,31,45,0.85) 0%, rgba(13,31,45,0.15) 50%, transparent 100%);
  pointer-events: none;
}

.lake-hero-content {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 40px 0;
  color: var(--white);
}
.lake-breadcrumb {
  font-size: 0.8rem;
  color: rgba(255,255,255,0.7);
  margin-bottom: 12px;
}
.lake-breadcrumb a { color: rgba(255,255,255,0.7); }
.lake-breadcrumb a:hover { color: var(--white); }

.lake-title-group h1 { color: var(--white); font-size: clamp(1.8rem, 4vw, 3rem); }
.lake-title-group .lake-location {
  display: flex;
  align-items: center;
  gap: 6px;
  color: rgba(255,255,255,0.8);
  font-size: 0.95rem;
  margin-top: 8px;
}

/* Hero category pills — hidden on mobile, shown on tablet + desktop.
   Categories are available in the "Listed In" section below on mobile. */
.hero-cat-pills {
  display: none;
}
@media (min-width: 768px) {
  .hero-cat-pills {
    display: contents; /* renders children inline without adding a box */
  }
}

.lake-content-wrap {
  display: grid;
  grid-template-columns: 1fr 340px;
  gap: 40px;
  padding: 60px 0;
  align-items: start;
}

.lake-sidebar { position: sticky; top: 100px; }

/* Booking widget box */
.booking-box {
  background: var(--white);
  border: 2px solid var(--primary-light);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-hover);
}
.booking-box-header {
  background: linear-gradient(135deg, var(--primary), var(--primary-light));
  color: var(--white);
  padding: 24px;
  text-align: center;
}
.booking-box-header h3 { color: var(--white); margin-bottom: 6px; font-size: 1.3rem; }
.booking-box-header p { color: rgba(255,255,255,0.85); margin: 0; font-size: 0.9rem; }
.booking-open-until {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  font-size: 0.78rem;
  color: rgba(255,255,255,0.75);
  font-style: italic;
}
.booking-box-body { padding: 24px; }
.booking-account-tip {
  font-size: 0.8rem;
  color: var(--text-light);
  margin-bottom: 14px;
  padding: 10px 12px;
  background: var(--mist);
  border-radius: 8px;
  line-height: 1.55;
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.booking-account-tip i { color: var(--primary); margin-top: 2px; flex-shrink: 0; }
.booking-features {
  margin-top: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.booking-feature {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--text-light);
}
.booking-feature::before {
  content: '✓';
  color: var(--accent);
  font-weight: 700;
}

/* ── Availability This Year box (single lake sidebar) ─────── */
.avail-box {
  background: var(--white);
  border: 2px solid var(--accent);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-top: 20px;
  box-shadow: 0 4px 16px rgba(39,174,96,0.1);
}
.avail-box-header {
  background: linear-gradient(135deg, var(--accent), #1e8449);
  color: var(--white);
  padding: 16px 20px;
  font-size: 0.95rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 8px;
}
.avail-box-body {
  padding: 16px 20px;
}
.avail-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.avail-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px;
  background: var(--mist);
  border-radius: 8px;
  flex-wrap: wrap;
}
.avail-dates {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--primary);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.avail-icon {
  color: var(--accent);
  font-size: 0.8rem;
}
.avail-arrow {
  color: var(--text-light);
  font-size: 0.75rem;
}
.avail-book-btn {
  background: var(--accent);
  color: var(--white);
  border: none;
  border-radius: 6px;
  padding: 6px 14px;
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  transition: var(--transition);
  font-family: var(--font-body);
}
.avail-book-btn:hover {
  background: #1e8449;
}
.avail-notice {
  font-size: 0.85rem;
  color: var(--text-light);
  margin: 0;
  text-align: center;
  padding: 8px 0;
}
.avail-footer {
  font-size: 0.75rem;
  color: var(--text-light);
  margin: 10px 0 0;
  display: flex;
  align-items: flex-start;
  gap: 6px;
  line-height: 1.5;
}

/* Lake category linkbacks (single lake page) */
.lake-categories {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 18px 20px;
  background: var(--mist);
  border-radius: var(--radius);
  margin-bottom: 28px;
  flex-wrap: wrap;
}
.lake-categories__label {
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-light);
  white-space: nowrap;
  padding-top: 6px;
}
.lake-categories__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.lake-cat-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--white);
  border: 1.5px solid var(--border);
  color: var(--primary);
  font-size: 0.82rem;
  font-weight: 700;
  padding: 6px 14px;
  border-radius: 50px;
  text-decoration: none;
  transition: var(--transition);
}
.lake-cat-pill:hover {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--white);
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(26,82,118,0.2);
}
.lake-cat-pill__count {
  background: var(--mist);
  color: var(--primary-light);
  font-size: 0.7rem;
  font-weight: 800;
  padding: 1px 7px;
  border-radius: 50px;
  transition: var(--transition);
}
.lake-cat-pill:hover .lake-cat-pill__count {
  background: rgba(255,255,255,0.2);
  color: var(--white);
}


  margin-bottom: 32px;
}
.tab-nav {
  display: flex;
  gap: 4px;
  border-bottom: 2px solid var(--border);
  margin-bottom: 28px;
  flex-wrap: wrap;
}
.tab-btn {
  padding: 12px 20px;
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--text-light);
  cursor: pointer;
  transition: var(--transition);
}
.tab-btn:hover { color: var(--primary); }
.tab-btn.active {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
  color: #fff;
  border-bottom-color: transparent;
  border-radius: 6px 6px 0 0;
}
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* Lake info grid */
.info-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 16px;
  margin-bottom: 28px;
}
.info-item {
  background: var(--mist);
  border-radius: var(--radius);
  padding: 16px;
  text-align: center;
}
.info-item .info-icon { font-size: 1.6rem; margin-bottom: 6px; }
.info-item .info-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-light);
  margin-bottom: 4px;
}
.info-item .info-value {
  font-weight: 700;
  color: var(--primary);
  font-size: 0.95rem;
}

/* Facilities */
.facilities-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 12px;
}
.facility-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
}
.facility-item .fac-icon {
  font-size: 1.2rem;
  width: 32px;
  height: 32px;
  background: var(--mist);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Fish stock */
.fish-stock-content {
  background: var(--cream);
  border-left: 4px solid var(--primary-bright);
  padding: 20px 24px;
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.95rem;
  line-height: 1.75;
  color: var(--text);
}
.fish-stock-content p { margin: 0 0 1em; }
.fish-stock-content p:last-child { margin-bottom: 0; }

/* Lake gallery grid */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.gallery-grid img {
  width: 100%;
  height: 180px;
  object-fit: cover;
  border-radius: var(--radius);
  cursor: pointer;
  transition: var(--transition);
}
.gallery-grid img:hover { transform: scale(1.03); }
.gallery-grid img:first-child {
  grid-column: span 2;
  height: 250px;
}

/* Google Map embed */
.lake-map { border-radius: var(--radius-lg); overflow: hidden; margin-top: 24px; }
.lake-map iframe { width: 100%; height: 380px; border: none; }

.driving-times {
  margin-top: 20px;
}
.driving-times h4 { font-size: 1rem; margin-bottom: 12px; color: var(--primary); }
.port-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.port-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: var(--mist);
  border-radius: 8px;
  font-size: 0.85rem;
}
.port-item .port-name { font-weight: 600; color: var(--primary); }
.port-item .port-time { color: var(--text-light); }
.port-item .port-link {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--primary-bright);
}

/* ============================================================
   SECTION: WHY FRANCEBOOKER
   ============================================================ */
.why-section { background: var(--white); }

.why-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 28px;
  margin-top: 48px;
}
.why-card {
  text-align: center;
  padding: 32px 24px;
  background: var(--cream);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  transition: var(--transition);
}
.why-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow);
}
.why-icon {
  width: 64px;
  height: 64px;
  background: var(--primary);
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.8rem;
  margin: 0 auto 20px;
}
.why-card h3 { font-size: 1.1rem; margin-bottom: 10px; }
.why-card p { font-size: 0.875rem; margin: 0; line-height: 1.6; }

/* ============================================================
   SECTION: SWIMBOOKER PROMO BANNER
   ============================================================ */
.swimbooker-banner {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
  padding: 60px 0;
  text-align: center;
  position: relative;
  overflow: hidden;
}
.swimbooker-banner::before {
  content: '';
  position: absolute;
  inset: 0;
  background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.04'%3E%3Ccircle cx='30' cy='30' r='10'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
.swimbooker-banner h2 { color: var(--white); position: relative; }
.swimbooker-banner p { color: rgba(255,255,255,0.85); font-size: 1.1rem; position: relative; max-width: 560px; margin: 16px auto 32px; }
.swimbooker-banner .btn { position: relative; }

/* ============================================================
   TESTIMONIALS
   ============================================================ */
.testimonials-section { background: var(--sand); }

.testimonials-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 24px;
  margin-top: 48px;
}
.testimonial-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  padding: 28px;
  box-shadow: var(--shadow);
  position: relative;
}
.testimonial-card::before {
  content: '"';
  font-family: var(--font-heading);
  font-size: 5rem;
  color: var(--mist);
  position: absolute;
  top: 10px;
  left: 20px;
  line-height: 1;
}
.testimonial-text {
  font-size: 0.95rem;
  color: var(--text);
  line-height: 1.7;
  margin-bottom: 20px;
  position: relative;
}
.testimonial-author {
  display: flex;
  align-items: center;
  gap: 12px;
}
.author-avatar {
  width: 44px;
  height: 44px;
  background: var(--mist);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  flex-shrink: 0;
}
.author-name { font-weight: 700; color: var(--primary); font-size: 0.9rem; }
.author-meta { font-size: 0.78rem; color: var(--text-light); }
.star-rating { color: #f39c12; font-size: 0.85rem; letter-spacing: 1px; }

/* ============================================================
   LIST YOUR LAKE PAGE
   ============================================================ */
.list-lake-hero {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
  padding: 100px 0;
  text-align: center;
  color: var(--white);
  position: relative;
  overflow: hidden;
}
.list-lake-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(26,82,118,0.35) 0%, rgba(26,82,118,0.20) 100%);
}
.list-lake-hero > .container { position: relative; z-index: 1; }
.list-lake-hero h1 { color: var(--white); }
.list-lake-hero p { color: rgba(255,255,255,0.85); font-size: 1.15rem; max-width: 580px; margin: 16px auto; }

.list-steps {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 28px;
  margin: 56px 0;
}
.step-card {
  text-align: center;
  padding: 32px 20px;
}
.step-number {
  width: 52px;
  height: 52px;
  background: var(--primary);
  color: var(--white);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-size: 1.4rem;
  font-weight: 700;
  margin: 0 auto 16px;
}
.step-card h3 { font-size: 1rem; margin-bottom: 8px; }
.step-card p { font-size: 0.85rem; margin: 0; }

.contact-form-wrap {
  width: 100%;
  max-width: 680px;
  margin: 0 auto;
  background: var(--white);
  border-radius: var(--radius-lg);
  padding: 48px;
  box-shadow: var(--shadow-hover);
  box-sizing: border-box;
}

.form-group {
  margin-bottom: 20px;
}
.form-group label {
  display: block;
  font-weight: 700;
  font-size: 0.875rem;
  color: var(--primary);
  margin-bottom: 8px;
}
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 12px 16px;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--text);
  background: var(--white);
  transition: var(--transition);
  outline: none;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--primary-light);
  box-shadow: 0 0 0 3px rgba(46,134,193,0.1);
}
.form-group textarea { min-height: 120px; resize: vertical; }

/* Checkbox group for multi-select fields */
.checkbox-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  background: var(--white);
}
.checkbox-label {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.9rem;
  color: var(--text);
  cursor: pointer;
  font-weight: normal;
  text-transform: none;
  letter-spacing: 0;
  margin: 0;
}
.checkbox-label input[type="checkbox"] {
  width: 18px;
  height: 18px;
  min-width: 18px;
  accent-color: var(--primary);
  cursor: pointer;
  border: none;
  padding: 0;
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer {
  background: var(--primary);
  color: rgba(255,255,255,0.8);
  padding: 64px 0 0;
}

.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 48px;
  border-bottom: 1px solid rgba(255,255,255,0.1);
}

.footer-brand .logo-text {
  font-family: var(--font-heading);
  font-size: 1.8rem;
  color: var(--white);
  margin-bottom: 12px;
}
.footer-brand .logo-text span { color: var(--accent-light); }
.footer-brand p {
  color: rgba(255,255,255,0.7);
  font-size: 0.9rem;
  line-height: 1.7;
  max-width: 300px;
}

.footer-col h4 {
  color: var(--white);
  font-size: 0.9rem;
  font-family: var(--font-body);
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin-bottom: 16px;
}
.footer-col ul { display: flex; flex-direction: column; gap: 8px; }
.footer-col ul li a {
  color: rgba(255,255,255,0.65);
  font-size: 0.875rem;
  transition: var(--transition);
}
.footer-col ul li a:hover { color: var(--white); }

.footer-social {
  display: flex;
  gap: 10px;
  margin-top: 20px;
}
.social-btn {
  width: 38px;
  height: 38px;
  background: rgba(255,255,255,0.1);
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--white);
  font-size: 0.9rem;
  transition: var(--transition);
}
.social-btn:hover {
  background: rgba(255,255,255,0.2);
  color: var(--white);
  transform: translateY(-2px);
}

.footer-bottom {
  padding: 20px 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 0.8rem;
  color: rgba(255,255,255,0.5);
}
.footer-bottom a { color: rgba(255,255,255,0.6); }
.footer-bottom a:hover { color: var(--white); }
.footer-links { display: flex; gap: 20px; }
.powered-badge {
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(255,255,255,0.08);
  padding: 6px 14px;
  border-radius: 50px;
  font-size: 0.78rem;
  color: rgba(255,255,255,0.7);
}
.powered-badge strong { color: var(--white); }

/* ============================================================
   BREADCRUMBS
   ============================================================ */
.breadcrumbs {
  background: var(--sand);
  padding: 14px 0;
  font-size: 0.82rem;
  color: var(--text-light);
}
.breadcrumbs a { color: var(--primary-light); font-weight: 600; }
.breadcrumbs span { margin: 0 8px; }

/* ============================================================
   SEARCH / FILTER BAR
   ============================================================ */
.filter-bar {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 20px 24px;
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: flex-end;
  margin-bottom: 32px;
  box-shadow: var(--shadow);
}
.filter-group { flex: 1; min-width: 180px; }
.filter-group label {
  display: block;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-light);
  margin-bottom: 6px;
}
.filter-group select,
.filter-group input {
  width: 100%;
  padding: 10px 14px;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--text);
  outline: none;
  background: var(--white);
  transition: var(--transition);
}
.filter-group select:focus,
.filter-group input:focus {
  border-color: var(--primary-light);
}

/* Voice date button */
.btn-voice {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 10px 16px;
  background: var(--primary);
  color: var(--white);
  border: none;
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: var(--transition);
  white-space: nowrap;
  align-self: flex-end;
}
.btn-voice:hover { background: var(--primary-light); }
.btn-voice--listening {
  background: #e74c3c;
  animation: pulse-mic 1s ease-in-out infinite;
}
@keyframes pulse-mic {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.65; }
}
.voice-hint {
  width: 100%;
  font-size: 13px;
  color: var(--text-light);
  margin: 4px 0 0;
  line-height: 1.5;
}
.voice-hint em { color: var(--primary); font-style: normal; font-weight: 600; }

/* ============================================================
   ABOUT PAGE
   ============================================================ */
.about-hero {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
  padding: 80px 0;
  position: relative;
  overflow: hidden;
}
.about-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(26,82,118,0.35) 0%, rgba(26,82,118,0.2) 100%);
}
.about-hero > .container { position: relative; z-index: 1; }
/* About page — feature pillar cards (3 across on desktop, stacked on mobile) */
.about-pillar-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-top: 48px;
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
}

/* About page — values cards (auto-fill on desktop, stacked on mobile) */
.about-values-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 28px;
}

@media (max-width: 767px) {
  .about-pillar-grid {
    grid-template-columns: 1fr;
    max-width: 100%;
  }
  .about-values-grid {
    grid-template-columns: 1fr;
  }
}

.about-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 60px;
  align-items: center;
}
.about-image {
  border-radius: var(--radius-lg);
  overflow: hidden;
  height: 400px;
}
.about-image img { width: 100%; height: 100%; object-fit: cover; }

.team-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 28px;
  margin-top: 48px;
}
.team-card {
  text-align: center;
  background: var(--white);
  border-radius: var(--radius-lg);
  padding: 28px 20px;
  border: 1px solid var(--border);
}
.team-avatar {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  margin: 0 auto 16px;
  background: var(--mist);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
}
.team-card h4 { margin-bottom: 4px; font-size: 1rem; }
.team-card .role { font-size: 0.8rem; color: var(--text-light); margin: 0; }

/* ============================================================
   PAGE HERO (internal pages)
   ============================================================ */
.page-hero {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
  padding: 72px 0;
  text-align: center;
  position: relative;
  overflow: hidden;
}
.page-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(26,82,118,0.35) 0%, rgba(26,82,118,0.20) 100%);
}
.page-hero h1 { color: var(--white); position: relative; }
.page-hero p { color: rgba(255,255,255,0.85); font-size: 1.1rem; max-width: 560px; margin: 12px auto 0; position: relative; }

/* ============================================================
   RELATED CATEGORIES CLUSTER SECTION
   Used on category archive pages and single lake pages.
   Builds topic cluster internal links for SEO.
   ============================================================ */

.related-cats-section {
  background: var(--white);
  padding: 72px 0;
  border-top: 1px solid var(--border);
}

.related-cats-header {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  align-items: start;
  margin-bottom: 40px;
}
@media (max-width: 768px) {
  .related-cats-header { grid-template-columns: 1fr; }
}

.related-cats-intro {
  color: var(--text-light);
  font-size: 1rem;
  line-height: 1.75;
  margin: 0;
  padding-top: 8px;
}
.related-cats-intro a {
  color: var(--primary-light);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-color: var(--mist);
  text-underline-offset: 3px;
  transition: var(--transition);
}
.related-cats-intro a:hover {
  color: var(--primary);
  text-decoration-color: var(--primary);
}

/* Category card grid */
.related-cats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 16px;
}

.related-cat-card {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 20px;
  background: var(--cream);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-lg);
  text-decoration: none;
  transition: var(--transition);
  position: relative;
  overflow: hidden;
}
.related-cat-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, var(--primary), var(--primary-light));
  opacity: 0;
  transition: var(--transition);
  z-index: 0;
}
.related-cat-card:hover {
  border-color: var(--primary);
  transform: translateY(-4px);
  box-shadow: var(--shadow-hover);
}
.related-cat-card:hover::before { opacity: 1; }

/* Highlighted — this lake belongs to this category */
.related-cat-card--current {
  border-color: var(--accent);
  background: linear-gradient(135deg, rgba(39,174,96,0.06), rgba(39,174,96,0.02));
}
.related-cat-card--current::before {
  background: linear-gradient(135deg, var(--accent), #1e8449);
}
.related-cat-card--current:hover { border-color: var(--accent); }

.related-cat-card__icon {
  font-size: 1.8rem;
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  background: var(--white);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
  transition: var(--transition);
  box-shadow: 0 2px 8px rgba(26,82,118,0.08);
}
.related-cat-card:hover .related-cat-card__icon {
  background: rgba(255,255,255,0.2);
  box-shadow: none;
}

.related-cat-card__body {
  flex: 1;
  position: relative;
  z-index: 1;
}

.related-cat-card__name {
  font-size: 0.95rem;
  color: var(--primary);
  margin-bottom: 6px;
  font-family: var(--font-body);
  font-weight: 800;
  line-height: 1.3;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  transition: var(--transition);
}
.related-cat-card:hover .related-cat-card__name { color: var(--white); }

.related-cat-card__this-lake {
  font-size: 0.65rem;
  background: var(--accent);
  color: var(--white);
  padding: 2px 8px;
  border-radius: 50px;
  font-weight: 700;
  letter-spacing: 0.5px;
  white-space: nowrap;
}
.related-cat-card:hover .related-cat-card__this-lake {
  background: rgba(255,255,255,0.25);
}

.related-cat-card__desc {
  font-size: 0.8rem;
  color: var(--text-light);
  line-height: 1.5;
  margin: 0 0 10px;
  transition: var(--transition);
}
.related-cat-card:hover .related-cat-card__desc { color: rgba(255,255,255,0.85); }

.related-cat-card__count {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--primary-light);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  transition: var(--transition);
}
.related-cat-card__count i { font-size: 0.65rem; transition: transform 0.3s ease; }
.related-cat-card:hover .related-cat-card__count { color: rgba(255,255,255,0.9); }
.related-cat-card:hover .related-cat-card__count i { transform: translateX(3px); }

/* ============================================================
   PAGINATION
   ============================================================ */
.pagination {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: 56px;
}
.pagination ul.page-numbers {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.pagination ul.page-numbers li {
  display: flex;
  align-items: center;
}
.page-link {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--text);
  transition: var(--transition);
}
.page-link:hover,
.page-link.active {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--white);
}

/* ============================================================
   RESPONSIVE - TABLET
   ============================================================ */
@media (max-width: 1024px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .lake-content-wrap { grid-template-columns: 1fr; }
  .lake-sidebar { position: static; }
  /* About page — feature pillar cards (3 across on desktop, stacked on mobile) */
.about-pillar-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-top: 48px;
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
}

/* About page — values cards (auto-fill on desktop, stacked on mobile) */
.about-values-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 28px;
}

@media (max-width: 767px) {
  .about-pillar-grid {
    grid-template-columns: 1fr;
    max-width: 100%;
  }
  .about-values-grid {
    grid-template-columns: 1fr;
  }
}

.about-grid { grid-template-columns: 1fr; }
  .about-image { height: 300px; }
}

/* ============================================================
   RESPONSIVE - MOBILE
   ============================================================ */
@media (max-width: 768px) {
  .top-bar { display: none; }

  .nav-toggle { display: flex; }

  .main-nav {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100vh;
    background: var(--white);
    box-shadow: none;
    flex-direction: column;
    align-items: stretch;
    padding: 80px 20px 24px;
    transition: transform 0.35s ease;
    transform: translateX(-100%);
    overflow-y: auto;
    z-index: 999;
    box-sizing: border-box;
  }
  .main-nav.open { transform: translateX(0); right: 0; }

  .nav-menu { flex-direction: column; gap: 0; }
  .nav-menu > li > a { padding: 14px 0; border-bottom: 1px solid var(--border); border-radius: 0; }

  .dropdown-menu {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    box-shadow: none;
    border: none;
    border-radius: 0;
    padding: 0 0 0 8px;
    background: var(--off-white);
    display: none;
    width: 100%;
    box-sizing: border-box;
  }

  /* Dropdown item links — allow text to wrap, constrain width */
  .dropdown-menu li a {
    white-space: normal;
    word-break: break-word;
    padding: 10px 8px;
    font-size: 0.875rem;
  }

  /* Shrink the icon boxes slightly on mobile */
  .dropdown-menu .cat-icon {
    width: 26px;
    height: 26px;
    flex-shrink: 0;
  }

  /* Show dropdown when parent li has .open class */
  .has-dropdown.open > .dropdown-menu { display: block; }

  /* Add a visual indicator arrow on the Browse Lakes link */
  .has-dropdown > a::after {
    content: ' ▸';
    font-size: 0.7rem;
    opacity: 0.6;
  }
  .has-dropdown.open > a::after { content: ' ▾'; }

  .header-cta { display: none; }

  .hero {
    min-height: auto;
    padding-top: 48px;
    padding-bottom: 48px;
    align-items: flex-start;
  }
  .hero-stats { display: none; } /* hidden inside hero on mobile — shown below as .hero-stats-mobile */
  .hero-stats-mobile { display: flex; } /* show the below-hero version on mobile */
  .hero-content { padding: 0; max-width: 100%; }
  .hero-search {
    max-width: 100%;
    box-sizing: border-box;
  }
  .hero-search input { font-size: 0.9rem; min-width: 0; }
  .hero-badge { font-size: 0.7rem; letter-spacing: 1px; }
  .hero-subtitle { font-size: 0.95rem; }

  .lakes-grid { grid-template-columns: 1fr; }
  .category-grid { grid-template-columns: repeat(2, 1fr); }

  .footer-grid { grid-template-columns: 1fr; gap: 28px; }
  .footer-bottom { flex-direction: column; text-align: center; }

  .form-row { grid-template-columns: 1fr; }
  .contact-form-wrap { padding: 28px 20px; max-width: 100%; }

  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
  .gallery-grid img:first-child { grid-column: span 2; }

  .filter-bar { flex-direction: column; }
  .filter-group { min-width: 100%; }

  /* Ensure all direct children of filter-bar fill full width on mobile */
  .filter-bar > * { width: 100%; box-sizing: border-box; }

  /* Date inputs on iOS Safari need explicit width and height treatment */
  .filter-bar input[type="date"] {
    width: 100%;
    min-width: 0;
    min-height: 44px;
    box-sizing: border-box;
    -webkit-appearance: none;
    appearance: none;
    display: flex;
    align-items: center;
  }

  /* Voice, Search, and Reset buttons — full width on mobile */
  .btn-voice,
  .filter-bar .btn-primary,
  .filter-bar .btn-outline {
    width: 100%;
    justify-content: center;
    box-sizing: border-box;
  }

  /* Voice hint — full width */
  .voice-hint { width: 100%; box-sizing: border-box; }

  .hero-cta-group { flex-direction: column; align-items: flex-start; }
  .hero h1 { font-size: 2rem; }

  /* Hero badge — prevent long text breaking out of viewport */
  .hero-badge {
    max-width: 100%;
    white-space: normal;
    text-align: left;
    box-sizing: border-box;
  }

  /* Hero search bar — prevent overflow on narrow screens */
  .hero-search {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    flex-wrap: nowrap;
  }
  .hero-search input {
    min-width: 0;
    width: 0; /* let flex grow handle it */
    flex: 1 1 0;
  }

  /* Hero section itself — clip anything that still tries to overflow */
  .hero { overflow: hidden; }
  .hero .container { box-sizing: border-box; }

  /* Buttons — allow text to wrap and reduce padding on small screens */
  .btn { white-space: normal; text-align: center; }
  .btn-lg { padding: 14px 24px; font-size: 0.95rem; }

  /* Lake hero — reduce overlay content on mobile so image shows clearly */
  .lake-hero-content { padding: 16px 0; }
  .lake-breadcrumb { display: none; } /* hidden inside hero on mobile — page breadcrumb above covers this */
  .lake-title-group h1 { font-size: 1.5rem; margin-bottom: 4px; }
  .lake-hero-slider .slide { height: 260px; } /* mobile slider height */

  /* Category pills hidden on mobile via .hero-cat-pills — no mobile overrides needed */
  .lake-location { flex-wrap: wrap; gap: 4px; margin-top: 6px; }

  /* Info grid — 2 columns on mobile */
  .info-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .info-item { padding: 12px 8px; }
  .info-item .info-icon { font-size: 1.3rem; }
  .info-item .info-value { font-size: 0.9rem; }
}

@media (max-width: 480px) {
  .category-grid { grid-template-columns: 1fr; }
  /* .stat-item — desktop hero stats handled by .hero-stats (hidden on mobile, all 4 shown on desktop) */
  .entry-content { padding-left: 5px; padding-right: 5px; }
}

/* ============================================================
   ACCESSIBILITY
   ============================================================ */
:focus-visible {
  outline: 3px solid var(--primary-bright);
  outline-offset: 3px;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
}

/* ============================================================
   SWIMBOOKER WIDGET
   The plugin manages its own overlay/modal rendering.
   Theme CSS here only handles the booking box container
   and any layout the plugin injects into .swimbooker-wrap.
   ============================================================ */
.swimbooker-wrap {
  width: 100%;
}
/* Give the plugin widget breathing room if it renders inline */
.lake-sidebar .swimbooker-wrap,
.booking-box-body .swimbooker-wrap {
  margin-top: 16px;
}

/* ============================================================
   BLOG — ARCHIVE (home.php) & SINGLE POST (single.php)
   ============================================================ */

/* ── Category filter pills ───────────────────────────────── */
.blog-cat-filter {
  background: var(--white);
  border-bottom: 1px solid var(--mist);
  padding: 16px 0;
}
.blog-cat-filter .container {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.blog-cat-pill {
  display: inline-block;
  padding: 6px 18px;
  border-radius: 50px;
  font-size: 0.8rem;
  font-weight: 600;
  border: 2px solid var(--mist);
  color: var(--text-light);
  background: var(--white);
  transition: var(--transition);
  text-decoration: none;
}
.blog-cat-pill:hover,
.blog-cat-pill.active {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--white);
}

/* ── Blog post grid ──────────────────────────────────────── */
.blog-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 32px;
}
.blog-grid-3 {
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

/* ── Blog card ───────────────────────────────────────────── */
.blog-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 2px 16px rgba(26,82,118,0.08);
  transition: var(--transition);
  display: flex;
  flex-direction: column;
}
.blog-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 32px rgba(26,82,118,0.15);
}

/* Card image */
.blog-card-img-wrap {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  background: var(--mist);
  text-decoration: none;
}
.blog-card-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  transition: transform 0.4s ease;
}
.blog-card:hover .blog-card-img { transform: scale(1.04); }

.blog-card-img-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3rem;
  background: linear-gradient(135deg, var(--primary), var(--primary-light));
}

/* Category badge on image */
.blog-card-cat {
  position: absolute;
  top: 12px;
  left: 12px;
  background: var(--primary);
  color: var(--white);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 4px 12px;
  border-radius: 50px;
  pointer-events: none;
}

/* Card body */
.blog-card-body {
  padding: 24px;
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 8px;
}
.blog-card-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--text-light);
}
.blog-card-title {
  font-family: var(--font-heading);
  font-size: 1.1rem;
  color: var(--primary);
  line-height: 1.4;
  margin: 0;
}
.blog-card-title a {
  color: inherit;
  text-decoration: none;
  transition: var(--transition);
}
.blog-card-title a:hover { color: var(--primary-light); }
.blog-card-excerpt {
  font-size: 0.875rem;
  color: var(--text-light);
  line-height: 1.6;
  margin: 0;
  flex: 1;
}

/* Pagination */
.blog-pagination {
  margin-top: 56px;
  display: flex;
  justify-content: center;
}
.blog-pagination .page-numbers {
  display: inline-flex;
  gap: 8px;
  list-style: none;
  padding: 0;
  margin: 0;
  flex-wrap: wrap;
  justify-content: center;
}
.blog-pagination .page-numbers li a,
.blog-pagination .page-numbers li span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.9rem;
  border: 2px solid var(--mist);
  color: var(--primary);
  text-decoration: none;
  transition: var(--transition);
}
.blog-pagination .page-numbers li a:hover,
.blog-pagination .page-numbers li span.current {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--white);
}

/* ── Single post hero ────────────────────────────────────── */
.single-post-hero {
  position: relative;
  height: 480px;
  background-size: cover;
  background-position: center;
  display: flex;
  align-items: flex-end;
}
.single-post-hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(13,31,45,0.88) 0%, rgba(13,31,45,0.3) 60%, transparent 100%);
}
.single-post-hero-content {
  position: relative;
  z-index: 1;
  padding-bottom: 48px;
}

/* ── Breadcrumb ──────────────────────────────────────────── */
.blog-breadcrumb {
  background: var(--white);
  border-bottom: 1px solid var(--mist);
  padding: 12px 0;
  font-size: 0.82rem;
  color: var(--text-light);
}
.blog-breadcrumb a {
  color: var(--primary-light);
  text-decoration: none;
}
.blog-breadcrumb a:hover { color: var(--primary); }

/* ── Single post layout (content + sidebar) ──────────────── */
.single-post-layout {
  display: grid;
  grid-template-columns: 1fr 300px;
  gap: 48px;
  align-items: start;
}

/* ── Article content typography ──────────────────────────── */
.post-entry-content {
  font-size: 1.05rem;
  line-height: 1.85;
  color: var(--text);
  max-width: 100%;
}
.post-entry-content h2 {
  font-family: var(--font-heading);
  color: var(--primary);
  font-size: 1.4rem;
  margin: 40px 0 16px;
  padding-bottom: 8px;
  border-bottom: 2px solid var(--mist);
}
.post-entry-content h3 {
  font-family: var(--font-heading);
  color: var(--primary);
  font-size: 1.15rem;
  margin: 32px 0 12px;
}
.post-entry-content p { margin-bottom: 1.2rem; }
.post-entry-content ul,
.post-entry-content ol {
  padding-left: 24px;
  margin-bottom: 1.2rem;
}
.post-entry-content li { margin-bottom: 6px; }
.post-entry-content strong { color: var(--text); font-weight: 700; }
.post-entry-content a { color: var(--primary-light); text-decoration: underline; }
.post-entry-content a:hover { color: var(--primary); }

/* Post footer meta */
.single-post-footer-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 40px;
  padding-top: 24px;
  border-top: 2px solid var(--mist);
}

/* Post nav */
.single-post-nav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-top: 40px;
}
.post-nav-link {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 20px;
  background: var(--mist);
  border-radius: var(--radius);
  text-decoration: none;
  transition: var(--transition);
}
.post-nav-link:hover { background: var(--primary); }
.post-nav-link:hover .post-nav-label,
.post-nav-link:hover .post-nav-title { color: var(--white); }
.post-nav-label {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-light);
}
.post-nav-title {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--primary);
  line-height: 1.4;
}
.post-nav-next { text-align: right; }

/* ── Sidebar ─────────────────────────────────────────────── */
.single-post-sidebar {
  display: flex;
  flex-direction: column;
  gap: 24px;
  position: sticky;
  top: 100px;
}
.sidebar-widget {
  background: var(--white);
  border-radius: var(--radius-lg);
  padding: 24px;
  box-shadow: 0 2px 16px rgba(26,82,118,0.07);
}
.sidebar-cta {
  background: var(--primary);
  text-align: center;
}
.sidebar-widget-title {
  font-family: var(--font-heading);
  color: var(--primary);
  font-size: 0.95rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin: 0 0 16px;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--mist);
}
.sidebar-cat-link {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--mist);
  text-decoration: none;
  color: var(--text);
  font-size: 0.9rem;
  transition: var(--transition);
}
.sidebar-cat-link:last-child { border-bottom: none; }
.sidebar-cat-link:hover { color: var(--primary-light); padding-left: 4px; }
.sidebar-cat-count {
  background: var(--mist);
  color: var(--primary);
  font-size: 0.75rem;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 50px;
}

/* ── Mobile responsive ───────────────────────────────────── */
@media (max-width: 900px) {
  .single-post-layout {
    grid-template-columns: 1fr;
  }
  .single-post-sidebar {
    position: static;
    order: -1; /* sidebar above content on mobile */
  }
  .single-post-hero { height: 320px; }
  .single-post-nav { grid-template-columns: 1fr; }
  .post-nav-next { text-align: left; }
}
@media (max-width: 767px) {
  .blog-grid { grid-template-columns: 1fr; gap: 24px; }
  .blog-grid-3 { grid-template-columns: 1fr; }
  .single-post-sidebar { order: 2; } /* back below on very small screens */
}


/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   COOKIE CONSENT BANNER — v1.0.52
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */

/* Overlay backdrop */
#sbh-cookie-overlay {
  position: fixed;
  inset: 0;
  background: rgba(26, 82, 118, 0.45);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  z-index: 99999;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 24px 16px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
#sbh-cookie-overlay.sbh-visible {
  opacity: 1;
  pointer-events: all;
}

/* Banner card */
#sbh-cookie-banner {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 48px rgba(26, 82, 118, 0.28);
  max-width: 520px;
  width: 100%;
  padding: 32px;
  transform: translateY(24px);
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
  border-top: 4px solid var(--primary);
}
#sbh-cookie-overlay.sbh-visible #sbh-cookie-banner {
  transform: translateY(0);
}

/* Header */
.sbh-cookie-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.sbh-cookie-icon {
  width: 40px;
  height: 40px;
  background: var(--mist);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--primary);
  font-size: 1.1rem;
}
.sbh-cookie-header h2 {
  margin: 0;
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--primary);
  font-family: var(--font-heading);
}

/* Body text */
.sbh-cookie-body p {
  margin: 0 0 4px;
  font-size: 0.92rem;
  color: var(--text-light);
  line-height: 1.6;
}

/* Utility */
.sbh-hidden { display: none !important; }

/* ── Toggle rows ──────────────────────────────────────────── */
.sbh-cookie-body#sbh-cookie-custom {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-bottom: 4px;
}
.sbh-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
}
.sbh-toggle-row:last-child { border-bottom: none; }

.sbh-toggle-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sbh-toggle-info strong {
  font-size: 0.9rem;
  color: var(--text);
  font-family: var(--font-heading);
  font-weight: 600;
}
.sbh-toggle-info span {
  font-size: 0.8rem;
  color: var(--text-light);
  line-height: 1.4;
}

/* Toggle switch */
.sbh-toggle {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  flex-shrink: 0;
  user-select: none;
}
.sbh-toggle-disabled { cursor: not-allowed; opacity: 0.6; }
.sbh-toggle input { display: none; }

.sbh-slider {
  position: relative;
  width: 44px;
  height: 24px;
  background: var(--light-grey);
  border-radius: 12px;
  transition: background 0.25s ease;
  flex-shrink: 0;
}
.sbh-slider::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  background: var(--white);
  border-radius: 50%;
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
  transition: transform 0.25s ease;
}
.sbh-toggle input:checked ~ .sbh-slider {
  background: var(--primary);
}
.sbh-toggle input:checked ~ .sbh-slider::after {
  transform: translateX(20px);
}

.sbh-toggle-label {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--primary);
  font-family: var(--font-heading);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.sbh-off-label { color: var(--mid-grey); }

/* ── Action buttons ───────────────────────────────────────── */
.sbh-cookie-actions {
  display: flex;
  gap: 10px;
  margin-top: 24px;
  flex-wrap: wrap;
}
.sbh-btn {
  flex: 1;
  min-width: 100px;
  padding: 12px 18px;
  border-radius: 8px;
  font-family: var(--font-heading);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  border: 2px solid transparent;
  transition: var(--transition);
  white-space: nowrap;
  text-align: center;
}
.sbh-btn-primary {
  background: var(--primary);
  color: var(--white);
  border-color: var(--primary);
}
.sbh-btn-primary:hover {
  background: var(--primary-light);
  border-color: var(--primary-light);
}
.sbh-btn-outline {
  background: transparent;
  color: var(--primary);
  border-color: var(--primary);
}
.sbh-btn-outline:hover {
  background: var(--mist);
}

/* ── Responsive ───────────────────────────────────────────── */
@media (max-width: 480px) {
  #sbh-cookie-banner { padding: 24px 20px; }
  .sbh-cookie-actions { flex-direction: column; }
  .sbh-btn { flex: none; width: 100%; }
  .sbh-toggle-info span { font-size: 0.75rem; }
}

