/*
  LL Movers — Liquid Glass design system (tokens + primitives)
  ==============================================================
  Source of truth: design-experiments/mockups/exploration-3-liquid-glass.html
  Approved by: project owner, see design-experiments/CONTEXT.md for background.

  HOW TO USE
  Include this file AFTER the existing stylesheet (css/style.css on the marketing
  site, css/styles.css on the pricing/invoicing app). Both already define the
  locked brand palette under the same variable names this file assumes exist:

    --espresso --terracotta --terracotta-dark --moss --sand --sand-2
    --charcoal --brass --font-display --font-body --font-accent

  Do not redefine those here — this file only ADDS the glass/squircle/panel
  system on top of them, plus a real light/dark theme (see THEME section).
  Pair with liquid-glass.js, which drives the squircle geometry, the pointer
  sheen, the scroll-compacting nav, and the theme toggle.

  MODE GUIDANCE (read before applying wholesale)
  This system has two registers, don't flatten them into one:
    - Persuade surfaces (marketing site: homepage, service pages) — use the
      full system: atmosphere blobs, glass hero, materialize entrance,
      section-panels, refraction on the one or two hero-grade elements.
    - Operate surfaces (the pricing/invoicing app's actual working screens —
      quote forms, tables, dashboards) — use only the quiet parts: squircle
      corners, --panel-surface framing, spring-eased buttons, the theme
      toggle. Skip the atmosphere gradient, blobs, refraction filter, and
      materialize entrance there — they cost real render performance and add
      nothing to a screen someone is trying to get a task done on.
*/

:root {
  /* ---- Motion ---- */
  --spring: cubic-bezier(.34, 1.56, .64, 1);
  --spring-soft: cubic-bezier(.25, 1.2, .4, 1);

  /* ---- Continuous ("squircle") corner radii ---- */
  --squircle-sm: 14px;
  --squircle-md: 22px;
  --squircle-lg: 34px;
  --squircle-xl: 44px;

  /* ---- Theme tokens: LIGHT is the default (no JS / no-preference fallback) ---- */
  --bg-base: #F5EFE3;
  --bg-gradient:
    radial-gradient(ellipse 900px 600px at 10% 0%, rgba(181, 98, 45, 0.10), transparent 60%),
    radial-gradient(ellipse 800px 700px at 100% 10%, rgba(91, 90, 61, 0.09), transparent 55%),
    radial-gradient(ellipse 700px 700px at 40% 100%, rgba(156, 124, 78, 0.12), transparent 55%);
  --glass-surface: rgba(255, 255, 255, 0.46);
  --glass-border: rgba(43, 33, 25, 0.10);
  --glass-rim: rgba(255, 255, 255, 0.85);
  --glass-shadow: rgba(43, 33, 25, 0.18);
  --panel-surface: rgba(255, 255, 255, 0.26);
  --panel-border: rgba(43, 33, 25, 0.06);
  --panel-shadow: rgba(43, 33, 25, 0.08);
  --text-primary: var(--espresso);
  --text-secondary: var(--charcoal);
  --text-tertiary: rgba(74, 64, 56, 0.62);
  --sheen: rgba(255, 255, 255, 0.55);
  --icon-tone: var(--moss);
  --divider: rgba(43, 33, 25, 0.08);
  --blob-1: rgba(181, 98, 45, 0.22);
  --blob-2: rgba(91, 90, 61, 0.18);
  --blob-3: rgba(156, 124, 78, 0.22);

  /* Fixed regardless of theme — the nav and footer stay dark espresso glass
     in both modes (matches CONTEXT.md's original direction: they read as
     the page's constant "bookends"). */
  --header-surface: rgba(21, 16, 11, 0.82);
  --header-border: rgba(237, 230, 216, 0.16);
  --footer-surface: #211914;
}

/* ---- THEME: system preference, overridable by data-theme ----
   liquid-glass.js stamps data-theme="dark"/"light" on <html> the moment the
   user picks one explicitly (and persists it); until then this follows the
   OS setting. Both blocks must fully restate every theme token — never mix
   partial overrides, or a stale light value will leak into dark mode. */
@media (prefers-color-scheme: dark) {
  :root {
    --bg-base: #17110C;
    --bg-gradient:
      radial-gradient(ellipse 900px 600px at 15% -5%, rgba(181, 98, 45, 0.22), transparent 60%),
      radial-gradient(ellipse 800px 700px at 100% 20%, rgba(156, 124, 78, 0.16), transparent 55%),
      radial-gradient(ellipse 700px 600px at 30% 100%, rgba(91, 90, 61, 0.20), transparent 55%);
    --glass-surface: rgba(58, 45, 33, 0.40);
    --glass-border: rgba(237, 230, 216, 0.16);
    --glass-rim: rgba(255, 255, 255, 0.16);
    --glass-shadow: rgba(0, 0, 0, 0.5);
    --panel-surface: rgba(58, 45, 33, 0.16);
    --panel-border: rgba(237, 230, 216, 0.09);
    --panel-shadow: rgba(0, 0, 0, 0.22);
    --text-primary: var(--sand);
    --text-secondary: rgba(237, 230, 216, 0.72);
    --text-tertiary: rgba(237, 230, 216, 0.5);
    --sheen: rgba(255, 255, 255, 0.16);
    --icon-tone: var(--brass);
    --divider: rgba(237, 230, 216, 0.1);
    --blob-1: rgba(181, 98, 45, 0.5);
    --blob-2: rgba(156, 124, 78, 0.4);
    --blob-3: rgba(91, 90, 61, 0.45);
  }
}
:root[data-theme="dark"] {
  --bg-base: #17110C;
  --bg-gradient:
    radial-gradient(ellipse 900px 600px at 15% -5%, rgba(181, 98, 45, 0.22), transparent 60%),
    radial-gradient(ellipse 800px 700px at 100% 20%, rgba(156, 124, 78, 0.16), transparent 55%),
    radial-gradient(ellipse 700px 600px at 30% 100%, rgba(91, 90, 61, 0.20), transparent 55%);
  --glass-surface: rgba(58, 45, 33, 0.40);
  --glass-border: rgba(237, 230, 216, 0.16);
  --glass-rim: rgba(255, 255, 255, 0.16);
  --glass-shadow: rgba(0, 0, 0, 0.5);
  --panel-surface: rgba(58, 45, 33, 0.16);
  --panel-border: rgba(237, 230, 216, 0.09);
  --panel-shadow: rgba(0, 0, 0, 0.22);
  --text-primary: var(--sand);
  --text-secondary: rgba(237, 230, 216, 0.72);
  --text-tertiary: rgba(237, 230, 216, 0.5);
  --sheen: rgba(255, 255, 255, 0.16);
  --icon-tone: var(--brass);
  --divider: rgba(237, 230, 216, 0.1);
  --blob-1: rgba(181, 98, 45, 0.5);
  --blob-2: rgba(156, 124, 78, 0.4);
  --blob-3: rgba(91, 90, 61, 0.45);
}
:root[data-theme="light"] {
  --bg-base: #F5EFE3;
  --bg-gradient:
    radial-gradient(ellipse 900px 600px at 10% 0%, rgba(181, 98, 45, 0.10), transparent 60%),
    radial-gradient(ellipse 800px 700px at 100% 10%, rgba(91, 90, 61, 0.09), transparent 55%),
    radial-gradient(ellipse 700px 700px at 40% 100%, rgba(156, 124, 78, 0.12), transparent 55%);
  --glass-surface: rgba(255, 255, 255, 0.46);
  --glass-border: rgba(43, 33, 25, 0.10);
  --glass-rim: rgba(255, 255, 255, 0.85);
  --glass-shadow: rgba(43, 33, 25, 0.18);
  --panel-surface: rgba(255, 255, 255, 0.26);
  --panel-border: rgba(43, 33, 25, 0.06);
  --panel-shadow: rgba(43, 33, 25, 0.08);
  --text-primary: var(--espresso);
  --text-secondary: var(--charcoal);
  --text-tertiary: rgba(74, 64, 56, 0.62);
  --sheen: rgba(255, 255, 255, 0.55);
  --icon-tone: var(--moss);
  --divider: rgba(43, 33, 25, 0.08);
  --blob-1: rgba(181, 98, 45, 0.22);
  --blob-2: rgba(91, 90, 61, 0.18);
  --blob-3: rgba(156, 124, 78, 0.22);
}

/* ============ Ambient atmosphere (Persuade surfaces only) ============ */
.atmosphere { position: fixed; inset: 0; z-index: -1; overflow: hidden; background: var(--bg-base); transition: background 500ms ease; }
.atmosphere::before { content: ''; position: absolute; inset: 0; background: var(--bg-gradient); transition: background 500ms ease; }
.blob { position: absolute; border-radius: 50%; filter: blur(90px); animation: lg-drift 42s ease-in-out infinite alternate; }
.blob-1 { width: 640px; height: 640px; top: -180px; left: -160px; background: var(--blob-1); }
.blob-2 { width: 520px; height: 520px; top: 30%; right: -180px; background: var(--blob-2); animation-duration: 55s; animation-delay: -8s; }
.blob-3 { width: 560px; height: 460px; bottom: -200px; left: 20%; background: var(--blob-3); animation-duration: 48s; animation-delay: -20s; }
@keyframes lg-drift { 0% { transform: translate(0, 0) scale(1); } 100% { transform: translate(40px, 30px) scale(1.08); } }

/* Small color-wash blob for use *inside* an individual glass/squircle panel (e.g. a
   split-layout media placeholder) — same blob tokens as .atmosphere, contained scope.
   Not in the reference mockup's own inline styles as a named class, but reuses its
   exact visual recipe; added here since it's a generic primitive other panels can use. */
.glow { position: absolute; inset: -20%; background: radial-gradient(circle at 40% 30%, var(--blob-1), transparent 65%); filter: blur(10px); pointer-events: none; }

/* ============ Secondary-page card glass (lighter-weight than .glass) ============
   The homepage's cards get .glass + squircle + rim/sheen markup added by hand.
   Doing that for every card on every inner page (feature-card, region-card,
   callout-box, split__media, placeholder-card) is a lot of markup surgery for a
   secondary payoff, and each one becomes another surface where an absolutely-
   positioned child could get clipped by the squircle engine the way the nav
   dropdown did. This reskins the same existing class names directly via CSS
   instead — translucency + blur + hover lift, plain border-radius (no JS
   clip-path, so nothing to accidentally clip), no rim/sheen/refraction. */
.feature-card, .region-card, .split__media, .placeholder-card {
  background: var(--panel-surface);
  border: 1px solid var(--panel-border);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  transition: transform 280ms cubic-bezier(.2,.8,.2,1), border-color 280ms ease, background 500ms ease;
}
.feature-card:hover, .region-card:hover, .split__media:hover {
  transform: translateY(-5px);
  border-color: rgba(181, 98, 45, 0.4);
}
/* callout-box ships with its own fixed background (espresso, or --sand for the
   --light variant) — a deliberate "always this color" accent card, not
   theme-following — so it's excluded from the shared reskin above. */

/* ============ Squircle + glass primitives ============ */
/* clip-path is computed at runtime by liquid-glass.js — real superellipse
   geometry, not a border-radius approximation. border-radius below is only
   the pre-JS / no-JS fallback so the element still looks intentional. */
.squircle { position: relative; border-radius: var(--squircle-md); }
.squircle.is-clipped { border-radius: 0; }

.glass {
  background: var(--glass-surface);
  border: 1px solid var(--glass-border);
  filter: drop-shadow(0 20px 46px var(--glass-shadow));
  backdrop-filter: blur(20px) saturate(1.5);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass { background: var(--glass-surface); opacity: 0.97; }
}
/* Real refraction — reserve for one or two hero-grade elements per page.
   Needs #liquidGlass defined once in the page (see liquid-glass.js comment
   or copy the <svg><filter> block from the reference mockup). Chromium-only;
   browsers without SVG-filter-in-backdrop-filter just fall back to blur. */
.glass--refract { backdrop-filter: url(#liquidGlass) blur(18px) saturate(1.6); -webkit-backdrop-filter: blur(18px) saturate(1.6); }

.glass__rim { position: absolute; inset: 0; pointer-events: none; background: linear-gradient(160deg, var(--glass-rim), transparent 40%); mix-blend-mode: overlay; opacity: 0.9; }
.glass__sheen {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(circle 220px at var(--mx, 50%) var(--my, 0%), var(--sheen), transparent 65%);
  transition: opacity 320ms ease;
  mix-blend-mode: overlay;
}
.glass:hover .glass__sheen, .glass:focus-within .glass__sheen { opacity: 1; }
@media (hover: none) { .glass__sheen { opacity: 0.6; } }

/* ============ Section panel — quiet framing, every surface ============ */
/* Give each distinct block of content (a section's heading + its cards, a
   footer's columns) its own bounded space instead of floating loose. Much
   quieter than .glass: no blur, no rim, just a hairline border and a fill a
   shade off the page background. */
.section-panel {
  padding: clamp(2.25rem, 4vw, 3.5rem);
  background: var(--panel-surface);
  border: 1px solid var(--panel-border);
  filter: drop-shadow(0 16px 40px var(--panel-shadow));
  transition: background 500ms ease, border-color 500ms ease;
}
/* The <section> wrapping a .section-panel still carries style.css's own
   `section { padding: var(--space-6) 0 }` (72px) — stacked on top of this
   panel's own ~56px, that's what was reading as oversized gaps between panels.
   Scope the reduction to sections that actually have a panel inside, so plain
   (non-glass) pages keep their normal --space-6 rhythm untouched. */
section:has(.section-panel) { padding: 1.75rem 0; }
/* Footer keeps its fixed dark surface regardless of theme — see --footer-surface. */
.section-panel--footer { background: var(--footer-surface); border-color: rgba(237, 230, 216, 0.14); filter: drop-shadow(0 16px 40px rgba(0, 0, 0, 0.35)); }

/* ============ Floating capsule nav (Persuade surfaces) ============ */
/* position: sticky, not fixed — fixed pinned this to viewport y:0 unconditionally,
   which sat directly on top of .top-bar (same coordinates, .top-bar just lost the
   paint order). Sticky lets it sit in normal flow below .top-bar's actual rendered
   height at load (whatever that is — 1 line desktop, 2 lines on narrow phones — no
   hardcoded offset needed), then stick to the viewport top once scrolled past. */
.nav-island-wrap { position: sticky; top: 0; left: 0; right: 0; z-index: 150; display: flex; justify-content: center; padding: 18px 20px 0; pointer-events: none; }
.nav-island {
  pointer-events: auto; width: min(1180px, 100%);
  background: var(--header-surface) !important;
  border-color: var(--header-border) !important;
  transition: width 480ms var(--spring), padding 480ms var(--spring);
  /* Plain border-radius, not the JS squircle engine — deliberately not a .squircle
     element. At data-squircle="pill" (r = height/2) a true superellipse and a
     circular arc are the same shape (no flat-to-curve corner to distinguish them),
     so the engine bought nothing here except one real bug: its clip-path clips
     ANY overflowing child to the pill's own bounds, which silently hid the
     Services dropdown menu since it renders below/outside that box. */
  border-radius: 999px;
}
/* .glass__rim/.glass__sheen normally get their clip-path from the JS squircle engine
   (applySquircle finds them as direct children of any .squircle element) — since
   nav-island isn't .squircle anymore, they need their own matching radius instead,
   or their square corners poke out past the pill's rounded ends. */
.nav-island > .glass__rim, .nav-island > .glass__sheen { border-radius: 999px; }
.nav-island.is-compact { width: min(560px, 92%); }
.nav-island.is-compact .nav-links,
.nav-island.is-compact .nav-phone { display: none; }

/* The floating pill is much narrower than the old full-bleed header (min(1180px,100%)
   of the *wrap*, not the viewport) — at phone widths there isn't room for logo +
   phone + theme-toggle + CTA + hamburger in one row without the squircle clip-path
   silently cutting off whatever overflows (that's what was clipping the hamburger
   entirely). Trim progressively; both dropped controls remain reachable via
   .mobile-nav (phone in its footer, a duplicate theme-toggle added there too). */
@media (max-width: 900px) {
  .nav-island .nav-phone,
  .nav-island .theme-toggle { display: none; }
}
@media (max-width: 480px) {
  .nav-island .nav-actions .btn-primary { display: none; }
}

/* The site's own .logo/.nav-phone/.nav-toggle rules assume the OLD light (--sand)
   header background and hardcode --espresso text. --header-surface is fixed dark in
   BOTH themes here, so that assumption is now wrong specifically inside .nav-island —
   scope the correction to it rather than editing those rules globally, since the same
   classes still sit on a light background on every other page that doesn't use this
   component. */
.nav-island .logo,
.nav-island .nav-phone,
.nav-island .nav-toggle,
.nav-island .nav-links { color: var(--sand); }
/* .theme-toggle's own base color (--text-primary) is correct for a theme-following
   surface; .nav-island isn't one, so pin it to sand here specifically. */
.nav-island .theme-toggle { color: var(--sand); }
.nav-island .theme-toggle:hover { background: rgba(237, 230, 216, 0.12); }
/* .mobile-nav is the same "always dark regardless of page theme" surface as the
   header — its own theme-toggle duplicate (added so mobile users still have a
   theme control once the header hides it below 900px) needs the same fixed-sand
   treatment as .nav-island's, not the theme-following --text-primary default. */
.mobile-nav .theme-toggle { color: var(--sand); align-self: center; }
.mobile-nav .theme-toggle:hover { background: rgba(237, 230, 216, 0.12); }

/* ============ Buttons — spring press, unchanged pill shape ============ */
.btn { transition: transform 260ms var(--spring), box-shadow 220ms ease, background 180ms ease; }
.btn:active { transform: scale(0.94); }
.btn-primary:hover { transform: translateY(-2px) scale(1.02); }

/* ============ Theme toggle control ============ */
/* Drop this markup anywhere (e.g. inside .nav-actions):
     <button class="theme-toggle" data-theme-toggle aria-label="Toggle color theme">
       <svg class="theme-toggle__sun" ...></svg>
       <svg class="theme-toggle__moon" ...></svg>
     </button>
   liquid-glass.js wires the click handler and swaps the icon via [data-theme]
   on <html>; style the two icons' visibility off that same attribute. */
.theme-toggle { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 999px; border: 1px solid var(--glass-border); background: transparent; color: var(--text-primary); cursor: pointer; transition: background 180ms ease, transform 220ms var(--spring); }
.theme-toggle:hover { background: var(--glass-surface); }
.theme-toggle:active { transform: scale(0.9); }
.theme-toggle svg { width: 18px; height: 18px; display: none; }
/* Icon reflects the theme currently RENDERED, not "no attribute yet" — fixed from the
   original logic, which showed the moon by default even while the page was actually
   rendering light (no data-theme set, OS in light mode). Base rule assumes light,
   the dark-preference media query flips the no-attribute case, and explicit
   [data-theme] always wins on specificity regardless of source order.
   Selector is ".theme-toggle .theme-toggle__sun" (0,2,0), not just ".theme-toggle__sun"
   (0,1,0) — the latter loses to ".theme-toggle svg { display: none }" (0,1,1) above,
   which was the actual reason the icon was invisible in both themes during testing. */
.theme-toggle .theme-toggle__sun { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-toggle__sun { display: none; }
  :root:not([data-theme]) .theme-toggle__moon { display: block; }
}
:root[data-theme="light"] .theme-toggle__sun { display: block; }
:root[data-theme="light"] .theme-toggle__moon { display: none; }
:root[data-theme="dark"] .theme-toggle__sun { display: none; }
:root[data-theme="dark"] .theme-toggle__moon { display: block; }

/* ============ Scroll reveal + hero materialize (Persuade surfaces) ============ */
.reveal { opacity: 0; transform: translateY(18px); transition: opacity 550ms ease, transform 550ms var(--spring-soft); }
.reveal.is-visible { opacity: 1; transform: translateY(0); }
.materialize { opacity: 0; filter: blur(10px); transform: translateY(14px) scale(0.98); animation: lg-materialize 900ms var(--spring-soft) forwards; }
@keyframes lg-materialize { to { opacity: 1; filter: blur(0); transform: translateY(0) scale(1); } }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1 !important; transform: none !important; }
  .materialize { opacity: 1 !important; filter: none !important; transform: none !important; animation: none !important; }
  .blob { animation: none; }
  .nav-island, .btn, .theme-toggle { transition: none !important; }
}
