/* ============================================================
   components.css: All reusable components
   ------------------------------------------------------------
   Loaded after base.css. Every component used across the
   network lives here. Do NOT duplicate any of these styles
   into per-site site.css files.
   ============================================================ */

/* ============================================================
   BUTTONS
   ============================================================ */
/* Mirrors HeyGuest's Tailwind .btn: compact padding, light font-weight,
   16px radius, flat (no shadow). Single size , hero CTAs match nav CTAs. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding-block: var(--space-1);
  padding-inline: var(--space-4);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-light);
  line-height: 1.5;
  letter-spacing: 0;
  border-radius: var(--radius-2xl);
  /* All hover-driven properties share one timing so the lift,
     colour change, and shadow appear / animate together. The
     base 250ms with the same easing keeps colour and transform
     in lockstep -- previously transform used --transition-fast
     (150ms) and box-shadow had no transition at all, which made
     the lift land before the colour change finished. */
  transition: background-color var(--transition-base),
              color            var(--transition-base),
              transform        var(--transition-base),
              box-shadow       var(--transition-base);
  white-space: nowrap;
  cursor: pointer;
  border: none;
  text-align: center;
}


.btn--primary {
  background-color: var(--accent-color);
  color: var(--accent-fg);
}

/* Page-level CTAs (Hero CTAs, Solution CTA, Pricing CTA, Products
   onboarding link) match the bottom-cta "Get Started" pill exactly:
   same padding, same font-size, fully rounded. They are the page's
   primary conversion targets so they share the same visual
   signature regardless of which color variant they use. The nav
   CTA is intentionally excluded so the header keeps its rhythm. */
.hero__ctas .btn,
.split__cta .btn,
.pricing__cta,
.products__onboarding,
.cf-submit {
  padding: var(--space-3) var(--space-8);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semi);
  border-radius: var(--radius-full);
}

/* HG: base lime button hover on LIGHT backgrounds goes dark */
.btn--primary:hover {
  background-color: var(--accent-hover);
  color: var(--accent-fg-hover);
}

/* HG: .bg-dark .btn:hover , on DARK backgrounds the lime button hover goes blue */
.section--dark .btn--primary:hover,
.hero .btn--primary:hover,
.site-nav .btn--primary:hover,
.products .btn--primary:hover,
.pricing .btn--primary:hover,
.contact-form-section .btn--primary:hover,
.contact-hero .btn--primary:hover {
  background-color: var(--accent-hover-on-dark);
}

/* Hero / Products / Pricing / Contact-form primary CTAs all
   get the same lift + shadow on hover that the .btn--dark Get
   Started CTA uses, so every primary conversion button on the
   network shares one tactile hover signature. Site-nav CTA is
   intentionally excluded -- a lifted nav button breaks the
   topbar rhythm. */
.hero .btn--primary:hover,
.products .btn--primary:hover,
.pricing .btn--primary:hover,
.cf-submit:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
}

.btn--secondary {
  background-color: var(--brand-blue);
  color: var(--color-white);
}

.btn--secondary:hover {
  background-color: var(--brand-blue-light);
}

.btn--dark {
  background-color: var(--brand-navy);
  color: var(--color-white);
}

/* All .btn--dark hovers (Solution CTA + Bottom-CTA "Get Started")
   share the on-dark accent hover used by every other primary CTA
   in the network: light-blue background with white text. Slight
   lift + shadow for emphasis. */
.btn--dark:hover {
  background-color: var(--accent-hover-on-dark);
  color: var(--color-white);
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
}

.btn--ghost {
  background-color: transparent;
  color: var(--color-text);
  box-shadow: inset 0 0 0 1px var(--color-gray-300);
}

.btn--ghost:hover {
  background-color: var(--color-gray-100);
  box-shadow: inset 0 0 0 1px var(--color-gray-400);
}

.section--dark .btn--ghost,
.hero .btn--ghost {
  color: var(--color-white);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3);
}

.section--dark .btn--ghost:hover,
.hero .btn--ghost:hover {
  background-color: rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
}

.btn--text {
  padding: 0;
  background: none;
  color: var(--accent-color);
  font-weight: var(--font-weight-semi);
  border: none;
}

.btn--text:hover {
  color: var(--accent-hover);
  transform: none;
}

.btn .icon-arrow {
  width: 1rem;
  height: 1rem;
  transition: transform var(--transition-base);
}

.btn:hover .icon-arrow { transform: translateX(3px); }

/* ============================================================
   NAVIGATION
   ============================================================ */
.site-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-nav);
  background-color: transparent;
  border: 1px solid transparent;
  border-bottom: none;
  /* contain: layout limits the reflow scope for the scrolled
     nav transitions (padding-block, height, font-size on the
     inner items). Applied only at >=1200px below, because at
     mobile widths it would re-anchor the position: fixed mobile
     menu to the nav box and collapse it.
     backdrop-filter is intentionally NOT in the transition list:
     animating a blur amount is very expensive; we snap it
     instead, and the simultaneous bg-color fade hides the cut. */
  transition:
    background-color var(--transition-base),
    box-shadow var(--transition-base),
    border-color var(--transition-base),
    border-radius var(--transition-base),
    top var(--transition-base),
    left var(--transition-base),
    right var(--transition-base);
}

/* Past the hero, the nav becomes a translucent glass surface so
   it reads over page content. On desktop it pulls away from the
   top edge and shrinks to a centered pill. The fallback (no
   backdrop-filter) uses a near-opaque navy. */
.site-nav.is-scrolled {
  background-color: rgba(10, 23, 38, 0.92);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.32);
  border-color: rgba(255, 255, 255, 0.08);
}

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-nav.is-scrolled {
    background-color: rgba(10, 23, 38, 0.75);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
  }
}

@media (min-width: 768px) {
  /* Pill width = inner-content-area + 24px (12px past where the
     logo's left margin and lang selector's right margin sit).
     Inner padding drops to 12px so logo / lang stay anchored at
     the same screen X as the unscrolled state. */
  .site-nav.is-scrolled {
    top: 6px;
    left: 0;
    right: 0;
    width: calc(min(100vw, var(--container-max)) - 2 * var(--section-pad-x) + 24px);
    margin-inline: auto;
    border-radius: var(--radius-xl, 16px);
    border-bottom-color: rgba(255, 255, 255, 0.08);
  }

  .site-nav.is-scrolled .site-nav__inner {
    padding-inline: 12px;
  }
}

/* Layout containment at desktop widths only -- the scrolled-pill
   transitions (padding-block, font-size, height) cause page-wide
   reflow without it. Applied at >=1200px because below that the
   hamburger menu can open, and contain: layout would re-anchor
   its fixed-position panel to the nav box. */
@media (min-width: 1200px) {
  .site-nav {
    contain: layout;
  }
}

.site-nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-block: 1rem;
  padding-inline: var(--section-pad-x);
  gap: var(--space-8);
  transition: padding-block var(--transition-base);
}

.site-nav.is-scrolled .site-nav__inner {
  padding-block: 0.55rem;
}

.site-nav__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 2.5rem;
  font-weight: var(--font-weight-extra);
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-white);
  flex-shrink: 0;
}

.site-nav__brand-mark {
  width: 32px;
  height: 32px;
  background: var(--accent-color);
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  color: var(--brand-navy);
  font-weight: var(--font-weight-extra);
  font-size: var(--font-size-lg);
}

.site-nav__brand-logo,
.site-footer__brand-logo {
  height: var(--logo-height, 60px);
  width: auto;
  display: block;
  transition: height var(--transition-base);
}

/* Logo and nav inner padding both shrink together when the
   nav transitions to its scrolled "pill" state, so the bar
   visibly compacts. */
.site-nav.is-scrolled .site-nav__brand-logo {
  height: calc(var(--logo-height, 60px) * 0.74);
}

.site-nav__menu {
  display: flex;
  align-items: center;
  gap: var(--space-7);
  flex: 1;
  justify-content: flex-end;
}

.site-nav__item--cta {
  margin-left: var(--space-2);
}

/* ============================================================
   Language selector
   Sits in the nav menu after the CTA. Trigger shows a flag +
   chevron; click opens a dropdown of available languages.
   New languages are added via the `languages.options` array
   in each locale's data.js -- no JS changes required.
   ============================================================ */
.site-nav__item--lang {
  margin-left: var(--space-3);
  display: flex;
  align-items: center;
}

.lang-select { position: relative; }

.lang-select__trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius-md, 8px);
  color: var(--color-white);
  cursor: pointer;
  transition:
    border-color var(--transition-fast),
    background-color var(--transition-fast);
}

.lang-select__trigger:hover,
.lang-select.is-open .lang-select__trigger {
  border-color: rgba(255, 255, 255, 0.38);
  background-color: rgba(255, 255, 255, 0.06);
}

.lang-select__trigger .icon-chevron {
  width: 12px;
  height: 12px;
  /* Without flex-shrink:0, the chevron is the only sizable
     flex child once the flag is locked, and it could collapse
     to 0 under pressure (e.g. longer locale labels pushing on
     the menu). Lock it so it stays visible at every nav width. */
  min-width: 12px;
  min-height: 12px;
  flex-shrink: 0;
  flex-grow: 0;
  transition: transform var(--transition-fast);
}

.lang-select.is-open .lang-select__trigger .icon-chevron {
  transform: rotate(180deg);
}

.lang-select__flag {
  display: block;
  width: 18px;
  height: 12px;
  /* Lock the size so the flex layout / base img reset can't
     compress the trigger flag (the dropdown ones rendered
     fine; the trigger one didn't). */
  min-width: 18px;
  max-width: 18px;
  flex-shrink: 0;
  flex-grow: 0;
  border-radius: 1px;
  object-fit: cover;
}

.lang-select__menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 200px;
  margin: 0;
  padding: 4px;
  list-style: none;
  background-color: var(--brand-navy);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius-md, 8px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition:
    opacity var(--transition-fast),
    transform var(--transition-fast);
}

.lang-select.is-open .lang-select__menu {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.lang-select__option a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 4px;
  font-size: var(--font-size-sm);
  color: var(--color-white);
  white-space: nowrap;
}

.lang-select__option a:hover {
  background-color: rgba(255, 255, 255, 0.08);
}

.lang-select__option[aria-selected="true"] a {
  font-weight: var(--font-weight-semi);
  background-color: rgba(255, 255, 255, 0.05);
}

.site-nav__item--cta .btn {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semi);
  transition:
    background-color var(--transition-base),
    color var(--transition-base),
    transform var(--transition-fast),
    font-size var(--transition-base),
    padding var(--transition-base);
}

/* Match the rest of the scrolled-state shrinking. */
.site-nav.is-scrolled .site-nav__item--cta .btn {
  font-size: var(--font-size-base);
  padding-inline: var(--space-3);
}

.site-nav__item {
  position: relative;
}

.site-nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) 0;
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-medium);
  color: var(--color-white);
  white-space: nowrap;
  transition:
    color var(--transition-fast),
    font-size var(--transition-base);
}

/* Underline wipes in from the left on hover/active. Animating
   scaleX is composite-only, no layout/paint cost per frame. */
.site-nav__link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background-color: var(--accent-color);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--transition-base);
}

.site-nav__link:hover::after,
.site-nav__link.is-active::after {
  transform: scaleX(1);
}

.site-nav.is-scrolled .site-nav__link {
  font-size: var(--font-size-base);
}

.site-nav__link:hover,
.site-nav__link.is-active {
  color: var(--color-white);
}

.site-nav__link[aria-expanded] .icon-chevron {
  width: 14px;
  height: 14px;
  transition: transform var(--transition-base);
}

.site-nav__link[aria-expanded="true"] .icon-chevron {
  transform: rotate(180deg);
}

.site-nav__dropdown {
  position: absolute;
  top: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%) translateY(-8px);
  min-width: 240px;
  padding: var(--space-3);
  background: var(--color-white);
  border: 1px solid var(--color-gray-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-base), transform var(--transition-base);
}

.site-nav__item:hover .site-nav__dropdown,
.site-nav__item:focus-within .site-nav__dropdown {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}

.site-nav__dropdown-link {
  display: block;
  padding: var(--space-3) var(--space-4);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  border-radius: var(--radius-md);
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.site-nav__dropdown-link:hover {
  background-color: var(--accent-light);
  color: var(--brand-navy);
}

.site-nav__toggle {
  display: none;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
}

.site-nav__toggle:hover { background: rgba(255, 255, 255, 0.1); }

.site-nav__toggle svg {
  width: 24px;
  height: 24px;
  color: var(--color-white);
}

/* Morphing hamburger -> close icon. The curve path uses two
   stroke-dasharray states (closed: short hamburger segments;
   open: a single longer segment) and the whole icon rotates
   -45deg on open, mirroring the React reference's behaviour
   without needing framer-motion. */
.site-nav__toggle-icon {
  width: 22px;
  height: 22px;
  color: var(--color-white);
  transition: transform 300ms ease-in-out;
}

.site-nav.is-open .site-nav__toggle-icon {
  transform: rotate(-45deg);
}

.site-nav__toggle-path {
  stroke-dasharray: 12 63;
  transition:
    stroke-dasharray 300ms ease-in-out,
    stroke-dashoffset 300ms ease-in-out;
}

.site-nav.is-open .site-nav__toggle-path {
  stroke-dasharray: 20 300;
  stroke-dashoffset: -32.42px;
}

@media (max-width: 1199px) {
  .site-nav__menu { display: none; }
  .site-nav__toggle { display: inline-flex; }

  .site-nav.is-open .site-nav__menu {
    display: flex;
    position: fixed;
    top: var(--nav-height);
    left: 0;
    right: 0;
    bottom: 0;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0;
    padding: var(--space-6);
    background: var(--brand-navy);
    overflow-y: auto;
  }

  .site-nav.is-open .site-nav__item {
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  }

  .site-nav.is-open .site-nav__item--cta {
    border-bottom: none;
    margin-top: var(--space-5);
    margin-left: 0;
  }

  .site-nav.is-open .site-nav__link {
    padding: var(--space-5) 0;
    font-size: var(--font-size-lg);
    width: 100%;
  }

  .site-nav.is-open .site-nav__dropdown {
    position: static;
    transform: none;
    opacity: 1;
    pointer-events: auto;
    border: none;
    box-shadow: none;
    padding: 0 0 var(--space-4);
    min-width: 0;
    background: transparent;
  }

  .site-nav.is-open .site-nav__dropdown-link {
    color: var(--color-text-on-dark-muted);
  }

  .site-nav.is-open .site-nav__dropdown-link:hover {
    background: rgba(255, 255, 255, 0.08);
    color: var(--color-white);
  }

  .site-nav.is-open .site-nav__item--cta .btn {
    display: inline-flex;
    justify-content: center;
    width: 100%;
    /* Larger tap target + bolder type for the mobile menu's
       primary CTA. */
    padding-block: var(--space-4);
    font-size: var(--font-size-lg);
  }

  /* Language selector in the open mobile menu: give it its own
     row with breathing space below the CTA, override the
     desktop flex/margins, stretch the trigger full-width, and
     anchor the dropdown to BOTH the trigger's left and right
     edges so it can never escape off-screen. */
  .site-nav.is-open .site-nav__item--lang {
    display: block;
    border-bottom: none;
    margin-top: var(--space-5);
    margin-left: 0;
  }

  .site-nav.is-open .lang-select {
    width: 100%;
  }

  .site-nav.is-open .lang-select__trigger {
    width: 100%;
    justify-content: space-between;
    padding-block: var(--space-3);
  }

  .site-nav.is-open .lang-select__menu {
    left: 0;
    right: 0;
    min-width: 0;
  }
}

/* Page offset for fixed nav */
.site-main {
  padding-top: var(--nav-height);
}

/* ============================================================
   HERO
   ============================================================ */
.hero {
  position: relative;
  isolation: isolate; /* contain .hero-wave's negative z-index */
  /* Pull the hero up under the fixed nav so the wave canvas
     (which fills this section) extends behind the nav too.
     Extra padding-top compensates so content keeps its
     visual position below the nav. */
  margin-top: calc(var(--nav-height) * -1);
  padding-top: calc(var(--section-pad-y-top) + var(--nav-height));
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--hero-bg);
  color: var(--color-text-on-dark);
  overflow: hidden;
}

.hero__inner {
  position: relative;
  display: grid;
  grid-template-columns: 0.8fr 1fr;
  gap: var(--space-16);
  align-items: center;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

/* Animated wave canvas. Sized to fill its host element via
   inset: 0; placed beneath siblings via z-index: -1, which is
   contained by the host's `isolation: isolate` so it doesn't
   slip behind the host's own background or any ancestor.
   Animation lives in shared/scripts/hero-wave.js. */
.hero-wave {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  display: block;
  pointer-events: none;
}

/* Full-page variant: viewport-sized fixed canvas painted
   behind every section, used on pages where we want one
   continuous wave instead of per-section canvases. body gets
   the .has-page-wave class so sections can flip their
   backgrounds to transparent. */
.hero-wave--full {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: -1;
  display: block;
  pointer-events: none;
}

.has-page-wave {
  isolation: isolate;
}

.has-page-wave .contact-hero,
.has-page-wave .contact-form-section,
.has-page-wave .legal-section {
  background: transparent;
}

/* Footer also goes transparent and its own per-section
   wave is hidden so the page-wide wave reads through it,
   instead of running two overlapping wave canvases at the
   bottom of the page. */
.has-page-wave .site-footer {
  background: transparent;
}

.has-page-wave .site-footer > .hero-wave {
  display: none;
}

.hero__content { max-width: 620px; }

/* HeyGuest renders its hero h1 with class="h2", so the hero headline
   uses H2's responsive scale (1.5rem -> 2.25rem -> 3rem) rather than H1's. */
.hero__headline {
  color: var(--color-white);
  margin-bottom: var(--space-6);
  line-height: var(--line-height-tight);
  font-size: var(--font-size-2xl);
}

@media (min-width: 48rem) {
  .hero__headline { font-size: var(--font-size-4xl); }
}

@media (min-width: 64rem) {
  .hero__headline { font-size: var(--font-size-5xl); }
}

.hero__subheadline {
  font-size: var(--font-size-lg);
  line-height: var(--line-height-loose);
  color: var(--color-text-on-dark-muted);
  margin-bottom: var(--space-10);
  max-width: 560px;
}

.hero__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: center;
}


.hero__media {
  position: relative;
  border-radius: var(--radius-2xl);
  overflow: hidden;
  box-shadow: var(--shadow-xl);
  aspect-ratio: 4 / 3;
  background: var(--dark-bg-soft);
}

/* <picture> is inline by default; force it to fill its media
   container so the <img> inside has a definite-height block to
   resolve `height: 100%` against. Without this, percentage
   height collapses to auto and the image sits top-aligned in a
   container with explicit aspect-ratio. Targets the static-image
   hero, the split sections, and the bottom CTA. The hero
   accordion variant doesn't need this -- its <img> uses
   absolute positioning, not percentage sizing. */
.hero__media picture,
.split__media picture,
.bottom-cta__media picture {
  display: block;
  width: 100%;
  height: 100%;
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ----------------------------------------------------------------
   HERO CHANNEL ACCORDION (optional hero.media.type variant)
   ----------------------------------------------------------------
   Five panels in a row inside .hero__media. The active panel
   grows to fill remaining space; the rest collapse to 60px
   "spines" with their title rotated 90deg. Width animates
   700ms ease-in-out. Hover/focus activates a panel; arrow keys
   navigate (see hero-accordion.js).

   When this variant is in use, `.hero__media--accordion` strips
   the wrapper's image-style framing so the panels can each carry
   their own border-radius with visible gaps between them. */
.hero__media--accordion {
  background: transparent;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
  aspect-ratio: auto;     /* the accordion controls its own height */
}

.hero-accordion {
  display: flex;
  flex-direction: row;
  gap: var(--space-4);
  width: 100%;
  height: 450px;
  align-items: stretch;
  padding: 0;
}

.hero-accordion__panel {
  position: relative;
  flex: 0 0 60px;
  height: 100%;
  border: none;
  padding: 0;
  margin: 0;
  background: transparent;
  border-radius: var(--radius-2xl);
  overflow: hidden;
  cursor: pointer;
  outline: none;
  /* flex-basis animates on the active swap; flex-grow / flex-shrink
     are split between resting (0/0) and active (1/1) so flexbox
     redistributes width smoothly. */
  transition: flex-basis 700ms cubic-bezier(0.4, 0, 0.2, 1),
              flex-grow  700ms cubic-bezier(0.4, 0, 0.2, 1);
}

.hero-accordion__panel.is-active {
  flex: 1 1 0;            /* takes the remaining horizontal space */
}

.hero-accordion__panel:focus-visible {
  outline: 3px solid var(--accent-color);
  outline-offset: 3px;
}

.hero-accordion__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.hero-accordion__overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  pointer-events: none;
  transition: background 300ms ease;
}

.hero-accordion__panel.is-active .hero-accordion__overlay {
  background: rgba(0, 0, 0, 0.25);
}

.hero-accordion__title {
  position: absolute;
  left: 50%;
  bottom: 6rem;
  color: var(--color-white);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semi);
  white-space: nowrap;
  /* Pill container: translucent on inactive panels, accent on
     active. Padding rotates with the text on inactive spines, so
     the pill stays around the title in both states. */
  padding: 6px 16px;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 9999px;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  transform: translateX(-50%) rotate(90deg);
  transform-origin: center;
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1),
              bottom 300ms cubic-bezier(0.4, 0, 0.2, 1),
              background-color 300ms ease,
              color 300ms ease;
  pointer-events: none;
}

.hero-accordion__panel.is-active .hero-accordion__title {
  bottom: 1.5rem;
  transform: translateX(-50%) rotate(0deg);
  background-color: var(--accent-color);
  color: var(--accent-fg);
}

/* Mobile fallback (option (a) from the integration brief): the
   row scroll-snaps horizontally; each panel is wide enough to
   read as a card. Active-panel expansion is dropped -- on touch,
   discovery comes from horizontal flicking, not hover. */
@media (max-width: 768px) {
  .hero-accordion {
    height: 360px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    gap: var(--space-3);
  }

  .hero-accordion__panel,
  .hero-accordion__panel.is-active {
    flex: 0 0 75%;
    scroll-snap-align: center;
  }

  .hero-accordion__title {
    bottom: 1.5rem;
    transform: translateX(-50%) rotate(0deg);
  }

  .hero-accordion__overlay {
    background: rgba(0, 0, 0, 0.3);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-accordion__panel,
  .hero-accordion__title,
  .hero-accordion__overlay {
    transition: none;
  }
}

@media (max-width: 1023px) {
  .hero__inner {
    grid-template-columns: 1fr;
    gap: var(--space-12);
  }
  .hero__media { max-width: 600px; margin-inline: auto; }
  /* Centre the headline, subheadline, and CTA when the layout
     stacks. Looks balanced under the centred hero image. */
  .hero__content {
    text-align: center;
    margin-inline: auto;
  }
  .hero__ctas { justify-content: center; }
}

/* ============================================================
   LOGOS BAR (marquee)
   ============================================================ */
.logos-bar {
  background: var(--marquee-bg);
  padding-top: var(--space-14);     /* 3.5rem / 56px */
  padding-bottom: var(--space-14);  /* 3.5rem / 56px - matches top */
  border-top: 1px solid var(--color-gray-100);
}

.logos-bar__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

.logos-bar__viewport {
  position: relative;
  overflow: hidden;
}

.logos-bar__viewport::before,
.logos-bar__viewport::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--marquee-fade-width);
  z-index: 2;
  pointer-events: none;
}

.logos-bar__viewport::before {
  left: 0;
  background: linear-gradient(90deg, var(--marquee-bg), rgba(255,255,255,0));
}

.logos-bar__viewport::after {
  right: 0;
  background: linear-gradient(-90deg, var(--marquee-bg), rgba(255,255,255,0));
}

.marquee {
  display: flex;
  width: max-content;
  gap: 0;
}

.marquee__set {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  animation: marquee var(--marquee-speed) linear infinite;
}

.marquee:hover .marquee__set { animation-play-state: paused; }

.marquee__item {
  flex-shrink: 0;
  padding-inline: var(--space-10);
  display: flex;
  align-items: center;
  justify-content: center;
  height: 48px;
}

/* Color SVG logos render in greyscale at rest -- desaturated
   and slightly dimmed so the marquee reads as ambient social
   proof rather than a row of competing brands. On hover, the
   filter lifts so the brand's natural colours come back. The
   transition smooths the desaturated-to-coloured handoff. */
.marquee__item img,
.marquee__item svg {
  max-height: 40px;
  width: auto;
  filter: grayscale(100%);
  opacity: 0.85;
  transition: filter var(--transition-base),
              opacity var(--transition-base);
}

.marquee__item:hover img,
.marquee__item:hover svg {
  filter: grayscale(0%);
  opacity: 1;
}

/* Placeholder logo tiles (rendered when a logo SVG is missing) */
.marquee__placeholder {
  font-family: var(--font-display);
  font-weight: var(--font-weight-extra);
  font-size: var(--font-size-lg);
  letter-spacing: 0.05em;
  color: var(--brand-navy);
  opacity: 0.55;
  transition: opacity var(--transition-base);
}

.marquee__item:hover .marquee__placeholder { opacity: 1; }

@keyframes marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-100%); }
}

@media (prefers-reduced-motion: reduce) {
  .marquee__set { animation: none; }
}

/* ============================================================
   TWO-COLUMN SPLITS (Challenge / Solution)
   ============================================================ */
.split {
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--color-white);
}

/* Challenge sits directly under the logos bar, which already supplies
   its own 4rem bottom padding, so the challenge gets no top padding. */
#challenge {
  padding-top: 0;
}

.split__inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
  align-items: stretch;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

/* Sweep-in entry animation. Each half (DOM order matches grid
   column order: 1st child = left, 2nd child = right) parks
   off-screen on its own side and slides into place when the
   .split__inner enters the viewport. The wrapper carries
   .reveal solely as the IntersectionObserver trigger; it
   doesn't fade itself. overflow: hidden on the wrapper clips
   the parked positions so they don't trigger horizontal page
   scroll. Opt-in per section via main.js's opts.reveal flag. */
.split__inner.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

/* Same parked-only clip pattern as .pricing__top above. */
.split__inner.reveal:not(.is-visible) {
  overflow: hidden;
}

.split__inner.reveal > * {
  transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1);
}

.split__inner.reveal > *:nth-child(1) { transform: translateX(-200%); }
.split__inner.reveal > *:nth-child(2) { transform: translateX(200%); }

/* Match the parked rule's specificity (nth-child included) so
   the unpark wins on cascade. Without nth-child here, the rule
   above stays applied even after .is-visible is added. */
.split__inner.is-visible > *:nth-child(1),
.split__inner.is-visible > *:nth-child(2) {
  transform: none;
}

@media (max-width: 768px) {
  /* When columns stack vertically, sweep-from-side stops being
     meaningful. Fall back to a fade-up with a short stagger. */
  .split__inner.reveal > *:nth-child(1),
  .split__inner.reveal > *:nth-child(2) {
    transform: translateY(40px);
    opacity: 0;
    transition: transform 600ms ease, opacity 600ms ease;
  }
  .split__inner.is-visible > *:nth-child(1) { transition-delay: 0ms;   transform: none; opacity: 1; }
  .split__inner.is-visible > *:nth-child(2) { transition-delay: 150ms; transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .split__inner.reveal > *,
  .split__inner.is-visible > * {
    transform: none;
    transition: none;
    opacity: 1;
  }
}

/* Asymmetric column ratios matching HeyGuest's alternating layout:
   Challenge row  -> narrow image + wide dark card  (DOM: media, content)
   Solution row   -> wide lime card + narrow image  (DOM: content, media) */
.split--card-dark .split__inner {
  grid-template-columns: 2fr 3fr;
}

.split--card-accent .split__inner {
  grid-template-columns: 3fr 2fr;
}

/* Card variants: content lives inside a colored card */
.split--card-dark .split__content {
  position: relative;
  isolation: isolate;       /* contain .hero-wave's negative z-index */
  overflow: hidden;          /* clip the canvas to the card's rounded corners */
  background: var(--brand-navy);
  color: var(--color-white);
  padding: var(--space-12) var(--space-10);
  border-radius: var(--radius-2xl);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.split--card-dark .split__headline,
.split--card-dark .split__content h2 {
  color: var(--color-white);
}

.split--card-dark .split__body p {
  color: var(--color-text-on-dark-muted);
}

.split--card-accent .split__content {
  background: var(--accent-color);
  color: var(--brand-navy);
  padding: var(--space-12) var(--space-10);
  border-radius: var(--radius-2xl);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.split--card-accent .split__headline,
.split--card-accent .split__content h2 {
  color: var(--brand-navy);
}

.split--card-accent .split__body p {
  color: var(--brand-navy);
  opacity: 0.85;
}

/* Reusable section eyebrow / kicker , sits above the H2 of any
   content section. Renders as small uppercase text so the heading
   group reads as colored kicker -> dark subheading -> muted body.

   Default uses the DEEP accent because most sections sit on a
   light background where the bright accent washes out. Sections
   on a navy / dark surface bump it back to the bright accent so
   the kicker pops instead of receding. */
.section-kicker,
.split__eyebrow {
  display: block;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--site-accent-deep);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-wider);
  margin-bottom: var(--space-3);
}

/* Dark surfaces , Challenge card, Products section, Bottom-CTA
   navy heading card. The bright accent reads strongest here. */
.split--card-dark .section-kicker,
.split--card-dark .split__eyebrow,
.products .section-kicker,
.bottom-cta__heading-card .section-kicker {
  color: var(--site-accent);
}

/* On the accent-card variant the kicker sits over the accent fill
   itself , drop to navy so it stays legible. */
.split--card-accent .section-kicker,
.split--card-accent .split__eyebrow {
  color: var(--brand-navy);
  opacity: 0.7;
}

.split__headline {
  margin-bottom: var(--space-6);
}

.split__body p {
  font-size: var(--font-size-base);
  line-height: var(--line-height-loose);
  margin-bottom: var(--space-5);
}

.split__body p:last-child { margin-bottom: 0; }

.split__cta {
  margin-top: var(--space-8);
}

.split__media {
  position: relative;
  border-radius: var(--radius-2xl);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  background: var(--color-gray-100);
  min-height: 420px;
}

.split__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 1023px) {
  .split__inner,
  .split--card-dark .split__inner,
  .split--card-accent .split__inner {
    grid-template-columns: 1fr;
    gap: var(--space-10);
  }
  .split--media-first .split__media {
    order: -1;
  }
}

/* ============================================================
   KEY OUTCOMES
   ============================================================ */
.outcomes {
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--color-white);
}

.outcomes__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

.outcomes__head {
  text-align: center;
  margin-bottom: var(--space-12);
}

.outcomes__head h2 {
  font-size: var(--font-size-xl);
  color: var(--color-text-muted);
  font-weight: var(--font-weight-medium);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-wide);
}

.outcomes__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-6);
  /* Children park off-screen left/right at startup; clip to the
     grid so they don't trigger horizontal page scroll. */
  overflow: hidden;
}

/* The grid wrapper carries .reveal solely as the IntersectionObserver
   trigger; the wrapper itself shouldn't fade or translate -- the
   children do the slide-in below. Same pattern as pricing modules. */
.outcomes__grid.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

/* Center card stays in place (translates up from below); side
   cards park off-screen left and right. Center-out stagger:
   centre first (0ms), then sides together (250ms). 400ms slide. */
.outcomes__grid .outcome {
  transition: transform 400ms cubic-bezier(0.22, 1, 0.36, 1),
              box-shadow var(--transition-base);
}

.outcomes__grid .outcome:nth-child(1) { transform: translateX(-200%); transition-delay: 250ms; }
.outcomes__grid .outcome:nth-child(2) { transform: translateY(150%);  transition-delay: 0ms;   }
.outcomes__grid .outcome:nth-child(3) { transform: translateX(200%);  transition-delay: 250ms; }

.outcomes__grid.is-visible .outcome {
  transform: none;
}

/* Icon spin: when each card finishes settling, its icon does
   a single 360deg rotateY flip. Delay = card's transition-delay
   + 400ms slide duration. perspective on the icon container
   makes the spin read as a real 3D rotation rather than a flat
   horizontal mirror; backface-visibility: visible keeps the icon
   visible at the back-half of the rotation (180-359deg) so it
   doesn't appear to flicker out mid-spin on some browsers. The
   default animation-fill-mode (none) lets the element fall back
   to its natural transform after the animation -- visually
   identical to rotateY(0) so there's no jump. */
.outcome__icon {
  perspective: 200px;
}

.outcomes__grid.is-visible .outcome__icon svg {
  animation: outcomeIconSpin 350ms cubic-bezier(0.4, 0, 0.2, 1);
  backface-visibility: visible;
}

.outcomes__grid.is-visible .outcome:nth-child(2) .outcome__icon svg { animation-delay: 400ms; }
.outcomes__grid.is-visible .outcome:nth-child(1) .outcome__icon svg,
.outcomes__grid.is-visible .outcome:nth-child(3) .outcome__icon svg { animation-delay: 650ms; }

@keyframes outcomeIconSpin {
  from { transform: rotateY(0); }
  to   { transform: rotateY(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .outcomes__grid .outcome,
  .outcomes__grid.is-visible .outcome { transform: none; transition: none; }
  .outcomes__grid.is-visible .outcome__icon svg { animation: none; }
}

.outcome {
  text-align: center;
  padding: var(--space-12) var(--space-8) var(--space-10);
  background: var(--brand-navy);
  border-radius: var(--radius-2xl);
  color: var(--color-white);
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}

.outcome:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

.outcome__icon {
  width: 80px;
  height: 80px;
  margin: 0 auto var(--space-6);
  display: grid;
  place-items: center;
  background: var(--accent-color);
  border-radius: var(--radius-full);
  color: var(--brand-navy);
  box-shadow: 0 0 0 8px rgba(209, 237, 115, 0.15);
}

.outcome__icon svg {
  width: 36px;
  height: 36px;
  stroke-width: 2.25;
}

.outcome__headline {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  margin-bottom: var(--space-3);
  color: var(--color-white);
  line-height: var(--line-height-snug);
}

.outcome__body {
  font-size: var(--font-size-sm);
  color: var(--color-text-on-dark-muted);
  line-height: var(--line-height-base);
}

@media (max-width: 768px) {
  .outcomes__grid {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }

  /* Past the breakpoint the layout stacks, so "off-screen left /
     right of centre" stops being meaningful. Swap to a generic
     fade-up with a short stagger -- same approach pricing uses
     at narrower viewports. Specificity is bumped via the explicit
     nth-child list so it beats the desktop translate rules above. */
  .outcomes__grid .outcome:nth-child(1),
  .outcomes__grid .outcome:nth-child(2),
  .outcomes__grid .outcome:nth-child(3) {
    transform: translateY(40px);
    opacity: 0;
    transition: transform 600ms ease, opacity 600ms ease;
    transition-delay: 0ms;
  }
  .outcomes__grid.is-visible .outcome:nth-child(1) { transition-delay: 0ms;   transform: none; opacity: 1; }
  .outcomes__grid.is-visible .outcome:nth-child(2) { transition-delay: 150ms; transform: none; opacity: 1; }
  .outcomes__grid.is-visible .outcome:nth-child(3) { transition-delay: 300ms; transform: none; opacity: 1; }
}

/* ============================================================
   PRODUCTS
   ============================================================ */
.products {
  position: relative;
  isolation: isolate; /* contain .hero-wave's negative z-index */
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--brand-navy);
  color: var(--color-white);
  overflow: hidden;
}

.products__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

/* Head: left-aligned intro block with onboarding CTA sitting
   directly below the intro, matching HeyGuest's layout */
.products__head {
  max-width: 680px;
  margin-bottom: var(--space-20);
}

.products__head h2 {
  color: var(--color-white);
  margin-bottom: var(--space-4);
  white-space: pre-line;
}

.products__intro {
  font-size: var(--font-size-base);
  line-height: var(--line-height-relaxed);
  color: var(--color-text-on-dark-muted);
  margin-bottom: var(--space-6);
}

.products__onboarding { /* onboarding CTA inherits .btn styling */ }

/* Pyramid-stagger row.
   Flex layout lets us apply negative margin for real horizontal
   overlap between cards (not possible with gap). Edge cards sit
   lowest, middle card sits highest and is a lighter navy so it
   stands out against the very dark section background. */
.products__grid {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-bottom: var(--space-16);
}

.product-card {
  /* The product cards are rendered as <a> for semantics but the
     URLs in data.js don't currently link anywhere navigable, so
     keep the default cursor instead of the pointer hand. */
  cursor: default;
  --stagger-y: 0;
  flex: 1 1 0;
  min-width: 0;
  max-width: 300px;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--space-12) var(--space-6) var(--space-10);
  background: var(--brand-blue);
  border: none;
  border-radius: var(--radius-2xl);
  text-align: center;
  color: var(--color-white);
  text-decoration: none;
  /* Stagger via margin-top, NOT transform.
     .reveal.is-visible applies `transform: none` which would wipe
     out any translate we tried to use here. margin-top is layout-based
     and is unaffected by the reveal animation. */
  margin-top: var(--stagger-y);
}

/* Horizontal overlap between adjacent cards */
.product-card + .product-card {
  margin-left: calc(var(--space-4) * -1);
}

/* Alternating shades of blue so adjacent same-color cards don't
   visually merge at the overlap zone. Edges use the brighter
   --brand-blue-light (#2E96FF), inner pair uses --brand-blue (#2B75BB). */
.product-card:nth-child(1),
.product-card:nth-child(5) {
  --stagger-y: var(--space-16);
  z-index: 1;
  background: var(--brand-blue-light);
}

.product-card:nth-child(2),
.product-card:nth-child(4) {
  --stagger-y: var(--space-8);
  z-index: 2;
}

.product-card:nth-child(3) {
  --stagger-y: 0;
  z-index: 3;
  background: var(--brand-navy-soft);
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.5);
}

/* Center-out scroll-in: cards visibly travel ~120px up into
   their final position rather than the generic 24px fade-in
   from .reveal. Cards stay fully opaque the whole time, so
   the motion reads as travel, not as fade. The middle card
   lands first; --reveal-delay (set inline by renderProducts)
   pushes the side and end cards out by 120/240ms. */
/* Cards park at translateY(200%) (two card-heights below) so
   they start fully outside the section. Slide is 400ms; combined
   with the 250ms stagger between cards, each card has ~250ms of
   solo airtime before the next card starts moving -- giving its
   icon spin a clear, isolated moment. */
.product-card.reveal {
  opacity: 1;
  transform: translateY(200%);
  transition: transform 400ms cubic-bezier(0.22, 1, 0.36, 1) var(--reveal-delay, 0ms);
}

/* Trigger the slide-up off the products HEAD's is-visible state
   rather than the cards' own. The head sits at the top of the
   section, so its IntersectionObserver fires as soon as the
   section first enters view -- exactly when the cards should
   start moving. The cards' own observers would fire much later
   (their parked rects are below the section), making the motion
   feel delayed. The .product-card.reveal.is-visible fallback
   covers the case where the head class is absent or already
   past the threshold on initial load. */
.products__head.is-visible ~ .products__grid .product-card.reveal,
.product-card.reveal.is-visible {
  transform: none;
}

/* perspective on the card sets up the 3D context for the icon's
   hover spin (rotateY would otherwise read as a flat horizontal
   mirror -- with perspective it reads as a true axis spin). */
.product-card {
  perspective: 800px;
}

/* Single 360deg flip on the icon when its card is hovered. The
   animation only applies during hover, so re-hovering re-triggers
   the spin from the start. */
.product-card__icon {
  width: 7rem;           /* 112px , matches HG's generously sized circles */
  height: 7rem;
  margin: 0 auto var(--space-6);
  border: 2px solid currentColor;
  border-radius: 50%;
  background: transparent;
  display: grid;
  place-items: center;
  color: inherit;
  transform-style: preserve-3d;
}

/* Per-card spins, triggered exclusively by .products__head's
   is-visible state. Cards are translated 200% down at rest, so
   their own IntersectionObservers fire much later (and would
   restart the animation timeline if used as a second trigger) --
   so the head's earlier-firing IO is the single source of truth.
   animation-delay = card's slide-delay + 400ms slide duration. */
.products__head.is-visible ~ .products__grid .product-card .product-card__icon {
  animation-name: cardIconSpin;
  animation-duration: 350ms;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

.products__head.is-visible ~ .products__grid .product-card:nth-child(3) .product-card__icon {
  animation-delay: 400ms;   /* middle: 0 + 400 */
}

.products__head.is-visible ~ .products__grid .product-card:nth-child(2) .product-card__icon,
.products__head.is-visible ~ .products__grid .product-card:nth-child(4) .product-card__icon {
  animation-delay: 650ms;   /* inner pair: 250 + 400 */
}

.products__head.is-visible ~ .products__grid .product-card:nth-child(1) .product-card__icon,
.products__head.is-visible ~ .products__grid .product-card:nth-child(5) .product-card__icon {
  animation-delay: 900ms;   /* outer pair: 500 + 400 */
}

@keyframes cardIconSpin {
  from { transform: rotateY(0); }
  to   { transform: rotateY(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .products__head.is-visible ~ .products__grid .product-card .product-card__icon { animation: none; }
}

.product-card__icon svg {
  width: 3rem;           /* 48px */
  height: 3rem;
  stroke-width: 1.5;
}

.product-card__name {
  font-size: var(--font-size-2xl);   /* 24px */
  font-weight: var(--font-weight-semi);
  color: var(--accent-color);
  margin-bottom: var(--space-2);
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
}

.product-card__tagline {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-normal);
  color: var(--color-white);
  margin: 0;
  margin-bottom: var(--space-8);
  line-height: var(--line-height-snug);
  flex: 1;
}

/* The middle (navy) card sits over a darker background; its accent
   reads stronger against the deeper navy so we leave it as is. The
   side blue cards lift the heading with the site accent on top of
   the saturated blue. */

@media (max-width: 1023px) {
  .products__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-4);
    padding-bottom: 0;
  }
  .product-card {
    --stagger-y: 0;
    flex: none;
    max-width: none;
  }
  .product-card + .product-card {
    margin-left: 0;
  }
  /* Centre the head copy + onboarding CTA when the layout
     stacks, mirroring the hero treatment at this breakpoint. */
  .products__head {
    text-align: center;
    margin-inline: auto;
  }
}

@media (max-width: 540px) {
  .products__grid {
    grid-template-columns: 1fr;
  }
}

/* ============================================================
   PRICING
   Mirrors HeyGuest.ai/pricing: left-aligned copy block with
   focus points, CTA, pricing explanation, then a 5-column
   module card grid. Each card has a navy header bar, tinted
   body with price, and a small accent pill at the bottom.
   ============================================================ */
.pricing {
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--section-bg-alt);
}

.pricing__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

/* 2-column layout: copy left, ROI calculator right */
.pricing__top {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
  align-items: start;
  margin-bottom: var(--space-12);
}

/* Sweep-in entry: copy block (1st child, left) parks off-screen
   left, the ROI calculator (2nd child, right) parks off-screen
   right; both glide in when the wrapper enters the viewport.
   Mirrors the .split__inner.reveal pattern used for Challenge,
   Solution, and Get Started. The wrapper carries .reveal solely
   as the IntersectionObserver trigger. */
.pricing__top.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

/* Clip parked off-screen halves only while they're parked, so
   the wrapper doesn't keep cropping child hover shadows (e.g.
   the Get a Free Demo button) once everything has landed. body
   carries overflow-x: hidden globally, so the brief unpark
   transit can't trigger horizontal page scroll. Same pattern
   as .network-hub__grid.reveal:not(.is-visible). */
.pricing__top.reveal:not(.is-visible) {
  overflow: hidden;
}

.pricing__top.reveal > * {
  transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1);
}

.pricing__top.reveal > *:nth-child(1) { transform: translateX(-200%); }
.pricing__top.reveal > *:nth-child(2) { transform: translateX(200%); }

/* Match parked specificity (nth-child included) so unpark wins. */
.pricing__top.is-visible > *:nth-child(1),
.pricing__top.is-visible > *:nth-child(2) {
  transform: none;
}

@media (max-width: 1023px) {
  .pricing__top {
    grid-template-columns: 1fr;
  }

  /* When columns stack, sweep-from-side stops being meaningful.
     Fall back to fade-up with a short stagger. */
  .pricing__top.reveal > *:nth-child(1),
  .pricing__top.reveal > *:nth-child(2) {
    transform: translateY(40px);
    opacity: 0;
    transition: transform 600ms ease, opacity 600ms ease;
  }
  .pricing__top.is-visible > *:nth-child(1) { transition-delay: 0ms;   transform: none; opacity: 1; }
  .pricing__top.is-visible > *:nth-child(2) { transition-delay: 150ms; transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .pricing__top.reveal > *,
  .pricing__top.is-visible > *:nth-child(1),
  .pricing__top.is-visible > *:nth-child(2) {
    transform: none;
    transition: none;
    opacity: 1;
  }
}

/* -- Copy block (left-aligned intro) ---------------------- */
.pricing__copy {
  max-width: 820px;
}

.pricing__copy h2 {
  margin-bottom: var(--space-5);
}

.pricing__copy > p {
  font-size: var(--font-size-base);
  line-height: var(--line-height-relaxed);
  color: var(--color-text-muted);
  margin-bottom: var(--space-4);
}

.pricing__focus {
  margin-bottom: var(--space-6);
  padding-left: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.pricing__focus li {
  font-size: var(--font-size-base);
  color: var(--color-text);
  line-height: var(--line-height-base);
  list-style: disc;
}

.pricing__focus li::marker {
  color: var(--color-text);
}

.pricing__cta {
  margin-bottom: var(--space-8);
}

.pricing__explanation {
  font-size: var(--font-size-base);
  line-height: var(--line-height-relaxed);
  color: var(--color-text-muted);
  margin-bottom: var(--space-4);
}

.pricing__footnote {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  font-style: italic;
}

/* -- 5-column module card grid ---------------------------- */
.pricing__modules {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  /* Children park off-screen left/right at startup; clip them
     to the grid so they don't trigger horizontal page scroll. */
  overflow: hidden;
}

/* The wrapper carries .reveal solely as an IntersectionObserver
   trigger; the wrapper itself shouldn't fade or translate, since
   that would also fade module 3 (which the brief says should be
   in position from the start). Children handle the slide-in. */
.pricing__modules.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

/* Module 3 stays in place. Modules 1 + 2 park off-screen left,
   4 + 5 park off-screen right, with module 1 / 5 furthest out
   so they read as "flowing in" from beyond their nearer
   neighbour. overflow: hidden on the grid keeps the parked
   positions from causing horizontal scroll. */
.pricing__modules .pricing-module {
  transition: transform 400ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* Center-out stagger using the same methodology as the product
   cards: 250ms stagger between groups, 400ms slide duration --
   each group has solo airtime before the next starts moving so
   the per-module currency spin reads as sequential, not piled. */
.pricing__modules .pricing-module:nth-child(1) { transform: translateX(-300%); transition-delay: 500ms; }
.pricing__modules .pricing-module:nth-child(2) { transform: translateX(-200%); transition-delay: 250ms; }
.pricing__modules .pricing-module:nth-child(3) { transform: translateY(150%);  transition-delay: 0ms;   }
.pricing__modules .pricing-module:nth-child(4) { transform: translateX(200%);  transition-delay: 250ms; }
.pricing__modules .pricing-module:nth-child(5) { transform: translateX(300%);  transition-delay: 500ms; }

.pricing__modules.is-visible .pricing-module {
  transform: none;
}

/* Currency symbol spin: when each card finishes its slide-in,
   the leading currency symbol flips once on its Y axis. Timed
   per nth-child so the animation fires right as the card
   finishes settling. perspective on the parent makes the spin
   read as a real rotation rather than a flat horizontal mirror. */
.pricing-module__price {
  perspective: 200px;
}

.pricing-module__currency {
  display: inline-block;
  position: relative;
  top: -14px;
  font-size: 2.5rem;
  transform-style: preserve-3d;
}

/* Keep the currency symbol and the digits on the same line --
   without this, narrow card widths or word-wrap quirks could
   push the symbol onto its own line. */
.pricing-module__price {
  white-space: nowrap;
}

.pricing__modules.is-visible .pricing-module__currency {
  animation: pricingCurrencySpin 350ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Each spin fires the instant its own module finishes sliding in.
   Delay = module's transition-delay + 400ms slide duration. */
.pricing__modules.is-visible .pricing-module:nth-child(3) .pricing-module__currency { animation-delay: 400ms; }
.pricing__modules.is-visible .pricing-module:nth-child(2) .pricing-module__currency,
.pricing__modules.is-visible .pricing-module:nth-child(4) .pricing-module__currency { animation-delay: 650ms; }
.pricing__modules.is-visible .pricing-module:nth-child(1) .pricing-module__currency,
.pricing__modules.is-visible .pricing-module:nth-child(5) .pricing-module__currency { animation-delay: 900ms; }

@keyframes pricingCurrencySpin {
  from { transform: rotateY(0); }
  to   { transform: rotateY(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .pricing__modules.is-visible .pricing-module__currency { animation: none; }
}

/* On narrow viewports the 5-module row wraps to 3+2 (tablet) or
   2+2+1 (mobile). Past 640px, "left/right of centre" stops being
   meaningful, so swap to a generic vertical fade-in instead.
   Specificity is bumped via the explicit nth-child list so it
   beats the desktop nth-child translate rules above. */
@media (max-width: 640px) {
  .pricing__modules .pricing-module:nth-child(1),
  .pricing__modules .pricing-module:nth-child(2),
  .pricing__modules .pricing-module:nth-child(3),
  .pricing__modules .pricing-module:nth-child(4),
  .pricing__modules .pricing-module:nth-child(5) {
    transform: translateY(40px);
    opacity: 0;
    transition: transform 600ms ease, opacity 600ms ease;
  }
  .pricing__modules.is-visible .pricing-module:nth-child(1),
  .pricing__modules.is-visible .pricing-module:nth-child(2),
  .pricing__modules.is-visible .pricing-module:nth-child(3),
  .pricing__modules.is-visible .pricing-module:nth-child(4),
  .pricing__modules.is-visible .pricing-module:nth-child(5) {
    transform: none;
    opacity: 1;
  }
  /* Mobile modules animate together (~400ms slide); spin fires
     for all of them just after the slide finishes. */
  .pricing__modules.is-visible .pricing-module:nth-child(1) .pricing-module__currency,
  .pricing__modules.is-visible .pricing-module:nth-child(2) .pricing-module__currency,
  .pricing__modules.is-visible .pricing-module:nth-child(3) .pricing-module__currency,
  .pricing__modules.is-visible .pricing-module:nth-child(4) .pricing-module__currency,
  .pricing__modules.is-visible .pricing-module:nth-child(5) .pricing-module__currency {
    animation-delay: 400ms;
  }
}

.pricing-module {
  border-radius: var(--radius-2xl);
  display: flex;
  flex-direction: column;
  background: var(--brand-blue-tint);
  overflow: hidden;
}

/* Each card defines --module-color; header and accent both consume it */
.pricing-module                { --module-color: var(--pricing-color-1); }
.pricing-module:nth-child(2)   { --module-color: var(--pricing-color-2); }
.pricing-module:nth-child(3)   { --module-color: var(--pricing-color-3); }
.pricing-module:nth-child(4)   { --module-color: var(--pricing-color-4); }
.pricing-module:nth-child(5)   { --module-color: var(--pricing-color-5); }

.pricing-module__header {
  background: var(--module-color);
  padding: var(--space-4) var(--space-5);
  text-align: center;
}

.pricing-module__header h3 {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  color: var(--color-white);
  margin-bottom: 0;
}

/* Module 5 header text needs dark color on lime bg */
.pricing-module:nth-child(5) .pricing-module__header h3 {
  color: var(--brand-navy);
}

.pricing-module__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-12) var(--space-4) var(--space-12);
  text-align: center;
}

/* 6. Price and period same color, period font larger */
.pricing-module__price {
  font-size: var(--font-size-6xl);
  font-weight: var(--font-weight-bold);
  color: var(--brand-navy);
  line-height: 1;
  margin-bottom: var(--space-2);
  letter-spacing: var(--letter-spacing-tight);
}

.pricing-module__period {
  font-size: var(--font-size-base);
  color: var(--brand-navy);
  font-weight: var(--font-weight-light);
}

/* Bottom accent: full-width bar, same color as header via --module-color */
.pricing-module__accent {
  height: 12px;
  background: var(--module-color);
}

@media (max-width: 1023px) {
  .pricing__modules {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 640px) {
  .pricing__modules {
    grid-template-columns: 1fr;
  }
  .pricing-module__price {
    font-size: var(--font-size-5xl);
  }
  /* Reset the upward offset on the currency symbol -- at this
     size the cards stack one per row and the price reads better
     baseline-aligned with the digits. */
  .pricing-module__currency {
    top: 0;
  }
}

/* ============================================================
   ROI CALCULATOR (inside pricing section)
   ============================================================ */
.rc-wrap {
  background: var(--color-white);
  border-radius: var(--radius-2xl);
  overflow: hidden;
  box-shadow: 0 4px 24px rgba(10, 23, 38, 0.10);
}

.rc-tabs {
  display: flex;
  background: var(--brand-navy);
}

.rc-tab {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-3);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-on-dark-muted);
  cursor: pointer;
  transition: background var(--transition-base), color var(--transition-base);
  border: none;
  background: transparent;
  white-space: nowrap;
}

.rc-tab svg {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

.rc-tab.active {
  background: var(--color-white);
  color: var(--brand-navy);
  border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
}

.rc-tab:not(.active):hover {
  color: var(--color-white);
}

.rc-card {
  padding: var(--space-6);
}

.rc-title {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  color: var(--brand-navy);
  margin-bottom: var(--space-1);
}

.rc-desc {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-6);
}

.rc-section-title {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semi);
  color: var(--brand-navy);
  margin-bottom: var(--space-4);
}

.rc-grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

.rc-grid-3 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

@media (max-width: 640px) {
  .rc-grid-2, .rc-grid-3 { grid-template-columns: 1fr; }
}

.rc-input-group { display: flex; flex-direction: column; gap: var(--space-2); }

.rc-label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--brand-navy);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.rc-label svg {
  width: 1rem;
  height: 1rem;
  color: var(--accent-color);
}

.rc-slider-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.rc-slider {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 6px;
  background: var(--color-gray-200);
  border-radius: var(--radius-full);
  outline: none;
}

.rc-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  background: var(--accent-color);
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid var(--color-white);
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}

.rc-slider::-moz-range-thumb {
  width: 18px;
  height: 18px;
  background: var(--accent-color);
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid var(--color-white);
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}

.rc-val {
  width: 64px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-gray-200);
  border-radius: var(--radius-lg);
  text-align: center;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--brand-navy);
  font-family: var(--font-primary);
}

.rc-divider {
  border: none;
  height: 1px;
  background: var(--color-gray-200);
  margin-block: var(--space-6);
}

.rc-results {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

@media (max-width: 640px) {
  .rc-results { grid-template-columns: repeat(2, 1fr); }
}

.rc-result {
  background: var(--color-gray-50);
  border: 1px solid var(--color-gray-200);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-3);
  text-align: center;
}

.rc-result-label {
  font-size: 0.6875rem;
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
  display: block;
}

.rc-result-value {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  color: var(--brand-navy);
  display: block;
}

.rc-final {
  background: var(--brand-navy);
  border-radius: var(--radius-2xl);
  padding: var(--space-6) var(--space-5);
  text-align: center;
}

.rc-final-label {
  font-size: var(--font-size-sm);
  color: var(--color-text-on-dark-muted);
  margin-bottom: var(--space-1);
}

.rc-final-sublabel {
  font-size: var(--font-size-xs);
  color: var(--color-text-on-dark-muted);
  margin-bottom: var(--space-4);
}

.rc-final-value {
  font-size: var(--font-size-4xl);
  font-weight: var(--font-weight-bold);
  color: var(--accent-color);
  margin-bottom: var(--space-4);
  line-height: 1;
}

.rc-roi-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--brand-navy-soft);
  color: var(--color-white);
  border-radius: var(--radius-full);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
}

.rc-checkbox-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.rc-checkbox {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-gray-300);
  border-radius: var(--radius-lg);
  cursor: pointer;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
  transition: border-color var(--transition-fast), color var(--transition-fast), background var(--transition-fast);
  user-select: none;
}

.rc-checkbox.checked {
  border-color: var(--accent-color);
  background: var(--accent-light);
  color: var(--brand-navy);
}

.rc-check-mark {
  width: 1rem;
  height: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.rc-check-mark svg {
  width: 0.75rem;
  height: 0.75rem;
}

.rc-checkbox:not(.checked) .rc-check-mark { visibility: hidden; }
.rc-checkbox.checked .rc-check-mark { color: var(--accent-color); }

.rc-pricing-note {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  margin-bottom: var(--space-4);
}

/* ============================================================
   NETWORK HUB (HeyBusiness only)
   ============================================================ */
.network-hub {
  position: relative;
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--brand-navy);
  color: var(--color-white);
  overflow: hidden;
  isolation: isolate;
}

/* Lighter navy radial glow centered behind the cards */
.network-hub::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 65% 60% at 50% 55%,
      rgba(43, 117, 187, 0.35) 0%,
      rgba(22, 40, 79, 0.55) 40%,
      transparent 75%);
  pointer-events: none;
  z-index: -2;
}

/* White dot-grid texture layered on top of the glow */
.network-hub::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(
    circle,
    rgba(255, 255, 255, 0.22) 1.5px,
    transparent 2px);
  background-size: 22px 22px;
  background-position: 0 0;
  mask-image: radial-gradient(ellipse 75% 70% at 50% 50%, #000 25%, transparent 85%);
  -webkit-mask-image: radial-gradient(ellipse 75% 70% at 50% 50%, #000 25%, transparent 85%);
  pointer-events: none;
  z-index: -1;
}

.network-hub__inner {
  position: relative;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

.network-hub__head {
  text-align: center;
  max-width: 720px;
  margin: 0 auto var(--space-12);
}

.network-hub__head h2 {
  margin-bottom: var(--space-5);
  color: var(--color-white);
}

.network-hub__intro {
  font-size: var(--font-size-base);
  line-height: var(--line-height-loose);
  color: var(--color-text-on-dark-muted);
}

.network-hub__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-6);
}

/* Clip parked off-grid cards ONLY during the entry animation
   (.reveal is set, .is-visible isn't yet). Once .is-visible
   fires, overflow goes back to visible so per-card hover glows
   aren't clipped at the grid edges. body has overflow-x:
   hidden globally, which keeps the flight from causing
   horizontal page scroll either way. */
.network-hub__grid.reveal:not(.is-visible) {
  overflow: hidden;
}

/* Card reveal: each of the 9 cards in the 3x3 layout flies in
   from its respective side or corner. Centre card pops via a
   scale-up. Stagger is radial -- centre first (0ms), then the
   four edge cards (200ms), then the four corners (400ms). The
   wrapper carries .reveal solely as the IntersectionObserver
   trigger. */
.network-hub__grid.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

.network-hub__grid.reveal .vertical-card {
  transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1),
              opacity   400ms ease-out;
}

/* Position map (3x3):
     1  2  3        TL  T  TR
     4  5  6        L   C  R
     7  8  9        BL  B  BR  */
.network-hub__grid.reveal .vertical-card:nth-child(1) { transform: translate(-200%, -200%); transition-delay: 400ms; }
.network-hub__grid.reveal .vertical-card:nth-child(2) { transform: translate(0,      -200%); transition-delay: 200ms; }
.network-hub__grid.reveal .vertical-card:nth-child(3) { transform: translate( 200%, -200%); transition-delay: 400ms; }
.network-hub__grid.reveal .vertical-card:nth-child(4) { transform: translate(-200%,  0);    transition-delay: 200ms; }
.network-hub__grid.reveal .vertical-card:nth-child(5) { transform: scale(0.5); opacity: 0;  transition-delay: 0ms;   }
.network-hub__grid.reveal .vertical-card:nth-child(6) { transform: translate( 200%,  0);    transition-delay: 200ms; }
.network-hub__grid.reveal .vertical-card:nth-child(7) { transform: translate(-200%,  200%); transition-delay: 400ms; }
.network-hub__grid.reveal .vertical-card:nth-child(8) { transform: translate(0,      200%); transition-delay: 200ms; }
.network-hub__grid.reveal .vertical-card:nth-child(9) { transform: translate( 200%,  200%); transition-delay: 400ms; }

/* Match parked specificity (nth-child included) so the unpark
   wins on cascade. */
.network-hub__grid.is-visible .vertical-card:nth-child(1),
.network-hub__grid.is-visible .vertical-card:nth-child(2),
.network-hub__grid.is-visible .vertical-card:nth-child(3),
.network-hub__grid.is-visible .vertical-card:nth-child(4),
.network-hub__grid.is-visible .vertical-card:nth-child(5),
.network-hub__grid.is-visible .vertical-card:nth-child(6),
.network-hub__grid.is-visible .vertical-card:nth-child(7),
.network-hub__grid.is-visible .vertical-card:nth-child(8),
.network-hub__grid.is-visible .vertical-card:nth-child(9) {
  transform: none;
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .network-hub__grid.reveal .vertical-card,
  .network-hub__grid.is-visible .vertical-card:nth-child(1),
  .network-hub__grid.is-visible .vertical-card:nth-child(2),
  .network-hub__grid.is-visible .vertical-card:nth-child(3),
  .network-hub__grid.is-visible .vertical-card:nth-child(4),
  .network-hub__grid.is-visible .vertical-card:nth-child(5),
  .network-hub__grid.is-visible .vertical-card:nth-child(6),
  .network-hub__grid.is-visible .vertical-card:nth-child(7),
  .network-hub__grid.is-visible .vertical-card:nth-child(8),
  .network-hub__grid.is-visible .vertical-card:nth-child(9) {
    transform: none;
    opacity: 1;
    transition: none;
  }
}

.vertical-card {
  display: flex;
  flex-direction: column;
  padding: var(--space-8);
  background: var(--color-white);
  border: 1px solid var(--color-gray-200);
  border-radius: var(--radius-xl);
  transition: transform var(--transition-base),
              box-shadow var(--transition-base),
              border-color var(--transition-base);
  text-decoration: none;
  color: inherit;
}

.vertical-card:hover {
  transform: translateY(-4px);
  /* Two-layer shadow: the existing elevation + a soft accent
     glow radiating around the card. 36px blur with a 6px
     positive spread pushes the halo a bit further out, and the
     65%-opacity color-mix gives it more saturation so the glow
     reads clearly on a white network-hub background. */
  box-shadow: var(--shadow-card-hover),
              0 0 36px 6px color-mix(in srgb, var(--accent-color) 65%, transparent);
  border-color: var(--accent-color);
}

.vertical-card__name {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  color: var(--color-text);
  margin-bottom: var(--space-2);
}

.vertical-card__descriptor {
  font-size: var(--font-size-base);
  color: var(--color-text-muted);
  margin-bottom: var(--space-5);
  flex: 1;
}

.vertical-card__url {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semi);
  color: var(--brand-navy);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.vertical-card__url svg {
  width: 14px;
  height: 14px;
  transition: transform var(--transition-base);
}

.vertical-card:hover .vertical-card__url svg {
  transform: translateX(3px);
}

@media (max-width: 1023px) {
  .network-hub__grid { grid-template-columns: repeat(2, 1fr); }

  /* When the 3x3 collapses to 2 columns (or 1), the corner/edge
     mapping stops being meaningful. Fall back to a sequential
     fade-up so cards still feel orchestrated. */
  .network-hub__grid.reveal .vertical-card:nth-child(1),
  .network-hub__grid.reveal .vertical-card:nth-child(2),
  .network-hub__grid.reveal .vertical-card:nth-child(3),
  .network-hub__grid.reveal .vertical-card:nth-child(4),
  .network-hub__grid.reveal .vertical-card:nth-child(5),
  .network-hub__grid.reveal .vertical-card:nth-child(6),
  .network-hub__grid.reveal .vertical-card:nth-child(7),
  .network-hub__grid.reveal .vertical-card:nth-child(8),
  .network-hub__grid.reveal .vertical-card:nth-child(9) {
    transform: translateY(40px);
    opacity: 0;
    transition: transform 500ms ease, opacity 500ms ease;
    transition-delay: 0ms;
  }
  .network-hub__grid.is-visible .vertical-card:nth-child(1) { transition-delay: 0ms;   transform: none; opacity: 1; }
  .network-hub__grid.is-visible .vertical-card:nth-child(2) { transition-delay: 60ms;  transform: none; opacity: 1; }
  .network-hub__grid.is-visible .vertical-card:nth-child(3) { transition-delay: 120ms; transform: none; opacity: 1; }
  .network-hub__grid.is-visible .vertical-card:nth-child(4) { transition-delay: 180ms; transform: none; opacity: 1; }
  .network-hub__grid.is-visible .vertical-card:nth-child(5) { transition-delay: 240ms; transform: none; opacity: 1; }
  .network-hub__grid.is-visible .vertical-card:nth-child(6) { transition-delay: 300ms; transform: none; opacity: 1; }
  .network-hub__grid.is-visible .vertical-card:nth-child(7) { transition-delay: 360ms; transform: none; opacity: 1; }
  .network-hub__grid.is-visible .vertical-card:nth-child(8) { transition-delay: 420ms; transform: none; opacity: 1; }
  .network-hub__grid.is-visible .vertical-card:nth-child(9) { transition-delay: 480ms; transform: none; opacity: 1; }
}

@media (max-width: 640px) {
  .network-hub__grid { grid-template-columns: 1fr; }
}

/* ============================================================
   TESTIMONIALS
   ============================================================ */
.testimonials {
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--color-white);
}

.testimonials__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

.testimonials__head {
  text-align: center;
  margin-bottom: var(--space-16);
}

.testimonials__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8);
}

.testimonial {
  display: flex;
  flex-direction: column;
  padding: var(--space-10) var(--space-8) var(--space-8);
  background: var(--brand-navy);
  border: none;
  border-radius: var(--radius-2xl);
  color: var(--color-white);
  text-align: center;
  align-items: center;
}

.testimonial__avatar {
  width: 112px;
  height: 112px;
  margin-bottom: var(--space-5);
  border-radius: var(--radius-full);
  overflow: hidden;
  flex-shrink: 0;
  /* Initials fallback */
  background: var(--accent-color);
  display: grid;
  place-items: center;
  color: var(--brand-navy);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-xl);
}

.testimonial__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-full);
  display: block;
}

.testimonial__stars {
  display: inline-flex;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
  color: var(--accent-color);
}

.testimonial__stars svg {
  width: 1rem;
  height: 1rem;
  /* Display block inside the wrapping span so transform +
     visual centring play nicely. */
  display: block;
}

.testimonial__star {
  display: inline-block;
  /* Hidden by default so stars don't flash visible before the
     section enters the viewport. .testimonials__grid.is-visible
     swaps in the keyframe animation, which uses both fill so
     the from/to states bracket the delay/post-end periods. */
  opacity: 0;
  transform: scale(0.4);
  transform-origin: center;
}

/* ----- Card sweep-in (3 cards: centre first, then sides) ---- */
.testimonials__grid {
  /* Children park off-screen left/right at startup; clip them
     to the grid so they don't trigger horizontal page scroll. */
  overflow: hidden;
}

.testimonials__grid.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

.testimonials__grid.reveal .testimonial {
  transition: transform 400ms cubic-bezier(0.22, 1, 0.36, 1);
}

.testimonials__grid.reveal .testimonial:nth-child(1) { transform: translateX(-200%); transition-delay: 250ms; }
.testimonials__grid.reveal .testimonial:nth-child(2) { transform: translateY(150%);  transition-delay: 0ms;   }
.testimonials__grid.reveal .testimonial:nth-child(3) { transform: translateX(200%);  transition-delay: 250ms; }

.testimonials__grid.is-visible .testimonial:nth-child(1),
.testimonials__grid.is-visible .testimonial:nth-child(2),
.testimonials__grid.is-visible .testimonial:nth-child(3) {
  transform: none;
}

/* ----- Per-card star reveal + post-reveal bulge ------------ */
/* Sequence per card:
     1. Cards finish settling at ~650ms (200ms slide + 250ms delay
        for sides + a small buffer).
     2. Card N's 5 stars reveal one-by-one (40ms stagger, 200ms
        each — last star fully visible at 360ms after cycle start).
     3. All 5 stars in card N "bulge" together (scale up + back
        over 180ms) right after the last star settles.
     4. Cycle length is 540ms (reveals 360 + bulge 180). The next
        card's stars start at HALF that point (270ms in), so
        cards overlap rather than waiting for full completion.

   Delays use --card-idx (0..2) and --star-idx (0..4) set inline
   on the markup so all the per-card / per-star math collapses
   to two calc() expressions. */
.testimonials__grid.is-visible .testimonial__star {
  animation:
    starReveal 200ms cubic-bezier(0.4, 0, 0.2, 1) both,
    starBulge  180ms ease-out both;
  animation-delay:
    calc(650ms  + var(--card-idx, 0) * 270ms + var(--star-idx, 0) * 40ms),
    calc(1010ms + var(--card-idx, 0) * 270ms);
}

@keyframes starReveal {
  from { opacity: 0; transform: scale(0.4); }
  to   { opacity: 1; transform: scale(1);   }
}

@keyframes starBulge {
  0%   { transform: scale(1);    }
  50%  { transform: scale(1.35); }
  100% { transform: scale(1);    }
}

@media (prefers-reduced-motion: reduce) {
  .testimonial__star { opacity: 1; transform: none; }
  .testimonials__grid.is-visible .testimonial__star { animation: none; }
  .testimonials__grid.reveal .testimonial,
  .testimonials__grid.is-visible .testimonial:nth-child(1),
  .testimonials__grid.is-visible .testimonial:nth-child(2),
  .testimonials__grid.is-visible .testimonial:nth-child(3) {
    transform: none;
    transition: none;
  }
}

@media (max-width: 768px) {
  /* When the 3-column grid stacks 1-column, sides-from-off-screen
     stops being meaningful. Fall back to fade-up. Stars still do
     their reveal+bulge sequence per card. */
  .testimonials__grid.reveal .testimonial:nth-child(1),
  .testimonials__grid.reveal .testimonial:nth-child(2),
  .testimonials__grid.reveal .testimonial:nth-child(3) {
    transform: translateY(40px);
    opacity: 0;
    transition: transform 600ms ease, opacity 600ms ease;
    transition-delay: 0ms;
  }
  .testimonials__grid.is-visible .testimonial:nth-child(1) { transition-delay: 0ms;   transform: none; opacity: 1; }
  .testimonials__grid.is-visible .testimonial:nth-child(2) { transition-delay: 150ms; transform: none; opacity: 1; }
  .testimonials__grid.is-visible .testimonial:nth-child(3) { transition-delay: 300ms; transform: none; opacity: 1; }
}

.testimonial__name {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-light);
  line-height: var(--line-height-tight);
  color: var(--color-white);
  margin-bottom: var(--space-4);
}

.testimonial__company {
  display: inline-block;
  padding: var(--space-2) var(--space-5);
  margin-bottom: var(--space-6);
  background: var(--accent-color);
  color: var(--brand-navy);
  border-radius: var(--radius-full);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  line-height: 1.3;
}

.testimonial__quote {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-light);
  line-height: var(--line-height-relaxed);
  color: var(--color-text-on-dark-muted);
  margin: 0;
  flex: 1;
}

@media (max-width: 1023px) {
  .testimonials__grid {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }
}

/* ============================================================
   BOTTOM CTA
   ============================================================ */
.bottom-cta {
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--color-white);
  position: relative;
}

.bottom-cta__inner {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: var(--space-6);
  align-items: stretch;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

/* Sweep-in entry: image (1st child, left column) parks off-screen
   left; the right-column block (2nd child) parks off-screen right;
   both glide back to position when the wrapper enters the viewport.
   The wrapper carries .reveal solely as the IntersectionObserver
   trigger -- it doesn't fade itself. The checkmark spin sequence
   inside .bottom-cta__benefits has its own .reveal trigger and
   fires independently once the benefits list is visible. */
.bottom-cta__inner.reveal {
  opacity: 1;
  transform: none;
  transition: none;
  /* Clip parked off-screen halves so they don't trigger
     horizontal page scroll. */
  overflow: hidden;
}

.bottom-cta__inner.reveal > * {
  transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1);
}

.bottom-cta__inner.reveal > *:nth-child(1) { transform: translateX(-200%); }
.bottom-cta__inner.reveal > *:nth-child(2) { transform: translateX(200%); }

/* Match parked specificity (nth-child included) so unpark wins. */
.bottom-cta__inner.is-visible > *:nth-child(1),
.bottom-cta__inner.is-visible > *:nth-child(2) {
  transform: none;
}

@media (max-width: 768px) {
  /* When the columns stack, sweep-from-side stops being meaningful.
     Fall back to a fade-up with a short stagger. */
  .bottom-cta__inner.reveal > *:nth-child(1),
  .bottom-cta__inner.reveal > *:nth-child(2) {
    transform: translateY(40px);
    opacity: 0;
    transition: transform 600ms ease, opacity 600ms ease;
  }
  .bottom-cta__inner.is-visible > *:nth-child(1) { transition-delay: 0ms;   transform: none; opacity: 1; }
  .bottom-cta__inner.is-visible > *:nth-child(2) { transition-delay: 150ms; transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .bottom-cta__inner.reveal > *,
  .bottom-cta__inner.is-visible > *:nth-child(1),
  .bottom-cta__inner.is-visible > *:nth-child(2) {
    transform: none;
    transition: none;
    opacity: 1;
  }
}

/* Attention-grabbing bulge on the Get Started CTA, fired after
   everything else in the section has finished animating: 500ms
   for the sweep-in plus ~1s for the checkmark stagger -- 1200ms
   lands ~150ms after the last checkmark settles. Uses transform-
   only so it composes with hover (which sets translateY); during
   the bulge, the bulge transform wins; after, natural returns. */
.bottom-cta__inner.is-visible .btn--dark {
  animation: ctaBulge 320ms cubic-bezier(0.4, 0, 0.2, 1) 1200ms;
}

@keyframes ctaBulge {
  0%   { transform: scale(1);    }
  50%  { transform: scale(1.08); }
  100% { transform: scale(1);    }
}

@media (prefers-reduced-motion: reduce) {
  .bottom-cta__inner.is-visible .btn--dark { animation: none; }
}

.bottom-cta__media {
  position: relative;
  border-radius: var(--radius-2xl);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  background: var(--color-gray-100);
  min-height: 420px;
}

.bottom-cta__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Right column: stacked navy heading card + lime benefits card */
.bottom-cta__right {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.bottom-cta__heading-card {
  background: var(--brand-navy);
  border-radius: var(--radius-2xl);
  padding: var(--space-10) var(--space-10) var(--space-8);
}

.bottom-cta__headline {
  color: var(--color-white);
  margin-bottom: var(--space-4);
  line-height: var(--line-height-tight);
}

.bottom-cta__accent {
  width: 60px;
  height: 3px;
  background: var(--accent-color);
  border-radius: var(--radius-full);
}

.bottom-cta__content {
  background: var(--accent-color);
  color: var(--brand-navy);
  padding: var(--space-8) var(--space-10);
  border-radius: var(--radius-2xl);
  display: flex;
  flex-direction: column;
  flex: 1;
}

.bottom-cta__intro {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  color: var(--brand-navy);
  margin-bottom: var(--space-5);
}

.bottom-cta__benefits {
  margin-bottom: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.bottom-cta__benefits li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--font-size-base);
  color: var(--brand-navy);
  line-height: var(--line-height-snug);
}

.bottom-cta__benefits li svg {
  width: 18px;
  height: 18px;
  color: var(--brand-navy);
  flex-shrink: 0;
  margin-top: 2px;
}

/* The benefits list uses .reveal only as a trigger for the
   per-checkmark spin below. Override base.css's reveal pose
   so the list itself stays fully visible -- only the icons
   animate. */
.bottom-cta__benefits.reveal {
  opacity: 1;
  transform: none;
}

/* Each checkmark spins once when the list enters the viewport.
   Stagger top-to-bottom so the spins read as a quick wave from
   the first benefit down to the last. */
.bottom-cta__benefits.is-visible li svg {
  animation-name: bottomCtaCheckSpin;
  animation-duration: 350ms;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  animation-fill-mode: backwards;
}
.bottom-cta__benefits.is-visible li:nth-child(1) svg { animation-delay:   0ms; }
.bottom-cta__benefits.is-visible li:nth-child(2) svg { animation-delay: 100ms; }
.bottom-cta__benefits.is-visible li:nth-child(3) svg { animation-delay: 200ms; }
.bottom-cta__benefits.is-visible li:nth-child(4) svg { animation-delay: 300ms; }
.bottom-cta__benefits.is-visible li:nth-child(5) svg { animation-delay: 400ms; }
.bottom-cta__benefits.is-visible li:nth-child(6) svg { animation-delay: 500ms; }
.bottom-cta__benefits.is-visible li:nth-child(7) svg { animation-delay: 600ms; }
.bottom-cta__benefits.is-visible li:nth-child(8) svg { animation-delay: 700ms; }

@keyframes bottomCtaCheckSpin {
  from { transform: rotate(0); }
  to   { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .bottom-cta__benefits.is-visible li svg { animation: none; }
}

.bottom-cta__closing {
  font-size: var(--font-size-base);
  color: var(--brand-navy);
  opacity: 0.85;
  margin-bottom: var(--space-6);
  line-height: var(--line-height-relaxed);
}

.bottom-cta__content .btn {
  padding: var(--space-3) var(--space-8);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semi);
  align-self: flex-start;
  border-radius: var(--radius-full);
}

@media (max-width: 1023px) {
  .bottom-cta__inner {
    grid-template-columns: 1fr;
    gap: var(--space-8);
  }
  .bottom-cta__media { max-width: 600px; margin-inline: auto; }
}

/* ============================================================
   CONTACT PAGE
   ============================================================ */
.contact-hero {
  position: relative;
  isolation: isolate;       /* contain the .hero-wave's negative z-index */
  overflow: hidden;
  background: var(--brand-navy);
  color: var(--color-white);
  /* Same trick as .hero: extend up under the fixed nav so the
     transparent nav reads over the navy hero, not the body bg. */
  margin-top: calc(var(--nav-height) * -1);
  padding-top: calc(var(--section-pad-y-top) + var(--nav-height));
  padding-bottom: var(--section-pad-y-bottom);
}

.contact-hero__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

/* 404 / "page not found" content. Sits inside .contact-hero so
   it inherits the dark navy background + wave canvas; the inner
   tweaks here just centre the column and lay out the helpful
   destination buttons in a row. */
.contact-hero__inner.not-found {
  text-align: center;
  max-width: 720px;
}

.contact-hero__inner.not-found h1 {
  color: var(--color-white);
  margin-bottom: var(--space-4);
}

.not-found__body {
  color: var(--color-text-on-dark-muted);
  font-size: var(--font-size-lg);
  line-height: var(--line-height-relaxed);
  margin-bottom: var(--space-8);
}

.not-found__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  list-style: none;
  padding: 0;
  margin: 0;
}

.not-found__links .btn {
  /* Match the pill style used by hero / pricing / contact CTAs. */
  padding: var(--space-3) var(--space-8);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semi);
  border-radius: var(--radius-full);
}

@media (max-width: 640px) {
  .not-found__links { flex-direction: column; }
  .not-found__links .btn { width: 100%; }
}

.contact-hero__inner h1 {
  color: var(--color-white);
}

.contact-form-section {
  background: var(--brand-navy);
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
}

.contact-form__inner {
  max-width: 820px;
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

/* Sidebar: address + phone in white text on navy */
.contact-form__sidebar {
  color: var(--color-text-on-dark-muted);
  padding-top: var(--space-4);
}

.contact-form__sidebar p {
  font-size: var(--font-size-base);
  line-height: var(--line-height-relaxed);
  color: var(--color-text-on-dark-muted);
  margin-bottom: var(--space-4);
}

.contact-form__sidebar a {
  color: var(--accent-color);
  font-weight: var(--font-weight-medium);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.contact-form__sidebar a:hover { color: var(--color-white); }

.contact-form__address {
  font-style: normal;
  font-size: var(--font-size-base);
  line-height: var(--line-height-relaxed);
  color: var(--color-white);
  margin-bottom: var(--space-4);
}

/* Form card */
.contact-form__card {
  background: var(--color-white);
  border-radius: var(--radius-2xl);
  padding: var(--space-10) var(--space-8);
}

.cf-heading {
  font-size: var(--font-size-3xl);
  font-weight: var(--font-weight-light);
  color: var(--brand-navy);
  margin-bottom: var(--space-4);
}

.cf-intro {
  font-size: var(--font-size-base);
  line-height: var(--line-height-relaxed);
  color: var(--color-text-muted);
  margin-bottom: var(--space-8);
}

/* Topic toggle buttons */
.cf-toggle-group {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.cf-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-gray-300);
  border-radius: var(--radius-lg);
  background: transparent;
  font-family: var(--font-primary);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast), background var(--transition-fast);
}

.cf-toggle svg {
  width: 1rem;
  height: 1rem;
}

.cf-toggle.active {
  border-color: #118926;
  color: #118926;
  background: #eefaf2;
}

.cf-toggle:not(.active):hover {
  border-color: var(--color-gray-400);
  color: var(--color-text);
}

.cf-field {
  margin-bottom: var(--space-5);
}

.cf-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
}

@media (max-width: 640px) {
  .cf-row { grid-template-columns: 1fr; }
}

.cf-label {
  display: block;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  color: var(--brand-navy);
  letter-spacing: var(--letter-spacing-wide);
  margin-bottom: var(--space-2);
}

.cf-req { color: #ef4444; }

.cf-input {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-gray-300);
  border-radius: var(--radius-lg);
  font-family: var(--font-primary);
  font-size: var(--font-size-base);
  color: var(--brand-navy);
  background: var(--color-white);
  transition: border-color .15s, box-shadow .15s;
}

.cf-input:focus {
  outline: none;
  border-color: #008b1d;
  box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.08);
}

.cf-input.cf-error {
  border-color: #ef4444;
}

.cf-select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%230A1726' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-4) center;
  padding-right: var(--space-10);
}

.cf-textarea {
  resize: vertical;
  min-height: 120px;
}

.cf-actions {
  text-align: center;
  margin-top: var(--space-6);
}

.cf-submit {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  /* Padding, font-size, font-weight, and pill border-radius
     all come from the shared page-CTA rule above. */
}

/* ============================================================
   LEGAL PAGES (Cookie Notice / Website Terms / Privacy Policy)
   ============================================================
   Long-form text content. White background, single readable
   column, generous top padding so the fixed nav doesn't crowd
   the title. One renderer in main.js (renderLegalPage) drives
   all three; the `id="..."` on the <section> picks which
   config block hydrates here. */
.legal-section {
  /* +nav-height so title clears the fixed nav on first paint. */
  padding-top: calc(var(--section-pad-y-top) + var(--nav-height));
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--color-white);
}

.legal__inner {
  max-width: 960px;
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

/* White card containing the legal content. Sits over the
   page-wide wave (when .has-page-wave is on body) so the wave
   reads as the surround. Mirrors .contact-form__card's
   visual treatment for consistency between the contact page,
   404, and legal pages. */
.legal__card {
  background: var(--color-white);
  border-radius: var(--radius-2xl);
  padding: var(--space-12) var(--space-10);
}

@media (max-width: 640px) {
  .legal__card {
    padding: var(--space-8) var(--space-6);
  }
}

.legal__title {
  /* Matches the contact form's .cf-heading visual treatment so
     "Cookie Notice" / "Privacy Policy" / "Terms of Use" all sit
     at the same scale and weight as "Get in touch". */
  font-size: var(--font-size-3xl);
  font-weight: var(--font-weight-light);
  color: var(--brand-navy);
  margin-bottom: var(--space-6);
}

.legal__effective {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.legal__intro {
  font-size: var(--font-size-lg);
  color: var(--color-text-muted);
  line-height: var(--line-height-relaxed);
  margin-bottom: var(--space-10);
}

.legal__section {
  margin-bottom: var(--space-10);
}

.legal__section h2 {
  font-size: var(--font-size-xl);
  margin-bottom: var(--space-3);
}

.legal__section p {
  margin-bottom: var(--space-4);
  line-height: var(--line-height-relaxed);
  color: var(--color-text);
}

.legal__last-updated {
  margin-top: var(--space-8);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  font-style: italic;
}

/* Cookie-category subsections (Necessary / Functional /
   Analytics / Advertisement). Each renders a small h3 heading,
   a one-paragraph description, and the per-category table. */
.legal__category {
  margin-bottom: var(--space-10);
}

.legal__category h3 {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semi);
  margin-bottom: var(--space-3);
}

.legal__category-desc {
  margin-bottom: var(--space-4);
  line-height: var(--line-height-relaxed);
  color: var(--color-text);
}

/* Wrapper allows horizontal scroll on narrow viewports rather
   than letting the table overflow the readable column. */
.legal__table-wrap {
  overflow-x: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-gray-200);
}

.legal__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-sm);
  background: var(--color-white);
}

.legal__table th,
.legal__table td {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-gray-200);
  vertical-align: top;
}

.legal__table thead th {
  background: var(--color-gray-50);
  font-weight: var(--font-weight-semi);
  color: var(--brand-navy);
  border-bottom: 2px solid var(--color-gray-200);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: var(--font-size-xs);
}

.legal__table tbody tr:nth-child(even) {
  background: var(--color-gray-50);
}

.legal__table tbody tr:last-child td {
  border-bottom: none;
}

.legal__table td code {
  font-family: ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, monospace;
  font-size: 0.85em;
  background: rgba(0, 0, 0, 0.04);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

/* On narrow viewports, the description column is the longest;
   give the first two columns a tighter cap so it gets the
   remaining space rather than wrapping the cookie name. */
.legal__table th:nth-child(1),
.legal__table td:nth-child(1) { width: 22%; min-width: 9ch; }
.legal__table th:nth-child(2),
.legal__table td:nth-child(2) { width: 18%; min-width: 8ch; }

/* Honeypot field. Off-screen rather than display:none -- some
   bots skip elements that are display:none, but happily fill
   absolutely-positioned inputs. tabindex=-1 + aria-hidden in
   the markup keep it out of the keyboard / screen-reader path. */
.cf-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* Per-field validation message slot. aria-live="polite" on the
   element means assistive tech announces a new message when it
   gets populated. The slot is empty by default; cf-error on the
   input pairs with this message visually. */
.cf-field-error {
  display: block;
  color: #d23a3a;
  font-size: var(--font-size-sm);
  margin-top: var(--space-1);
  min-height: 1em;
}
.cf-field-error:empty {
  min-height: 0;
}
.cf-input.cf-error {
  border-color: #d23a3a;
}

.cf-privacy {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  margin-top: var(--space-3);
}

.cf-success {
  text-align: center;
  padding: var(--space-12) var(--space-6);
}

.cf-success-icon {
  width: 56px;
  height: 56px;
  margin: 0 auto var(--space-5);
  display: grid;
  place-items: center;
  background: var(--accent-color);
  border-radius: 50%;
  font-size: var(--font-size-2xl);
  color: var(--brand-navy);
}

.cf-success h3 {
  font-size: var(--font-size-2xl);
  color: var(--brand-navy);
  margin-bottom: var(--space-3);
}

.cf-success p {
  color: var(--color-text-muted);
}

/* ============================================================
   MODAL
   ============================================================ */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  background: rgba(10, 23, 38, 0.6);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-base);
}

.modal-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
}

.modal {
  position: relative;
  background: var(--color-white);
  border-radius: var(--radius-2xl);
  padding: var(--space-12) var(--space-10);
  max-width: 440px;
  width: 100%;
  text-align: center;
  box-shadow: 0 24px 64px rgba(10, 23, 38, 0.25);
  transform: translateY(16px);
  transition: transform var(--transition-base);
}

.modal-overlay.is-open .modal {
  transform: translateY(0);
}

.modal__close {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-full);
  color: var(--color-text-muted);
  transition: background var(--transition-fast), color var(--transition-fast);
}

.modal__close:hover {
  background: var(--color-gray-100);
  color: var(--color-text);
}

.modal__close svg {
  width: 1.25rem;
  height: 1.25rem;
}

.modal__title {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-medium);
  color: var(--brand-navy);
  margin-bottom: var(--space-4);
}

.modal__body {
  font-size: var(--font-size-base);
  color: var(--color-text-muted);
  line-height: var(--line-height-relaxed);
}

/* Inline prose links share the nav/footer's wipe-in underline.
   We use a background-image gradient (not an absolute ::after)
   so the underline tracks correctly when a link wraps onto a
   second line -- ::after only paints the first line box. */
.modal__body a,
.legal__intro a,
.legal__section p a,
.legal__section li a {
  color: var(--brand-blue);
  font-weight: var(--font-weight-medium);
  text-decoration: none;
  background-image: linear-gradient(var(--accent-color), var(--accent-color));
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1px;
  padding-bottom: 2px;
  transition:
    background-size var(--transition-base),
    color var(--transition-fast);
}

.modal__body a:hover,
.modal__body a:focus-visible,
.legal__intro a:hover,
.legal__intro a:focus-visible,
.legal__section p a:hover,
.legal__section p a:focus-visible,
.legal__section li a:hover,
.legal__section li a:focus-visible {
  color: var(--accent-color);
  background-size: 100% 1px;
}

@media (prefers-reduced-motion: reduce) {
  .modal__body a,
  .legal__intro a,
  .legal__section p a,
  .legal__section li a {
    transition: none;
    background-size: 100% 1px;
  }
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer {
  position: relative;
  isolation: isolate;        /* contain .hero-wave's negative z-index */
  overflow: hidden;
  background: var(--brand-navy); /* match other wave hosts so the bg the canvas covers is consistent */
  color: var(--color-text-on-dark-muted);
  padding-block: var(--space-16) var(--space-8);
}

.site-footer__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

.site-footer__top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: var(--space-12);
  padding-bottom: var(--space-12);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.site-footer__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-white);
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-extra);
  margin-bottom: var(--space-5);
}

.site-footer__brand .site-nav__brand-mark { background: var(--accent-color); }

.site-footer__tagline {
  font-size: var(--font-size-sm);
  line-height: var(--line-height-loose);
  color: var(--color-text-on-dark-muted);
  max-width: 360px;
}

.site-footer__col-title {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--color-white);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-wide);
  margin-bottom: var(--space-5);
}

.site-footer__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.site-footer__list a {
  font-size: var(--font-size-sm);
  color: var(--color-text-on-dark-muted);
  transition: color var(--transition-fast);
}

.site-footer__list a:hover { color: var(--accent-on-dark); }

.site-footer__network {
  padding-block: var(--space-10);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.site-footer__network-title {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--color-white);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-wide);
  margin-bottom: var(--space-5);
}

.site-footer__network-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3) var(--space-6);
}

.site-footer__network-grid a {
  justify-self: start; /* shrink to text width so the underline pseudo doesn't span the whole 1fr column */
  font-size: var(--font-size-sm);
  color: var(--color-text-on-dark-muted);
  padding-block: var(--space-1);
  transition: color var(--transition-fast);
}

.site-footer__network-grid a:hover { color: var(--accent-on-dark); }

.site-footer__bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding-top: var(--space-8);
}

.site-footer__copyright {
  font-size: var(--font-size-sm);
  color: var(--color-text-on-dark-muted);
}

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
}

.site-footer__legal a {
  font-size: var(--font-size-sm);
  color: var(--color-text-on-dark-muted);
}

.site-footer__legal a:hover { color: var(--accent-on-dark); }

/* Wipe-in underline on every footer link: 1px (thinner than
   the nav's 2px), grows from the left on hover. Same scaleX
   pattern as .site-nav__link::after. */
.site-footer__list a,
.site-footer__network-grid a,
.site-footer__legal a {
  position: relative;
  display: inline-block;
}

.site-footer__list a::after,
.site-footer__network-grid a::after,
.site-footer__legal a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background-color: var(--accent-on-dark);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--transition-base);
}

.site-footer__list a:hover::after,
.site-footer__network-grid a:hover::after,
.site-footer__legal a:hover::after {
  transform: scaleX(1);
}

/* Hey Network links carry padding-block, which pushed their
   underline below where it sits on the other footer links.
   Lift it by the bottom padding amount so all three groups
   share a baseline. */
.site-footer__network-grid a::after {
  bottom: var(--space-1);
}

@media (max-width: 1023px) {
  .site-footer__top {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-10);
  }
  .site-footer__brand-col { grid-column: 1 / -1; }
  .site-footer__network-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 640px) {
  /* Footer stays in 2-col here so Company + Get in touch sit
     side by side. The 1023px breakpoint already spans
     site-footer__brand-col across both columns above them. */
  .site-footer__top { grid-template-columns: 1fr 1fr; }
  .site-footer__network-grid { grid-template-columns: 1fr; }
  .site-footer__bottom { flex-direction: column; align-items: flex-start; }
}

/* ============================================================
   Text rotator -- inline H1 word that swaps phrases on a
   timer with per-character slide+fade. Vanilla port of the
   React/framer-motion "TextRotate" pattern; behavior lives
   in main.js (initTextRotators).
   ============================================================ */

/* Wrapper around the connector word + rotator + suffix that
   prevents the rotator from ever wrapping onto a line by
   itself. The wrapper itself can still wrap as a unit; only
   internal breaking is forbidden. */
.hero__headline-nobreak {
  white-space: nowrap;
}

.text-rotate {
  display: inline-flex;
  align-items: baseline;
  vertical-align: baseline;
  color: var(--site-accent);
  white-space: nowrap;
}

.text-rotate__inner {
  display: inline-flex;
}

.text-rotate__char {
  display: inline-block;
  transform: translateY(0);
  opacity: 1;
  transition:
    transform 380ms cubic-bezier(0.22, 1, 0.36, 1),
    opacity 250ms ease-out;
  will-change: transform, opacity;
}

/* Outgoing pose: slide up out of the line and fade. */
.text-rotate.is-leaving .text-rotate__char {
  transform: translateY(-100%);
  opacity: 0;
}

/* Entry pose: chars start below the baseline with no
   transition; the class is dropped on the next frame so
   the chars animate to the resting pose. */
.text-rotate__char.is-fresh {
  transform: translateY(100%);
  opacity: 0;
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .text-rotate__char,
  .text-rotate.is-leaving .text-rotate__char,
  .text-rotate__char.is-fresh {
    transform: none;
    opacity: 1;
    transition: none;
  }
}


/* ============================================================
   About page (heybusiness/about.html, heyauto/about.html)
   ------------------------------------------------------------
   Four sections rendered by shared/scripts/about.js:
   hero (headline + subhead + 4-up stats), story, team grid
   (15 members), values grid (6 cards). The page closes on
   the standard #bottom-cta block painted by main.js, so no
   bottom-CTA styles needed here.

   Spacing + type scale follow the existing section conventions
   (.section-kicker, --section-pad-*, --container-max) so all
   pages share the same rhythm.
   ============================================================ */

/* -- Hero --------------------------------------------------
   Two-column: text + stats on the left, lifestyle image on
   the right. Stacks on tablet so the image stays large enough
   to read. Base h1/h2/p rules force --color-text everywhere,
   which would be invisible on navy -- the explicit color
   overrides below put them back to white.

   Layout mirrors .hero on the home page: position:relative +
   isolation:isolate + overflow:hidden contain the animated
   canvas.hero-wave that about.js injects as the first child.
   The negative margin-top pulls the hero up under the fixed
   nav so the wave canvas extends behind the nav, then extra
   padding-top compensates so content sits below the nav. */
.about-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--brand-navy);
  color: var(--color-white);
  margin-top: calc(var(--nav-height) * -1);
  padding-top: calc(var(--section-pad-y-top) + var(--nav-height) + var(--space-12));
  padding-bottom: var(--space-20);
}

.about-hero__inner {
  position: relative;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: var(--space-16);
  align-items: center;
}

.about-hero__text {
  /* Stats sit inside the left column so the right column can
     be a clean image panel matching the standard hero rhythm
     used on every other page. */
}

.about-hero__headline {
  color: var(--color-white);
  font-size: var(--font-size-5xl);
  line-height: var(--line-height-tight);
  font-weight: var(--font-weight-bold);
  margin: 0 0 var(--space-6);
  max-width: 18ch;
}

.about-hero__subhead {
  color: rgba(255, 255, 255, 0.82);
  font-size: var(--font-size-lg);
  line-height: var(--line-height-relaxed);
  margin: 0 0 var(--space-10);
  max-width: 50ch;
}

.about-hero__stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-8) var(--space-10);
  margin: 0;
}

/* Icon-left layout matching the value cards: stat is a 2-col
   grid (icon | text), icon spans both rows so it stays vertically
   aligned to the top of the number. */
.about-hero__stat {
  padding: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-4);
  align-items: start;
}

.about-hero__stat-icon {
  grid-column: 1;
  grid-row: 1 / span 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.08);
  color: var(--site-accent);
  margin-bottom: 0;
  align-self: start;
}

.about-hero__stat-icon .about-icon {
  width: 30px;
  height: 30px;
}

.about-hero__stat-number {
  grid-column: 2;
  grid-row: 1;
  font-size: var(--font-size-4xl);
  font-weight: var(--font-weight-bold);
  color: var(--site-accent);
  line-height: 1;
  margin: 0;
  /* tabular-nums keeps every digit the same width, so the
     count-up animation doesn't reflow the row as the number
     grows from 0 to its target. */
  font-variant-numeric: tabular-nums;
}

.about-hero__stat-label {
  grid-column: 2;
  grid-row: 2;
  font-size: var(--font-size-sm);
  color: rgba(255, 255, 255, 0.7);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-wide);
  margin: var(--space-2) 0 0;
}

.about-hero__media {
  border-radius: var(--radius-2xl);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}

.about-hero__media picture,
.about-hero__media img {
  display: block;
  width: 100%;
  height: auto;
}

@media (max-width: 1024px) {
  .about-hero__inner { grid-template-columns: 1fr; gap: var(--space-12); }
  .about-hero__headline { max-width: 22ch; }
}

@media (max-width: 768px) {
  .about-hero__headline { font-size: var(--font-size-4xl); }
  .about-hero__stats { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); }
}

/* -- Story -------------------------------------------------- */
.about-story {
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--color-white);
}

.about-story__inner {
  max-width: 880px;
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
  text-align: center;
}

/* Story eyebrow mirrors the closing line's display treatment
   (font-size-3xl, accent-deep, bold) so the section opens and
   closes on the same visual note. Overrides the .section-kicker
   uppercase/small kicker defaults since both classes sit on
   the same element. */
.about-story__eyebrow {
  display: block;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--font-size-3xl);
  line-height: var(--line-height-tight);
  font-weight: var(--font-weight-bold);
  color: var(--site-accent-deep);
  margin-bottom: var(--space-8);
}

@media (max-width: 768px) {
  .about-story__eyebrow { font-size: var(--font-size-2xl); }
}

.about-story__body {
  text-align: left;
  font-size: var(--font-size-lg);
  line-height: var(--line-height-relaxed);
  color: var(--color-text);
}

.about-story__body p {
  margin: 0 0 var(--space-6);
}

.about-story__body p:last-child {
  margin-bottom: 0;
}

/* Emphasised closing line. Echoes iovox's "The Power of Voice"
   treatment -- standalone, larger, accent-coloured. Sits below
   the three story paragraphs with extra breathing room. */
.about-story__closing {
  font-size: var(--font-size-3xl);
  line-height: var(--line-height-tight);
  font-weight: var(--font-weight-bold);
  color: var(--site-accent-deep);
  margin-top: var(--space-12);
  margin-bottom: 0;
}

@media (max-width: 768px) {
  .about-story__closing { font-size: var(--font-size-2xl); }
}

/* -- Team grid --------------------------------------------- */
.about-team {
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--color-gray-50, #F9FAFB);
}

.about-team__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

.about-team__eyebrow {
  display: block;
  text-align: center;
  margin-bottom: var(--space-4);
}

.about-team__headline {
  text-align: center;
  font-size: var(--font-size-3xl);
  margin: 0 auto var(--space-12);
  max-width: 30ch;
}

/* Compact 5-up grid matching iovox's About page rhythm: 15
   members fit in three clean rows at desktop, the photo is
   capped via a narrower grid max-width so headshots stop
   blowing up at 1600px-wide viewports. */
.team-grid {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-8) var(--space-6);
  max-width: 1200px;
}

@media (max-width: 1280px) { .team-grid { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 1024px) { .team-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 640px)  { .team-grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-6) var(--space-4); } }

/* Each card is a stack: square photo on top, name + title +
   LinkedIn icon below. Card is text-on-page (no white surface,
   no shadow) so the section reads as a directory of people
   rather than a feature grid -- matches iovox's flatter
   treatment. */
.team-card {
  background: transparent;
  text-align: left;
}

.team-card__photo {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--color-gray-100, #F3F4F6);
  border-radius: var(--radius-xl);
  overflow: hidden;
  margin-bottom: var(--space-3);
}

.team-card__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.team-card__body {
  position: relative;
  padding: 0;
}

.team-card__name {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-bold);
  margin: 0 0 2px;
  color: var(--color-text);
  line-height: var(--line-height-snug, 1.3);
}

.team-card__title {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin: 0;
  padding-right: var(--space-6);
  line-height: var(--line-height-snug, 1.35);
}

/* LinkedIn icon sits inline at the bottom-right of the body
   so the icon hangs off the title line. Belinda's card omits
   it (linkedinUrl: null in ABOUT_BASE) and looks identical
   minus the icon -- mirrors the iovox treatment. */
.team-card__linkedin {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  transition: color var(--transition-fast);
}

.team-card__linkedin:hover,
.team-card__linkedin:focus-visible {
  color: var(--brand-blue);
}

.team-card__linkedin svg {
  width: 100%;
  height: 100%;
}

/* -- Values grid ------------------------------------------- */
.about-values {
  padding-top: var(--section-pad-y-top);
  padding-bottom: var(--section-pad-y-bottom);
  background: var(--color-white);
}

.about-values__inner {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-x);
}

.about-values__eyebrow {
  display: block;
  text-align: center;
  margin-bottom: var(--space-4);
}

.about-values__headline {
  text-align: center;
  font-size: var(--font-size-4xl);
  margin: 0 auto var(--space-12);
  max-width: 30ch;
}

.values-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-6);
  max-width: 1100px;
  margin-inline: auto;
}

@media (max-width: 1024px) { .values-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px)  { .values-grid { grid-template-columns: 1fr; } }

/* Icon-left layout: card is a 2-col grid with the icon in
   column 1 spanning both rows so it stays vertically aligned
   to the top of the heading, and name + description flow in
   column 2. No HTML wrapper needed -- about.js renders icon,
   name, and description as flat siblings. */
.value-card {
  background: var(--brand-blue-tint);
  border-radius: var(--radius-2xl);
  padding: var(--space-6);
  border-left: 3px solid var(--site-accent);
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-5);
  align-items: start;
}

/* Larger circle (64x64) than the hero stats since the value
   cards have more horizontal real estate; matches the
   "feature-card with icon" pattern used elsewhere on the
   site. */
.value-card__icon {
  grid-column: 1;
  grid-row: 1 / span 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: var(--radius-full);
  background: var(--color-white);
  color: var(--site-accent-deep);
  margin-bottom: 0;
  align-self: start;
}

.value-card__icon .about-icon {
  width: 32px;
  height: 32px;
}

.value-card__name {
  grid-column: 2;
  grid-row: 1;
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  margin: 0 0 var(--space-2);
  color: var(--color-text);
}

.value-card__description {
  grid-column: 2;
  grid-row: 2;
}

.value-card__description {
  font-size: var(--font-size-base);
  line-height: var(--line-height-relaxed);
  color: var(--color-text-muted);
  margin: 0;
}

/* -- About reveals -----------------------------------------
   Team grid keeps a simple fade-up with a per-card
   --reveal-delay (set inline by about.js) for a row-wave
   landing. The wrapper itself doesn't fade -- it carries
   .reveal solely as the IntersectionObserver trigger. */
.team-grid.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

.team-grid.reveal .team-card {
  opacity: 0;
  transform: translateY(20px);
  transition:
    opacity 500ms ease var(--reveal-delay, 0ms),
    transform 500ms cubic-bezier(0.22, 1, 0.36, 1) var(--reveal-delay, 0ms);
}

.team-grid.reveal.is-visible .team-card {
  opacity: 1;
  transform: none;
}

/* Values grid uses the same fly-in pattern as the home page's
   Network Hub: each card parks off the grid in its corresponding
   direction, then flies in when the grid scrolls into view.
   Layout is 3x2 (no center cell), so the center column (T, B)
   lands first and the four corners land 200ms behind. */
.values-grid.reveal {
  opacity: 1;
  transform: none;
  transition: none;
}

/* Clip parked off-grid cards only while they're parked, mirroring
   .network-hub__grid.reveal:not(.is-visible). body's global
   overflow-x: hidden keeps the flight from triggering horizontal
   page scroll either way. */
.values-grid.reveal:not(.is-visible) {
  overflow: hidden;
}

.values-grid.reveal .value-card {
  transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1),
              opacity   400ms ease-out;
}

/* 3x2 position map:
     1  2  3        TL  T  TR
     4  5  6        BL  B  BR
   Center column (T, B) flies in straight from above/below at
   0ms; corners fly in diagonally at 200ms so the row settles
   in two beats rather than landing all at once. */
.values-grid.reveal .value-card:nth-child(1) { transform: translate(-200%, -100%); transition-delay: 200ms; }
.values-grid.reveal .value-card:nth-child(2) { transform: translate(0,      -200%); transition-delay: 0ms;   }
.values-grid.reveal .value-card:nth-child(3) { transform: translate( 200%, -100%); transition-delay: 200ms; }
.values-grid.reveal .value-card:nth-child(4) { transform: translate(-200%,  100%); transition-delay: 200ms; }
.values-grid.reveal .value-card:nth-child(5) { transform: translate(0,       200%); transition-delay: 0ms;   }
.values-grid.reveal .value-card:nth-child(6) { transform: translate( 200%,  100%); transition-delay: 200ms; }

/* Match parked specificity (nth-child included) so the unpark
   wins on cascade. */
.values-grid.is-visible .value-card:nth-child(1),
.values-grid.is-visible .value-card:nth-child(2),
.values-grid.is-visible .value-card:nth-child(3),
.values-grid.is-visible .value-card:nth-child(4),
.values-grid.is-visible .value-card:nth-child(5),
.values-grid.is-visible .value-card:nth-child(6) {
  transform: none;
}

/* Below the desktop 3-up layout the grid collapses to 2-up
   then 1-up; the 3x2 nth-child positions don't map there, so
   fall back to a simpler horizontal/vertical entrance.
   :not(.is-visible) means the rule auto-releases once the
   IntersectionObserver fires -- without it, these rules would
   keep the cards parked off-screen even after .is-visible
   since they share specificity with the unpark rules above
   but were defined later in the cascade. */
@media (max-width: 1024px) {
  .values-grid.reveal:not(.is-visible) .value-card:nth-child(odd)  { transform: translateX(-200%); transition-delay: 0ms;   }
  .values-grid.reveal:not(.is-visible) .value-card:nth-child(even) { transform: translateX( 200%); transition-delay: 100ms; }
}

@media (max-width: 600px) {
  .values-grid.reveal:not(.is-visible) .value-card {
    transform: translateY(40px);
    opacity: 0;
    transition-delay: 0ms;
  }
}

@media (prefers-reduced-motion: reduce) {
  .team-grid.reveal .team-card,
  .values-grid.reveal .value-card,
  .values-grid.is-visible .value-card:nth-child(1),
  .values-grid.is-visible .value-card:nth-child(2),
  .values-grid.is-visible .value-card:nth-child(3),
  .values-grid.is-visible .value-card:nth-child(4),
  .values-grid.is-visible .value-card:nth-child(5),
  .values-grid.is-visible .value-card:nth-child(6) {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
