/* ==========================================================================
   Bluetik Social — Design tokens
   ========================================================================== */
:root{
  --bg-navy: #030210;
  --bg-navy-soft: #0a0820;
  --card-dark: #2c2b36;
  --accent-blue: #2563EB; /* "Deep Blue" — passes AA on white (5.17:1); on navy for
                             small text it's AA-large-only (3.98:1), see commit note */
  --accent-blue-on-dark: #3B82F6; /* one step lighter in the same blue ramp (Tailwind
                             blue-500, --accent-blue is blue-600) — passes AA for small
                             text on the solid --bg-navy fill (5.6:1). Use this for small
                             text that sits directly on a solid dark/navy background:
                             .site-footer__col-heading, .belief__lead. */
  --accent-blue-on-video: #60A5FA; /* Tailwind blue-400 — one step lighter again.
                             --accent-blue-on-dark measures only ~4.1-4.4:1 against the
                             hero's actual video+overlay backdrop (sampled across the
                             full loop, not the flat navy fill), which fails the 4.5:1
                             AA minimum. This tint measures 5.98-6.40:1 there instead.
                             Use for .hero__greeting only. */
  --white: #ffffff;
  --ink: #121026;
  --muted: #6b7280;
  --muted-2: #8d8fa3;
  --muted-on-dark: #a9a7c4;
  --border-on-dark: rgba(255,255,255,0.10);
  --border-on-light: rgba(18,16,38,0.08);

  --radius-card: 20px;
  --radius-lg: 24px;
  --radius-pill: 999px;

  --font-display: 'Bricolage Grotesque', 'Segoe UI', sans-serif;
  --font-body: 'Inter', 'Segoe UI', sans-serif;
  --font-h1: 'Space Grotesk', 'Segoe UI', sans-serif;
  --font-serif: 'Fraunces', Georgia, serif;
  --font-mono: 'JetBrains Mono', 'Courier New', monospace;

  /* Typographic scale — shared tokens for consistent section heading sizing */
  --text-h2: clamp(32px, 4vw, 48px);
  --text-h3: clamp(22px, 3vw, 32px);

  --header-h: 60px;
  --container-w: 1280px;

  --ease-premium: cubic-bezier(.16,.84,.44,1);
}

@media (min-width: 768px){
  :root{ --header-h: 72px; }
}

/* ==========================================================================
   Reset
   ========================================================================== */
*, *::before, *::after{ box-sizing: border-box; }
html{
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior: auto; }
}
body{
  margin: 0;
  background: var(--white);
  color: var(--ink);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, svg{ display:block; max-width: 100%; }
a{ color: inherit; text-decoration: none; }
button{ font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul{ margin: 0; padding: 0; list-style: none; }
h1,h2,h3,p{ margin: 0; }
.container{
  width: 100%;
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
}
section[id]{
  scroll-margin-top: var(--header-h);
}
:focus-visible{
  outline: 2px solid var(--accent-blue);
  outline-offset: 3px;
  border-radius: 4px;
}

.eyebrow{
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-blue);
}
/* .eyebrow's default --accent-blue only passes AA (5.17:1) on white/light
   section backgrounds (.service-split, .service-included, .service-process,
   .service-platforms). Sections with a solid --bg-navy fill need the
   lighter --accent-blue-on-dark token instead (3.98:1 -> 5.6:1) — see the
   scoped overrides on .service-hero, .service-results, and .faq below. */
/* Houdini custom property — enables conic-gradient angle animation */
@property --pill-btn-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.pill-btn{
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border-radius: var(--radius-pill);
  background: var(--accent-blue);
  color: var(--bg-navy);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  padding: 16px 34px;
  position: relative;
  isolation: isolate;
  transition: transform .25s var(--ease-premium), background-color .25s ease, box-shadow .25s ease;
  box-shadow: 0 8px 24px rgba(37,99,235,0.35);
}
.pill-btn:hover{
  background: var(--accent-blue);
  transform: translateY(-2px);
  box-shadow: 0 12px 30px rgba(37,99,235,0.45);
}

/* Outer soft glow that bleeds beyond the button edge */
.pill-btn:not(.pill-btn--outline)::before{
  content: '';
  position: absolute;
  inset: -5px;
  border-radius: var(--radius-pill);
  background: conic-gradient(from var(--pill-btn-angle),
    transparent 0deg,
    transparent 225deg,
    rgba(37,99,235,0.45) 260deg,
    rgba(140,176,255,0.75) 285deg,
    rgba(37,99,235,0.45) 315deg,
    transparent 360deg
  );
  filter: blur(10px);
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
  z-index: -1;
}
/* Crisp ring masked to the border area only */
.pill-btn:not(.pill-btn--outline)::after{
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: var(--radius-pill);
  padding: 2px;
  background: conic-gradient(from var(--pill-btn-angle),
    transparent 0deg,
    transparent 225deg,
    rgba(255,255,255,0.55) 260deg,
    rgba(255,255,255,0.95) 285deg,
    rgba(255,255,255,0.55) 315deg,
    transparent 360deg
  );
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
  z-index: 1;
}
.pill-btn:not(.pill-btn--outline):hover::before,
.pill-btn:not(.pill-btn--outline):focus-visible::before,
.pill-btn:not(.pill-btn--outline):hover::after,
.pill-btn:not(.pill-btn--outline):focus-visible::after{
  opacity: 1;
  animation: pill-btn-ring-spin 2s linear infinite;
}
@keyframes pill-btn-ring-spin{
  to { --pill-btn-angle: 360deg; }
}
@media (prefers-reduced-motion: reduce){
  .pill-btn:not(.pill-btn--outline):hover::before,
  .pill-btn:not(.pill-btn--outline):focus-visible::before{
    animation: none;
    opacity: 0.4;
    background: rgba(160,190,255,0.3);
  }
  .pill-btn:not(.pill-btn--outline):hover::after,
  .pill-btn:not(.pill-btn--outline):focus-visible::after{
    animation: none;
    opacity: 0;
  }
}

/* ambient glow-wave backdrop used on dark sections */
.glow-wave{
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
}
.glow-wave span{
  position: absolute;
  border-radius: 50%;
  filter: blur(100px);
  opacity: 0.15;
  animation: glowDrift 18s ease-in-out infinite;
}
.glow-wave span:nth-child(1){
  width: 420px; height: 420px;
  background: var(--accent-blue);
  top: -10%; left: -8%;
  animation-duration: 22s;
}
.glow-wave span:nth-child(2){
  width: 360px; height: 360px;
  background: var(--accent-blue);
  bottom: -15%; right: -5%;
  animation-duration: 26s;
  animation-delay: -6s;
}
.glow-wave span:nth-child(3){
  width: 280px; height: 280px;
  background: var(--accent-blue);
  top: 35%; right: 20%;
  animation-duration: 20s;
  animation-delay: -12s;
}
@keyframes glowDrift{
  0%, 100%{ transform: translate(0,0) scale(1); }
  33%{ transform: translate(30px,-24px) scale(1.08); }
  66%{ transform: translate(-24px,20px) scale(0.96); }
}
@media (prefers-reduced-motion: reduce){
  .glow-wave span{ animation: none; }
}
/* Mobile performance: each span pairs an infinite, always-running
   transform animation with a static filter:blur(100px) — the animation
   itself is fine (transform-only), but the 100px blur radius means the
   browser recomputes that blurred bitmap every frame for as long as the
   hero/stories section is in view, on hardware that's typically weaker
   than desktop. Stopping the animation leaves the glow visible (same
   colour/opacity/position, just not drifting) while eliminating the
   continuous per-frame recompute cost — same technique already used
   above for prefers-reduced-motion, just extended to mobile generally. */
@media (max-width: 768px){
  .glow-wave span{ animation: none; }
}

/* ==========================================================================
   Nav
   ========================================================================== */
.nav{
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  height: var(--header-h);
  background: transparent;
  /* backdrop-filter is deliberately NOT in this list (see commit note):
     animating it forces the browser to recompute the blur every
     composited frame for the full 350ms, exactly while the user is
     mid-scroll — the scroll-jank culprit. background-color/box-shadow
     are cheap to animate and still carry the same fade-in feel; the
     blur itself just switches on/off instantly instead of ramping. */
  transition: background-color .35s ease, box-shadow .35s ease;
}
.nav.is-scrolled{
  background: rgba(3,2,16,0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 1px 0 var(--border-on-dark);
}
.nav__inner{
  height: 100%;
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
}
.nav__logo{
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.01em;
  color: var(--white);
  white-space: nowrap;
  flex: none;
}
.nav__links{
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  flex: none;
  width: max-content;
  gap: 1px;
}
.nav__link{
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 8.6px;
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--white);
  white-space: nowrap;
  padding: 8px 3px;
  border-radius: var(--radius-pill);
  transition: color .2s ease, background-color .2s ease;
}
.nav__link:hover{ color: var(--accent-blue); }

.nav__dropdown{ position: relative; flex: none; }
.nav__dropdown-trigger{
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 8.6px;
  letter-spacing: -0.01em;
  color: var(--white);
  white-space: nowrap;
  padding: 8px 3px;
  border-radius: var(--radius-pill);
}
.nav__caret{
  width: 8px; height: 8px;
  flex: none;
  transition: transform .25s var(--ease-premium);
}
.nav__dropdown[data-open="true"] .nav__caret{ transform: rotate(180deg); }
.nav__dropdown[data-open="true"] .nav__dropdown-trigger{ color: var(--accent-blue); }

.nav__dropdown-menu{
  position: absolute;
  top: calc(100% + 12px);
  right: 0;
  min-width: 220px;
  background: var(--card-dark);
  border: 1px solid var(--border-on-dark);
  border-radius: 16px;
  padding: 8px;
  box-shadow: 0 20px 50px rgba(0,0,0,0.45);
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity .2s ease, transform .2s var(--ease-premium);
}
.nav__dropdown[data-open="true"] .nav__dropdown-menu{
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.nav__dropdown-menu a{
  display: block;
  font-size: 14px;
  font-weight: 500;
  color: var(--white);
  padding: 10px 12px;
  border-radius: 10px;
  white-space: nowrap;
  transition: background-color .2s ease, color .2s ease;
}
.nav__dropdown-menu a:hover{
  background: rgba(37,99,235,0.16);
  color: var(--accent-blue);
}

@media (min-width: 768px){
  .nav__inner{ padding-inline: 28px; gap: 16px; }
  .nav__logo{ font-size: 18px; }
  .nav__links{ gap: 14px; }
  .nav__link, .nav__dropdown-trigger{ font-size: 13.5px; padding: 10px 8px; }
  .nav__caret{ width: 10px; height: 10px; }
}
@media (min-width: 1280px){
  .nav__inner{ padding-inline: 40px; gap: 28px; }
  .nav__logo{ font-size: 21px; letter-spacing: 0.06em; }
  .nav__links{ gap: 26px; }
  .nav__link, .nav__dropdown-trigger{ font-size: 15px; padding: 10px; }
}

/* Mobile nav: hamburger + slide-in panel. Below 768px the always-on-one-
   line row above shrinks nav text to ~8.6px to avoid overflowing — real-
   browser measurement (375/414/768px) confirmed via puppeteer showed no
   horizontal page overflow, but text that small fails basic legibility,
   tap targets land well under the ~44px accessibility minimum, and the
   Services dropdown-menu (position:absolute; right:0) clips ~19px off
   the left edge of a 375px viewport. Hence a real hamburger + panel here,
   not just a CSS tweak — see launch-audit item #12. Reuses the existing
   [data-dropdown]/[data-dropdown-trigger] click-toggle JS as-is for the
   Services/IT Services accordions; only the CSS target of that same
   data-open state changes below (absolute overlay -> static accordion). */
.nav__toggle{
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  background: transparent;
  flex: none;
  cursor: pointer;
}
.nav__toggle-bar{
  display: block;
  width: 22px;
  height: 2px;
  border-radius: 2px;
  background: var(--white);
  transition: transform .3s var(--ease-premium), opacity .2s ease;
}
.nav[data-mobile-open="true"] .nav__toggle-bar:nth-child(1){ transform: translateY(7px) rotate(45deg); }
.nav[data-mobile-open="true"] .nav__toggle-bar:nth-child(2){ opacity: 0; }
.nav[data-mobile-open="true"] .nav__toggle-bar:nth-child(3){ transform: translateY(-7px) rotate(-45deg); }

.nav__scrim{
  position: fixed;
  inset: var(--header-h) 0 0 0;
  background: rgba(3,2,16,0.55);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s ease;
}
.nav[data-mobile-open="true"] .nav__scrim{ opacity: 1; pointer-events: auto; }
/* Promotes the scrim to its own compositor layer for the fade's duration
   only (class added/removed by JS around the transition) — backdrop-filter
   otherwise makes that opacity transition expensive to recomposite on
   real mobile GPUs. Not left on permanently to avoid the memory overhead
   of a standing compositor layer. */
.nav__scrim--animating{ will-change: opacity; }

@media (max-width: 767px){
  .nav__toggle{ display: flex; }

  /* The header row itself is transparent until .is-scrolled (12px)
     applies, and only 82% opaque even then — so opening the panel at the
     top of the page (or over a bright hero) let content show through
     behind the BLUETIK logo/close button. Force it fully opaque whenever
     the mobile panel is open, independent of scroll position. */
  .nav[data-mobile-open="true"]{ background: rgba(3,2,16,0.98); }

  /* position:fixed (top set inline by JS to the negative scroll offset)
     is what actually keeps the panel/scrim correctly pinned to the
     visible viewport on mobile WebKit/Blink; overflow:hidden alone lets
     them mis-render relative to a scrolled document. width:100% keeps
     body from collapsing once it leaves normal flow. The toggle button
     that sets this class is only present below this same breakpoint. */
  body.nav-open-lock{
    position: fixed;
    overflow: hidden;
    width: 100%;
  }

  .nav__links{
    position: fixed;
    top: var(--header-h);
    right: 0;
    bottom: 0;
    flex-direction: column;
    align-items: stretch;
    width: min(320px, 82vw);
    gap: 2px;
    background: rgba(10,8,32,0.98);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-left: 1px solid var(--border-on-dark);
    box-shadow: -20px 0 50px rgba(0,0,0,0.4);
    padding: 20px 16px;
    overflow-y: auto;
    transform: translateX(100%);
    transition: transform .35s var(--ease-premium);
  }
  .nav[data-mobile-open="true"] .nav__links{ transform: translateX(0); }
  /* Same reasoning as .nav__scrim--animating above: the panel's own
     backdrop-filter blur(20px) is the main GPU cost during the slide, so
     give the compositor a standing layer only while it's actually moving. */
  .nav__links--animating{ will-change: transform; }

  .nav__link, .nav__dropdown-trigger{
    width: 100%;
    font-size: 16px;
    padding: 14px 12px;
    justify-content: space-between;
  }
  .nav__dropdown{ width: 100%; }
  .nav__caret{ width: 12px; height: 12px; }

  /* Same data-open state the desktop overlay uses, restyled as an
     in-place accordion instead of an absolute-positioned popover. */
  .nav__dropdown-menu{
    position: static;
    min-width: 0;
    opacity: 1;
    transform: none;
    pointer-events: auto;
    background: rgba(255,255,255,0.04);
    border: none;
    box-shadow: none;
    padding: 0;
    margin-bottom: 4px;
    max-height: 0;
    overflow: hidden;
    transition: max-height .3s var(--ease-premium), padding .3s ease;
  }
  .nav__dropdown[data-open="true"] .nav__dropdown-menu{
    max-height: 480px;
    padding: 4px 0 10px 14px;
  }
  .nav__dropdown-menu a{ font-size: 14.5px; padding: 10px 8px; }
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero{
  position: relative;
  background: var(--bg-navy); /* fallback if video cannot load */
  padding-top: calc(var(--header-h) + 48px);
  padding-bottom: 56px;
  overflow: hidden;
  min-height: 92vh;
}
/* Full-bleed video background */
.hero__bg{
  position: absolute;
  inset: 0;
  z-index: 0;
}
.hero__bg-video{
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.hero__overlay{
  position: absolute;
  inset: 0;
  background:
    linear-gradient(160deg, rgba(10,14,35,0.82) 0%, rgba(10,14,35,0.60) 55%, rgba(10,14,35,0.45) 100%),
    radial-gradient(ellipse 80% 55% at 15% 0%, rgba(37,99,235,0.09), transparent 60%);
}
.hero__inner{
  position: relative;
  z-index: 2;
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 32px;
}
.hero__text{
  flex: 1 1 0;
  min-width: 0;
}
.hero__tags{
  display: none; /* shown at desktop */
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 20px;
}
.hero__tag{
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(37,99,235,0.4);
  color: var(--accent-blue);
  background: rgba(37,99,235,0.08);
}
.hero__greeting{
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-blue-on-video);
  margin-bottom: 14px;
}
.hero__title{
  font-family: var(--font-h1);
  font-weight: 700;
  font-size: clamp(36px, 9vw, 48px);
  line-height: 1.06;
  letter-spacing: -0.03em;
  color: var(--white);
}
.hero__highlight{
  text-decoration: underline;
  text-decoration-color: var(--accent-blue);
  text-underline-offset: 6px;
  text-decoration-thickness: 3px;
}
.hero__subtext{
  margin-top: 18px;
  font-size: 18px;
  line-height: 1.6;
  color: var(--muted-on-dark);
  max-width: 46ch;
  text-align: center; /* was already computing to center via inheritance
                          from .hero__inner{text-align:center} (verified:
                          no rule anywhere overrode it back to left) — set
                          explicitly here too so this element's alignment
                          doesn't depend on staying inside that ancestor. */
}
.hero__ctas{
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 28px;
}
.pill-btn--outline{
  background: transparent;
  border: 2px solid rgba(255,255,255,0.3);
  color: var(--white);
}
.pill-btn--outline:hover{
  background: transparent;
  border-color: var(--accent-blue);
  color: var(--accent-blue);
}
@media (min-width: 768px){
  .hero{ padding-top: calc(var(--header-h) + 72px); padding-bottom: 80px; }
  .hero__title{ font-size: clamp(52px, 7vw, 68px); }
  .hero__subtext{ font-size: 20px; }
}
@media (min-width: 900px){
  .hero__tags{ display: flex; }
  .hero__subtext{ max-width: 56ch; }
}
@media (min-width: 1280px){
  .hero{ padding-top: calc(var(--header-h) + 120px); padding-bottom: 140px; }
  .hero__title{ font-size: 76px; }
}

/* ==========================================================================
   Trusted-by marquee
   ========================================================================== */
.trusted{
  background: var(--bg-navy-soft);
  padding-block: 72px;
}
.trusted__label{
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: var(--muted-on-dark);
  margin-bottom: 36px;
}
.marquee{
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee__track{
  display: flex;
  align-items: center;
  gap: 28px;
  width: max-content;
  animation: marqueeScroll 26s linear infinite;
}
.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track{
  animation-play-state: paused;
}
@keyframes marqueeScroll{
  from{ transform: translateX(0); }
  to{ transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce){
  .marquee__track{ animation: none; flex-wrap: wrap; justify-content: center; }
}
/* Glassmorphic rounded-square logo card. */
.logo-card{
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 168px;
  height: 168px;
  padding: 28px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-on-dark);
  background: rgba(255,255,255,0.05);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow: 0 16px 32px rgba(0,0,0,0.35);
}
/* Vandni and Pillai's Perspective are genuinely transparent
   PNGs (verified via alpha-channel inspection — not a baked-in opaque
   background), but the ink itself is drawn black/near-black, which
   disappears against dark glass. A flat rgba(255,255,255,0.88) fill
   fixed legibility but read as a mismatched solid gray box next to the
   other cards' frosted-glass depth, so this is a *light* glass pane
   instead — same gradient/inner-highlight/shadow language as the dark
   cards, just inverted toward white, so it reads as a deliberate glass
   variant rather than a leftover chip. */
.logo-card--light{
  background: linear-gradient(155deg, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.82) 100%);
  border-color: rgba(255,255,255,0.55);
  box-shadow: 0 16px 32px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.6);
}
/* max-height raised from 56px to 80px (+43%) so every mark in the row
   — Pillai's Perspective and Purayidom included — reads larger and
   more legible; applies uniformly to all logos since there's no
   per-breakpoint override on this rule (the card itself grows at
   768px+, 168px->188px, so 80px has even more headroom there — see
   the fit check in the following comment). */
.logo-card__img{
  max-width: 100%;
  max-height: 80px;
  width: auto;
  height: auto;
  object-fit: contain;
}
/* Commentary Box's source file has more internal transparent padding than
   the other marks, so its visible mark reads smaller at the same box
   height — bump just this logo up (~25%) to look proportionate. Scaled
   up from 70px to 100px to preserve that same 1.25x ratio against the
   new 80px base rather than leaving it at a stale absolute value. */
.logo-card__img--lg{
  max-height: 100px;
}
/* HP World Sysmantech's mark is an extreme-wide horizontal lockup
   (~6.6:1) — inside a strict square card it shrinks to an illegibly
   thin sliver. Give just this card a wider (not square) footprint at
   the same height so its wordmark renders at a readable size. */
.logo-card--wide{
  width: 260px;
}
@media (min-width: 768px){
  .logo-card{ width: 188px; height: 188px; padding: 32px; }
  .logo-card--wide{ width: 320px; }
}

/* Three of the six marks (Zenora, Pillai's Perspective, Purayidom
   Realtors) are #id links that smooth-scroll down to their matching
   card in Success Stories (see the story-card ids + logo-card hrefs in
   index.html), so only those cards — not Commentary Box, Vandni or HP
   World Sysmantech — get a pointer cursor and a hover glow to signal
   that. Glow color matches the site's other
   accent-blue hover/breathing treatments (.pill-btn, ctaBreath below);
   each variant restates its own base box-shadow plus the glow layer
   since box-shadow can't be additively composed across two rules. */
.logo-card--link{
  cursor: pointer;
  transition: transform 0.3s var(--ease-premium), box-shadow 0.3s var(--ease-premium);
}
.logo-card--link:hover,
.logo-card--link:focus-visible{
  transform: scale(1.05);
  box-shadow: 0 16px 32px rgba(0,0,0,0.35), 0 0 24px rgba(37,99,235,0.35);
}
.logo-card--link.logo-card--light:hover,
.logo-card--link.logo-card--light:focus-visible{
  box-shadow: 0 16px 32px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.6), 0 0 24px rgba(37,99,235,0.35);
}

/* ==========================================================================
   Success stories carousel
   ========================================================================== */
.stories{
  position: relative;
  background: var(--bg-navy);
  padding-block: 72px;
  overflow: hidden;
}
.stories__header{
  position: relative;
  z-index: 2;
  max-width: 640px;
}
.stories__heading{
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-h2);
  color: var(--white);
  line-height: 1.15;
}
.stories__subtext{
  margin-top: 14px;
  font-size: 18px;
  line-height: 1.6;
  color: var(--muted-on-dark);
}
/* ==========================================================================
   Success-story cards — horizontal scrolling carousel/gallery. Cards
   are fixed-width (flex-basis percentage per breakpoint, so ~4-5 sit
   side-by-side at 1280px+, ~3 at tablet, ~2-3 at mobile) inside a
   native overflow-x scroll container — no JS needed for the scroll
   itself (touch drag and trackpad horizontal swipe work natively);
   see js/main.js initStoriesWheelScroll for the one native gap this
   fills (plain vertical mouse-wheel does not natively pan a horizontal
   overflow container in Chrome/Safari).

   This replaces the earlier hover-expand accordion (flex-grow widening
   the hovered card, compressing siblings) — fixed-width cards in an
   already-overflowing row have no positive free space for flex-grow to
   redistribute, so that width-change mechanic could not coexist with a
   scrolling gallery and was removed. Every other hover effect on the
   card is unchanged: photo zoom, scrim deepen, tag-pill reveal, name
   font-size/wrap change, description reveal, CTA-text reveal, and the
   arrow's color/rotate — none of those depend on the card's own width,
   so they still fire on hover/focus exactly as before.

   Card order is unchanged (still no reordering — see the original
   redesign commit for why). Reference's heart/favorite icon is still
   the repositioned tag pill (no duplicate favorite affordance invented
   for a site with no such concept). Scroll-snap-align:center on cards
   per the task's own suggestion, for a settled/polished stop point
   rather than leaving cards stopped at arbitrary mid-scroll positions. */
.stories__row{
  position: relative;
  z-index: 2;
  margin-top: 40px;
  display: flex;
  gap: 12px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
  scrollbar-width: none; /* matches this site's existing carousel convention
                             (the original pre-redesign carousel also hid its
                             scrollbar) — snap points + a peeking next card
                             provide the "this scrolls" affordance instead */
}
.stories__row::-webkit-scrollbar{ display: none; }
@media (prefers-reduced-motion: reduce){
  .stories__row{ scroll-behavior: auto; }
}

/* Each card is a single <a> — the whole tile is the click target (a
   larger, easier target than the old text-only "Read the case study →"
   link), so no nested anchor for the arrow/CTA. Fixed-width (grow:0,
   shrink:0) rather than the old flex:1 1 0, since a scroll gallery's
   cards need a stable size the browser won't compress to fit. */
.story-card{
  position: relative;
  display: block;
  flex: 0 0 40%; /* ~2.3 cards visible at 375px, proportional (not a fixed
                     px width) so it scales with viewport within this
                     breakpoint band; see the 768px/1280px overrides below
                     for the ~3 and ~4.5 visible targets at those sizes */
  scroll-snap-align: center;
  /* Three cards (Zenora, Pillai's Perspective, Purayidom Realtors) are
     #id fragment targets from the Trusted By logos — this keeps the
     fixed nav from covering a card's top edge on the vertical jump to
     .stories, same as section[id] above. Harmless on the other cards:
     it only matters if the card is itself the scroll destination. */
  scroll-margin-top: var(--header-h);
  border-radius: var(--radius-card);
  overflow: hidden;
  background: linear-gradient(160deg, var(--card-dark) 0%, var(--bg-navy) 100%);
}

/* Full-bleed photo layer. background-image is set as an inline style
   directly on this element (style="background-image:url('assets/
   case-study/<slug>.<ext>')") — NOT via a CSS custom property, because
   a var()'s url() would resolve relative to this stylesheet's own
   location rather than index.html's (verified: that indirection
   produced 404s for css/assets/case-study/... instead of
   assets/case-study/...). A card without a real photo would simply
   have no inline style here, so background-image stays at its initial
   `none` and the card's own dark gradient (below, on .story-card)
   shows through instead of a broken-image icon — all 8 cards currently
   have a real photo. To swap one: replace the file at
   assets/case-study/<slug>.<ext> and update the style attribute on
   that card's <span class="story-card__photo"> in index.html — no
   CSS/JS change needed. */
.story-card__photo{
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  transition: transform .5s ease;
}
.story-card:hover .story-card__photo,
.story-card:focus .story-card__photo{
  transform: scale(1.06);
}

/* Bottom-up dark scrim, tall enough that revealed text stays legible
   even over a bright source photo (Pillai's Perspective's photo has a
   near-white background) — deepens further on hover since expand adds
   more text on top of it. Tint uses --bg-navy's own RGB (3,2,16) rather
   than a generic black, so it reads as this site's dark navy, not the
   reference's teal. */
.story-card__scrim{
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(3,2,16,0) 0%,
    rgba(3,2,16,0.25) 45%,
    rgba(3,2,16,0.82) 75%,
    rgba(3,2,16,0.95) 100%);
  transition: background .4s ease;
}
.story-card:hover .story-card__scrim,
.story-card:focus .story-card__scrim{
  background: linear-gradient(180deg,
    rgba(3,2,16,0.05) 0%,
    rgba(3,2,16,0.45) 35%,
    rgba(3,2,16,0.92) 65%,
    rgba(3,2,16,0.97) 100%);
}

/* Heart-icon replacement: the existing tag-pill styling, repositioned
   top-right and hidden until hover/expand (it is not shown at rest, so
   it never appears twice on an expanded card). */
.story-card__tag{
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-blue);
  background: rgba(37,99,235,0.16);
  padding: 6px 12px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity .3s ease, transform .3s ease;
  pointer-events: none;
}
.story-card:hover .story-card__tag,
.story-card:focus .story-card__tag{
  opacity: 1;
  transform: translateY(0);
}

.story-card__content{
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  padding: 16px;
  padding-right: 52px; /* reserve space for the circular arrow button */
  padding-bottom: 46px; /* clear the .story-card__cta row, which is
                            absolutely positioned independently of this
                            block — without this, a 2-line description's
                            last line overlaps "Read case study" */
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* At rest, narrow compressed cards (as little as ~84px) can't wrap a
   name like "Purayidom Realtors" or "Avenir Aviation Academy" without
   breaking mid-word into an unreadable single-letter-per-line column —
   confirmed visually via screenshot, not assumed — so rest state clips
   to one line with an ellipsis instead. Hover/expand has room, so it
   switches back to normal wrapping there. */
.story-card__name{
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--white);
  font-size: 14px;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: font-size .35s ease;
}
.story-card:hover .story-card__name,
.story-card:focus .story-card__name{
  font-size: 19px;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: break-word;
}
/* Description: hidden at rest, revealed on hover/expand. Copy is reused
   verbatim from the existing, already-approved case-study text — no new
   copy written for this redesign. */
.story-card__desc{
  margin: 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--muted-on-dark);
  overflow-wrap: break-word;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height .4s ease, opacity .3s ease;
}
.story-card:hover .story-card__desc,
.story-card:focus .story-card__desc{
  max-height: 140px;
  opacity: 1;
}

/* Rest-state affordance: circular arrow only (reusing the same arrow
   glyph as the hero's outline CTA, for sitewide consistency). On hover,
   the "Read case study" text grows in beside it rather than the arrow
   being the only cue — the first of the two options the task offered. */
.story-card__cta{
  position: absolute;
  right: 14px;
  bottom: 14px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 8px;
}
.story-card__cta-text{
  font-size: 12px;
  font-weight: 600;
  color: var(--white);
  white-space: nowrap;
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-width .35s ease, opacity .3s ease;
}
.story-card:hover .story-card__cta-text,
.story-card:focus .story-card__cta-text{
  max-width: 140px;
  opacity: 1;
}
.story-card__arrow{
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(255,255,255,0.12);
  border: 1px solid var(--border-on-dark);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--white);
  transition: background .25s ease, transform .3s ease;
}
.story-card:hover .story-card__arrow,
.story-card:focus .story-card__arrow{
  background: var(--accent-blue);
  transform: rotate(45deg);
}

@media (prefers-reduced-motion: reduce){
  .story-card,
  .story-card__photo,
  .story-card__scrim,
  .story-card__tag,
  .story-card__name,
  .story-card__desc,
  .story-card__cta-text,
  .story-card__arrow{
    transition: none !important;
  }
}

/* Mobile: hover doesn't exist on touch, so the tag pill stays
   unconditionally visible (same "make hover-only content reachable
   without hover" approach used sitewide, e.g. reduced-motion forcing
   revealed states open) — carried over from the previous vertical-stack
   design.

   The description and CTA-text are deliberately NOT forced open here,
   unlike that earlier design: at this card width (~150px, ~100px of
   actual text column after padding) two things were tried and screen-
   shotted, not assumed —
     1. unclamped: wrapped into a very tall, choppy single-narrow-column
        AND its height overflowed up into the top-right tag pill (the
        two are independently absolutely-positioned, so nothing else
        stopped the collision);
     2. line-clamp(3): fixed the overflow/collision, but ~100px is too
        narrow for 3 lines of body text to read as more than a stack of
        short broken fragments — clamping made it valid, not legible.
   Since the whole card is already a single <a> straight to the full
   case study, the description stays reachable at one tap rather than
   forced into an illegible inline sliver. Name stays visible (primary
   identifying info, and short enough at 15px to read cleanly wrapped
   in ~100px) and also gets slightly smaller than the old full-width
   stacked design's 18px, so it wraps to 2-3 lines instead of 4-5. */
@media (max-width: 767px){
  .story-card{ height: 300px; }
  .story-card__tag{
    opacity: 1;
    transform: none;
    /* Desktop pins this to `right:14px` only and lets it grow leftward
       to fit the nowrap text — fine at 400px+ card widths, but at the
       ~134px mobile card width the longer tags ("Aviation Education",
       "AI Sales Assistant", "Retail Automation", "Furniture & Retail")
       are wider than the card itself, so the left end lands outside
       the card's padding box and .story-card's overflow:hidden clips
       it. Pinning `left` too bounds the pill to the card's actual
       available width and wraps the nowrap text into it instead of
       letting it run off the edge — same padding/background/font.

       white-space:normal alone wraps at word boundaries, but a single
       word can still be wider than the ~82px a wrapped line has to work
       with here — "EDUCATION"/"ASSISTANT" are right at that edge, and
       run wide enough in the real webfont (vs. this environment's
       load-blocked fallback font, checked separately) to overflow their
       own line and get clipped by the card again, one word later.
       overflow-wrap forces a mid-word break as a last resort so a line
       can never exceed the box, and the slightly tighter side padding
       (12px -> 9px, mobile only) buys each line a few more px before
       that fallback is ever needed. */
    left: 14px;
    white-space: normal;
    overflow-wrap: break-word;
    padding-inline: 9px;
  }
  .story-card__name{
    font-size: 15px;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: break-word;
  }
}
@media (min-width: 768px){
  .story-card{
    flex-basis: 32%; /* ~3 cards visible */
    height: 340px;
  }
}
@media (min-width: 1280px){
  .stories{ padding-block: 100px; }
  .story-card{
    flex-basis: 21%; /* ~4.5 cards visible */
    height: 400px;
  }
}

/* ==========================================================================
   How we drive growth / video placeholder
   ========================================================================== */
.growth{
  background: var(--bg-navy);
  padding-block: 72px;
  position: relative;
  overflow: hidden;
}
/* Ambient glow — same technique as .belief::before, reused for consistency
   rather than the animated .glow-wave (hero/stories), since this section
   is a static heading + video, not a moving hero. */
.growth::before{
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 80% at 50% 50%, rgba(37,99,235,0.06), transparent 70%);
  pointer-events: none;
}
.growth .container{ position: relative; }
.growth__heading{
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-h2);
  color: var(--accent-blue);
  max-width: 20ch;
}
.video-frame{
  margin-top: 36px;
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: linear-gradient(135deg, var(--bg-navy) 0%, var(--card-dark) 100%);
  border: 1px solid var(--border-on-dark);
  display: flex;
  align-items: center;
  justify-content: center;
}
video.video-frame{
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
}
.growth .video-frame{
  margin-inline: auto;
}
.video-frame__play{
  width: 64px; height: 64px;
  border-radius: 50%;
  background: rgba(255,255,255,0.14);
  border: 1px solid rgba(255,255,255,0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(6px);
}
.video-frame__play svg{ width: 22px; height: 22px; margin-left: 3px; color: var(--white); }
.video-frame__label{
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 13px;
  color: rgba(255,255,255,0.7);
  white-space: nowrap;
}
@media (min-width: 1280px){
  .video-frame__play{ width: 84px; height: 84px; }
  .video-frame__play svg{ width: 28px; height: 28px; }
}

/* ==========================================================================
   CTA band
   ========================================================================== */
.cta-section{
  background: var(--white);
  padding-block: 48px 80px;
}
.cta-band{
  position: relative;
  overflow: hidden;
  background: var(--bg-navy);
  border-radius: var(--radius-lg);
  padding: 48px 24px;
  text-align: center;
}
.cta-band__heading{
  position: relative;
  z-index: 2;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-h2);
  letter-spacing: 0.01em;
  color: var(--white);
}
.cta-band__heading em{
  font-style: normal;
  color: var(--accent-blue);
}
.cta-band__sub{
  position: relative;
  z-index: 2;
  margin-top: 12px;
  font-size: 18px;
  color: var(--muted-on-dark);
}
.cta-band .pill-btn{
  position: relative;
  z-index: 2;
  margin-top: 28px;
}
@media (min-width: 768px){
  .cta-band{ padding: 72px 48px; }
}

/* ==========================================================================
   Contact / footer
   ========================================================================== */
.contact{
  background: var(--white);
  padding-block: 72px 48px;
}
.contact__grid{
  display: grid;
  grid-template-columns: 1fr;
  gap: 36px;
}
.contact__block-label{
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 14px;
}
.contact__list{
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.contact__list a{
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
  transition: color .2s ease;
}
.contact__list a:hover{ color: var(--accent-blue); }
.contact__address{
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
}
.contact__address a{
  display: inline-block;
  margin-top: 10px;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-blue);
}
@media (min-width: 768px){
  .contact__grid{ grid-template-columns: 1fr 1fr; gap: 48px; }
}

/* ==========================================================================
   Site Footer
   ========================================================================== */
.site-footer{
  background: var(--bg-navy);
  color: var(--white);
  padding-block: 80px 40px;
  border-top: 1px solid var(--border-on-dark);
}
.site-footer__inner{
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
}

/* CTA strip */
.site-footer__cta{
  text-align: center;
  padding-bottom: 64px;
  border-bottom: 1px solid var(--border-on-dark);
}
.site-footer__cta-heading{
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(32px, 5vw, 56px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--white);
  margin-bottom: 32px;
}
.site-footer__cta-heading span{
  /* Was a two-stop gradient (accent-blue-dark -> accent-blue) before the
     accent-blue-dark token was retired; #2563EB passes AA on white on
     its own so the two-tone gradient no longer applies here — flat
     color instead of a gradient(x,x) no-op. */
  color: var(--accent-blue);
}

/* Grid */
.site-footer__grid{
  display: grid;
  grid-template-columns: 1fr;
  gap: 48px;
  padding-block: 56px;
  border-bottom: 1px solid var(--border-on-dark);
}
@media (min-width: 600px){
  .site-footer__grid{ grid-template-columns: 1fr 1fr; gap: 40px 48px; }
}
@media (min-width: 960px){
  .site-footer__grid{ grid-template-columns: 2fr 1fr 1fr 1fr; gap: 0 48px; }
}

/* Brand column */
.site-footer__brand-logo{
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 22px;
  letter-spacing: 0.08em;
  color: var(--white);
  display: block;
  margin-bottom: 12px;
}
.site-footer__brand-tagline{
  font-size: 14px;
  line-height: 1.6;
  color: var(--muted-on-dark);
  max-width: 220px;
}

/* Column headings */
.site-footer__col-heading{
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-blue-on-dark);
  margin-bottom: 16px;
}

/* Nav links */
.site-footer__nav{
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.site-footer__nav a{
  font-size: 14px;
  color: var(--muted-on-dark);
  text-decoration: none;
  transition: color 0.2s;
}
.site-footer__nav a:hover{ color: var(--white); }

/* Contact items */
.site-footer__contact{
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.site-footer__contact-item{
  font-size: 14px;
  color: var(--muted-on-dark);
  line-height: 1.5;
}
.site-footer__contact-item a{
  color: var(--muted-on-dark);
  text-decoration: none;
  transition: color 0.2s;
}
.site-footer__contact-item a:hover{ color: var(--white); }

/* Social icons */
.site-footer__social{
  display: flex;
  gap: 14px;
  margin-top: 4px;
}
.site-footer__social a{
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--border-on-dark);
  color: var(--muted-on-dark);
  text-decoration: none;
  transition: border-color 0.2s, color 0.2s;
}
.site-footer__social a:hover{
  border-color: var(--accent-blue);
  color: var(--white);
}
.site-footer__social svg{
  width: 16px;
  height: 16px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Bottom bar */
.site-footer__bottom{
  padding-top: 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
}
@media (min-width: 600px){
  .site-footer__bottom{ flex-direction: row; justify-content: space-between; text-align: left; }
}
.site-footer__copy,
.site-footer__legal{
  font-size: 13px;
  color: var(--muted-on-dark);
}
.site-footer__legal a{
  color: var(--muted-on-dark);
  text-decoration: none;
}
.site-footer__legal a:hover{ color: var(--white); }

/* ==========================================================================
   Placeholder pages (team / it-services)
   ========================================================================== */
.placeholder-hero{
  min-height: 45vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding-top: var(--header-h);
  background:
    radial-gradient(ellipse 80% 60% at 20% 0%, rgba(37,99,235,0.12), transparent 60%),
    var(--bg-navy);
}
.placeholder-hero h1{
  font-family: var(--font-h1);
  font-weight: 700;
  letter-spacing: -0.025em;
  font-size: clamp(28px, 7vw, 48px);
  color: var(--white);
}
.placeholder-hero p{
  margin-top: 14px;
  font-size: 16px;
  color: var(--muted-on-dark);
}

/* ==========================================================================
   Service detail pages
   ========================================================================== */
.service-hero{
  position: relative;
  background:
    radial-gradient(ellipse 90% 60% at 15% 0%, rgba(37,99,235,0.13), transparent 60%),
    radial-gradient(ellipse 70% 50% at 100% 20%, rgba(37,99,235,0.06), transparent 60%),
    linear-gradient(180deg, var(--bg-navy) 0%, var(--bg-navy-soft) 60%, var(--bg-navy) 100%);
  padding-top: calc(var(--header-h) + 56px);
  padding-bottom: 56px;
}
.service-hero__inner{
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
}
.service-hero .eyebrow{ display: block; margin-bottom: 14px; color: var(--accent-blue-on-dark); }
.service-hero__title{
  font-family: var(--font-h1);
  font-weight: 700;
  font-size: clamp(36px, 7vw, 58px);
  line-height: 1.08;
  letter-spacing: -0.025em;
  color: var(--white);
  max-width: 18ch;
}
.service-hero__subtext{
  margin-top: 16px;
  font-size: 18px;
  line-height: 1.6;
  color: var(--muted-on-dark);
  max-width: 56ch;
}
@media (min-width: 768px){
  .service-hero{ padding-top: calc(var(--header-h) + 80px); padding-bottom: 72px; }
  .service-hero__title{ font-size: clamp(44px, 5.5vw, 64px); }
  .service-hero__subtext{ font-size: 20px; }
}

/* Full-bleed image-only hero banner (performance-marketing, social-media-
   marketing, seo-geo-aeo, branding-creative-strategy, video-production,
   marketing-automation, influencer-marketing, and all 6 it-services pages).
   Scoped to this modifier so it never touches the plain text-only
   .service-hero used elsewhere. Width stays full-bleed (no max-width) so
   this never regresses into the margin bug from a prior session. Height is
   a fixed value per breakpoint (chosen, not derived from any one image's
   ratio) with object-fit: cover, so all 13 hero pages read as the same
   height — by design this crops the top/bottom (or, for the widest-ratio
   outlier, the sides) of some images rather than showing empty
   letterboxing; see project notes for the per-image crop-risk review. */
.service-hero--banner{
  position: relative;
  padding-top: var(--header-h);
  height: 320px;
  overflow: hidden;
}
.service-hero--banner .service-hero__media{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.service-hero--banner .service-hero__img{
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Scrim: uniform overlay so centered text reads against any image zone */
.service-hero__scrim{
  position: absolute;
  inset: 0;
  background: rgba(3,2,16,0.50);
  pointer-events: none;
}
/* Text overlay — vertically centered, aligned to site container */
.service-hero__overlay{
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: max(24px, calc((100% - var(--container-w)) / 2 + 24px));
  right: max(24px, calc((100% - var(--container-w)) / 2 + 24px));
  max-width: 590px;
  z-index: 2;
}
.service-hero__eyebrow{
  display: block;
  margin-bottom: 10px;
}
.service-hero__overlay-title{
  font-family: var(--font-h1);
  font-weight: 700;
  font-size: clamp(40px, 6vw, 72px);
  line-height: 1.04;
  letter-spacing: -0.03em;
  color: var(--white);
  margin: 0 0 14px;
}
.service-hero__overlay-subtext{
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.6;
  color: rgba(255,255,255,0.85);
  margin: 0;
}
@media (min-width: 768px){
  .service-hero--banner{ height: 460px; }
  .service-hero__overlay-subtext{ font-size: 19px; }
}
@media (min-width: 1280px){
  .service-hero--banner{ height: 575px; }
}
/* Mobile: hide subtext if banner is too short to fit comfortably */
@media (max-width: 480px){
  .service-hero__overlay-subtext{ display: none; }
}
/* Mobile: banner is much shorter relative to width here than at desktop
   sizes, so object-fit: cover crops far more of the image. Every hero
   image in this set puts its device/UI graphic right-of-center (with
   empty space on the left reserved for the text overlay), so shifting
   the crop window rightward keeps that subject in frame instead of
   centering on the empty left side and cutting the subject off. */
@media (max-width: 767px){
  .service-hero--banner .service-hero__img{ object-position: 70% center; }
}
/* Challenge / Approach split */
.service-split{
  background: var(--white);
  padding-block: 64px;
}
.service-split__inner{
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 40px;
}
.service-challenge__heading,
.service-approach__heading{
  margin-top: 12px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(24px, 4vw, 36px);
  line-height: 1.2;
  color: var(--ink);
}
.service-challenge__body{
  margin-top: 14px;
  font-size: 18px;
  line-height: 1.65;
  color: var(--muted);
  max-width: 48ch;
}
.service-approach__intro{
  margin-top: 14px;
  font-size: 18px;
  line-height: 1.65;
  color: var(--muted);
}
.service-approach__list{
  margin-top: 24px;
  display: flex;
  flex-direction: column;
}
.service-approach__item{
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding-block: 11px;
  border-bottom: 1px solid rgba(0,0,0,0.07);
}
.service-approach__item:last-child{ border-bottom: none; }
.service-approach__icon{
  flex: none;
  width: 36px; height: 36px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(37,99,235,0.12);
  color: var(--accent-blue);
  margin-top: 1px;
}
.service-approach__icon svg{ width: 18px; height: 18px; stroke: var(--accent-blue); }
.service-approach__text{
  font-size: 16px;
  line-height: 1.6;
  color: var(--muted);
}
.service-approach__text strong{
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 1px;
}
@media (min-width: 768px){
  .service-split__inner{ grid-template-columns: 1fr 1fr; gap: 56px; }
}

/* Platforms row */
.service-platforms{
  background: var(--white);
  padding-block: 0 64px;
}
.service-platforms__inner{
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
}
.service-platforms__row{
  margin-top: 20px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.service-platforms__chip{
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 18px 8px 8px;
  border-radius: var(--radius-pill);
  background: var(--white);
  border: 1px solid var(--border-on-light);
}
.service-platforms__icon{
  flex: none;
  width: 28px; height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-blue);
  color: var(--bg-navy);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.02em;
}
.service-platforms__label{
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
}

/* Results */
.service-results{
  background: var(--bg-navy);
  padding-block: 64px;
}
.service-results .eyebrow{ color: var(--accent-blue-on-dark); }
.service-results__inner{
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
}
.service-results__grid{
  margin-top: 28px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 16px;
}
.service-results__item{
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--card-dark);
  border: 1px solid var(--border-on-dark);
  border-radius: var(--radius-card);
  padding: 20px;
}
.service-results__icon{
  flex: none;
  width: 32px; height: 32px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(37,99,235,0.18);
}
.service-results__icon svg{ width: 15px; height: 15px; stroke: var(--accent-blue); }
.service-results__text{
  font-size: 14px;
  font-weight: 500;
  line-height: 1.45;
  color: var(--muted-on-dark);
}

/* What's Included */
.service-included{
  background: var(--white);
  padding-block: 64px 80px;
}
.service-included__inner{
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
}
.service-included__grid{
  margin-top: 28px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 16px;
}
.service-included__item{
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  background: var(--card-dark);
  border: 1px solid var(--border-on-dark);
  border-radius: var(--radius-card);
  padding: 20px;
}
.service-included__icon{
  width: 32px; height: 32px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(37,99,235,0.15);
}
.service-included__icon svg{ width: 15px; height: 15px; stroke: var(--accent-blue); }
.service-included__caption{
  font-size: 14px;
  font-weight: 500;
  line-height: 1.45;
  color: var(--muted-on-dark);
}

/* ==========================================================================
   CRM Development page — single-column "How We Help" + "Key Features"
   (no Challenge/Approach split on this page), plus a fresh "Our Process"
   step row. Reuses existing service-detail tokens/classes throughout.
   ========================================================================== */
.service-help{
  background: var(--white);
  padding-block: 64px;
}
.service-help__inner{
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
}
.service-features{ margin-top: 40px; }
@media (min-width: 768px){
  .service-features .service-approach__list{
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 56px;
  }
  .service-features .service-approach__item:nth-last-child(-n+2){
    border-bottom: none;
  }
}

/* CRM grid modifier classes — retained for future use; auto-fit handles
   5 and 7 items naturally without forced column counts. */

/* ==========================================================================
   Our Process — connected horizontal timeline (Aureate-style adapted)
   Layout: 1-col mobile → 2-col at 640px (rail hidden) → 5-col at 1080px (rail shown)
   Bead: outlined inactive → filled accent on .is-active (set by initProcessReveal in main.js)
   Rail: ::before = gray track, ::after = purple fill animated via --fill-pct CSS var
   Pop animation: CSS @keyframes beadPop triggered by .is-active (no library needed)
   ========================================================================== */
.service-process{
  background: var(--white);
  padding-block: 72px 88px;
}
.service-process__inner{
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: 20px;
}

/* Grid list — holds the rail pseudo-elements at desktop */
.service-process__list{
  margin-top: 40px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 28px;
  list-style: none;
  padding: 0;
  position: relative; /* anchor for ::before / ::after rail */
}

/* Rail track & fill — hidden at mobile/tablet, shown at 1080px+ */
.service-process__list::before,
.service-process__list::after{
  content: '';
  display: none;
  position: absolute;
  /* top = half of 48px bead height */
  top: 24px;
  height: 2px;
  border-radius: 2px;
  pointer-events: none;
}
.service-process__list::before{
  /* Static light track spanning full width */
  left: 0; right: 0;
  background: rgba(37, 99, 235, 0.18);
}
.service-process__list::after{
  /* Animated blue fill, width driven by JS via --fill-pct */
  left: 0;
  width: var(--fill-pct, 0%);
  background: var(--accent-blue);
  transition: width 1.4s cubic-bezier(.4,0,.2,1);
  z-index: 1;
}

/* Each step item */
.service-process__item{
  display: flex;
  align-items: flex-start;
  gap: 16px;
  position: relative;
  z-index: 2; /* sit above the rail lines */
}

/* Bead — default INACTIVE state (outlined, not filled) */
.service-process__number{
  flex: none;
  width: 48px; height: 48px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid rgba(37, 99, 235, 0.30);
  background: var(--white); /* white fill so it occludes the rail behind it */
  color: rgba(37, 99, 235, 0.55);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  transition: background 0.35s ease, border-color 0.35s ease, color 0.35s ease,
              box-shadow 0.35s ease;
  position: relative;
  z-index: 3;
}

/* Bead — ACTIVE state (set by JS adding .is-active to the <li>) */
.service-process__item.is-active .service-process__number{
  background: var(--accent-blue);
  border-color: var(--accent-blue);
  color: var(--bg-navy);
  box-shadow: 0 0 0 5px rgba(37, 99, 235, 0.22);
  /* Trigger the pop keyframe */
  animation: beadPop 520ms cubic-bezier(0.34, 1.56, 0.64, 1) 120ms both;
}

/* Step description */
.service-process__label{
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
  padding-top: 12px;
  transition: color 0.35s ease;
}
.service-process__item.is-active .service-process__label{
  color: var(--ink);
}

/* Pop keyframe — CSS-native spring substitute for GSAP elastic.out */
@keyframes beadPop{
  0%   { transform: scale(1); }
  45%  { transform: scale(1.26); }
  100% { transform: scale(1); }
}

/* ── 2-col grid at 640–1079px (rail stays hidden) ── */
@media (min-width: 640px) and (max-width: 1079px){
  .service-process__list{
    grid-template-columns: 1fr 1fr;
    gap: 32px 48px;
  }
  .service-process__item{
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }
  .service-process__label{ padding-top: 0; }
}

/* ── 5-col desktop — rail visible ── */
@media (min-width: 1080px){
  .service-process__list{
    grid-template-columns: repeat(5, 1fr);
    gap: 0 16px;
  }
  .service-process__list::before,
  .service-process__list::after{ display: block; }
  .service-process__item{
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 16px;
  }
  .service-process__label{ padding-top: 0; }
}

/* ── Reduced-motion overrides ── */
@media (prefers-reduced-motion: reduce){
  .service-process__item.is-active .service-process__number{
    animation: none;
  }
  .service-process__list::after{
    transition: none;
  }
}

/* ==========================================================================
   Chat bubbles — shared keyframes + base styles
   (used by .chatbot-demo on ai-chatbots.html)
   ========================================================================== */
@keyframes bubbleIn{
  from{ opacity:0; transform:translateY(10px); }
  to  { opacity:1; transform:translateY(0);    }
}
@keyframes typingDot{
  0%,60%,100%{ opacity:0.3; transform:translateY(0);    }
  30%        { opacity:1;   transform:translateY(-3px); }
}
.chat-bubble{
  font-family: var(--font-body);
  font-size: 13.5px;
  line-height: 1.5;
  border-radius: 14px;
  padding: 8px 14px;
  max-width: 82%;
}
.chat-bubble--user{
  align-self: flex-end;
  background: rgba(37,99,235,0.22);
  color: var(--white);
  border-bottom-right-radius: 4px;
}
.chat-bubble--bot{
  align-self: flex-start;
  background: var(--card-dark);
  border: 1px solid var(--border-on-dark);
  color: var(--muted-on-dark);
  border-bottom-left-radius: 4px;
}
.chat-bubble--typing{
  align-self: flex-start;
  background: var(--card-dark);
  border: 1px solid var(--border-on-dark);
  padding: 10px 14px;
  border-radius: 14px;
  border-bottom-left-radius: 4px;
  line-height: 0;
}
.chat-bubble--typing span{
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--muted-on-dark);
  vertical-align: middle;
  animation: typingDot .9s ease-in-out infinite;
}
.chat-bubble--typing span + span{ margin-left: 4px; }
.chat-bubble--typing span:nth-child(2){ animation-delay: .15s; }
.chat-bubble--typing span:nth-child(3){ animation-delay: .30s; }

/* ==========================================================================
   Chatbot demo — inline animated sequence (ai-chatbots.html, approach col)
   ========================================================================== */
.chatbot-demo{
  margin-top: 28px;
  background: var(--bg-navy-soft);
  border: 1px solid var(--border-on-dark);
  border-radius: var(--radius-card);
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.chatbot-demo .chat-bubble:nth-child(1){ animation: bubbleIn .5s var(--ease-premium) 0s   both; }
.chatbot-demo .chat-bubble:nth-child(2){ animation: bubbleIn .5s var(--ease-premium) .5s  both; }
.chatbot-demo .chat-bubble:nth-child(3){ animation: bubbleIn .5s var(--ease-premium) 1.1s both; }
.chatbot-demo .chat-bubble:nth-child(4){ animation: bubbleIn .5s var(--ease-premium) 1.7s both; }
.chatbot-demo .chat-bubble:nth-child(5){ animation: bubbleIn .5s var(--ease-premium) 2.3s both; }

@media (prefers-reduced-motion: reduce){
  .chatbot-demo .chat-bubble{ animation: none; }
  .chatbot-demo .chat-bubble--typing span{ animation: none; opacity: 0.6; }
}

/* ==========================================================================
   Part A — Sitewide h2 letter-spacing tightening
   ========================================================================== */
.stories__heading,
.growth__heading,
.site-footer__cta-heading,
.faq__heading,
.belief__quote,
.service-split__heading,
.service-results__heading,
.service-included__heading,
.service-process__heading,
.service-help__heading,
.cta-band__heading{
  letter-spacing: -0.02em;
}

/* ==========================================================================
   Part B — Brand Belief block
   ========================================================================== */
.belief{
  background: var(--bg-navy);
  padding-block: 80px 88px;
  text-align: center;
  position: relative;
  overflow: hidden;
}
.belief::before{
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 80% at 50% 50%, rgba(37,99,235,0.06), transparent 70%);
  pointer-events: none;
}
.belief .container{ position: relative; }
.belief__lead{
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-blue-on-dark);
  margin-bottom: 28px;
}
.belief__quote{
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(28px, 4.5vw, 52px);
  line-height: 1.25;
  color: var(--white);
  max-width: 18ch;
  margin: 0 auto 28px;
}
.belief__body{
  font-size: 17px;
  line-height: 1.7;
  color: var(--muted-on-dark);
  max-width: 52ch;
  margin: 0 auto;
}
@media (min-width: 768px){
  .belief{ padding-block: 96px 104px; }
}

/* ==========================================================================
   Part B — FAQ accordion
   ========================================================================== */
.faq{
  background: var(--bg-navy);
  padding-block: 72px 88px;
  position: relative;
  overflow: hidden;
}
.faq .eyebrow{ color: var(--accent-blue-on-dark); }
/* Ambient glow — same technique as .belief::before / .growth::before. */
.faq::before{
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 80% at 50% 50%, rgba(37,99,235,0.06), transparent 70%);
  pointer-events: none;
}
.faq .container{ position: relative; }
.faq__heading{
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-h2);
  line-height: 1.15;
  color: var(--white);
  margin-top: 14px;
  margin-bottom: 40px;
}
.faq__list{
  max-width: 780px;
  display: flex;
  flex-direction: column;
  gap: 0;
}
.faq__item{
  border-bottom: 1px solid var(--border-on-dark);
}
.faq__item:first-child{ border-top: 1px solid var(--border-on-dark); }
.faq__question{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 17px;
  color: var(--white);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.faq__question::-webkit-details-marker{ display: none; }
.faq__question::after{
  content: '+';
  flex: none;
  font-size: 22px;
  font-weight: 400;
  color: var(--accent-blue);
  transition: transform 0.25s var(--ease-premium);
}
.faq__item details[open] > .faq__question::after{
  transform: rotate(45deg);
}
.faq__answer{
  padding: 0 0 20px;
  font-size: 16px;
  line-height: 1.7;
  color: var(--muted-on-dark);
  max-width: 68ch;
}
@media (min-width: 768px){
  .faq{ padding-block: 88px 104px; }
  .faq__question{ font-size: 18px; }
}

/* ==========================================================================
   Part C — Breathing glow (two highest-priority CTA buttons only)
   ========================================================================== */
@keyframes ctaBreath{
  0%, 100%{ box-shadow: 0 8px 24px rgba(37,99,235,0.35); }
  50%      { box-shadow: 0 8px 32px rgba(37,99,235,0.62), 0 0 22px rgba(37,99,235,0.28); }
}
.hero .pill-btn:not(.pill-btn--outline),
.site-footer__cta .pill-btn{
  animation: ctaBreath 2.5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce){
  .hero .pill-btn:not(.pill-btn--outline),
  .site-footer__cta .pill-btn{
    animation: none;
    box-shadow: 0 8px 28px rgba(37,99,235,0.50);
  }
}

/* ==========================================================================
   Floating chat-widget greeting bubble
   The launcher itself (#btk-bubble) is injected at runtime by the
   third-party widget script tagged at the end of every page's <body> —
   we don't own its DOM, though a couple of narrowly-scoped overrides
   below do reach into its CSS (see the mobile full-screen and launcher
   icon sections further down). This bubble is a wholly
   separate fixed element, positioned beside it with offsets that mirror
   the launcher's own fixed/mobile offsets (24px desktop, 16px at its
   ≤420px breakpoint) rather than reading its live rect, so no JS
   dependency on the widget's internals beyond "does #btk-bubble exist
   yet" (see initChatGreeting in js/main.js). Shares the site's existing
   dark-glass treatment (.nav.is-scrolled) and reveal-fade timing.
   ========================================================================== */
@keyframes chatGreetingIn{
  from{ opacity: 0; transform: translateX(8px); }
  to  { opacity: 1; transform: translateX(0); }
}
/* Wave twice, then rest for the remainder of the cycle — not a constant
   wiggle. transform-origin sits near the emoji's wrist so it pivots
   instead of spinning around its center. */
@keyframes chatGreetingWave{
  0%, 60%, 100%{ transform: rotate(0deg); }
  10%, 30%     { transform: rotate(14deg); }
  20%          { transform: rotate(-8deg); }
  40%          { transform: rotate(14deg); }
  50%          { transform: rotate(-4deg); }
}
.chat-greeting{
  position: fixed;
  right: 92px; /* 24px launcher offset + 56px launcher width + 12px gap */
  bottom: 24px;
  z-index: 2147483645; /* one below the launcher's own 2147483646 */
  height: 56px; /* matches launcher height, so shared bottom + height
                    centers this on it with no extra transform math */
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px;
  border-radius: var(--radius-pill);
  background: rgba(10,8,32,0.72);
  border: 1px solid var(--border-on-dark);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 8px 24px rgba(3,2,16,0.35);
  pointer-events: none; /* decorative only — never intercepts the click */
  opacity: 0;
  animation: chatGreetingIn .5s var(--ease-premium) .6s forwards;
}
.chat-greeting.is-dismissed{ display: none; }
.chat-greeting__wave{
  font-size: 20px;
  line-height: 1;
  transform-origin: 70% 70%;
  animation: chatGreetingWave 2.6s ease-in-out 1.4s infinite;
}
.chat-greeting__text{
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--white);
  white-space: nowrap;
}
@media (max-width: 420px){
  /* Matches the launcher's own ≤420px offset change (24px -> 16px). */
  .chat-greeting{ right: 80px; bottom: 16px; }
}
@media (max-width: 480px){
  .chat-greeting{ padding: 0 12px; gap: 6px; }
  .chat-greeting__text{ font-size: 12px; }
}
@media (max-width: 360px){
  /* Narrowest phones: keep the waving emoji, drop the caption so the
     pill can't overflow the viewport's left edge. */
  .chat-greeting__text{ display: none; }
  .chat-greeting{ padding: 0 10px; }
}
@media (prefers-reduced-motion: reduce){
  .chat-greeting{ animation: none; opacity: 1; }
  .chat-greeting__wave{ animation: none; }
}

/* ==========================================================================
   Aria chat widget — mobile full-screen override
   #btk-window (the chat panel) and #btk-bubble (launcher) are injected at
   runtime by the third-party widget script (see comment above) into the
   regular DOM — no shadow root — so its layout is reachable from here.
   Everything below is !important because widget.js appends its own
   <style id="btk-styles"> to <head> at runtime, after this stylesheet, so
   on tied specificity its rules would otherwise win the cascade.
   Only position/size is touched; the internal header/messages/input
   markup and styling stay exactly as the widget script renders them.
   Desktop floating-panel layout (>767px) is untouched.

   inset:0 alone (the first pass at this fix) matched pixel-for-pixel in
   headless Chrome but real mobile browsers left a sliver of the site
   header visible at the top and page content bleeding in at the bottom
   — the classic case of position:fixed being sized against the static
   "layout viewport" rather than the address-bar-aware visible one.
   height:100dvh (the viewport-relative unit built for exactly this,
   recalculated live as the browser's toolbar shows/hides) fixes it;
   100vh above it is a value for browsers that don't understand dvh —
   never applied on top of it, since a browser new enough for dvh always
   uses the later, more specific dvh declaration. bottom:0 stays as a
   harmless no-op once height is set explicitly (over-constrained
   fixed-position boxes resolve by ignoring bottom, per spec) — kept
   only so nothing collapses if height ever fails to compute.
   Dropped env(safe-area-inset-*): this site's viewport meta has no
   viewport-fit=cover, so browsers already letterbox around notches on
   their own and that env() always resolved to 0 anyway — it was inert,
   not the cause, but combining a nonzero top inset with height:100dvh
   would itself reproduce this exact top-gap bug if that meta ever
   changes, so plain 0 is the more robust choice here. */
@media (max-width: 767px){
  #btk-window{
    top: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    width: auto !important;
    height: 100vh !important;
    height: 100dvh !important;
    border-radius: 0 !important;
    z-index: 2147483647 !important;
  }
  /* Once open, the launcher button doubles as the widget's own close
     control (it swaps to an X icon), but it sits at a higher z-index
     than #btk-window and would float over the full-screen input row.
     The header already has its own #btk-close button, so hide the
     redundant floating one while the panel covers the screen. */
  #btk-bubble[aria-expanded="true"]{
    display: none !important;
  }
}

/* ==========================================================================
   Aria chat widget — launcher icon
   #btk-bubble ships with its own purple-gradient fill and an inline
   message-bubble <svg id="btk-icon-chat">; swapping in the brand's
   robot-orb art means replacing that gradient (background, not
   background-image, since the widget's own rule sets `background:`
   too — a background-image-only override would layer the art on top
   of the gradient's *color*, not replace it) and hiding the old SVG
   glyph it would otherwise still paint on top.
   assets/aria-icon.png is a cropped, background-removed version of the
   original assets/chatbot-icon.jpg reference (that file is kept only
   as the source reference, not used here) — rembg cut the sphere out
   from its light gradient backdrop, then it was cropped tight to the
   sphere's own bounding box (the source had ~29% transparent margin on
   every side, which made the orb read as a tiny dot at 56px). Painted
   as two background layers, image first: `contain` (not `cover`, now
   that there's no background left to crop into) keeps the whole sphere
   in frame, and the widget's original purple gradient stays as the
   second/bottom layer so the button still reads as a solid circular
   badge — without it, the PNG's transparent edges would show whatever
   page content sits behind the fixed button through a "hole" instead
   of a floating orb on a chip.
   #btk-icon-x (the open/close "X") is left alone — the widget still
   swaps to it on click, now shown over the new artwork exactly as it
   was over the old gradient, so the launcher keeps its open/closed
   affordance. Applies at all widths: unlike the full-screen block
   above, this isn't a mobile-only concern, and desktop's launcher
   still needs the new icon too. !important for the same reason as the
   rest of this file's widget overrides — widget.js's own <style> tag
   loads after this stylesheet, so tied specificity would otherwise let
   its rules win regardless of source order.

   Bounce: a gentle vertical float, same restraint as the greeting
   bubble's wave (slow, small amplitude, ease in/out rather than linear)
   — deliberately no rotation, so the orb never tips off-axis. Animates
   background-position (the icon layer only, left at its default 50%
   50% on the gradient layer) rather than `transform` — #btk-bubble
   already has its own `:hover{ transform:scale(1.09) }` from widget.js,
   and a running animation drives its target property every frame,
   which would permanently fight/override that hover transform for as
   long as the animation loops. Split into the background-* longhands
   (rather than the `background` shorthand used above) since keyframing
   just one layer's position needs to address it independently of the
   image/size/repeat lists. */
@keyframes ariaIconBounce{
  0%, 100%{ background-position: center calc(50% + 3px), center; }
  50%{ background-position: center calc(50% - 3px), center; }
}
#btk-bubble{
  background-image:
    url('../assets/aria-icon.png'),
    linear-gradient(135deg, #544b9f 0%, #7a6ee8 100%) !important;
  background-size: 68% auto, cover !important;
  background-repeat: no-repeat, no-repeat !important;
  background-position: center, center !important;
  animation: ariaIconBounce 2.4s ease-in-out infinite !important;
}
#btk-icon-chat{
  display: none !important;
}
@media (prefers-reduced-motion: reduce){
  #btk-bubble{ animation: none !important; }
}

/* ==========================================================================
   Aria chat widget — glassmorphism panel
   Re-verified fresh (headless Chrome, current widget.js build): #btk-window
   is still a plain DOM node with no shadow root, so its own injected
   <style id="btk-styles"> (loaded after this stylesheet, same as every
   other override in this file) can still be beaten on tied specificity
   with !important. Layout/structure/markup are untouched — only backgrounds,
   borders and shadows change, following the same opacity + blur + hairline
   border + soft shadow recipe as .nav.is-scrolled and .chat-greeting above,
   adapted from this site's dark-glass palette to the widget's own light,
   violet-accented one (a dark-tinted glass here would fight the widget's
   white bubbles and dark message text).
   Backdrop-filter is layered: the window blurs the page behind it, then
   each translucent surface inside (header, messages, input row, footer,
   bubbles) sits on top of that already-blurred/tinted result rather than
   re-sampling the raw page, which is why bubble alphas were tuned by eye
   against both a light and a dark section of the page rather than picked
   to look right against #fff alone — see contrast note above .btk-bub.

   "Liquid glass" pass: pushed the window's own blur from 22px to 36px
   (heavy frosting rather than a light tint) and bumped saturate to 180%
   so colour bleeding through the frost reads richer/more refractive.
   border-radius goes from widget.js's default 18px to 30px for the
   smooth rounded-card shape in the reference — safe because #btk-window
   already has `overflow:hidden` from widget.js, so header/messages/input/
   footer clip to the new radius automatically without touching their own
   markup. The extra `inset 0 1px 0` / `inset 0 0 1px` box-shadow layers
   are the glossy top-edge light-catch: a bright hairline along the top
   inner edge and a faint all-round inner rim, mimicking curved-glass
   refraction without adding any element. Nested surfaces (header, input
   row) also got a touch more blur for consistency; bubble alphas were
   nudged up slightly since the stronger saturate() makes blurred colour
   bleed through a bit more vividly, re-verified for contrast below.
   The 30px radius is scoped to the >=768px desktop breakpoint below
   rather than set here unscoped: this rule and the mobile full-screen
   block above both target #btk-window's border-radius with !important,
   so on tied specificity the one later in source order wins regardless
   of viewport — scoping keeps this pass from re-rounding the mobile
   full-screen sheet, which must stay flush (border-radius:0) per the
   earlier fix.

   Dark-theme pass: swapped the panel's own tint from white to
   --bg-navy-soft's rgb (10,8,32) at similar alpha — same recipe as
   .chat-greeting/.nav.is-scrolled above, just applied to this widget
   instead of the site chrome. Shape is untouched (radius, glossy
   inset-highlight border and blur values are identical to the previous
   "liquid glass" pass); only backgrounds and text colours flip to dark.
   Every surface widget.js paints with a light background and dark text
   by default (bot bubble, typing dots, input, footer) needs both halves
   overridden together, or the dark bg + still-dark default text would
   be illegible — bot/typing bubbles now reuse this site's own
   --card-dark + --border-on-dark "card on dark" pattern (same one
   .chat-bubble--bot already uses elsewhere), and input/footer text
   move to --white/--muted-on-dark. Contrast was re-checked by sampling
   actual rendered pixels behind each text run (not just against a flat
   navy swatch), since the true backdrop is the blurred page underneath
   several stacked translucent layers, which can land lighter than any
   single layer's own colour over a bright section of the page.
   Concretely: scrolling the still-open widget over the "Trusted By"
   logo row (several of those tiles are white/light-grey cards) measured
   real composites as bright as rgb(145,106,126) behind the input and
   rgb(138,139,145) behind the footer with a first-pass set of alphas —
   both well under 4.5:1. Window/messages/footer/input alphas below were
   raised to hold contrast against that worst realistic case (re-swept
   across the whole page's scroll range, not just the hero), and
   footer/placeholder text moved from --muted-on-dark to a lighter
   lavender-white (--muted-on-dark reads fine at the site's normal body
   text size but doesn't have enough headroom at 10-13px against a glass
   panel that can end up over near-white content). */
#btk-window{
  background: rgba(10,8,32,0.6) !important;
  backdrop-filter: blur(36px) saturate(180%) !important;
  -webkit-backdrop-filter: blur(36px) saturate(180%) !important;
  border: 1px solid rgba(255,255,255,0.6) !important;
  box-shadow:
    0 2px 8px rgba(3,2,16,0.25),
    0 24px 60px rgba(3,2,16,0.45),
    inset 0 1px 0 rgba(255,255,255,0.75),
    inset 0 0 0 1px rgba(255,255,255,0.14) !important;
}
@media (min-width: 768px){
  #btk-window{ border-radius: 30px !important; }
}
#btk-header{
  /* Same violet hue/stops as widget.js's own gradient — only alpha and a
     blur are added, so the accent colour itself never changes. */
  background: linear-gradient(135deg, rgba(84,75,159,0.88) 0%, rgba(122,110,232,0.88) 100%) !important;
  backdrop-filter: blur(22px) !important;
  -webkit-backdrop-filter: blur(22px) !important;
  border-bottom: 1px solid rgba(255,255,255,0.18) !important;
  /* Header name/sub text already ship white/rgba(255,255,255,.8) from
     widget.js — untouched, since the violet header was never light. */
}
#btk-messages{
  background: rgba(10,8,32,0.38) !important;
}
/* "Card on dark" bubble treatment — background AND text both flip
   together (widget.js's own .btk-bub.btk-bot rule is #1e293b-on-white,
   which would be unreadable left as-is against a dark fill). */
.btk-bub.btk-bot{
  background: rgba(44,43,54,0.68) !important;
  color: var(--white) !important;
  border: 1px solid var(--border-on-dark) !important;
  box-shadow: 0 1px 3px rgba(0,0,0,0.3) !important;
  backdrop-filter: blur(8px) !important;
  -webkit-backdrop-filter: blur(8px) !important;
}
.btk-bub.btk-user{
  background: rgba(122,110,232,0.44) !important;
  color: var(--white) !important;
  backdrop-filter: blur(8px) !important;
  -webkit-backdrop-filter: blur(8px) !important;
}
.btk-bub.btk-err{
  background: rgba(190,18,60,0.4) !important;
  color: #ffe1e6 !important;
  border: 1px solid rgba(251,113,133,0.35) !important;
}
.btk-typing{
  background: rgba(44,43,54,0.68) !important;
  border: 1px solid var(--border-on-dark) !important;
  box-shadow: 0 1px 3px rgba(0,0,0,0.3) !important;
  backdrop-filter: blur(8px) !important;
  -webkit-backdrop-filter: blur(8px) !important;
}
.btk-dot{
  background: var(--muted-on-dark) !important;
}
#btk-input-row{
  background: rgba(10,8,32,0.42) !important;
  backdrop-filter: blur(20px) !important;
  -webkit-backdrop-filter: blur(20px) !important;
  border-top: 1px solid rgba(255,255,255,0.12) !important;
}
#btk-input{
  /* A light frosted-white overlay (the idiom used for #btk-avatar) tested
     unreliable here: at low alpha it only brightens whatever's already
     behind it, so scrolling the page under the open widget so a busy,
     colourful section landed behind the input pushed its composite too
     light for the placeholder text (measured 3.3:1 against actual
     rendered pixels). A navy-tinted overlay actively darkens the blurred
     backdrop instead of just lightening it, holding contrast regardless
     of what's behind — re-verified across the full page's scroll range. */
  background: rgba(10,8,32,0.4) !important;
  border-color: rgba(255,255,255,0.2) !important;
  color: var(--white) !important;
}
#btk-input:focus{
  /* widget.js's own :focus rule sets background:#fff — override or the
     input flips back to an opaque light field the moment it's tapped. */
  background: rgba(10,8,32,0.52) !important;
}
#btk-input::placeholder{
  /* --muted-on-dark measured under 4.5:1 at this font-size once the
     backdrop behind the widget got bright (see block comment above) —
     lighter than the site's usual on-dark secondary colour for headroom. */
  color: #d6d4ec !important;
  opacity: 1 !important; /* Firefox's UA stylesheet dims placeholders further by default */
}
#btk-footer{
  background: rgba(10,8,32,0.5) !important;
  border-top: 1px solid rgba(255,255,255,0.1) !important;
  color: #d6d4ec !important;
}

/* ==========================================================================
   Aria chat widget — background media
   The <img> itself is inserted by initChatBgMedia() in js/main.js (CSS
   can't create new elements inside widget.js's own markup) as the first
   child of #btk-window. #btk-window is `position:fixed` per widget.js's
   own stylesheet, which is what makes an absolutely-positioned, negative
   z-index child work as "behind everything else, in front of the window's
   own background" here: painting order inside a stacking context is (1)
   the box's own background, (2) negative-z-index positioned descendants,
   (3) in-flow static children — so z-index:-1 slots this element between
   the window's dark-glass fill and every other child (header, messages,
   input row, footer), all of which are plain static elements. No z-index
   is needed on those siblings for this to hold.
   Sizing with position:absolute + inset:0 takes it out of #btk-window's
   own `display:flex; flex-direction:column` flow entirely, so it can't
   shift or resize the header/messages/input layout (requirement: no
   layout shift). #btk-window's existing `overflow:hidden` (needed for
   its own rounded corners) clips it to those same corners for free — no
   radius needed here.
   Opacity 0.22 plus every surface already painted on top of it
   (messages/input-row's own rgba(10,8,32,~0.4-0.5) fills, bubbles'
   card-on-dark fills) keeps message text legible — re-verified by
   sampling actual rendered pixels behind each text run across several
   points in the animated GIF's loop, the same way contrast was checked
   for the dark-theme pass above. This was a <video>+.mp4 originally;
   the asset was swapped for an animated .gif under a different filename
   (see js/main.js comment), and <video> can't decode a GIF as a source
   at all, so the element itself had to change to <img>, not just the src. */
.btk-bg-media{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: -1;
  opacity: 0.22;
  pointer-events: none;
}
