/* ============================================================
   Efficioloop — Redesign (2026)
   "Quiet leverage", rebuilt around two reference feelings:
     · PandaOS  — calm, airy, centered, generous negative space
     · Bloxs    — faint 1px structural lines, framed cards, no
                  heavy boxes or drop shadows, engineered texture
   ------------------------------------------------------------
   The whole page sits on ONE consistent almost-white canvas.
   No pure white on large surfaces. No navy band. No giant loop
   shapes. Structure comes from hairlines (brand Dark Blue @10%),
   not fills or shadows.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800;900&family=Lora:ital,wght@0,400;0,500;0,600;1,400&display=swap');

/* ============================================================
   PALETTES — three minimal directions. Switched via the
   [data-palette] attribute on <html>. Each one keeps the
   structural hairline as the brand Dark Blue at 10% opacity
   (per brief); they differ in canvas warmth + the single accent.
   ============================================================ */

:root,
:root[data-palette="indigo"] {
  /* A · Indigo — brand-faithful. Cool almost-white, violet accent. */
  --canvas:      #FAFAFB;   /* the one true canvas — almost white */
  --canvas-sink: #F5F5F8;   /* very faint recessed wells (inputs, code) */
  --surface:     #FCFCFD;   /* cards — a hair lighter than canvas, never pure white */
  --ink:         #1B1A38;   /* headings — deep indigo (slightly desaturated) */
  --ink-body:    #43475C;   /* body text */
  --ink-muted:   #777C90;   /* meta / captions */
  --ink-faint:   #A2A6B6;   /* placeholder */

  --rule:        22, 16, 84;     /* brand Dark Blue, as r,g,b for alpha rules */
  --line:        rgba(var(--rule), 0.10);   /* THE structural hairline */
  --line-soft:   rgba(var(--rule), 0.06);
  --line-strong: rgba(var(--rule), 0.16);

  --accent:      #6862F0;
  --accent-deep: #524ED9;
  --accent-tint: rgba(104, 98, 240, 0.08);
  --on-accent:   #FAFAFB;
}

:root[data-palette="slate"] {
  /* B · Slate Blue — cooler, engineered. Neutral white, blue accent. */
  --canvas:      #F7F8FA;
  --canvas-sink: #F0F2F5;
  --surface:     #FBFBFD;
  --ink:         #16202E;
  --ink-body:    #44505F;
  --ink-muted:   #79828F;
  --ink-faint:   #A4ABB5;

  --rule:        22, 32, 46;
  --line:        rgba(var(--rule), 0.10);
  --line-soft:   rgba(var(--rule), 0.06);
  --line-strong: rgba(var(--rule), 0.16);

  --accent:      #2F56E0;
  --accent-deep: #2243C4;
  --accent-tint: rgba(47, 86, 224, 0.07);
  --on-accent:   #FAFAFB;
}

:root[data-palette="graphite"] {
  /* C · Graphite — near-monochrome, warm-neutral white, ink accent.
     The most minimal direction: almost no colour at all. */
  --canvas:      #FAF9F7;   /* warm almost-white */
  --canvas-sink: #F4F3F0;
  --surface:     #FCFBFA;
  --ink:         #1C1A24;
  --ink-body:    #4A4754;
  --ink-muted:   #817D89;
  --ink-faint:   #ABA7B0;

  --rule:        28, 24, 44;
  --line:        rgba(var(--rule), 0.10);
  --line-soft:   rgba(var(--rule), 0.06);
  --line-strong: rgba(var(--rule), 0.15);

  --accent:      #1C1A24;   /* the ink itself is the accent — quiet */
  --accent-deep: #000000;
  --accent-tint: rgba(47, 86, 224, 0.08);
  --on-accent:   #FAFAFB;
}

/* ============================================================
   MUTED LIGHT THEME SCOPE
   ============================================================ */
.theme-muted {
  /* A very faint, highly sophisticated cool grey/blue. 
     Just dark enough to look like a separate section, light enough to remain airy. */
  --canvas:      #F0F1F6;   
  --canvas-sink: #E9EAEF;   
  --surface:     #FAFAFB;   /* Pure structural off-white for cards, forcing them to pop. */
  
  --line:        rgba(var(--rule), 0.08);
  --line-soft:   rgba(var(--rule), 0.04);
  --line-strong: rgba(var(--rule), 0.15);
  
  --shadow-color: rgba(0, 0, 0, 0.06);
  
  background-color: var(--canvas);
}

/* ============================================================
   TOKENS — type, space, motion (palette-independent)
   ============================================================ */
:root {
  --font-display: 'Montserrat', system-ui, sans-serif;
  --font-body:    'Lora', Georgia, serif;
  --font-mono:    'Montserrat', system-ui, sans-serif;

  /* TYPE SCALE — same modular ratios, bumped ~7% again across the whole
     ladder so every step keeps its proportional relationship.
        display 62 · h1 46 · h2 39 · h3 27 · h4 20 · lead 19 · body 18 */
  --fs-display: clamp(44px, 6vw, 92px);  /* hero headline */
  --fs-h1:      clamp(33px, 3.2vw, 46px);  /* page / CTA title */
  --fs-h2:      clamp(28px, 2.9vw, 39px);  /* section title */
  --fs-h3:      clamp(22px, 1.8vw, 27px);  /* card title */
  --fs-h4:      20px;                      /* small heading */
  --fs-lead:    clamp(17px, 15px + 0.3vw, 19px);  /* subtitle / intro */
  --fs-body:    clamp(16px, 14px + 0.3vw, 18px);
  --fs-small:   16px;
  --fs-micro:   14px;

  --lh-tight:   1.08;
  --lh-snug:    1.22;
  --lh-normal:  1.5;
  --lh-relaxed: 1.62;

  --ls-tight:  -0.02em;
  --ls-snug:   -0.01em;
  --ls-wide:    0.14em;

  --maxw: 1340px;          /* the framed content column */
  --gutter: clamp(22px, 4.5vw, 64px);

  --r-xs: 4px;
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-pill: 999px;

  --ease:   cubic-bezier(0.22, 1, 0.36, 1);
  --fast:   140ms;
  --base:   240ms;
  --slow:   560ms;
}

/* DENSITY — the type/space scale can be nudged for the whole page.
   Default (regular) is the smaller scale the brief asked for. */
html[data-density="compact"] {
  --fs-display: clamp(31px, 3.4vw, 47px);
  --fs-h1: clamp(26px, 2.5vw, 35px);
  --fs-h2: clamp(22px, 2.2vw, 30px);
  --fs-h3: clamp(19px, 1.5vw, 21px);
  --fs-lead: 16px;
  --fs-body: 15px;
}
html[data-density="spacious"] {
  --fs-display: clamp(39px, 4.4vw, 62px);
  --fs-h1: clamp(32px, 3.1vw, 46px);
  --fs-h2: clamp(27px, 2.8vw, 39px);
  --fs-h3: clamp(21px, 1.7vw, 25px);
  --fs-lead: 18px;
  --gutter: clamp(24px, 5vw, 80px);
}

/* ============================================================
   RESET / BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  /* Smooth scroll only enabled AFTER load (via .smooth-scroll class
     added by app.jsx). Default 'auto' so refresh-time scroll restoration
     snaps instantly instead of animating from y=0 to the saved
     position (the "page jumps down on refresh" symptom). */
  scroll-behavior: auto;
  scroll-padding-top: 88px;
}
html.smooth-scroll { scroll-behavior: smooth;
}
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-normal);
  color: var(--ink-body);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* overflow-x: clip prevents horizontal overflow WITHOUT promoting body
     to a scroll container — which `hidden` does, breaking position:sticky
     for descendants. Required for the pinned How-It-Works section. */
  overflow-x: clip;
}

/* The texture is the calmest possible whisper: a soft cool light at the
   very top of the page (like the PandaOS sky), NOT a grid. It fades out
   fast so the canvas stays open and airy. Toggleable via [data-texture]. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(140% 55% at 50% -8%, var(--accent-tint) 0%, transparent 60%);
  opacity: 1;
  transition: opacity var(--slow) var(--ease);
}
html[data-texture="off"] body::before { opacity: 0; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--ink);
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-tight);
  margin: 0;
  text-wrap: balance;
}
h3, h4 { letter-spacing: var(--ls-snug); line-height: var(--lh-snug); }
p { margin: 0; text-wrap: pretty; }
a { color: inherit; text-decoration: none; }
svg { display: block; }
::selection { background: var(--accent-tint); color: var(--ink); }
/* On dark panels (stats, CTA) the default lavender-tinted highlight
   leaves the (forced-dark) text unreadable against the navy bg. Invert
   the selection palette inside these panels: solid accent bg + near-white text. */
.stats-section ::selection,
.cta-section ::selection {
  background: color-mix(in srgb, var(--accent) 55%, transparent);
  color: #F5F5FB;
}

/* ============================================================
   LAYOUT — an open, centered column. No rails, no frame. Sections
   are separated by generous whitespace; lines appear only as small,
   calm details (a short centered tick above a label; soft card
   borders). This keeps the airy PandaOS feeling.
   ============================================================ */
.page { position: relative; z-index: 1; }

/* Promote every top-level section container so its content rasterizes
   as a flat texture before the nav's backdrop-filter samples it. Using
   will-change instead of an explicit transform avoids the subpixel
   jitter that translate3d causes during Lenis smooth scroll. */
.hero,
.section,
.cta,
.footer,
.proof {
  will-change: transform;
}

.frame {
  width: min(100%, var(--maxw));
  margin-inline: auto;
  position: relative;
}
/* the column sits on the canvas; content gets breathing gutters */
.inner { padding-inline: var(--gutter); }

/* a SUBTLE detail: a short centered vertical tick above a section label
   (the PandaOS divider). The only "line" the eye notices, and it's tiny. */
.tick-label { display: flex; flex-direction: column; align-items: center; }
.tick-label::before {
  content: "";
  width: 1px; height: 30px;
  background: linear-gradient(to bottom, transparent, var(--line-strong));
  margin-bottom: 22px;
}

/* ============================================================
   ELEMENTS — eyebrow, lead, links
   ============================================================ */
.eyebrow {
  font-family: var(--font-display);
  /* Scales down on small viewports — 14px reads heavy and crowds the
     narrow content column on phones; 11px is the floor where the
     uppercase + wide-tracking still feels deliberate. */
  font-size: clamp(11px, 1.3vw, 14px);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  color: var(--ink-muted);
  display: inline-flex;
  align-items: center;
  gap: 9px;
  white-space: nowrap;
}
/* Brackets the eyebrow text on both sides with a small vertical pill
   in light violet. Pure CSS shape — no mask, no filter — so it reads
   as a clean, calm punctuation mark on each side of the label. */
.eyebrow::before, .eyebrow::after {
  content: "";
  width: 3px;
  height: 12px;
  border-radius: var(--r-pill);
  /* Derived from --accent so future hue shifts propagate automatically.
     35% accent + 65% white = light tint of the brand colour. */
  background-color: color-mix(in srgb, var(--accent) 35%, white);
  flex: none;
}
/* letter-spacing adds tracking after the LAST glyph too, so the gap to
   ::after looks ~0.14em wider than the gap to ::before. Pulling ::after
   left by that much cancels the asymmetry. */
.eyebrow::after { margin-left: -0.14em; }
/* eyebrow rendered as a bordered pill (used over centered heads) */
.eyebrow.pill {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 7px 15px 7px 13px;
  background: var(--surface);
  white-space: nowrap;
}

/* Drop the "Custom" prefix on very small screens — at ≤400px the pill
   gets cramped enough that trimming one word buys meaningful breathing
   room without changing the message materially. */
@media (max-width: 400px) {
  .eyebrow-trim { display: none; }
}

.lead {
  font-size: var(--fs-lead);
  line-height: var(--lh-relaxed);
  color: var(--ink-body);
}
.accent-word {
  background-image: url('textures/background-texture.jpg');
  background-size: cover;
  background-position: center;
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  /* Dropping the brightness keeps the exact hue as the cards but
     ensures the text is dark enough to read against the white canvas */
  filter: hue-rotate(15deg) saturate(1.3) contrast(1.15) brightness(0.95);
  /* Tiny right padding so the heavy 900-weight rightmost glyph isn't
     clipped at the filter's render-region boundary. background-clip:
     text means this added space stays empty (no extra texture). */
  padding-right: 0.05em;
}

/* ============================================================
   BUTTONS — squared, compact, confident.
   Primary = solid accent (ink in graphite). Secondary = framed.
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-small);
  letter-spacing: var(--ls-snug);
  line-height: 1;
  /* asymmetric padding: comfortable text on the left, tight on the right
     so the arrow chip is inset 6px from the edge */
  padding: 6px 6px 6px 22px;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--base) var(--ease), border-color var(--base) var(--ease),
              color var(--base) var(--ease), transform var(--base) var(--ease);
}
/* The arrow is now a small chip — a contrasting tile inset on the right
   of the button. On purple primary it lifts via a translucent white
   overlay; on light secondary it deepens via a translucent ink overlay.
   No more sliding-arrow on hover; the chip's background shifts instead. */
.btn .arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-sm);
  font-size: 14px;
  line-height: 1;
  transition: background var(--base) var(--ease);
}

.btn-primary { background: var(--ink); color: var(--on-accent); border-color: var(--ink); }
.btn-primary .arrow { background: rgba(255, 255, 255, 0.18); }
.btn-primary:hover {
  background: color-mix(in srgb, var(--ink) 86%, #000);
  border-color: color-mix(in srgb, var(--ink) 86%, #000);
  transform: translateY(-1px);
}
.btn-primary:hover .arrow { background: rgba(255, 255, 255, 0.26); }
.btn-primary:active { transform: scale(0.985); }

.btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
.btn-secondary .arrow { background: rgba(22, 16, 84, 0.06); color: var(--ink); }
.btn-secondary:hover { border-color: var(--ink-muted); transform: translateY(-1px); }
.btn-secondary:hover .arrow { background: rgba(22, 16, 84, 0.10); }
.btn-secondary:active { transform: scale(0.985); }

.btn-ghost { background: transparent; color: var(--ink); }
.btn-ghost:hover { background: var(--canvas-sink); }

/* ============================================================
   NAV — clean, open PandaOS-style top bar. Logo left, links
   centered, CTA right. No frame; just a hairline base on scroll.
   ============================================================ */
/* Clean PandaOS-style glass: a single flat translucent fill + backdrop
   blur + a hairline base. No gradient layers, no inset highlight, no
   saturate — just a calm, uniform glass surface. */
.nav-wrap {
  position: sticky; top: 0; z-index: 50;
  /* Premium glass recipe: saturation lift for color body, soft vertical
     gradient for depth, 1px inner top highlight for optical thickness,
     soft outer shadow to separate nav from content beneath. */
  background:
    linear-gradient(
      to bottom,
      color-mix(in srgb, var(--canvas) 72%, transparent) 0%,
      color-mix(in srgb, var(--canvas) 64%, transparent) 100%
    );
  backdrop-filter: blur(18px) saturate(1.6);
  -webkit-backdrop-filter: blur(18px) saturate(1.6);
  border-bottom: 1px solid var(--line-soft);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    0 1px 2px rgba(20, 24, 50, 0.04);
  transition:
    background var(--base) var(--ease),
    box-shadow var(--base) var(--ease),
    border-bottom-color var(--base) var(--ease);
}
.nav-wrap.scrolled {
  background:
    linear-gradient(
      to bottom,
      color-mix(in srgb, var(--canvas) 86%, transparent) 0%,
      color-mix(in srgb, var(--canvas) 80%, transparent) 100%
    );
}
/* When the mobile menu is open, the nav matches the menu panel's
   surface (96/94 canvas mix, same blur) and drops its glass-specific
   details (inset top highlight, soft outer shadow, bottom border)
   so the nav + panel read as one continuous surface. On close, the
   class is removed and the glass recipe transitions back smoothly. */
.nav-wrap.is-menu-open {
  background:
    linear-gradient(
      to bottom,
      color-mix(in srgb, var(--canvas) 96%, transparent) 0%,
      color-mix(in srgb, var(--canvas) 94%, transparent) 100%
    );
  border-bottom-color: transparent;
  box-shadow: none;
}

.nav {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  height: 68px; gap: 24px;
}
/* Wordmark inherits fill from currentColor (var(--ink) = deep navy) */
.nav-logo { color: var(--ink); height: 35px; display: inline-flex; justify-self: start; }
.nav-logo svg { height: 100%; width: auto; }
/* On really small viewports the full wordmark crowds the right-side
   nav cluster (hamburger + Book a call). Swap to the icon mark only. */
.nav-logo-mark { display: none; }
@media (max-width: 480px) {
  .nav-logo-full { display: none; }
  /* Mark renders a tiny bit smaller than the wordmark's full height —
     icon marks feel right at slightly more compact proportions, and
     it gives the hamburger room without crowding. */
  .nav-logo-mark { display: block; height: 82%; }
}
.nav-links { display: flex; align-items: center; gap: 32px; justify-self: center; }
.nav-links a {
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-small);
  color: var(--ink-body); letter-spacing: var(--ls-snug);
  transition: color var(--fast) var(--ease);
}
.nav-links a:hover { color: var(--ink); }
.nav-right { display: flex; align-items: center; gap: 18px; justify-self: end; }
.nav-login {
  font-family: var(--font-display); font-weight: 500; font-size: var(--fs-small);
  color: var(--ink-body); letter-spacing: var(--ls-snug);
  transition: color var(--fast) var(--ease);
}
.nav-login:hover { color: var(--ink); }
@media (max-width: 860px) { .nav { grid-template-columns: 1fr auto; } .nav-links, .nav-login { display: none; } }

/* ============================================================
   MOBILE NAV — hamburger toggle + dropdown panel
   ============================================================
   Only visible at ≤860px (where .nav-links is hidden). Toggle sits in
   .nav-right next to the primary CTA. Panel slides down from the bottom
   of the sticky nav-wrap; a fixed backdrop below the nav intercepts
   outside clicks to close. */
.nav-menu-toggle {
  display: none;
  background: transparent;
  border: 0;
  width: 40px;
  height: 40px;
  padding: 0;
  cursor: pointer;
  color: var(--ink-body);
  border-radius: var(--r-pill);
  align-items: center;
  justify-content: center;
  transition: color var(--fast) var(--ease), background-color var(--fast) var(--ease);
}
.nav-menu-toggle:hover {
  color: var(--ink);
}
/* Animated hamburger — two lines that rotate + translate into an X.
   transform-based so it runs on the GPU compositor; expressive curve so
   the morph reads as deliberate. */
.nav-menu-toggle-icon {
  display: block;
  position: relative;
  width: 18px;
  height: 14px;
}
.nav-menu-toggle-line {
  position: absolute;
  left: 0;
  width: 100%;
  /* Integer pixel values for crisp rendering on all DPRs. */
  height: 2px;
  background: currentColor;
  border-radius: 1px;
  transform-origin: center;
  transition: transform 380ms cubic-bezier(0.65, 0, 0.35, 1);
}
/* Container is 14px tall. Lines at top:3/bottom:3 sit with centers at
   y=4 and y=10. Container center is y=7. Each line needs to travel 3px
   (not 4) to converge exactly at the center — that's the fix for the
   asymmetric X. */
.nav-menu-toggle-line:nth-child(1) { top: 3px; }
.nav-menu-toggle-line:nth-child(2) { bottom: 3px; }
.nav-menu-toggle[aria-expanded="true"] .nav-menu-toggle-line:nth-child(1) {
  transform: translateY(3px) rotate(45deg);
}
.nav-menu-toggle[aria-expanded="true"] .nav-menu-toggle-line:nth-child(2) {
  transform: translateY(-3px) rotate(-45deg);
}
@media (max-width: 860px) {
  /* On mobile, the desktop "Let's talk" button is hidden — its job moves
     inside the dropdown menu (.nav-menu-cta). Hamburger gets bigger to
     compensate for being the only nav-right control. */
  .nav-right .btn-primary { display: none; }
  .nav-menu-toggle {
    display: inline-flex;
    width: 56px;
    height: 56px;
  }
  .nav-menu-toggle-icon { width: 32px; height: 24px; }
  .nav-menu-toggle-line:nth-child(1) { top: 6px; }
  .nav-menu-toggle-line:nth-child(2) { bottom: 6px; }
  /* Re-tune the X morph for the larger icon — container center is at
     y=12; lines start at y=7 / y=17, so each travels 5px to converge. */
  .nav-menu-toggle[aria-expanded="true"] .nav-menu-toggle-line:nth-child(1) {
    transform: translateY(5px) rotate(45deg);
  }
  .nav-menu-toggle[aria-expanded="true"] .nav-menu-toggle-line:nth-child(2) {
    transform: translateY(-5px) rotate(-45deg);
  }
}

/* Panel sits absolute below the nav-wrap. Because nav-wrap is
   position: sticky, it's the positioning ancestor — panel stays
   anchored as the nav stays at the top of the viewport. */
.nav-menu-panel {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background:
    linear-gradient(
      to bottom,
      color-mix(in srgb, var(--canvas) 96%, transparent) 0%,
      color-mix(in srgb, var(--canvas) 94%, transparent) 100%
    );
  backdrop-filter: blur(18px) saturate(1.6);
  -webkit-backdrop-filter: blur(18px) saturate(1.6);
  border-bottom: 1px solid var(--line);
  z-index: 1;
  /* Default (closed) state — transitions handle BOTH appear and
     disappear when the .is-open class is added/removed. */
  opacity: 0;
  transform: translateY(-12px);
  visibility: hidden;
  pointer-events: none;
  /* Visibility transition delayed until after opacity/transform
     finish, so the panel stays interactive throughout the fade-out. */
  transition: opacity 380ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 380ms cubic-bezier(0.16, 1, 0.3, 1),
              visibility 0s linear 380ms;
}
.nav-menu-panel.is-open {
  opacity: 1;
  transform: translateY(0);
  visibility: visible;
  pointer-events: auto;
  /* On open: visibility kicks in immediately (no delay). */
  transition: opacity 380ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 380ms cubic-bezier(0.16, 1, 0.3, 1),
              visibility 0s linear 0s;
}
.nav-menu-links {
  list-style: none;
  /* Vertical breathing room above (from nav) and below (before foot). */
  margin: clamp(24px, 5vh, 40px) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
/* Items cascade in with staggered delays on open. On close, all items
   transition together (no stagger) so the panel can complete its
   collapse cleanly. */
.nav-menu-links li {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 320ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 320ms cubic-bezier(0.16, 1, 0.3, 1);
}
.nav-menu-panel.is-open .nav-menu-links li {
  opacity: 1;
  transform: translateY(0);
}
.nav-menu-panel.is-open .nav-menu-links li:nth-child(1) { transition-delay: 140ms; }
.nav-menu-panel.is-open .nav-menu-links li:nth-child(2) { transition-delay: 220ms; }
.nav-menu-panel.is-open .nav-menu-links li:nth-child(3) { transition-delay: 300ms; }
.nav-menu-links a {
  display: block;
  /* Big centered display type for the editorial menu pattern. Sized so
     the scale stays sensible across every breakpoint where the burger
     menu shows (0–860px): the previous 6.4vw curve hit its ceiling
     and then sat at 44px for the whole 688–860px range, which read as
     way too big on tablet-portrait viewports. */
  padding: 6px 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(18px, 13px + 1.1vw, 22px);
  line-height: 1.1;
  letter-spacing: var(--ls-tight);
  color: var(--ink);
  text-align: center;
  text-decoration: none;
  transition: color var(--fast) var(--ease);
}
.nav-menu-links a:hover { color: var(--accent); }

/* Foot block at the bottom of the mobile menu — small "Get in touch"
   label above a styled "Let's talk" text link. Replaces the previous
   .nav-menu-cta button to match the editorial menu pattern (centered
   big links above + small connect block below). */
.nav-menu-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 12px;
  /* Space between the big links above and the Connect block. */
  margin-top: clamp(28px, 5vh, 48px);
  margin-bottom: clamp(24px, 4vh, 36px);
  /* Cascade in after the menu links. */
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 320ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 320ms cubic-bezier(0.16, 1, 0.3, 1);
}
.nav-menu-panel.is-open .nav-menu-foot {
  opacity: 1;
  transform: translateY(0);
  transition-delay: 300ms;
}
.nav-menu-foot-label {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  color: var(--ink-muted);
}
.nav-menu-foot-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: 700;
  /* Distinctly smaller than .nav-menu-links a at every viewport so the
     visual weight (underline + arrow chip) doesn't make the CTA appear
     larger. Faster vw rate (0.95 vs 1.1) shrinks sooner on small
     viewports; lower ceiling caps it well below the link size. */
  font-size: clamp(15px, 11px + 0.95vw, 19px);
  letter-spacing: var(--ls-tight);
  line-height: 1.2;
  color: var(--ink);
  transition: color var(--fast) var(--ease);
}
/* Underline lives ONLY on the text span — not on the arrow. */
.nav-menu-foot-cta-text {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--line-strong);
  transition: text-decoration-color var(--base) var(--ease);
}
.nav-menu-foot-cta:hover { color: var(--accent); }
.nav-menu-foot-cta:hover .nav-menu-foot-cta-text {
  text-decoration-color: transparent;
}
.nav-menu-foot-cta .arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1em;
  height: 1.1em;
  font-size: 0.7em;
}

/* Backdrop covers everything below the nav so taps anywhere outside
   the panel close it. z-index 49 = under nav-wrap (z-50) and panel,
   over page content. Subtle backdrop-filter blur adds an atmospheric
   recede instead of a flat color wash. Always rendered; visibility
   driven by the .is-open class so transitions can play both ways. */
.nav-menu-backdrop {
  position: fixed;
  top: 68px;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(20, 18, 58, 0.22);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  z-index: 49;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 320ms cubic-bezier(0.16, 1, 0.3, 1),
              visibility 0s linear 320ms;
}
.nav-menu-backdrop.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 320ms cubic-bezier(0.16, 1, 0.3, 1),
              visibility 0s linear 0s;
}

/* ============================================================
   HERO — centered, airy. The negative space IS the design.
   No background mark, no wash. Eyebrow pill · headline · sub ·
   buttons · meta, then a framed product placeholder below.
   ============================================================ */
.hero {
  text-align: center;
  display: flex; flex-direction: column; align-items: center;
  padding-block: clamp(84px, 12.5vh, 150px) clamp(112px, 15.5vh, 190px);
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
/* Tighter hero on small phones — the desktop clamp values are too tall
   for narrow viewports where the headline is also wrapping to more
   lines (adding intrinsic height). */
@media (max-width: 600px) {
  .hero {
    padding-block: clamp(56px, 9vh, 88px) clamp(72px, 11vh, 112px);
  }
}
/* Paper texture wash across the hero. Same image + multiply treatment as
   the how-cards so the hero feels materially related. Very low opacity
   and a vertical fade keep it whisper-quiet behind the headline. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: url('textures/background-texture.jpg');
  background-size: cover;
  background-position: center;
  opacity: 0.18;
  mix-blend-mode: multiply;
  filter: hue-rotate(16deg) saturate(1.3) contrast(1.15);
  /* Radial fade originating from top center — clears the texture around
     the headline area so the type reads on clean canvas, while letting
     the texture build up in the lower corners and bottom edges. */
  -webkit-mask-image: radial-gradient(ellipse 66% 95% at 50% 0%,
    transparent 0%, transparent 55%, rgba(0,0,0,0.4) 80%, #000 100%);
          mask-image: radial-gradient(ellipse 66% 95% at 50% 0%,
    transparent 0%, transparent 55%, rgba(0,0,0,0.4) 80%, #000 100%);
  pointer-events: none;
}
/* the inner column inside the Frame needs to flex its children centered
   too — h1 and lead p have max-widths and would otherwise sit left-aligned */
.hero .inner {
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* hero text content sits above the mark */
.hero > .frame { position: relative; z-index: 1; }

/* Particle canvas — full viewport width, centered against the hero.
   The 100vw + translateX(-50%) trick breaks out of any parent width
   constraint so particles can spawn across the entire screen. */
.hero-particles {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100vw;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}
.hero .frame { position: relative; z-index: 1; }
.hero .eyebrow.pill { margin-bottom: 30px; }
.hero h1 {
  font-size: var(--fs-display);
  /* Base weight 600 for non-highlighted words. The .accent-word span
     overrides this back to 800 below so highlighted phrases retain
     their visual weight against the texture treatment. */
  font-weight: 600;
  /* Tighter tracking than the global --ls-tight (-0.02em) so the
     display-size headline reads as more compact. Only applied to the
     hero — section h2/h3 keep the gentler default. */
  letter-spacing: -0.03em;
  line-height: 1.06;
  max-width: 28ch;
}
.hero h1 .accent-word { font-weight: 900; }
.hero .lead {
  margin-top: 24px;
  /* Narrower at the viewport where the headline starts shrinking. The
     headline ceiling (92px) is hit when 6vw = 92px → at ~1533px. Above
     that, the subtitle keeps its wider 60ch measure (paired with the
     fully-grown headline). Below it, the narrower 46ch lets the
     subtitle wrap sooner so it doesn't outstretch the shrinking title. */
  max-width: 46ch;
  /* Hero subtitle is bumped above the standard --fs-lead so it pairs
     better with the larger hero headline — but with a lower floor so
     small phones can scale below 19px instead of being pinned there.
     Other lead text on the page keeps the smaller --fs-lead size. */
  font-size: clamp(17px, 14.5px + 0.55vw, 22px);
}
/* At wide viewports (≥1533px) the headline is at its ceiling (92px) and
   the subtitle reverts to the wider 60ch measure — paired with the
   fully-grown headline. Below that, the .hero .lead rule above keeps
   the narrower 46ch in play. */
@media (min-width: 1533px) {
  .hero .lead { max-width: 60ch; }
}
.hero-ctas { margin-top: 34px; display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
/* Hero CTA text swaps to a shorter label on small screens. */
.hero-cta-short { display: none; }
@media (max-width: 600px) {
  .hero-cta-full { display: none; }
  .hero-cta-short { display: inline; }
}
.hero-meta {
  margin-top: 26px; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 8px 18px;
  font-family: var(--font-display); font-size: var(--fs-small); color: var(--ink-muted); letter-spacing: var(--ls-snug);
}
.hero-meta .dot { width: 6px; height: 6px; border-radius: var(--r-pill); background: var(--accent); display: inline-block; margin-right: 8px; }
.hero-meta .sep { color: var(--line-strong); }

/* ============================================================
   SECTION SCAFFOLD
   ============================================================ */
.section { padding-block: clamp(44px, 8vh, 100px); }
.section.theme-muted { padding-bottom: clamp(44px, 8vh, 100px); }
/* Tighter section spacing on mobile — the desktop clamp leaves too
   much vertical air on phones, where each section already feels
   bigger relative to viewport. */
@media (max-width: 600px) {
  .section { padding-block: clamp(28px, 5.5vh, 56px); }
  .section.theme-muted { padding-bottom: clamp(28px, 5.5vh, 56px); }
}
.section-head {
  /* Parent max-width sets the SUBTITLE's wrap width.
     h2 below has its own slightly narrower max-width. */
  max-width: 780px; margin-inline: auto;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.section-head .eyebrow { margin-bottom: 16px; }
/* Right-positioned variant — sized to match the RIGHT COLUMN of a
   2-column grid with the same gap as .how-layout. Block sits on the
   right of the page, text inside stays left-aligned for readability.
   max-width = 50% of the container minus half the grid gap, so the
   block corresponds exactly to where the right column would land if
   this section used the same 2-col grid as how-we-work. */
.section-head--right {
  max-width: calc(50% - clamp(20px, 3vw, 48px));
  margin-left: auto;
  margin-right: 0;
  text-align: left;
  align-items: flex-start;
}
/* On viewports where .how-layout collapses to 1 column, the right
   variant should too — give it full container width. */
@media (max-width: 860px) {
  .section-head--right {
    max-width: 100%;
    margin-left: 0;
  }
}
.section-head h2 { font-size: var(--fs-h2); line-height: 1.14; max-width: 680px; }
.section-head p { margin-top: 16px; color: var(--ink-body); font-size: var(--fs-body); line-height: var(--lh-relaxed); }

/* ============================================================
   SOCIAL PROOF — a quiet logo row between two rungs
   ============================================================ */
/* asymmetric padding: top uses the same vh-based scale as full sections
   so the proof band gets a proper "section break" after the hero, while
   bottom stays tight to keep the band feeling like a transitional rail */
.proof {
  padding-top: clamp(64px, 9vh, 104px);
  padding-bottom: clamp(36px, 5vw, 52px);
}
.proof-label {
  text-align: center; font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-small); text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--ink-muted); margin-bottom: 36px;
}
.proof-row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: clamp(26px, 5vw, 60px);
}
.client { display: inline-flex; align-items: center; gap: 9px; color: var(--ink-faint);
  transition: color var(--base) var(--ease); }
.client:hover { color: var(--ink-body); }
.client svg { width: 20px; height: 20px; }
.client span { font-family: var(--font-display); font-weight: 500; font-size: clamp(18px, 2.5vw, 26px); color: var(--ink-muted); white-space: nowrap; }
/* Subtle name label paired with each logo — the logo does the visual
   heavy lifting, this just aids recognition. Smaller and slightly
   lighter than the original text-only treatment so the logo stays
   primary. Higher-specificity selector (.client .brand-name = 0,2,0)
   beats the .client span rule above (0,1,1). */
.client .brand-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(13px, 1.2vw, 16px);
  color: var(--ink-muted);
  letter-spacing: var(--ls-snug);
  white-space: nowrap;
  transition: color var(--base) var(--ease);
}
.client:hover .brand-name { color: var(--ink-body); }
/* Brand logo via mask-image — each <span class="brand-logo"> has its
   own mask-image set inline. The element's background-color paints
   through the mask, so the logo inherits the .client text color
   (--ink-faint at rest, --ink-body on hover via the parent's hover). */
.client .brand-logo {
  display: block;
  width: clamp(26px, 3.4vw, 34px);
  height: clamp(26px, 3.4vw, 34px);
  background-color: currentColor;
  mask-repeat: no-repeat;
  mask-position: center;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
  -webkit-mask-size: contain;
  /* Reset any inherited text styles (so the .client span rule above
     doesn't try to size this as text). */
  font-size: 0;
  line-height: 0;
}

/* MARQUEE — infinite horizontal scroll with edge fades.
   Trailing padding on each track equals the inter-item gap, so when one
   track translates fully offscreen the next sits exactly in its place. */
.proof-marquee {
  display: flex;
  overflow: hidden;
  /* Inset the marquee element itself (margin, not padding) so its fade
     mask and edges sit visibly inside the section's gutter. */
  margin-inline: clamp(16px, 3vw, 48px);
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 8%, #000 92%, transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 8%, #000 92%, transparent 100%);
}
.proof-track {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: clamp(48px, 8vw, 120px);
  padding: 0 clamp(48px, 8vw, 120px) 0 0;
  margin: 0;
  list-style: none;
  animation: proofMarquee 52s linear infinite;
  will-change: transform;
}
@keyframes proofMarquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-100%); }
}
@media (prefers-reduced-motion: reduce) {
  .proof-track { animation: none; }
}

/* ============================================================
   STATS — three figures, separated by generous space (a faint
   hairline only between them as a calm detail).
   ============================================================ */
/* Stats sit on the canvas with the rest of the page — no card chrome.
   The only structural detail is a single hairline between columns,
   matching the airy / hairline-driven language of the rest. */
/* Section itself is the panel — full-width band with rounded corners.
   Soft light blue tint, slightly inset from viewport edges so the
   rounded corners read as intentional. */
.stats-section {
  /* Dark navy panel — pulls the brand's deep ink toward a richer blue
     so the stats read as a confident "the numbers" moment. */
  background: #1B1A38;
  color: #F5F5FB;
  border-radius: 28px;
  overflow: hidden;
  padding: clamp(40px, 5vw, 72px) clamp(40px, 5vw, 88px);
  /* Padding shorthand overrides .section's padding-block, so we restore
     outer breathing room BELOW via margin (so panel-bottom → how-eyebrow
     matches section → section gap). Top is intentionally untouched —
     the .proof section above keeps its tight padding-bottom for the
     close, transitional-rail feel into the stats panel. */
  margin-bottom: clamp(44px, 8vh, 100px);
}
@media (max-width: 600px) {
  .stats-section { margin-bottom: clamp(28px, 5.5vh, 56px); }
}

.stats {
  display: grid;
  /* minmax(0, 1fr) — without the 0 min, columns can grow past 1fr if
     their content's min-content is wider, breaking equality. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
}
.stat {
  /* Symmetric padding on every stat (no first/last overrides) so the
     dividers sit visually centered between stat content. The panel's
     own horizontal padding handles the gap to the rounded corners. */
  padding: clamp(20px, 2vw, 32px) clamp(20px, 2.5vw, 36px);
  border-left: 1px solid rgba(255, 255, 255, 0.12);
}
.stat:first-child { border-left: none; }
.stat-num {
  font-family: var(--font-mono); font-weight: 700; letter-spacing: -0.03em;
  font-size: clamp(48px, 6vw, 72px); line-height: 1;
  /* On dark panel: numbers in near-white, units in the brand violet
     (still pops against the deep navy). */
  color: #F5F5FB;
  font-variant-numeric: tabular-nums lining-nums; display: flex; align-items: baseline;
  /* When the container is too narrow to fit number + unit on one line
     (e.g., "1-3 workflows" on mobile), allow the unit to wrap to a new
     line BELOW the number — instead of the number breaking at its
     internal hyphen ("1-" / "3"). The nowrap on direct children keeps
     the number and the unit text each atomic. */
  flex-wrap: wrap;
  row-gap: 4px;
}
.stat-num > span { white-space: nowrap; }
.stat-num .unit { font-size: 0.36em; margin-left: 8px; color: #9D9CF7; font-weight: 600; letter-spacing: var(--ls-snug); }
.stat-num .accent { color: #9D9CF7; }
.stat-label { margin-top: 18px; color: rgba(245, 245, 251, 0.72); font-size: var(--fs-lead); line-height: var(--lh-relaxed); max-width: 34ch; }
@media (max-width: 760px) {
  .stats { grid-template-columns: 1fr; padding: clamp(32px, 6vw, 48px) clamp(24px, 5vw, 36px); }
  .stat {
    border-left: none;
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    padding: 32px 0;
  }
  .stat:first-child { border-top: none; padding-top: 0; }
  .stat:last-child  { padding-bottom: 0; }
}

/* ============================================================
   CARD GRID — how-it-works + case studies. Separate, soft cards
   with gaps and a faint border — calm and airy, not a connected
   table. No heavy shadow; hover is a gentle lift.
   ============================================================ */
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 52px; }
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; margin-top: 52px; }
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: clamp(26px, 3vw, 34px) clamp(24px, 2.6vw, 30px);
  position: relative;
  transition: border-color var(--base) var(--ease), transform var(--base) var(--ease), box-shadow var(--base) var(--ease);
}


.card:hover {
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: 0 14px 34px rgba(var(--rule), 0.06);
}
@media (max-width: 860px) {
  .grid-3 { grid-template-columns: 1fr; }
  .grid-2 { grid-template-columns: 1fr; }
}

/* ============================================================
   HOW IT WORKS — CSS Sticky Stack
   ============================================================ */

/* Two-column layout: text intro + step list on the left, process flow viz on the right. */
.how-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(40px, 6vw, 96px);
  /* Vertically center each column against the other (since the left column
     with the title + step list is taller, the right-side flow viz centers
     against it). */
  align-items: center;
  position: relative;
  z-index: 1;
}
/* Left column — section title, left-aligned. No sticky / no min-height
   since the ProcessFlow on the right is in normal flow (no card stack). */
.how-head {
  display: flex;
  flex-direction: column;
  text-align: left;
  align-items: flex-start;
  max-width: none;
}
.how-head h2 { max-width: none; text-align: left; text-wrap: auto; }
.how-head p  { text-align: left; }
/* Right column — keeps the sticky card stack behavior intact. */
.how-stack {
  display: block;
  position: relative;
}
@media (max-width: 860px) {
  .how-layout { grid-template-columns: 1fr; gap: clamp(44px, 6vh, 64px); }
  .how-head { position: static; top: auto; }
}

.how-stack-side {
  position: sticky;
  top: clamp(140px, 26vh, 280px);
  height: clamp(300px, 38vh, 380px);
  display: flex;
  justify-content: center;
  align-items: center;
  /* Anchor to the TOP of the grid cell. The cell stretches to match the
     tall cards container (cards + dwell padding); without this the leaf
     would be centred in that tall cell and appear pulled downward. */
  align-self: start;
}

/* Glass leaf — two stacked LeafMark layers inside a square box:
     .leaf-base   : faint always-on copy (the "glass body")
     .leaf-pulse  : brighter copy revealed through a soft diagonal beam
                    mask. The mask sits on the OUTER layer so it stays
                    viewport-aligned, while the LeafMark inside the rotor
                    rotates with --rot — light glints across the rotating
                    glass as scroll advances.
   --sweep drives the beam position; --rot drives the rotation. Both are
   written by the JS in HowItWorks every scroll frame. */
.how-leaf {
  position: relative;
  width: clamp(110px, 13vw, 160px);
  aspect-ratio: 1;
  /* color is set per-layer below — base in neutral ink, pulse in violet,
     mirroring the hero loop mark's base/spot/spec split */
}
.leaf-base, .leaf-pulse {
  position: absolute;
  inset: 0;
}
.leaf-rotor {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: rotate(var(--rot, 0deg));
  transform-origin: center;
  transition: transform 80ms linear;
}
.leaf-rotor svg {
  width: 70%;
  height: auto;
  display: block;
}
/* Single glassy leaf in soft neutral ink: low opacity for the see-through
   feel, gentle blur to soften edges (frosted glass), and a tiny diffuse
   drop-shadow so it casts a faint ambient halo into the canvas. */
.leaf-base {
  color: var(--ink);
  opacity: 0.025;
  filter: blur(0.6px) drop-shadow(0 4px 10px rgba(20, 18, 58, 0.04));
}
/* Mirror leaf on the right column: scaleX(-1) flips the SVG shape
   horizontally (true mirror image of the left leaf), and because the
   flip reverses the rotation's visual direction, applying the same
   --rot value makes it appear to spin opposite the left leaf. */
.how-leaf--mirror .leaf-rotor {
  transform: scaleX(-1) rotate(var(--rot, 0deg));
}

.how-stack-cards {
  display: flex;
  flex-direction: column;
  gap: clamp(32px, 4vh, 64px);
  position: relative;
  /* No extra dwell — the section's own padding handles the spacing to
     the next section. All cards still unstick together (same sticky top). */
  padding-bottom: 0;
}

.how-card {
  position: sticky;
  top: clamp(140px, 26vh, 280px);
  height: clamp(300px, 38vh, 380px);
  background: var(--surface);
  border-radius: var(--r-md);
  padding: clamp(28px, 3.2vw, 40px);
  display: flex;
  flex-direction: column;
  /* Three zones — header / body / chips — anchored top, center, bottom
     so the card breathes instead of collapsing everything to center. */
  justify-content: space-between;
  gap: clamp(20px, 2vw, 32px);
  
  /* Drop Effect */
  --elev: 0;
  --shadow-op: 1;
  --card-scale: 1;
  --stack-offset: 0px;
  /* Skiper-style stacked deck. All cards stick at the SAME top so they
     unstick simultaneously when the section ends (preventing the last
     card from sliding up over the others). The visible stagger comes
     from translateY, applied per nth-child below. --card-scale (set
     by JS) shrinks older cards as later ones arrive — front card stays
     at scale 1, text stays sharp. */
  top: clamp(140px, 26vh, 280px);
  transform: translateY(var(--stack-offset)) scale(var(--card-scale));
  transform-origin: top center;
  box-shadow: 0 calc(-4px - (var(--elev) * 8px)) calc(24px + (var(--elev) * 24px)) rgba(0, 0, 0, calc((0.04 + (var(--elev) * 0.04)) * var(--shadow-op)));
  transition: transform 0.15s ease-out, box-shadow 0.15s ease-out;
  overflow: hidden; /* clip the texture to the border radius */
}
/* Visual deck stagger via translate (NOT via different sticky tops —
   that caused premature unsticking of the last card). */
.how-card:nth-child(2) { --stack-offset: 26px; }
.how-card:nth-child(3) { --stack-offset: 52px; }
.how-card:nth-child(4) { --stack-offset: 78px; }

/* Texture Overlay — radial fade toward the upper-right corner so the
   grain dissolves there while the card's underlying hue stays intact
   (the fade is on the ::before mask only, not on the card itself). */
.how-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: url('textures/background-texture.jpg');
  background-size: cover;
  background-position: center;
  opacity: 0.26;
  mix-blend-mode: multiply;
  filter: hue-rotate(16deg) saturate(1.3) contrast(1.15);
  -webkit-mask-image: radial-gradient(ellipse 110% 120% at 100% 0%,
    transparent 0%, rgba(0,0,0,0.45) 35%, #000 75%);
          mask-image: radial-gradient(ellipse 110% 120% at 100% 0%,
    transparent 0%, rgba(0,0,0,0.45) 35%, #000 75%);
  pointer-events: none;
}

/* Ensure card text sits above the texture */
.how-card > * {
  position: relative;
  z-index: 1;
}

.how-card h3        { font-size: var(--fs-h3); margin-bottom: 10px; }
.how-card .card-body{ color: var(--ink-body); font-size: var(--fs-body); line-height: var(--lh-relaxed); }
.how-card .chips    { margin-top: 0; }

/* Header zone: step number + a hairline that fills the rest of the row.
   The rule visually anchors the number and creates a clear "header band". */
.how-card-head {
  display: flex;
  align-items: center;
  gap: 14px;
}
.how-card-head .step-num {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.12em;
  color: var(--accent);
  margin: 0;
}
/* Body zone: title + paragraph grouped together, flex column. */
.how-card-body { display: flex; flex-direction: column; }

/* ============================================================
   PROCESS FLOW — three mini frosted cards stacked vertically,
   connected by straight dotted lines with a traveling pulse.
   ============================================================ */
.process-flow {
  position: relative;
  padding-block: clamp(8px, 2vw, 24px);
}
.process-beams {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}
/* Base dashed line — always visible, thin, low opacity.
   Round linecap + equal dash/gap = rounded dashes (the reference look). */
.beam-base {
  fill: none;
  stroke: var(--ink-muted);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 6 6;
  opacity: 0.35;
}
/* Overlay — SAME dash pattern, but 50% THICKER. Color comes from the
   animated gradient (transparent outside the bright zone). Where the
   bright section is, this thicker stroke is visible on top of the base,
   making those dashes appear bigger/brighter — the "thickens under the
   pulse" effect from the reference. */
.beam-overlay {
  fill: none;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 6 6;
}

/* Vertical stack of mini cards — compact, square-ish proportions. */
.process-cards {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Generous gap gives the dashed line + traveling pulse room to read. */
  gap: clamp(96px, 11vh, 160px);
}
.process-card {
  position: relative;
  background: var(--surface);
  border-radius: var(--r-md);
  /* Wider + tighter vertical padding = less elongated, more horizontal card. */
  padding: 16px 24px;
  width: 100%;
  max-width: 280px;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(20, 18, 58, 0.04);
  text-align: center;
}
.process-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: url('textures/background-texture.jpg');
  background-size: cover;
  background-position: center;
  opacity: 0.22;
  mix-blend-mode: multiply;
  filter: hue-rotate(16deg) saturate(1.3) contrast(1.15);
  pointer-events: none;
}
.process-card > * {
  position: relative;
  z-index: 1;
}
/* Icon stacks ABOVE the title. Block element + auto margin centers it. */
.process-card-icon {
  display: block;
  margin: 0 auto 6px;
  color: var(--ink);
  line-height: 0;
}
.process-card-icon svg {
  width: 32px;
  height: 32px;
  display: inline-block;
}
.process-card-title {
  font-size: 18px;
  margin: 0;
}

/* Hover-expand accordion for the left column. Each row collapses to its
   head ("01 · Audit"); hovering or focusing a row reveals its body.
   Only one is open at a time — driven by React state. Row height stays
   stable by min-heighting the list, so the right-column ProcessFlow
   doesn't shift as the active step changes. */
.how-steps-detail {
  /* --step-indent is the single source of truth for the blueprint axis:
     it sets the number column's width AND where dividers start AND the
     body text's left padding, so all three align on one vertical edge. */
  --step-indent: clamp(28px, 2.6vw, 36px);
  list-style: none;
  margin: clamp(24px, 3vw, 36px) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  /* Hard-lock total list height. The per-row body-h lock keeps the
     "one open" invariant when only one row is mid-transition, but fast
     hover-switching that passes through a middle row briefly puts two
     rows mid-transition on independent timelines — and the sum can
     deviate by a few px until they settle. Locking the container via
     calc() (which tracks viewport through the same clamp() expressions
     the layout uses) means the column can't shift no matter what the
     children do. Formula: 3 × (1px border + 2 × head-pad-y + head line-box)
     + the locked body height. */
  height: calc(
    3 * (1px + 2 * clamp(16px, 1.6vw, 22px) + 1.35em)
    + var(--body-h, 180px)
  );
  overflow: hidden;
}
/* Indented dividers via background-image so we don't need an extra
   pseudo-element or wrapper. Two color stops at the same position =
   hard edge: transparent on the left (the number gutter), hairline
   on the right. Last child gets a second layer for the bottom rule. */
.how-step-detail {
  background:
    linear-gradient(to right, transparent var(--step-indent), var(--line) var(--step-indent)) top / 100% 1px no-repeat;
}
.how-step-detail:last-child {
  background:
    linear-gradient(to right, transparent var(--step-indent), var(--line) var(--step-indent)) top / 100% 1px no-repeat,
    linear-gradient(to right, transparent var(--step-indent), var(--line) var(--step-indent)) bottom / 100% 1px no-repeat;
}
/* Head is a 2-col grid: fixed number gutter | flexible title column.
   Number naturally left-aligns inside its cell; title sits at the same
   x-axis as the divider start and the body text below. */
.how-step-head {
  display: grid;
  grid-template-columns: var(--step-indent) 1fr auto;
  align-items: baseline;
  width: 100%;
  padding: clamp(16px, 1.6vw, 22px) 0;
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.how-step-head:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.how-step-num {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-body);
  letter-spacing: 0.06em;
  color: var(--accent);
}
.how-step-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-body);
  line-height: 1.3;
  color: var(--ink);
}
/* Small chevron at the right edge of each row head — caret-down by
   default, rotated 180° when the row is open. Acts as a touch-friendly
   affordance (no hover state on mobile) and a quiet open/closed signal
   on desktop. Uses transform so the rotation is GPU-accelerated. */
.how-step-chevron {
  /* Desktop has hover affordance — open state is signaled by the
     body expanding under the title. Chevron only earns its keep on
     touch, where there's no hover. */
  display: none;
  align-items: center;
  /* Override the head row's baseline alignment so the icon sits
     visually centered against the title text (baseline alignment
     would put the SVG box bottom on the title baseline = too low). */
  align-self: center;
  color: var(--ink-muted);
  transform-origin: center;
  transition: transform 280ms cubic-bezier(0.4, 0, 0.2, 1), color 200ms ease;
}
.how-step-detail.is-open .how-step-chevron {
  transform: rotate(180deg);
}
@media (max-width: 860px) {
  .how-step-chevron { display: inline-flex; }
}
/* Height-based expand. --body-h is set by HowSteps to the tallest body's
   measured scrollHeight, so every step opens to the same height — the
   column total never shifts, and ProcessFlow on the right stays put.
   Fallback covers the one-frame gap before useEffect runs on first paint. */
.how-step-body-wrap {
  height: 0;
  overflow: hidden;
  transition: height 320ms cubic-bezier(0.4, 0, 0.2, 1);
}
.how-step-detail.is-open .how-step-body-wrap {
  height: var(--body-h, 180px);
}
.how-step-body-inner {
  opacity: 0;
  transition: opacity 180ms ease;
}
.how-step-detail.is-open .how-step-body-inner {
  opacity: 1;
  transition: opacity 240ms ease 100ms;
}
/* Higher specificity to defeat .section-head p font-size rule. */
.how-step-detail .how-step-body {
  /* Indent body to align with the title text and the divider start —
     all three share --step-indent for one clean vertical axis. */
  margin: 0;
  padding: 0 0 clamp(18px, 1.8vw, 24px) var(--step-indent);
  color: var(--ink-body);
  font-size: var(--fs-body);
  line-height: var(--lh-normal);
  max-width: 52ch;
}
@media (prefers-reduced-motion: reduce) {
  .how-step-body-wrap,
  .how-step-body-inner,
  .how-step-detail { transition: none; }
}

@media (prefers-reduced-motion: reduce) {
  .beam-overlay { opacity: 0; }
}
@media (max-width: 760px) {
  /* Larger gap on mobile so the dotted line + traveling pulse have
     room to read between the stacked cards. Beams stay on — JS
     ResizeObserver recomputes the SVG geometry when the layout
     changes, so the dotted line connects correctly in either flow. */
  .process-cards { gap: clamp(72px, 12vh, 110px); }
}

@media (max-width: 860px) {
  .how-stack { grid-template-columns: 1fr; margin-top: 48px; gap: 0; }
  .how-stack-side { display: none; }
  .how-stack-cards { gap: 16px; }
  .how-card { position: static; top: auto; height: auto; box-shadow: none; }
  .how-card:not(:last-child) { margin-bottom: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .how-stack-cards { gap: 20px; }
  .how-card { position: static; top: auto; height: auto; box-shadow: none; }
  .how-card:not(:last-child) { margin-bottom: 0; }
}

/* step card */
.step-num {
  font-family: var(--font-mono); font-weight: 700; font-size: 12px; letter-spacing: 0.08em;
  color: var(--accent); display: inline-block; margin-bottom: 20px;
}
.card h3 { font-size: var(--fs-h3); margin-bottom: 12px; }
.card .card-body { color: var(--ink-body); font-size: var(--fs-body); line-height: var(--lh-normal); }
.chips { margin-top: 22px; display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
  font-family: var(--font-display); font-weight: 500; font-size: 12px; letter-spacing: var(--ls-snug);
  color: var(--ink-body); background: var(--canvas-sink);
  border: 1px solid var(--line); border-radius: 5px; padding: 4px 11px;
}

/* case card */
.case-metric {
  font-family: var(--font-mono); font-weight: 700; letter-spacing: -0.02em;
  font-size: clamp(34px, 4vw, 46px); line-height: 1; color: var(--ink);
  font-variant-numeric: tabular-nums lining-nums; display: flex; align-items: baseline; gap: 8px;
}
.case-metric .unit { font-size: 0.36em; color: var(--accent); font-weight: 600; letter-spacing: var(--ls-snug); }
.card h4 { margin-top: 18px; font-size: var(--fs-h4); line-height: 1.3; color: var(--ink); }
.card .case-body { margin-top: 10px; margin-bottom: 20px; color: var(--ink-body); font-size: var(--fs-small); line-height: var(--lh-normal); }
.case-client { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--line-soft);
  display: flex; align-items: center; gap: 10px; font-family: var(--font-display); font-weight: 500;
  font-size: 13px; color: var(--ink-muted); letter-spacing: var(--ls-snug); }
.case-avatar { width: 20px; height: 20px; border-radius: var(--r-pill); background: var(--accent); opacity: 0.85; flex: none; }

/* Case card — clickable button. Premium polish layers:
   1. Paper-texture ::before ties the cards to the rest of the site's
      visual language (same texture used in hero, gallery items, process cards).
   2. Layered hover: violet-tinted shadow + faint accent border, slower easing.
   3. Arrow chip is the signature interaction (fills accent + slides
      diagonally toward the top-right, the case's "exit direction").
   4. Title gets a draw-in underline in muted accent. */
button.case-card {
  font-family: inherit;
  color: inherit;
  text-align: left;
  width: 100%;
  cursor: pointer;
  appearance: none;
  display: flex;
  flex-direction: column;
  /* overflow+isolation: scope the texture ::before to the card's rounded
     box and below the content via z-index: -1. */
  overflow: hidden;
  isolation: isolate;
  transition:
    border-color 420ms cubic-bezier(0.16, 1, 0.3, 1),
    transform 420ms cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 420ms cubic-bezier(0.16, 1, 0.3, 1);
}
button.case-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: url('textures/background-texture.jpg');
  background-size: cover;
  background-position: center;
  /* Lower than hero/gallery — cards are content-dense, so the texture
     reads as a faint paper grain not a wash. */
  opacity: 0.025;
  mix-blend-mode: multiply;
  filter: hue-rotate(16deg) saturate(1.3) contrast(1.15);
  pointer-events: none;
  transition: opacity 420ms ease;
}
button.case-card:hover {
  transform: translateY(-3px);
  /* Neutral border deepening — stays in the ink/hairline family
     instead of tinting violet on hover. */
  border-color: var(--line-strong);
  /* Neutral multi-layer shadow — all three layers use ink-tone alpha
     so the lift reads as depth, not brand color. */
  box-shadow:
    0 1px 2px rgba(20, 18, 58, 0.04),
    0 18px 36px -8px rgba(20, 18, 58, 0.10),
    0 6px 14px -4px rgba(20, 18, 58, 0.05);
}
button.case-card:hover::before { opacity: 0.09; }
button.case-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Title draw-in underline. Wrapping the title in a span lets the
   background-image gradient track the text's inline box and animate
   from 0% → 100% width. For multi-line titles, modern browsers draw
   the background across all line fragments. */
.case-card-title-text {
  background-image: linear-gradient(
    color-mix(in srgb, var(--accent) 38%, transparent),
    color-mix(in srgb, var(--accent) 38%, transparent)
  );
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 2px;
  padding-bottom: 2px;
  transition: background-size 540ms cubic-bezier(0.16, 1, 0.3, 1);
}
button.case-card:hover .case-card-title-text {
  background-size: 100% 2px;
}
.case-foot {
  /* Pinned to the bottom (margin-top: auto) so the byline + arrow align
     across cards regardless of body length. The body itself carries a
     margin-bottom so it never goes flush against the hairline; foot
     padding-top is small since the hairline already separates the zones. */
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.case-foot .case-client {
  margin: 0;
  padding: 0;
  border: 0;
}
.case-arrow {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: 0;
  color: var(--ink);
  background: rgba(27, 26, 56, 0.06);
  flex: none;
  /* Premium signature interaction: chip fills with accent + slides
     diagonally on hover. Each property gets its own duration/curve so
     the motion feels orchestrated, not uniform. */
  transition:
    background 320ms cubic-bezier(0.16, 1, 0.3, 1),
    color 240ms ease,
    transform 360ms cubic-bezier(0.16, 1, 0.3, 1);
}
.case-arrow svg { width: 14px; height: 14px; display: block; }
button.case-card:hover .case-arrow {
  background: var(--accent);
  color: #fff;
  /* Slides up-right — the direction the arrow itself points, as if
     leading into the case study. */
  transform: translate(1.5px, -1.5px);
}

/* ============================================================
   CASE STUDY MODAL — image gallery + body + tech stack.
   Built on top of the existing .modal/.modal-backdrop pattern.
   ============================================================ */
.case-modal-backdrop {
  /* Heavier scrim than booking modal since the case content is denser.
     Small accent infusion so opening a case feels brand-tinted, not
     the generic gray-out used by every site's default modal. */
  background: color-mix(in srgb, var(--accent) 5%, color-mix(in srgb, var(--ink) 50%, transparent));
}
/* Double class for specificity 0,2,0 — beats the base `.modal` rule
   (0,1,0) which defines max-width: 440px later in the stylesheet. */
.modal.case-modal {
  max-width: min(1020px, calc(100vw - 48px));
  width: 100%;
  max-height: 88vh;
  padding: 0;
  /* Modal itself doesn't scroll — its inner .case-modal-scroll does.
     This keeps the close button (absolutely positioned on the modal)
     pinned to the visible top-right corner instead of scrolling away. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Refined entrance — same expressive curve as the card hovers, slightly
     longer duration with a touch more travel. Makes opening a case feel
     like a continuation of the card hover, not a separate animation. */
  animation: caseModalIn 420ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes caseModalIn {
  from { opacity: 0; transform: translateY(14px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
.case-modal-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* Shared "premium modal" close button — used by both the case-study
   modal and the booking modal so they look and behave identically.
   Stronger presence than the base .modal .close (solid white bg, soft
   shadow, backdrop blur) since both modals overlay content (images /
   Cal.com embed) where the close needs to read against anything.

   IMPORTANT: must use the COMPOUND class selector `.modal.case-modal`
   (specificity 0,3,0) rather than just `.case-modal` (0,2,0). The base
   `.modal .close` rule lives later in this stylesheet with specificity
   0,2,0; without the compound selector it would override these styles
   for the case modal due to source order. Booking's selector was
   already compound (.modal.booking-modal) and worked correctly. */
.modal.case-modal .close,
.modal.booking-modal .close {
  top: 14px;
  right: 14px;
  width: 30px;
  height: 30px;
  font-size: 16px;
  background: rgba(255, 255, 255, 0.95);
  border-color: var(--line-strong);
  color: var(--ink);
  box-shadow: 0 2px 10px rgba(20, 18, 58, 0.12);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 2;
}
.modal.case-modal .close:hover,
.modal.booking-modal .close:hover {
  background: rgba(255, 255, 255, 1);
  color: var(--ink);
}
/* The wrap provides the inset visual frame. The inner scrollable element
   is constrained to the wrap's content area, so scrolled images get
   clipped at the inner edge — not at the modal's outer edge. */
.case-gallery-wrap {
  padding: clamp(20px, 2.4vw, 32px);
  padding-bottom: 12px;
}
.case-gallery {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}
.case-gallery-item {
  flex: 0 0 min(78%, 500px);
  aspect-ratio: 2 / 1;
  border-radius: var(--r-md);
  scroll-snap-align: start;
  display: grid;
  place-items: end;
  padding: 18px;
  position: relative;
  overflow: hidden;
}
.case-gallery-item::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url('textures/background-texture.jpg');
  background-size: cover;
  background-position: center;
  opacity: 0.35;
  mix-blend-mode: multiply;
  filter: hue-rotate(16deg) saturate(1.3) contrast(1.15);
  pointer-events: none;
}
.case-gallery-tone-a { background: #E5E9FB; }
.case-gallery-tone-b { background: #EEF1FB; }
.case-gallery-tone-c { background: #DDE4F7; }
/* Real-image variant — neutral light bg in case the image has transparency. */
.case-gallery-item--img { background: var(--canvas-sink); }
.case-gallery-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.case-gallery-label {
  position: relative;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: var(--ls-snug);
  color: var(--ink);
  /* Subtle, transparent grey pill — reads as a glass chip on top of
     the image without competing for attention. */
  background: rgba(232, 232, 238, 0.45);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 6px 12px;
  border-radius: var(--r-pill);
}

.case-modal-body {
  padding: clamp(24px, 3vw, 40px) clamp(20px, 2.4vw, 32px) clamp(32px, 4vw, 48px);
  /* Distinct visual break from the gallery above. */
  padding-top: clamp(20px, 2.5vw, 32px);
  display: flex;
  flex-direction: column;
  /* Generous gap so byline, metric, title, story, and stack each read
     as distinct content blocks rather than a wall of text. */
  gap: 22px;
}
/* Quiet byline at the top — client name as context, not headline. */
.case-modal-byline {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: var(--ls-snug);
  color: var(--ink-muted);
}
.case-modal-title {
  font-size: var(--fs-h3);
  line-height: 1.2;
  margin: 0;
}
.case-modal-metric {
  font-size: clamp(34px, 3.4vw, 42px);
  /* Negative top margin to pull tight against the byline (they read as
     a pair: who → what stat), but the gap from the flex parent still
     gives separation from the title below. */
  margin-top: -6px;
}
.case-modal-text {
  color: var(--ink-body);
  font-size: var(--fs-body);
  line-height: var(--lh-relaxed);
}
/* Constrain body text to a comfortable reading measure on roomy viewports.
   On small screens the modal itself is narrower than this, so we don't
   need (or want) the constraint there. */
@media (min-width: 700px) {
  .case-modal-text { max-width: 60ch; }
}

/* Small-phone tuning: gallery items go full-width so each product
   screenshot dominates the carousel — no peek to the next image
   (legibility wins over the swipe-affordance pattern at small sizes).
   Label shrinks + sits closer to the corner so it stops covering UI.
   Close button bumps to 44×44 to meet the touch target. */
@media (max-width: 600px) {
  .case-gallery-item {
    flex: 0 0 100%;
    padding: 10px;
  }
  .case-gallery-label {
    font-size: 11px;
    padding: 4px 9px;
  }
  /* No close-button bump on mobile — both modals stay at the base 30×30
     size for visual consistency with the rest of the close-button
     language. Touch target is still acceptable at 30×30 since the
     button is in the corner with no adjacent tap targets. */
}

/* Highlight inside the body text — a quiet underline behind key phrases.
   Uses a faded accent so it reads as emphasis without shouting. */
.case-highlight {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  text-decoration-skip-ink: auto;
}

/* Structured story sections — "The problem / solution / outcome" labels
   read as quiet section eyebrows above each paragraph. */
.case-modal-story {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  /* Each story section (Problem / Solution / Outcome) reads as its
     own beat with deliberate space between them. */
  gap: 28px;
}
.case-modal-title + .case-modal-text { margin-top: 10px; }
.case-modal-story-label {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  color: var(--ink);
  /* Small accent dot before the label — quiet editorial marker tying
     the labels to the brand color without using a full eyebrow pill. */
  display: flex;
  align-items: center;
  gap: 8px;
}
.case-modal-story-label::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}
.case-stack {
  /* Bigger gap before the stack so it reads as a separate footer
     section, not crammed against the outcome paragraph. */
  margin-top: 18px;
  padding-top: 32px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.case-stack-label {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--ink-muted);
}
.case-stack-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* ============================================================
   FINAL CTA — stays on the SAME almost-white canvas (no navy
   band). A framed, centered panel closes the page calmly.
   ============================================================ */
.cta {
  text-align: center; display: flex; flex-direction: column; align-items: center;
  /* Asymmetric padding — top is ~1.7× bottom so the content (title,
     subtitle, button) sits in the lower half of the panel. Reads as
     visually bottom-justified, letting the loop-mark and the space
     above the title carry the panel's vertical breathing. */
  padding-top: clamp(120px, 18vh, 200px);
  padding-bottom: clamp(64px, 11vh, 128px);
  position: relative;
}
/* CTA as a rounded dark panel — matches the Stats section's deep navy
   so the page closes on a confident, branded call. Overflow stays
   visible so the textured loop mark can peek above the top edge. */
.cta-section {
  background: #1B1A38;
  color: #F5F5FB;
  border-radius: 28px;
  padding-inline: clamp(28px, 3vw, 56px);
  position: relative;
  margin-top: clamp(64px, 8vw, 110px);
}
/* Textured loop mark — sits centered on the panel's top edge, half above
   and half below. Same paper-texture treatment as the hero highlight word,
   plus a soft drop shadow so it reads as a physical, elevated object. */
.cta-mark {
  position: absolute;
  top: 0;
  left: 50%;
  width: clamp(120px, 14vw, 180px);
  height: auto;
  transform: translate(-50%, -48%);
  filter:
    hue-rotate(15deg) saturate(1.08) contrast(1.04) brightness(1.1)
    drop-shadow(0 22px 26px rgba(60, 60, 130, 0.22))
    drop-shadow(0 6px 10px rgba(60, 60, 130, 0.14));
  pointer-events: none;
  z-index: 2;
}
.cta .eyebrow { margin-bottom: 18px; }
.cta-section h2 { font-size: var(--fs-h1); max-width: 18ch; color: #F5F5FB; }
.cta-section p { margin-top: 18px; color: rgba(245, 245, 251, 0.78); font-size: var(--fs-lead); max-width: 46ch; line-height: var(--lh-relaxed); }
.cta .hero-ctas { margin-top: 32px; }
.cta-section .hero-meta { margin-top: 24px; color: rgba(245, 245, 251, 0.6); }
.cta-section .hero-meta .dot { background: #9D9CF7; }
.cta-section .hero-meta .sep { color: rgba(245, 245, 251, 0.35); }
/* Button needs to invert on the dark panel — light bg, dark text. */
.cta-section .btn-primary {
  background: #F5F5FB;
  color: #1B1A38;
  border-color: #F5F5FB;
}
.cta-section .btn-primary .arrow { background: rgba(20, 18, 58, 0.14); }
/* Keep the bg/border unchanged on hover (only the arrow chip darkens). */
.cta-section .btn-primary:hover { background: #F5F5FB; border-color: #F5F5FB; }
.cta-section .btn-primary:hover .arrow { background: rgba(20, 18, 58, 0.20); }

/* ============================================================
   FOOTER — minimal editorial close.
   Top: email (primary action) + small inline nav.
   Bottom: small wordmark "signature" with cursor reveal, copyright,
   social, and legal — all in muted hairline-level visual weight.
   ============================================================ */
.footer {
  background: var(--canvas);
  padding-top: clamp(64px, 8vw, 112px);
  padding-bottom: clamp(28px, 3vw, 44px);
  position: relative;
}

/* ---- top: left column (email + wordmark) | right column (nav) ---- */
.footer-top {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: clamp(32px, 5vw, 96px);
  align-items: start;
  padding-bottom: clamp(40px, 5vw, 64px);
}
.footer-label {
  display: block;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  color: var(--ink-muted);
  margin-bottom: 16px;
}
.footer-left { display: flex; flex-direction: column; align-items: flex-start; }
.footer-email {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font-display);
  font-weight: 600;
  /* Smaller — quieter as a footer action while still the primary click. */
  font-size: clamp(18px, 1.8vw, 24px);
  color: var(--ink);
  /* No underline on the <a> itself — it would paint under the bullet
     too. We put the underline on the email-text span instead. */
  text-decoration: none;
  line-height: 1;
}
.footer-email-bullet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Fixed em-sized square box; the SVG fills it. This way the spark is
     visually centered against the email text and rotation happens cleanly
     around its true center (Unicode glyphs wobble during rotation because
     their glyph metrics rarely match the line-box center). */
  width: 0.72em;
  height: 0.72em;
  color: var(--ink-muted);
  transform-origin: center center;
  transition: transform 600ms cubic-bezier(0.65, 0, 0.35, 1);
}
.footer-email-bullet svg {
  width: 100%;
  height: 100%;
  display: block;
}
.footer-email:hover .footer-email-bullet {
  transform: rotate(180deg);
}
.footer-email-text {
  text-decoration: underline;
  text-underline-offset: 8px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--line-strong);
  /* Underline fades out smoothly on hover (color → transparent). */
  transition: text-decoration-color 320ms var(--ease);
}
.footer-email:hover .footer-email-text { text-decoration-color: transparent; }
.footer-nav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.footer-nav .footer-label { margin-bottom: 6px; }
.footer-nav a {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-small);
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  /* Fixed gap when the dot is visible — the dot itself is layout-out
     at rest via negative margin (below), so text aligns flush left
     with the EXPLORE label and shifts right only on hover. */
  gap: 7px;
  transition: color var(--fast) var(--ease);
}
.footer-nav a::before {
  content: "";
  display: block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--ink) 28%, transparent);
  transform: scale(0);
  transform-origin: center;
  /* Negative margin (= -dot width - gap) pulls the text leftward so
     the dot effectively takes 0 layout space at rest. On hover the
     margin transitions to 0 → text slides right by 13px to make room
     for the now-visible dot. */
  margin-right: -13px;
  transition: transform 360ms cubic-bezier(0.34, 1.56, 0.64, 1),
              margin-right 360ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.footer-nav a:hover::before {
  transform: scale(1);
  margin-right: 0;
}

/* ---- bottom: hairline + slim copyright/legal row ---- */
.footer-meta {
  border-top: 1px solid var(--line);
  padding-top: clamp(20px, 2.4vw, 32px);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--ink-muted);
}
.footer-meta a {
  color: var(--ink-muted);
  transition: color var(--fast) var(--ease);
}
.footer-meta a:hover { color: var(--ink); }
.footer-legal { display: inline-flex; gap: 24px; }

/* ---- the wordmark: decent-size brand mark sitting below the email,
   with the full hero-style cursor reveal (4 layers). ---- */
.footer-wordmark {
  position: relative;
  display: inline-block;
  width: clamp(280px, 30vw, 400px);
  margin-top: clamp(40px, 5vw, 72px);
  text-decoration: none;
  /* defaults; the cursor hook overwrites these on mousemove */
  --mx: 50%;
  --my: 50%;
  --lens: 0;
}
.wordmark-layer {
  display: block;
  color: var(--ink);
}
.wordmark-layer svg {
  width: 100%;
  height: auto;
  display: block;
}
/* Footer wordmark reveal — hero pattern, scaled to the wordmark's width.
   Base is a faint ghost (like the hero's mark-base), the cursor reveal
   lights it up. */
.wordmark-base { opacity: 0.05; }
.wordmark-spot,
.wordmark-tex,
.wordmark-spec {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.wordmark-spot {
  --r: clamp(240px, 26vw, 360px);
  color: var(--accent);
  opacity: calc(0.18 * var(--lens));
  filter: blur(4px);
  -webkit-mask-image: radial-gradient(circle var(--r) at var(--mx) var(--my),
    rgba(0,0,0,1.00) 0%, rgba(0,0,0,0.92) 15%, rgba(0,0,0,0.72) 32%,
    rgba(0,0,0,0.41) 50%, rgba(0,0,0,0.17) 68%, rgba(0,0,0,0.03) 86%, transparent 100%);
          mask-image: radial-gradient(circle var(--r) at var(--mx) var(--my),
    rgba(0,0,0,1.00) 0%, rgba(0,0,0,0.92) 15%, rgba(0,0,0,0.72) 32%,
    rgba(0,0,0,0.41) 50%, rgba(0,0,0,0.17) 68%, rgba(0,0,0,0.03) 86%, transparent 100%);
}
.wordmark-spot svg :is(path, rect) { fill: url(#efl-grad-word); }

.wordmark-tex {
  --r: clamp(120px, 14vw, 200px);
  mix-blend-mode: multiply;
  opacity: calc(0.40 * var(--lens));
  filter: hue-rotate(15deg) saturate(1.05) contrast(1.05) brightness(1.15);
  -webkit-mask-image: radial-gradient(circle var(--r) at var(--mx) var(--my),
    rgba(0,0,0,1.00) 0%, rgba(0,0,0,0.92) 15%, rgba(0,0,0,0.72) 32%,
    rgba(0,0,0,0.41) 50%, rgba(0,0,0,0.17) 68%, rgba(0,0,0,0.03) 86%, transparent 100%);
          mask-image: radial-gradient(circle var(--r) at var(--mx) var(--my),
    rgba(0,0,0,1.00) 0%, rgba(0,0,0,0.92) 15%, rgba(0,0,0,0.72) 32%,
    rgba(0,0,0,0.41) 50%, rgba(0,0,0,0.17) 68%, rgba(0,0,0,0.03) 86%, transparent 100%);
}

.wordmark-spec {
  --r: clamp(160px, 18vw, 260px);
  color: #B3A0FF;
  opacity: calc(0.18 * var(--lens));
  filter: blur(1px);
  -webkit-mask-image: radial-gradient(circle var(--r) at var(--mx) var(--my),
    rgba(0,0,0,1.00) 0%, rgba(0,0,0,0.92) 15%, rgba(0,0,0,0.72) 32%,
    rgba(0,0,0,0.41) 50%, rgba(0,0,0,0.17) 68%, rgba(0,0,0,0.03) 86%, transparent 100%);
          mask-image: radial-gradient(circle var(--r) at var(--mx) var(--my),
    rgba(0,0,0,1.00) 0%, rgba(0,0,0,0.92) 15%, rgba(0,0,0,0.72) 32%,
    rgba(0,0,0,0.41) 50%, rgba(0,0,0,0.17) 68%, rgba(0,0,0,0.03) 86%, transparent 100%);
}
.wordmark-spec svg :is(path, rect) { fill: url(#efl-grad-word); }

@media (max-width: 760px) {
  .footer-top { grid-template-columns: 1fr; }
  .footer-meta { gap: 16px; }
  .footer-meta-right { flex-wrap: wrap; }
}

/* ============================================================
   BOOKING MODAL
   ============================================================ */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: color-mix(in srgb, var(--ink) 32%, transparent);
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  display: flex; align-items: center; justify-content: center; padding: 24px;
  animation: fade 200ms var(--ease);
}
.modal {
  background: var(--canvas); border: 1px solid var(--line-strong); border-radius: var(--r-md);
  width: 100%; max-width: 440px; padding: 28px; position: relative;
  box-shadow: 0 30px 70px rgba(var(--rule), 0.16);
  animation: pop 260ms var(--ease);
}
.modal .close {
  position: absolute; top: 16px; right: 16px; width: 30px; height: 30px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-muted); cursor: pointer;
  font-size: 17px; line-height: 1; display: flex; align-items: center; justify-content: center;
}
.modal .close:hover { color: var(--ink); border-color: var(--ink-muted); }

/* Booking modal — sized for the Cal.com inline embed (calendar grid +
   time slots need significant height). Double class beats the base
   .modal max-width: 440px and padding. */
.modal.booking-modal {
  max-width: min(1100px, calc(100vw - 32px));
  width: 100%;
  height: 88vh;
  max-height: 88vh;
  /* Breathing room around the Cal embed so it doesn't sit flush
     against the modal's rounded corners. Padding scales with viewport
     — tighter on phone, more generous on desktop. */
  padding: clamp(14px, 1.6vw, 20px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* Container the Cal embed mounts into. Allows vertical scroll so when
   Cal switches to its tall mobile layout (event details above the
   calendar, then time slots below), the whole stack can scroll
   inside the modal instead of being clipped. -webkit-overflow-scrolling
   keeps the inertial-scroll behavior on older iOS. */
.booking-modal-cal {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}
.modal h3 { font-size: var(--fs-h3); }
.modal p { margin-top: 8px; color: var(--ink-body); font-size: var(--fs-small); }
.slots { margin-top: 18px; display: grid; gap: 8px; }
.slot {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  padding: 13px 16px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
  font-family: var(--font-display); font-weight: 500; font-size: var(--fs-small); color: var(--ink);
  cursor: pointer; transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.slot:hover { border-color: var(--accent); background: var(--accent-tint); }
.slot .time { color: var(--ink-muted); }

/* ============================================================
   TOPICAL TEXTURES
   ============================================================ */
.texture-dots {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* A delicate 1px dot grid using the structural hairline color */
  background-image: radial-gradient(var(--line-strong) 1px, transparent 1px);
  background-size: 24px 24px;
  /* Mask it out at the edges so it softly blooms from the center without harsh bounds */
  -webkit-mask-image: radial-gradient(ellipse at center, black 0%, transparent 70%);
          mask-image: radial-gradient(ellipse at center, black 0%, transparent 70%);
  opacity: 0.8;
}

/* ============================================================
   ENTRANCE / REVEAL
   ============================================================ */
/* Reveal is a purely additive enhancement and NEVER hides content: the
   entrance animates transform only (a subtle slide-up), so even if animation
   time is frozen or JS doesn't run, every section stays fully visible. */
.reveal.in { animation: revealIn var(--slow) var(--ease) both; }
@keyframes revealIn {
  from { transform: translateY(13px); }
  to   { transform: translateY(0); }
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes pop { from { opacity: 0; transform: translateY(8px) scale(0.985); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .reveal.in { animation: none !important; }
  * { scroll-behavior: auto !important; }
}
