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

html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ========== THEME VARIABLES ========== */
[data-theme='light'] {
  --bg-primary: #ffffff;
  --bg-secondary: #f9f6ef;
  --bg-tertiary: #e5e5e5;
  --text-primary: #111111;
  --text-secondary: #5a5a5a;
  /* Slightly lighter than --text-secondary; used by the redesigned body
     copy (blurbs, paragraphs, pullquote, process step paragraphs). Resolves
     warmer in dark mode. */
  --text-muted: #6b6b6b;
  --accent: #c9a96e;
  --accent-hover: #b8954f;
  --accent-secondary: #6e2432; /* bordeaux — warm counterpoint to gold */
  /* Owner preference: accent-text matches the brand gold across both themes
     for visual consistency, accepting the lower (~2.6:1) contrast on light
     backgrounds. Don't use --accent-text for body copy on light bg — reserve
     it for headings, icons, and accent-tinted UI chrome where the colour is
     decorative rather than the only thing carrying meaning. */
  --accent-text: #c9a96e;
  --accent-hover-text: #b8954f;
  --nav-bg: rgba(255, 255, 255, 0.95);

  /* Status tokens — see SEMANTIC STATUS COLOURS block below for the rationale. */
  --status-neutral: var(--text-secondary);
  --status-progress: var(--text-secondary);
  --status-confirmed: var(--accent);
  --status-deposit-paid: var(--accent);
  --status-complete: var(--accent);
  --status-cancelled: #a8474a; /* warm brick red — matches .form-alert--error */

  /* Gold-tinted resting border for cards / photos / form inputs / hero
     bottoms / FAQ items. Light-theme value is doubled because gold at 20%
     opacity on a near-white background is barely a few values per channel
     different from white — visually invisible at a glance. 40% reads as a
     deliberate hairline without becoming heavy. */
  --border-tinted: color-mix(in srgb, var(--accent) 40%, transparent);
}

[data-theme='dark'] {
  --bg-primary: #0d0d0d;
  --bg-secondary: #1a1a1a;
  --bg-tertiary: #2a2a2a;
  --text-primary: #f0f0f0;
  --text-secondary: #b0a99a;
  /* Warm taupe — matches --text-secondary in dark mode. Pairs with the
     light-mode #6b6b6b for the redesigned body-copy class. */
  --text-muted: #b0a99a;
  --accent: #c9a96e;
  --accent-hover: #dfc08a;
  --accent-secondary: #a64353; /* lighter bordeaux for legibility on near-black */
  /* Gold on near-black has plenty of contrast (~6.5:1), so the readable
     foreground variants resolve to the brand colour itself. */
  --accent-text: var(--accent);
  --accent-hover-text: var(--accent-hover);
  --nav-bg: rgba(13, 13, 13, 0.95);

  /* Status tokens — see SEMANTIC STATUS COLOURS block below for the rationale. */
  --status-neutral: var(--text-secondary);
  --status-progress: var(--text-secondary);
  --status-confirmed: var(--accent);
  --status-deposit-paid: var(--accent);
  --status-complete: var(--accent);
  --status-cancelled: #c25a5a; /* warm brick red — matches .form-alert--error */

  /* Gold-tinted resting border. 20% on near-black is the original value —
     reads as a deliberate hairline. See light-theme block above for why
     the two values diverge. */
  --border-tinted: color-mix(in srgb, var(--accent) 20%, transparent);
}

/* ========== SEMANTIC STATUS COLOURS ==========
   Lifecycle palette mirrored across the booking / order status badges and the
   email templates (getPalette in src/services/email.js — keep in sync).
   Two functional states + one warning, no traffic-light primaries:
     neutral  = "awaiting / in progress" (text-secondary, muted warm grey)
     positive = "confirmed / paid / settled" (brand gold)
     warning  = "cancelled / failed" (brick red, same hue as form alerts)
   Customers see the text label for the specific state; colour is just the
   affective layer (good / awaiting / stopped). The admin dashboard keeps
   richer per-state distinction — see admin.css. */

/* ========== RADIUS TOKENS ==========
   Buttons, nav, form inputs stay hard-edged (radius 0) — the flat geometric
   commitment. Soft corners are reserved for:
     --radius-sm: small chips, skip-link, flatpickr day cells (~4 px)
     --radius-md: content cards, image containers, summary blocks (~8 px)
     .status-badge uses 20px (capsule) — intentionally different shape vocabulary
   Resist the urge to introduce a fourth radius. */
:root {
  --radius-sm: 4px;
  --radius-md: 8px;
}

/* ========== FLUID TYPOGRAPHY ========== */
:root {
  --fs-h1: clamp(3rem, 7vw, 6rem);
  --fs-h2: clamp(2rem, 4.5vw, 3.5rem);
  --fs-h3: clamp(1.2rem, 2.5vw, 1.6rem);
  --fs-body: 1rem;
  --fs-small: 0.85rem;
}

body {
  font-family: 'DM Sans', sans-serif;
  font-weight: 400;
  line-height: 1.7;
  color: var(--text-primary);
  background-color: var(--bg-primary);
  transition:
    background-color 0.3s ease,
    color 0.3s ease;
  overflow-x: hidden;
  /* Positioned so body::before's `position: absolute` anchors to body's
     full height (texture scrolls with content) rather than the viewport
     (parallax). */
  position: relative;
  /* Sticky-footer layout: body is at least full-viewport tall, and <main>
     flex-grows to fill any remaining space. Stops short pages (utility flows
     like /my/bookings, /auth/forgot, payment-confirmed) from leaving a gap
     below the footer when content doesn't reach the bottom of the viewport. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Subtle paper texture overlay. Sits behind page content via z-index: -1
   but on top of the body's background colour. mix-blend-mode: multiply
   melds the grain into whatever colour is underneath rather than appearing
   as a separate image layer. position: fixed keeps the texture stationary
   as the page scrolls — feels like the paper itself, not a moving pattern.
   Light theme only by default; dark theme has its own overlay below. */
body::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background-image: image-set(
    url('/images/textures/subtle-texture.webp') type('image/webp'),
    url('/images/textures/subtle-texture.jpg') type('image/jpeg')
  );
  background-repeat: repeat;
  background-size: 600px 600px;
  opacity: 0.2;
  mix-blend-mode: multiply;
}

/* Dark theme: same texture, different blend. soft-light lifts the texture's
   lighter variations into subtle "atmospheric grain" on the dark surface
   without darkening it the way multiply would. Slightly higher opacity than
   light mode because dark surfaces need more contrast for the texture to
   register. */
[data-theme='dark'] body::before {
  mix-blend-mode: soft-light;
  opacity: 0.4;
}


main {
  flex: 1 0 auto;
}

h1,
h2,
h3,
h4 {
  font-family: 'Playfair Display', serif;
  line-height: 1.05;
  font-weight: 700;
}

h1 {
  font-size: var(--fs-h1);
}
h2 {
  font-size: var(--fs-h2);
}
h3 {
  font-size: var(--fs-h3);
}

a {
  color: var(--accent-text);
  text-decoration: none;
  transition: color 0.3s ease;
}

a:hover {
  color: var(--accent-hover-text);
}

address {
  font-style: normal;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

/* ========== CONTAINERS ==========
   Horizontal padding grows with the viewport. Mobile stays tight (16px);
   tablet gets editorial breathing room (32px); desktop defaults to 20px
   because the container max-width is already doing the boundary work. */
.container,
.container-narrow,
.container-wide {
  width: 100%;
  margin: 0 auto;
  padding: 0 16px;
}

.container {
  max-width: 1200px;
}

.container-narrow {
  max-width: 800px;
}

.container-wide {
  max-width: 1400px;
}

@media (min-width: 768px) {
  .container,
  .container-narrow,
  .container-wide {
    padding: 0 32px;
  }
}

@media (min-width: 1200px) {
  .container,
  .container-narrow,
  .container-wide {
    padding: 0 20px;
  }
}

/* ========== SKIP LINK ========== */
.skip-link {
  position: absolute;
  top: -100%;
  left: 16px;
  z-index: 2000;
  padding: 12px 24px;
  background: var(--accent);
  color: #1a1a1a;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: 0.85rem;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  transition: top 0.2s ease;
}

.skip-link:focus {
  top: 0;
  outline: none;
  color: #1a1a1a;
}

/* ========== ACCESSIBLE-HIDE UTILITY ==========
   Hides content visually but keeps it readable for screen readers and
   discoverable by accessibility tools. Used for h2 section labels that
   are visually represented by tabs / pills but need to exist in the DOM
   for proper heading hierarchy. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ========== FOCUS STYLES ========== */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ========== SECTION HEADING ========== */
.section-heading {
  text-align: center;
  font-size: var(--fs-h2);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 60px;
  position: relative;
}

.section-heading::after {
  content: '';
  display: block;
  width: 80px;
  height: 4px;
  background: var(--accent);
  margin: 20px auto 0;
}

/* ========== BUTTONS ========== */
.btn {
  display: inline-block;
  padding: 16px 40px;
  font-family: 'DM Sans', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 3px;
  border-radius: 0;
  cursor: pointer;
  transition: all 0.3s ease;
  border: 2px solid var(--accent);
}

.btn-primary {
  background: var(--accent);
  color: #1a1a1a;
}

.btn-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: #1a1a1a;
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(201, 169, 110, 0.3);
}

.btn-outline {
  background: transparent;
  color: var(--accent-text);
}

.btn-outline:hover {
  background: var(--accent);
  color: #1a1a1a;
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(201, 169, 110, 0.3);
}

/* Press feedback — cancel the :hover lift and dim the shadow so the button
   feels physically depressed. Applies to all .btn variants. */
.btn:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(201, 169, 110, 0.15);
  transition-duration: 0.05s;
}

/* Disabled — neutralised hover/active and visually muted. Used during async
   submits (Stripe redirect) and any field-blocked state. */
.btn:disabled,
.btn[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

.btn-full {
  width: 100%;
  text-align: center;
}

/* ========== OLIVE BRANCH DIVIDER ==========
   The signature micro-interaction. When the divider enters the viewport the
   IntersectionObserver in shared.js adds `.visible`, triggering the reveal:
     1. Hairlines grow outward from the centre (width 0 → 60px).
     2. The olive icon fades + scales in on a small delay.
     3. The icon then enters a slow idle sway (gentleSway), continuous.
   Unique to the brand; not reachable by a template. Gated on
   prefers-reduced-motion. */
.olive-branch-divider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 0 auto 30px;
}

.branch-left,
.branch-right {
  display: block;
  width: 60px;
  height: 1px;
  background: var(--accent);
}

.olive-icon {
  color: var(--accent-text);
  font-size: 1rem;
  line-height: 1;
  display: inline-block;
}

.olive-icon svg {
  height: 14px;
  width: auto;
  display: block;
}

/* Scroll-in animation. Applied by adding `.animate-on-scroll` to the divider
   so the existing observer in shared.js picks it up. The outer container's
   default translateY + fade (from `.animate-on-scroll`) are overridden here
   — the reveal lives on the internal parts, not the container. */
.olive-branch-divider.animate-on-scroll {
  opacity: 1;
  transform: none;
}

.olive-branch-divider.animate-on-scroll .branch-left,
.olive-branch-divider.animate-on-scroll .branch-right {
  width: 0;
  transition: width 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}

.olive-branch-divider.animate-on-scroll .olive-icon {
  opacity: 0;
  transform: scale(0.4) rotate(-18deg);
}

.olive-branch-divider.animate-on-scroll.visible .branch-left,
.olive-branch-divider.animate-on-scroll.visible .branch-right {
  width: 60px;
}

.olive-branch-divider.animate-on-scroll.visible .olive-icon {
  animation:
    oliveReveal 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.35s forwards,
    gentleSway 5s ease-in-out 1.4s infinite;
}

@keyframes oliveReveal {
  from {
    opacity: 0;
    transform: scale(0.4) rotate(-18deg);
  }
  to {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
}

@keyframes gentleSway {
  0%,
  100% {
    transform: rotate(0deg);
  }
  25% {
    transform: rotate(-4deg);
  }
  75% {
    transform: rotate(4deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .olive-branch-divider.animate-on-scroll .branch-left,
  .olive-branch-divider.animate-on-scroll .branch-right {
    width: 60px;
    transition: none;
  }
  .olive-branch-divider.animate-on-scroll .olive-icon {
    opacity: 1;
    animation: none;
  }
}

/* Generic opt-in for any motif wrapper that wants the same reveal-then-sway
   the divider's leaf has. Pair `motif-animated` with `animate-on-scroll` on
   the wrapper; the inner SVG fades + scales in once the IntersectionObserver
   adds `.visible`, then enters the gentle sway. The wrapper's own slide-up
   from `animate-on-scroll` is overridden so only the SVG animates. */
.motif-animated.animate-on-scroll {
  opacity: 1;
  transform: none;
}

.motif-animated.animate-on-scroll svg {
  opacity: 0;
  transform: scale(0.4) rotate(-18deg);
  transform-origin: center;
}

.motif-animated.animate-on-scroll.visible svg {
  animation:
    oliveReveal 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.35s forwards,
    gentleSway 5s ease-in-out 1.4s infinite;
}

@media (prefers-reduced-motion: reduce) {
  .motif-animated.animate-on-scroll svg {
    opacity: 1;
  }
  .motif-animated.animate-on-scroll.visible svg {
    animation: none;
  }
}

/* ========== ANIMATIONS ========== */
.animate-on-scroll {
  opacity: 0;
  transform: translateY(50px);
  transition:
    opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

.animate-on-scroll.visible {
  opacity: 1;
  transform: translateY(0);
}

.animate-slide-left {
  opacity: 0;
  transform: translateX(-50px);
  transition:
    opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

.animate-slide-left.visible {
  opacity: 1;
  transform: translateX(0);
}

.animate-slide-right {
  opacity: 0;
  transform: translateX(50px);
  transition:
    opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

.animate-slide-right.visible {
  opacity: 1;
  transform: translateX(0);
}

.animate-scale {
  opacity: 0;
  transform: scale(0.9);
  transition:
    opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

.animate-scale.visible {
  opacity: 1;
  transform: scale(1);
}

.animate-delay-1 {
  transition-delay: 0.1s;
}
.animate-delay-2 {
  transition-delay: 0.2s;
}
.animate-delay-3 {
  transition-delay: 0.3s;
}
.animate-delay-4 {
  transition-delay: 0.4s;
}
.animate-delay-5 {
  transition-delay: 0.5s;
}
.animate-delay-6 {
  transition-delay: 0.6s;
}
.animate-delay-7 {
  transition-delay: 0.7s;
}
.animate-delay-8 {
  transition-delay: 0.8s;
}

/* ========== REDUCED MOTION ========== */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .animate-on-scroll,
  .animate-slide-left,
  .animate-slide-right,
  .animate-scale {
    opacity: 1;
    transform: none;
  }
  html {
    scroll-behavior: auto;
  }
}

/* ========== SITE-WIDE GRAIN OVERLAY ==========
   Fine noise texture layered above backgrounds, below content. Uses an inline
   SVG feTurbulence so there's no external file to load. mix-blend-mode: overlay
   keeps it subtle on both light and dark themes. opacity kept low (~3.5%) —
   enough to add "hand-made" depth, not enough to interfere with readability. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.9 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  background-repeat: repeat;
  background-size: 180px 180px;
}

/* Interactive / fixed UI elements that need to sit above the grain. */
.skip-link,
.back-to-top,
.navbar,
.mobile-overlay,
.lightbox {
  z-index: 2;
}
.navbar {
  z-index: 1000;
}
.mobile-overlay {
  z-index: 999;
}
.lightbox {
  z-index: 2000;
}

@media (prefers-reduced-motion: reduce) {
  body::after {
    display: none; /* grain can flicker on some OLED displays — kill when user opts out */
  }
}
