/* ============================================
   Lettiva V2 Site Interactions — Shared CSS
   Companion styles for site-v2-interactions.js
   ============================================ */

/* --- Nav items: never wrap mid-label ---
   Flex items in the nav <ul> shrink by default, so long labels like
   "Get in Touch" get squeezed below their natural width and break onto
   two lines. nowrap forces flex to keep each label whole and wrap the
   row instead, which is the right behaviour. */
nav li a {
  white-space: nowrap;
}

/* --- Active nav state ---
   `aria-current="page"` is injected on the matching anchor by V2SiteRenderer
   when it pre-processes navHtml. Scoped to nav <li> anchors so the logo
   link (no <li> wrapping) and the CTA button (also outside <li>) are not
   restyled. !important is used because Tailwind utilities for text colour
   (text-muted-foreground / hover:text-foreground) live in @layer utilities
   and at certain cascade points can outrank our un-layered rule on these
   anchors — we want the active marker to be unmistakable, so brute-force
   the colour and add a subtle underline. */
nav li a[aria-current="page"] {
  color: var(--color-accent, #009933) !important;
  font-weight: 600 !important;
  text-decoration: underline;
  text-decoration-color: var(--color-accent, #009933);
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}

/* --- Base --- */
/* Prevent horizontal page scroll from overflowing buttons/content.
   overflow-x: clip (not hidden) avoids the fixed-positioning containment
   side-effect that overflow-x: hidden creates in some browsers. */
html, body {
  overflow-x: clip;
  max-width: 100%;
}

/* Override Tailwind preflight "img { height: auto }" — without this,
   images inside aspect-ratio containers don't fill vertically. */
figure img,
[style*="aspect-ratio"] img,
[class*="aspect-"] > img {
  height: 100% !important;
  object-fit: cover;
}

/* --- Scroll Reveal ---
   Only applied when .animations-ready is on <html> (added by JS).
   Without JS, all content is fully visible (progressive enhancement). */
.animations-ready [data-reveal] {
  opacity: 0;
  will-change: opacity, transform;
  transition: opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.animations-ready [data-reveal="fade-up"] {
  transform: translateY(30px);
}

.animations-ready [data-reveal="fade-in"] {
  transform: none;
}

.animations-ready [data-reveal="slide-left"] {
  transform: translateX(-40px);
}

.animations-ready [data-reveal="slide-right"] {
  transform: translateX(40px);
}

.animations-ready [data-reveal="scale-up"] {
  transform: scale(0.95);
}

[data-reveal].revealed {
  opacity: 1;
  transform: none;
}

/* --- Stagger Reveal ---
   AI agents sometimes write [data-reveal-stagger] > * { opacity: 0 } to
   hide children for a staggered entrance. Our JS adds .revealed to each
   child when the parent enters the viewport. This rule provides a safety
   net so .revealed always wins, even if the agent forgot the reveal state. */
[data-reveal-stagger] > *.revealed {
  opacity: 1 !important;
  transform: none !important;
}

/* --- Lightbox --- */
.lightbox-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0);
  backdrop-filter: blur(0);
  opacity: 0;
  visibility: hidden;
  transition: background 0.35s ease, backdrop-filter 0.35s ease, opacity 0.35s ease, visibility 0.35s ease;
}

.lightbox-overlay.active {
  background: rgba(0, 0, 0, 0.92);
  backdrop-filter: blur(8px);
  opacity: 1;
  visibility: visible;
}

.lightbox-image-wrapper {
  position: relative;
  max-width: 90vw;
  max-height: 85vh;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: scale(0.92);
  opacity: 0;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s ease;
}

.lightbox-overlay.active .lightbox-image-wrapper {
  transform: scale(1);
  opacity: 1;
}

.lightbox-image-wrapper img {
  max-width: 90vw;
  max-height: 85vh;
  object-fit: contain;
  border-radius: 4px;
  user-select: none;
  -webkit-user-drag: none;
}

.lightbox-close {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 10000;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease;
  line-height: 1;
}

.lightbox-close:hover {
  background: rgba(255, 255, 255, 0.2);
  transform: scale(1.1);
}

.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 10000;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 50%;
  color: #fff;
  font-size: 22px;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease;
  line-height: 1;
}

.lightbox-nav:hover {
  background: rgba(255, 255, 255, 0.2);
  transform: translateY(-50%) scale(1.1);
}

.lightbox-nav--prev {
  left: 16px;
}

.lightbox-nav--next {
  right: 16px;
}

.lightbox-counter {
  position: absolute;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  color: rgba(255, 255, 255, 0.7);
  font-size: 14px;
  font-family: inherit;
  user-select: none;
}

/* Cursor hint on lightbox-enabled images */
[data-lightbox] img,
img[data-lightbox] {
  cursor: zoom-in;
}

/* --- Accordion --- */
.accordion-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 1rem 0;
  background: none;
  border: none;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  color: inherit;
  transition: color 0.2s ease;
}

.accordion-trigger:hover {
  opacity: 0.8;
}

.accordion-trigger::after {
  content: '+';
  flex-shrink: 0;
  margin-left: 1rem;
  font-size: 1.25em;
  font-weight: 300;
  transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}

.accordion-trigger[aria-expanded="true"]::after {
  content: '\2212'; /* minus sign */
  transform: rotate(180deg);
}

.accordion-content {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transition: max-height 0.4s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.3s ease,
              padding 0.3s ease;
  padding: 0;
}

.accordion-content.open {
  opacity: 1;
  padding-bottom: 1rem;
}

/* --- Carousel --- */
.carousel-wrapper {
  position: relative;
  overflow: hidden;
}

.carousel-track {
  position: relative;
  min-height: 100px;
}

.carousel-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s ease, visibility 0.5s ease;
}

.carousel-slide.active {
  position: relative;
  opacity: 1;
  visibility: visible;
}

.carousel-dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 1.25rem;
  padding: 0;
  list-style: none;
}

.carousel-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.15);
  cursor: pointer;
  padding: 0;
  transition: background 0.25s ease, transform 0.25s ease;
}

.carousel-dot:hover {
  background: rgba(0, 0, 0, 0.3);
}

.carousel-dot.active {
  background: rgba(0, 0, 0, 0.6);
  transform: scale(1.2);
}

/* --- Sticky Nav --- */
[data-sticky-nav] {
  transition: background-color 0.35s cubic-bezier(0.4, 0, 0.2, 1),
              background 0.35s cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 0.35s cubic-bezier(0.4, 0, 0.2, 1),
              border-color 0.35s cubic-bezier(0.4, 0, 0.2, 1),
              backdrop-filter 0.35s cubic-bezier(0.4, 0, 0.2, 1),
              padding 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Nav background fallback: provides a sensible default for sites where the
   agent-generated CSS omits a nav background entirely. Wrapped in :where()
   so specificity is 0-0-0 — any site-specific #site-nav rule wins regardless
   of source order (this <link> stylesheet loads after inline <style> blocks).
   build-shared-layout.ts already sets background: var(--color-background)
   on new sites; this is a safety net for older agent-generated sites. */
:where(#site-nav),
:where([data-sticky-nav]) {
  background: var(--color-background);
}

[data-sticky-nav].nav-scrolled {
  background: color-mix(in srgb, var(--color-background) 80%, transparent) !important;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--color-primary) 6%, transparent);
}

/* --- Active Page Indicator --- */
/* Animated underline for nav links (safety net for pre-existing sites
   generated before build-shared-layout added these rules). */
.nav-links a {
  position: relative;
}

.nav-links a::after {
  content: '';
  position: absolute;
  bottom: -4px; left: 0;
  width: 0; height: 2px;
  background: var(--color-accent);
  transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.nav-links a:hover::after { width: 100%; }
.nav-links a[aria-current="page"] { color: var(--color-accent); }
.nav-links a[aria-current="page"]::after { width: 100%; }

.mobile-menu-links a[aria-current="page"] { color: var(--color-accent); }

/* --- Mobile Menu --- */

/* Ensure nav sits above hero / page content so the hamburger is always tappable */
[data-sticky-nav] {
  z-index: 100;
}

/* Prevent double-tap-to-zoom on interactive elements */
[data-mobile-menu-toggle],
[data-lightbox] img,
img[data-lightbox] {
  touch-action: manipulation;
}

.mobile-menu-open {
  overflow: hidden;
}

/* Hide mobile menu from document flow before JS adds .mobile-menu-panel.
   Without this, the menu links render in normal flow on initial paint and
   cause horizontal overflow (body.scrollWidth > viewport width).
   Once JS runs initMobileMenu(), .mobile-menu-panel is added and this
   rule no longer applies — the element becomes a fixed-positioned panel. */
[data-mobile-menu]:not(.mobile-menu-panel) {
  display: none;
}

[data-mobile-menu] {
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.35s ease,
              visibility 0.35s ease;
}

/* Default: hidden off-screen right for mobile menu panels */
@media (max-width: 768px) {
  [data-mobile-menu].mobile-menu-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: auto; /* Override inset: 0 from buildSharedLayout — anchor to right edge */
    width: min(320px, 85vw);
    z-index: 9990;
    transform: translateX(100%);
    opacity: 0;
    visibility: hidden;
    overflow-y: auto;
  }

  [data-mobile-menu].mobile-menu-panel.menu-open {
    transform: translateX(0);
    opacity: 1;
    visibility: visible;
  }
}

/* Agent CSS correction: agent sometimes generates
   [data-mobile-menu].mobile-menu-panel { opacity: 1 } as the open state.
   But mobile-menu-panel is a PERMANENT setup class added at DOMContentLoaded —
   using it as an open trigger makes the panel always-visible after JS init.
   The JS always uses .menu-open for the toggled open state.
   Force our rules to win over agent CSS with !important. */
@media (max-width: 768px) {
  [data-mobile-menu].mobile-menu-panel:not(.menu-open) {
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
    transform: translateX(100%) !important;
  }

  [data-mobile-menu].mobile-menu-panel.menu-open {
    opacity: 1 !important;
    visibility: visible !important;
    pointer-events: auto !important;
    transform: translateX(0) !important;
  }
}

/* Body overlay when mobile menu is open */
.mobile-menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 9989;
  background: rgba(0, 0, 0, 0);
  visibility: hidden;
  transition: background 0.35s ease, visibility 0.35s ease;
}

.mobile-menu-backdrop.active {
  background: rgba(0, 0, 0, 0.5);
  visibility: visible;
}

/* --- Mobile Menu Close Button --- */
/* Make the X button prominent and tappable (44px minimum touch target) */
.mobile-menu-close,
[data-mobile-menu] [data-mobile-menu-toggle][aria-label*="Close"] {
  min-width: 44px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.75rem;
  cursor: pointer;
  touch-action: manipulation;
}

/* --- Mobile Menu "More" Section --- */
/* Improve the visual treatment of the overflow section in mobile nav */
.mobile-menu-divider {
  height: 1px !important;
  background: rgba(0, 0, 0, 0.1) !important;
  margin: 1rem 0 0.5rem !important;
}

.mobile-menu-heading {
  font-size: 0.7rem !important;
  font-weight: 700 !important;
  text-transform: uppercase !important;
  letter-spacing: 0.12em !important;
  color: rgba(0, 0, 0, 0.35) !important;
  padding-bottom: 0.25rem !important;
}

/* Overflow links are slightly smaller than primary nav links */
.mobile-menu-heading ~ li a {
  font-size: 1.15rem !important;
  font-weight: 500 !important;
  color: rgba(0, 0, 0, 0.55) !important;
}

.mobile-menu-heading ~ li a:hover {
  color: var(--color-accent) !important;
}

/* --- Lightbox Mobile Improvements --- */
@media (max-width: 768px) {
  .lightbox-close {
    width: 48px;
    height: 48px;
    font-size: 24px;
    top: 12px;
    right: 12px;
    background: rgba(255, 255, 255, 0.25);
  }

  .lightbox-nav {
    width: 40px;
    height: 40px;
  }

  .lightbox-nav--prev { left: 8px; }
  .lightbox-nav--next { right: 8px; }
}

/* --- Back to Top --- */
[data-back-to-top] {
  position: fixed;
  bottom: 2rem;
  right: 2rem;
  z-index: 999;
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
  transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s ease;
}

[data-back-to-top].visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

[data-back-to-top]:hover {
  transform: translateY(-2px);
}

/* --- Empty Section Guards --- */
/* Hide sections whose React island returns null (no data in DB).
   The agent writes static headings + data-component placeholder; when the
   component has no data, it returns null — leaving an orphan section heading.
   CSS :has() checks whether the rendered component class is present in the DOM.
   Supported: Chrome 105+, Firefox 121+, Safari 15.4+

   IMPORTANT: The class names here MUST match what each React wrapper actually
   renders. If you change a wrapper's output class, update the guard here too.
   Validated by: public/site-v2-interactions.test.ts */
/* Hero layout fix: the hero is display:flex (row), so extra wrappers placed
   outside the main z-10 content div become flex siblings and steal width on mobile.
   Fix: force the hero to flex-wrap so on narrow screens the extra wrappers
   (trustpilot, discount) stack below the main content instead of beside it. */
[data-section="hero"] {
  flex-wrap: wrap;
}
[data-section="hero"] > div:not([class*="absolute"]) {
  position: relative;
  z-index: 10;
  width: 100%;
}

/* Empty section guards: hide component-only sections when the React component
   returns empty (data-component-empty). Only targets sections explicitly marked
   as component-only by the skeleton builder (data-component-section).
   Sections with mixed content (hero + optional discount-display) are never hidden.

   Macro-safe: the :not(:has(.mt-6 > *:not([data-component-empty]))) clause
   prevents collateral hiding of multi-component content-section-1 macros
   (e.g. "FAQ + Booking Policies + House Rules" bundled in one wrapper) when
   only some of the bundled components return empty. The macro hides only if
   EVERY .mt-6 component slot contains only an empty marker — i.e. there is no
   substantive content left. Single-component sections (where placeholders are
   direct children, no .mt-6 wrappers) still hide on the first empty marker
   because the negated :has() is vacuously true. */
section[data-component-section]:has([data-component-empty]):not(:has(.mt-6 > *:not([data-component-empty]))) { display: none; }

/* --- Mobile Responsive Overrides --- */

/* Buttons: allow text to wrap on narrow screens.
   Generated .btn CSS always sets white-space:nowrap, which causes overflow
   when button text is long (e.g. "Celebrating Something Special?"). */
@media (max-width: 768px) {
  .btn, a.btn, button.btn {
    white-space: normal;
    max-width: 100%;
  }
}

/* Inline-style grids: collapse multi-column grids to 1 column on mobile.
   The agent sometimes uses inline style="grid-template-columns:repeat(N,1fr)"
   instead of the .grid-N helper classes, which bypasses class-based media queries.
   Covers 2-, 3- and 4-column variants. */
@media (max-width: 768px) {
  [style*="grid-template-columns:repeat(3"],
  [style*="grid-template-columns: repeat(3"],
  [style*="grid-template-columns:repeat(4"],
  [style*="grid-template-columns: repeat(4"] {
    grid-template-columns: 1fr !important;
  }
}

@media (max-width: 480px) {
  [style*="grid-template-columns:repeat(2"],
  [style*="grid-template-columns: repeat(2"] {
    grid-template-columns: 1fr !important;
  }
}

/* ============================================
   Trust Bar — component layout overrides
   ============================================ */
/* .sb-trust-badges has width:100% by default (correct when standalone).
   When placed as a flex item inside a trust-bar section alongside sibling
   text, that 100% width pushes the text to the next row. Override it to
   auto so the two flex children sit side by side.
   NOTE: trust-strip is a standalone badges-only section (gallery, booking pages)
   where badges should remain centered. Only trust-bar has the side-by-side layout. */
[data-section="trust-bar"] .sb-trust-badges,
[data-section="trust"] .sb-trust-badges {
  width: auto;
}

/* Align badges to the left within the trust-bar (badges sit alongside text).
   trust-strip is excluded — standalone badge sections should stay centered. */
[data-section="trust-bar"] .sb-trust-badges__list--horizontal,
[data-section="trust"] .sb-trust-badges__list--horizontal {
  justify-content: flex-start;
}

/* Legacy trust-bar text override removed — TrustBadgeStripWrapper now
   handles its own light/dark contrast via isDark prop + Preline tokens.
   The old rule force-applied dark text with !important, which made the
   new banner variant invisible on dark brand sections. */

/* ============================================
   Reduced Motion — respect user preferences
   ============================================ */
@media (prefers-reduced-motion: reduce) {
  .animations-ready [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  [data-reveal-stagger] > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .lightbox-overlay,
  .lightbox-image-wrapper,
  .accordion-content,
  .carousel-slide,
  [data-sticky-nav],
  [data-mobile-menu],
  [data-back-to-top],
  .lightbox-close,
  .lightbox-nav,
  .carousel-dot,
  .mobile-menu-backdrop {
    transition: none !important;
  }

  [data-parallax] {
    transform: none !important;
  }
}

/* ─── Prose labels ─────────────────────────────────────────────────────
   Header-only paragraphs in scraped/owner source (e.g. "**Kitchen**" in
   an access statement) are emitted by the worker's markdown-ish paragraph
   renderer (block-handlers.ts stripInlineMarkdown) as
   <strong data-prose-label>. Style them as quiet section labels rather
   than stray bold lines. Em-based so they scale with the host block's
   body size; colours inherit — the brand layer owns them. */
strong[data-prose-label] {
  display: block;
  font-size: 0.8125em;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.85;
  margin-top: 0.75em;
}
