/* Border-box reset. Without this, any width:100% element that also has
   padding overflows the viewport, which is what the old nav wrapper did,
   pushing every shared-header page 40px wide at 360px. index.html never
   showed it because the Tailwind CDN ships its own preflight reset; these
   pages have no such safety net. */
*, *::before, *::after { box-sizing: border-box; }

/* ==========================================================
   TYPE RULE — do not change. Montserrat for everything, Oswald
   for headings. Nothing on this site uses any other family.
   Without this on body, any element that does not declare a
   family (form controls especially, which never inherit) fell
   back to the browser default serif.
   ========================================================== */
body {
  font-family: "Montserrat", Arial, sans-serif;
  color: #46584f;
}
h1, h2, h3, h4, h5, h6 { font-family: "Oswald", Arial, sans-serif; }
button, input, textarea, select { font-family: inherit; }

/* Shared site chrome — loaded by every page. Fonts + nav + prose primitives. */
/* Transparent overlay bar, exactly as the homepage had it: no background
   of its own, just a 12px backdrop blur so whatever scrolls underneath
   blurs through it, plus the faint hairline it carried. The solid white
   version replaced this and lost the effect entirely. */


/* Mobile burger — visible < 900px */

/* Mobile drawer */

/* --- Page hero primitives --- */
.v-page {
  background: #ffffff;
  color: #46584f;
  font-family: "Montserrat", Arial, sans-serif;
}
.v-page__wrap {
  max-width: 1240px; margin: 0 auto;
  padding: clamp(64px, 10vw, 128px) clamp(24px, 4vw, 64px);
}
.v-page__eyebrow {
  display: inline-block;
  font-family: "Montserrat", Arial, sans-serif;
  font-weight: 700; font-size: 12px;
  letter-spacing: 0.32em; text-transform: uppercase;
  color: #0b7a58; margin-bottom: 24px;
}
.v-page__title {
  font-family: "Oswald", Arial, sans-serif;
  font-weight: 700; text-transform: uppercase;
  font-size: clamp(2.25rem, 5.6vw, 5.25rem);
  line-height: 0.98;
  letter-spacing: -0.02em;
  color: #0e1f19;
  margin: 0 0 clamp(20px, 3vw, 32px);
  max-width: 18ch;
}
.v-page__title .em { color: #109e75; }
.v-page__lead {
  font-family: "Montserrat", Arial, sans-serif;
  font-weight: 400;
  font-size: clamp(1.05rem, 1.3vw, 1.35rem);
  line-height: 1.6;
  color: #46584f;
  max-width: 62ch;
  margin: 0;
}

/* ==========================================================
   Nav motion — this is what the homepage nav did that the
   shared one did not: the logo lifts into an emerald glow, and
   each link rolls its label up while an underline grows out
   from the centre. Kept here so every page behaves identically.
   ========================================================== */
.v-logo {
  position: relative; display: inline-flex;
  transition: transform 500ms cubic-bezier(.2,.8,.2,1);
}
.v-logo::after {
  content: ""; position: absolute; inset: -12px; border-radius: 20px;
  background: radial-gradient(closest-side, rgba(16,158,117,0.25), transparent 70%);
  opacity: 0; transition: opacity 500ms ease; pointer-events: none; z-index: -1;
}
.v-logo:hover { transform: scale(1.04); }
.v-logo:hover::after { opacity: 1; }

.v-navlink {
  position: relative; display: inline-block; overflow: hidden;
  height: 1.6em; line-height: 1.6em; padding: 0 2px;
  font-family: "Montserrat", Arial, sans-serif;
  font-size: clamp(15px, 1.05vw, 18px); font-weight: 500;
  color: #46584f; text-decoration: none;
}
.v-navlink__row {
  display: block;
  transition: transform 620ms cubic-bezier(.65,0,.35,1);
  will-change: transform;
}
.v-navlink__row--in { position: absolute; top: 100%; left: 0; right: 0; color: #0b7a58; }
.v-navlink:hover .v-navlink__row--out,
.v-navlink:hover .v-navlink__row--in { transform: translateY(-100%); }
.v-navlink[aria-current="page"] { color: #0e1f19; font-weight: 600; }
.v-navlink__underline {
  position: absolute; left: 50%; bottom: -2px;
  width: 0; height: 2px; background: #109e75;
  transform: translateX(-50%);
  transition: width 620ms cubic-bezier(.22,.9,.28,1);
  box-shadow: 0 0 12px rgba(20,225,133,0.7);
}
.v-navlink:hover .v-navlink__underline,
.v-navlink[aria-current="page"] .v-navlink__underline { width: 100%; }

@media (prefers-reduced-motion: reduce) {
  .v-logo, .v-logo::after, .v-navlink__row, .v-navlink__underline { transition: none !important; }
}

/* ==========================================================
   Footer — a conventional multi-column footer.

   Replaced a two-panel layout (deep-emerald slab + light panel,
   420-580px tall) that butted straight against the final CTA. It
   looked distinctive and read as chaotic: no separation from the
   section above it, and only two link groups.

   Now: dark band, four columns, a rule, and a bottom bar. The dark
   surface is what separates it from the white CTA above, and the
   generous padding-top is the breathing room that separation needs.
   ========================================================== */
.v-ft {
  position: relative;
  background: #04120c;
  color: #a8c4b8;
  padding: clamp(64px, 7vw, 104px) 0 clamp(26px, 3vw, 38px);
  border-top: 1px solid rgba(20, 225, 133, 0.16);
}
.v-ft__inner {
  max-width: 1240px; margin: 0 auto;
  padding: 0 clamp(1.5rem, 4vw, 4rem);
}
.v-ft__cols {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}

/* --- brand column --- */
.v-ft__logo { width: clamp(150px, 15vw, 190px); height: auto; margin: 0 0 18px; }
.v-ft__tag {
  font-family: "Oswald", Arial, sans-serif; font-weight: 700; text-transform: uppercase;
  font-size: clamp(1rem, 1.3vw, 1.2rem); letter-spacing: 0.005em;
  color: #ffffff; margin: 0 0 10px;
}
.v-ft__about {
  font-family: "Montserrat", Arial, sans-serif; font-size: 0.86rem; line-height: 1.75;
  color: #8fb3a3; margin: 0; max-width: 42ch;
}
.v-ft__ch {
  font-family: "Montserrat", Arial, sans-serif; font-weight: 700; font-size: 0.68rem;
  letter-spacing: 0.2em; text-transform: uppercase; color: #14E185; margin: 0 0 18px;
}
.v-ft__ch--sp { margin-top: 26px; }
.v-ft__loc {
  display: flex; align-items: flex-start; gap: 9px;
  margin: 18px 0 0;
  font-family: "Montserrat", Arial, sans-serif; font-size: 13px; line-height: 1.6;
  color: #a8c4b8;
}
.v-ft__loc svg { width: 16px; height: 16px; flex: none; margin-top: 2px; color: #14E185; }

.v-ft__social { display: flex; gap: 9px; }
.v-ft__social a {
  width: 36px; height: 36px; border-radius: 10px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.10);
  transition: background 220ms ease, border-color 220ms ease, transform 220ms ease;
}
.v-ft__social a:hover {
  background: rgba(20, 225, 133, 0.16); border-color: rgba(20, 225, 133, 0.45);
  transform: translateY(-2px);
}
.v-ft__social i {
  width: 15px; height: 15px; display: block;
  -webkit-mask: var(--ico) center / contain no-repeat;
          mask: var(--ico) center / contain no-repeat;
  background: #dff7ec;
}

/* --- link columns --- */
.v-ft__col { min-width: 0; }
.v-ft__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; }
.v-ft__list a {
  position: relative;
  font-family: "Montserrat", Arial, sans-serif; font-size: 0.86rem; font-weight: 500;
  color: #a8c4b8; text-decoration: none;
  transition: color 200ms ease, padding-left 200ms ease;
}
.v-ft__list a::before {
  content: ""; position: absolute; left: -11px; top: 50%;
  width: 5px; height: 5px; border-radius: 50%; background: #14E185;
  transform: translateY(-50%) scale(0); transition: transform 220ms ease;
}
.v-ft__list a:hover { color: #ffffff; padding-left: 5px; }
.v-ft__list a:hover::before { transform: translateY(-50%) scale(1); }

/* --- contact column --- */
.v-ft__people { list-style: none; margin: 0 0 22px; padding: 0; display: flex; flex-direction: column; gap: 18px; }
.v-ft__people li { display: flex; flex-direction: column; gap: 2px; }
.v-ft__who {
  font-family: "Montserrat", Arial, sans-serif; font-weight: 700; font-size: 0.88rem; color: #ffffff;
}
.v-ft__role {
  font-family: "Montserrat", Arial, sans-serif; font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: #14E185; margin-bottom: 4px;
}
.v-ft__people a {
  font-family: "Montserrat", Arial, sans-serif; font-size: 0.83rem;
  color: #a8c4b8; text-decoration: none; width: fit-content;
  border-bottom: 1px solid transparent;
  transition: color 200ms ease, border-color 200ms ease;
}
.v-ft__people a:hover { color: #ffffff; border-bottom-color: rgba(20, 225, 133, 0.55); }
/* .v-ft__cta is styled by the shared button rule below.
   max-width stops it pushing the page sideways: the footer stays four
   columns down to 900px, and between roughly 900 and 1100 the last column
   is narrower than the button's intrinsic 219px, so it escaped and gave the
   whole page 11px of horizontal scroll. */
.v-ft__cta { max-width: 100%; }

/* --- rule + bottom bar --- */
.v-ft__rule {
  height: 1px; background: rgba(255, 255, 255, 0.09);
  margin: clamp(42px, 5vw, 64px) 0 clamp(18px, 2vw, 26px);
}
.v-ft__bottom {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
}
.v-ft__copy {
  font-family: "Montserrat", Arial, sans-serif; font-size: 0.76rem; color: #6f8f80; margin: 0;
}
.v-ft__legal { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 22px; }
.v-ft__legal a {
  font-family: "Montserrat", Arial, sans-serif; font-size: 0.76rem;
  color: #6f8f80; text-decoration: none; transition: color 200ms ease;
}
.v-ft__legal a:hover { color: #14E185; }

@media (max-width: 900px) {
  .v-ft__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 34px; }
  .v-ft__brand { grid-column: 1 / -1; }
}
@media (max-width: 520px) {
  .v-ft__cols { grid-template-columns: 1fr; }
  .v-ft__bottom { flex-direction: column; align-items: flex-start; gap: 14px; }
  .v-ft__legal { gap: 16px; }
}

/* No-JS document. React wipes #root on mount so this is only ever
   seen without JavaScript — but it is real content, so it should read
   properly rather than as raw markup. */
.v-noscript {
  max-width: 46rem; margin: 0 auto; padding: 3rem clamp(1.25rem,4vw,2rem);
  font-family: "Montserrat", Arial, sans-serif; color: #26382f; line-height: 1.65;
}
.v-noscript h1 {
  font-family: "Oswald", Arial, sans-serif; font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.9rem,5vw,2.8rem); line-height: 1.05; color: #0e1f19; margin: 0 0 1rem;
}
.v-noscript h2 {
  font-family: "Oswald", Arial, sans-serif; font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.2rem,3vw,1.6rem); color: #0e1f19; margin: 2.5rem 0 0.75rem;
}
.v-noscript dt { font-weight: 700; color: #0e1f19; margin-top: 1rem; }
.v-noscript dd { margin: 0.25rem 0 0; }
.v-noscript a { color: #109e75; }
.v-noscript li { margin-bottom: 0.5rem; }




/* ==========================================================
   Section typography + the pill button. These were defined in
   index.html only, so other pages could not match the homepage
   without copying values that would then drift. One definition,
   used everywhere.
   ========================================================== */
/* Section headings break out of the 1240px .v-wrap so each half of the
   headline can sit on one line. Measured the longest plain half on the
   about page ("Businesses weren't short on technology.") at 1740px wide at
   the old 96px cap, against only 1112px of usable width — which is why
   headings were wrapping into three and four lines.

   Full-bleed then pad back to 1560, the same technique the chips marquee
   uses. margin-inline rather than a transform, because .v-sec__head carries
   reveal classes that animate transform and the two would fight. */
.v-sec__head {
  max-width: none;
  margin-inline: calc(50% - 50vw);
  /* Tighter gutters than the body wrap: a centred headline does not need
     58px margins, and those gutters were the last 30px keeping the longest
     heading off one line at 1440. */
  padding-inline: max(clamp(1.25rem,2.5vw,3rem), calc((100vw - 1560px) / 2));
  text-align: center;
}
.v-sec__eyebrow {
  display: inline-block; font-family: "Montserrat", Arial, sans-serif;
  font-weight: 700; font-size: 12px; letter-spacing: 0.32em; text-transform: uppercase;
  margin-bottom: clamp(20px,3vw,28px);
}
.v-sec__title {
  font-family: "Oswald", Arial, sans-serif;
  font-weight: 700; text-transform: uppercase;
  /* Capped at 5.2rem (was 6rem). At 96px the longest plain half measured
     1740px, which does not fit even the widened 1560px column; at 83px it
     comes to ~1504px and fits on one line. */
  font-size: clamp(2.3rem, 5.1vw, 5.2rem);
  line-height: 0.98; letter-spacing: -0.015em;
  margin: 0 0 clamp(14px,1.6vw,20px);
}
.v-sec__lead {
  font-family: "Montserrat", Arial, sans-serif; font-weight: 400;
  font-size: clamp(1rem,1.15vw,1.2rem); line-height: 1.6;
  max-width: 760px; margin: 0 auto;
}


/* Label and icon ride above the wipe. */

/* The background stays black; the wipe supplies the colour, so the
   old background swap is removed to stop it flashing before the sweep. */


/* ==========================================================
   BUTTONS — emerald at rest, dark on hover. Three variants,
   each with a different reveal borrowed from the TechButton
   pack, so not every button on the page behaves the same:

     .v-btn            diagonal wipe   (primary)
     .v-btn--rise      vertical fill   (secondary)
     .v-btn--edge      centre-out bar  (tertiary / ghost)

   Shared skeleton first, then the per-variant sweep.
   ========================================================== */
/* ==========================================================
   THE button. One style, everywhere.

   Adapted from Botan button-48: a diagonal clip-path wipe that
   sweeps across on hover while the label inverts. It replaced
   three separate variants (pill sweep / rise / centre-out edge)
   and every one-off button on the site, so the whole thing now
   has a single, consistent action.

   Palette follows the locked rule — pitch black at rest, emerald
   on hover. The wipe is a clip-path on ::before rather than a
   width/height transition, which is what makes the diagonal edge
   stay crisp at any button size.

   The icon is a plain inline glyph, not a circular chip: a round
   chip inside a square button reads as two competing shapes.
   ========================================================== */
.v-btn, .v-cta, .v-cta-nav,
.v-sub, .v-ft__cta, .v-pj__new, .v-ctf__alt {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: inline-flex; align-items: center; justify-content: center; gap: 12px;
  padding: 16px 30px;
  border: 0;
  border-radius: 4px;
  background: #04120c;
  color: #ffffff;
  font-family: "Montserrat", Arial, sans-serif;
  font-size: 13px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; line-height: 1;
  text-align: center; text-decoration: none; white-space: nowrap;
  cursor: pointer;
  -webkit-appearance: none; appearance: none;
  text-rendering: geometricprecision;
  user-select: none; -webkit-user-select: none; touch-action: manipulation;
  transition: color 140ms cubic-bezier(0.694, 0, 0.335, 1);
}
.v-cta-nav { padding: 13px 24px; font-size: 12px; gap: 10px; }

/* label + icon ride above the wipe */
.v-btn > *, .v-cta > *, .v-cta-nav > *,
.v-sub > *, .v-ft__cta > *, .v-pj__new > *, .v-ctf__alt > * {
  position: relative; z-index: 1;
}

/* the diagonal wipe */
.v-btn::before, .v-cta::before, .v-cta-nav::before,
.v-sub::before, .v-ft__cta::before, .v-pj__new::before, .v-ctf__alt::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background: #14E185;
  backface-visibility: hidden;
  transform: translateZ(0);
  clip-path: polygon(-1% 0, 0 0, -25% 100%, -1% 100%);
  transition: clip-path 500ms cubic-bezier(0.165, 0.84, 0.44, 1);
  pointer-events: none;
}
.v-btn:hover::before, .v-cta:hover::before, .v-cta-nav:hover::before,
.v-sub:hover:not(:disabled)::before, .v-ft__cta:hover::before,
.v-pj__new:hover::before, .v-ctf__alt:hover::before {
  clip-path: polygon(0 0, 101% 0, 101% 101%, 0 101%);
}
.v-btn:hover, .v-cta:hover, .v-cta-nav:hover,
.v-sub:hover:not(:disabled), .v-ft__cta:hover,
.v-pj__new:hover, .v-ctf__alt:hover { color: #04120c; }

/* inline icon — no chip */
.v-cta__ico, .v-cta-nav__ico, .v-sub__ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: auto; height: auto; flex: none;
  background: none; border-radius: 0; color: currentColor;
  transition: transform 320ms cubic-bezier(.22, 1, .28, 1);
}
.v-cta__ico svg, .v-cta-nav__ico svg, .v-sub__ico svg { width: 15px; height: 15px; }
.v-btn:hover .v-cta__ico, .v-cta:hover .v-cta__ico,
.v-cta-nav:hover .v-cta-nav__ico, .v-sub:hover:not(:disabled) .v-sub__ico {
  transform: translateX(4px);
}

/* Secondary: same button, outlined. Kept because a page with two
   CTAs side by side needs one of them to recede — it is the same
   shape and the same wipe, only the resting fill differs. */
.v-btn--edge, .v-btn--rise {
  background: transparent;
  color: #04120c;
  box-shadow: inset 0 0 0 1.5px rgba(4, 18, 12, 0.35);
}
.v-btn--edge:hover, .v-btn--rise:hover { color: #04120c; box-shadow: inset 0 0 0 1.5px #14E185; }
.v-on-dark .v-btn--edge, .v-abt .v-btn--edge,
.v-on-dark .v-btn--rise, .v-abt .v-btn--rise {
  color: #ffffff; box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.35);
}
.v-on-dark .v-btn--edge:hover, .v-abt .v-btn--edge:hover,
.v-on-dark .v-btn--rise:hover, .v-abt .v-btn--rise:hover {
  color: #04120c; box-shadow: inset 0 0 0 1.5px #14E185;
}

/* On dark sections the solid button would disappear into the
   background, so it inverts: emerald at rest, dark label on the
   white wipe.

   This must list every alias the base rule above styles, not just
   .v-btn. It used to cover .v-btn alone, which is why the footer's
   .v-ft__cta rested at #04120c on a #04120c footer — the same colour,
   1.00:1, an invisible button. Add an alias to the base rule and it
   has to be added here too. */
.v-on-dark .v-btn:not(.v-btn--edge):not(.v-btn--rise),
.v-on-dark .v-cta, .v-on-dark .v-cta-nav, .v-on-dark .v-sub,
.v-on-dark .v-ft__cta, .v-on-dark .v-pj__new, .v-on-dark .v-ctf__alt,
.v-abt .v-btn:not(.v-btn--edge):not(.v-btn--rise),
.v-abt .v-cta, .v-abt .v-cta-nav, .v-abt .v-sub,
.v-abt .v-ft__cta, .v-abt .v-pj__new, .v-abt .v-ctf__alt {
  background: #14E185; color: #04120c;
}
.v-on-dark .v-btn:not(.v-btn--edge):not(.v-btn--rise)::before,
.v-on-dark .v-cta::before, .v-on-dark .v-cta-nav::before, .v-on-dark .v-sub::before,
.v-on-dark .v-ft__cta::before, .v-on-dark .v-pj__new::before, .v-on-dark .v-ctf__alt::before,
.v-abt .v-btn:not(.v-btn--edge):not(.v-btn--rise)::before,
.v-abt .v-cta::before, .v-abt .v-cta-nav::before, .v-abt .v-sub::before,
.v-abt .v-ft__cta::before, .v-abt .v-pj__new::before, .v-abt .v-ctf__alt::before {
  background: #ffffff;
}

.v-sub:disabled { opacity: 0.55; cursor: not-allowed; }

@media (prefers-reduced-motion: reduce) {
  .v-btn::before, .v-cta::before, .v-cta-nav::before,
  .v-sub::before, .v-ft__cta::before, .v-pj__new::before, .v-ctf__alt::before,
  .v-cta__ico, .v-cta-nav__ico, .v-sub__ico { transition: none; }
}

/* ==========================================================
   CUSTOM CURSOR — small green arrow, tilted up-left like a real
   pointer. One layer, no halo and no hover states: the only
   motion is the gradient panning through it, in several shades
   of green so the shape stays readable on any background.

   Only replaces the pointer where one actually exists, and the
   cursor:none class is added by JS, so a script failure leaves
   the native cursor alone.
   ========================================================== */
.v-cursor { display: none; }

@media (hover: hover) and (pointer: fine) {
  html.v-cursor-on, html.v-cursor-on * { cursor: none !important; }

  .v-cursor {
    display: block;
    position: fixed; left: 0; top: 0;
    width: 21px; height: 21px;
    z-index: 2147483000;
    pointer-events: none;
    /* The SVG is drawn as a real pointer already - tip at top-left - so
       no rotation is needed. Only a small nudge to seat that tip on the
       hotspot, since the path starts a little inside the viewBox. */
    transform: translate3d(var(--x, -100px), var(--y, -100px), 0)
               translate(-10%, -6%);
    will-change: transform;
  }

  .v-cursor__core {
    position: absolute; inset: 0;
    /* Inlined rather than referenced: the standalone SVG was being served
       from cache after the shape changed, so the old triangle kept
       rendering against the new CSS. A data URI cannot go stale. */
    -webkit-mask: url("data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><path d=%22M14 8 L14 78 L33 61 L46 90 L61 83 L48 55 L72 53 Z%22 fill=%22%23000%22 stroke=%22%23000%22 stroke-width=%229%22 stroke-linejoin=%22round%22 stroke-linecap=%22round%22/></svg>") center/contain no-repeat;
            mask: url("data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><path d=%22M14 8 L14 78 L33 61 L46 90 L61 83 L48 55 L72 53 Z%22 fill=%22%23000%22 stroke=%22%23000%22 stroke-width=%229%22 stroke-linejoin=%22round%22 stroke-linecap=%22round%22/></svg>") center/contain no-repeat;
    /* several greens so the gradient reads as movement, not a flat fill */
    background-image: linear-gradient(120deg,
      #063f2b 0%, #0d8a66 18%, #14E185 38%, #9dffd0 52%,
      #14E185 66%, #0d8a66 84%, #063f2b 100%);
    background-size: 300% 300%;
    animation: v-cursor-flow 3.6s linear infinite;
  }
  @keyframes v-cursor-flow {
    from { background-position:   0% 50%; }
    to   { background-position: 300% 50%; }
  }
}

@media (prefers-reduced-motion: reduce) {
  .v-cursor { display: none !important; }
  html.v-cursor-on, html.v-cursor-on * { cursor: auto !important; }
}


/* =====================================================================
   Skim highlight — <mark class="v-hl">

   Ported from the V2 build (src/styles/base.css there). The earlier
   attempt here was a half-height underline tint; it was too quiet to do
   the job. This is the real thing: a solid emerald pill that wipes in
   left to right across the WHOLE phrase, then the ink flips to near
   black and a glow settles in behind it.

   The three transitions are deliberately out of step — the wipe runs
   first, the colour flip lands at 0.65s once the plate is mostly there
   (so the text never sits dark on a transparent background), and the
   glow arrives last at 0.9s.

   Marks light one at a time rather than together; reveal.js stages them
   380ms apart once their section is in view.

   Use it on the phrase that carries the point, not on whole sentences —
   a page where everything is marked marks nothing.
   ===================================================================== */
.v-hl {
  color: var(--v-hl-rest, #109e75);
  background: linear-gradient(90deg, #14E185, #14E185) 0 0 / 0% 100% no-repeat;
  padding: 2px 8px;
  margin: 0 1px;
  border-radius: 4px;
  font-weight: 700;
  box-shadow: 0 0 0 0 rgba(20, 225, 133, 0);
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
  transition:
    background-size 1.1s cubic-bezier(0.65, 0.05, 0.36, 1),
    color 0.35s ease 0.65s,
    box-shadow 0.5s ease 0.9s;
}
.v-hl.is-lit {
  color: #050E0B;
  background-size: 100% 100%;
  box-shadow: 0 0 24px 0 rgba(20, 225, 133, 0.35);
}

/* On dark panels the resting ink is the bright mint; on light it is the
   deeper emerald, or the phrase disappears before it lights. */
.v-hl--dark, .v-on-dark .v-hl { --v-hl-rest: #14E185; }

@media (prefers-reduced-motion: reduce) {
  .v-hl {
    transition: none;
    background-size: 100% 100%;
    color: #050E0B;
    box-shadow: none;
  }
}



/* =====================================================================
   Final CTA — shared by the homepage and the about page.

   Lifted out of index.html so both pages render the identical section
   instead of the about page carrying a near-copy that drifts. Only the
   copy differs between them; every rule below is common.
   ===================================================================== */
/* ==========================================================
   12 · Final CTA — ONE flat white surface filling the whole
   section, no inner card. Sixteen icon chips drift across it on six
   different motion styles, each with its own duration, delay and
   direction so nothing ever syncs up.
   ========================================================== */
.v-fc {
  position: relative; overflow: hidden; isolation: isolate;
  background: #ffffff;
  color: #46584f;
  /* Bottom padding was 2-3rem, which left the CTA's buttons almost
     touching the footer. Matched to the top so the section closes
     properly before the footer band starts. */
  padding: clamp(6rem,12vw,10rem) 0 clamp(5rem,8vw,8rem);
  min-height: clamp(560px, 62vw, 760px);
  display: flex; align-items: center;
}
.v-fc::after {                      /* faint grid, same idiom as the rest */
  content: ""; position: absolute; inset: -60px; z-index: 0; pointer-events: none;
  background-image:
    linear-gradient(rgba(20,96,75,0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(20,96,75,0.05) 1px, transparent 1px);
  background-size: 48px 48px, 48px 48px;
  -webkit-mask-image: radial-gradient(ellipse at center, black 18%, transparent 76%);
          mask-image: radial-gradient(ellipse at center, black 18%, transparent 76%);
  animation: v-fc-grid 36s linear infinite;
}
@keyframes v-fc-grid {
  from { background-position: 0 0, 0 0; }
  to   { background-position: 48px 48px, 48px 48px; }
}

.v-fc__wrap { position: relative; width: 100%; }
/* Chips live in their own layer spanning the SECTION. Parenting them
   to the copy wrapper made top:% resolve against the text block, so
   every chip bunched around the headline. */
.v-fc__field { position: absolute; inset: 0; z-index: 1; pointer-events: none; }

/* --- Chips. Positioned against the whole section, not a card. --- */
.v-fc__chip {
  position: absolute; z-index: 1;
  width: clamp(44px,4vw,60px); aspect-ratio: 1;
  display: grid; place-items: center;
  border-radius: 17px;
  background: #ffffff;
  border: 1px solid rgba(16,158,117,0.15);
  box-shadow: 0 10px 26px rgba(14,31,25,0.09);
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
  animation-delay: var(--d, 0s);
  animation-duration: var(--t, 8s);
  will-change: transform;
}
.v-fc__chip--fill {
  background: linear-gradient(150deg,#14E185,#109e75);
  border-color: transparent;
  box-shadow: 0 12px 30px rgba(16,158,117,0.28);
}
.v-fc__chip--soft {
  background: rgba(16,158,117,0.07);
  border-color: rgba(16,158,117,0.20);
  box-shadow: 0 8px 20px rgba(14,31,25,0.05);
}
.v-fc__chip i {
  display: block; width: 50%; height: 50%;
  background: linear-gradient(150deg,#14E185,#109e75);
  -webkit-mask: var(--ico) center/contain no-repeat;
          mask: var(--ico) center/contain no-repeat;
}
.v-fc__chip--fill i { background: #ffffff; }

/* --- Six motion styles. Mixed across the chips so the field reads
   as random drift rather than one repeated bob. --- */
.v-fc__chip--m1 { animation-name: v-fc-bob; }
.v-fc__chip--m2 { animation-name: v-fc-sway; }
.v-fc__chip--m3 { animation-name: v-fc-orbit; }
.v-fc__chip--m4 { animation-name: v-fc-drift; }
.v-fc__chip--m5 { animation-name: v-fc-breathe; }
.v-fc__chip--m6 { animation-name: v-fc-wander; }
.v-fc__chip--rev { animation-direction: reverse; }

@keyframes v-fc-bob {          /* straight vertical bob */
  0%,100% { transform: translateY(0)     rotate(0deg); }
  50%     { transform: translateY(-18px) rotate(5deg); }
}
@keyframes v-fc-sway {         /* pendulum */
  0%,100% { transform: translateX(0)    rotate(-7deg); }
  50%     { transform: translateX(14px) rotate(7deg); }
}
@keyframes v-fc-orbit {        /* small circle */
  0%,100% { transform: translate(0, 0); }
  25%     { transform: translate(12px, -12px); }
  50%     { transform: translate(0, -20px); }
  75%     { transform: translate(-12px, -12px); }
}
@keyframes v-fc-drift {        /* lazy diagonal */
  0%,100% { transform: translate(0, 0)         rotate(0deg); }
  50%     { transform: translate(-16px, 12px)  rotate(-6deg); }
}
@keyframes v-fc-breathe {      /* scale pulse with a lift */
  0%,100% { transform: scale(1)    translateY(0); }
  50%     { transform: scale(1.12) translateY(-9px); }
}
@keyframes v-fc-wander {       /* figure-eight-ish */
  0%,100% { transform: translate(0, 0)        rotate(0deg); }
  30%     { transform: translate(13px, -9px)  rotate(6deg); }
  60%     { transform: translate(-9px, -15px) rotate(-5deg); }
  85%     { transform: translate(-13px, 5px)  rotate(3deg); }
}

/* 700px left only ~572px of usable width after padding, while the headline's
   plain half measures 611px, so "Have a problem" was wrapping onto two lines
   in a CTA that had room to spare. Widened for the headline; the lead and the
   note keep a readable measure of their own below. */
.v-fc__in { position: relative; z-index: 3; max-width: 1100px; margin: 0 auto; text-align: center;
            padding: 0 clamp(1.5rem,4vw,4rem); }
.v-fc__eyebrow {
  display: inline-block; font-family: "Montserrat", Arial, sans-serif;
  font-weight: 700; font-size: 12px; letter-spacing: 0.32em; text-transform: uppercase;
  color: #0b7a58; margin-bottom: clamp(16px,2vw,22px);
}
.v-fc__title {
  font-family: "Oswald", Arial, sans-serif; font-weight: 700; text-transform: uppercase;
  font-size: clamp(2.6rem,6.4vw,5.6rem); line-height: 0.95; letter-spacing: -0.02em;
  color: #0e1f19; margin: 0 0 clamp(16px,2vw,24px);
}
.v-fc__title .em { color: #109e75; display: block; }
/* The container is wide for the headline's sake; prose keeps its own measure
   so it does not run to 1100px. */

/* ---------------------------------------------------------------------
   Section headlines break on their colour.

   Every headline on the site is written as "plain half <span class='em'>
   accented half</span>", and the accent is ALWAYS the closing clause -
   checked all 28 of them, there is no mid-sentence .em in a heading. So
   giving the span its own line splits the headline on meaning rather than
   on wherever the text happened to run out of width, which was orphaning
   words like "Different" onto the plain line.

   display:block rather than <br>: the span already wraps exactly the
   accented text, so the break follows the copy if the copy changes.

   Listed by heading class, never as a bare `.em`. Of the 53 .em spans on
   the site only 28 are in headings; the other 25 sit inside body
   paragraphs and must keep flowing inline.

   Safe against the reveal engine, which word-splits these headings:
   splitWords() walks text nodes and only recurses into element nodes, so
   the span survives, and the .r-wm word masks are inline-block so they
   flow normally inside a block parent.

   .v-fc__title above and .v-wb__note__t on index already did this; this
   extends the same convention to the rest.

   The class list came from the RENDERED DOM, not a source regex. The first
   pass used a regex over the markup and missed .v-ct__h and .v-hb__title
   entirely, because those headings are composed by helper functions where
   the title arrives as a variable, so the regex never saw an .em inside
   them. Fourteen headings sat wrapping mid-phrase as a result. If a class is
   ever added here, confirm it against the rendered page.
   --------------------------------------------------------------------- */
.v-sec__title .em,      /* index, about, services section headings */
.v-cj__title .em,       /* index, client journey */
.v-page__title .em,     /* partnership hero */
.v-cth__title .em,      /* contact hero */
.v-ct__h .em,           /* contact section headings */
.v-close__t .em,        /* contact closing */
.v-hh__title .em,       /* hub hero */
.v-hb__title .em,       /* hub section headings */
.v-tc__title .em,       /* services, technology */
.v-vis__dir__t .em {    /* about, vision direction cards */
  display: block;
}
.v-fc__lead {
  font-family: "Montserrat", Arial, sans-serif; font-size: clamp(1rem,1.15vw,1.15rem);
  line-height: 1.62; color: #46584f;
  max-width: 540px; margin: 0 auto clamp(28px,3.5vw,40px);
}
.v-fc__btns { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }
/* Light-surface ghost — a dark ghost is invisible on white. */
/* `auto` on the sides, not 0. The container was widened to 1100px for the
   headline's sake, so a note with zero side margins sat hard against the left
   edge of that box while the heading, lead and buttons stayed centred, which
   is what made the block look misaligned. Its own max-width lives here too:
   an earlier shared rule tried to set both, but this margin shorthand came
   later in the file and silently reset the sides back to 0. */
.v-fc__note {
  max-width: 720px;
  margin: clamp(22px,2.6vw,30px) auto 0;
  font-family: "Montserrat", Arial, sans-serif; font-size: 0.85rem; color: #5d7068;
}

@media (prefers-reduced-motion: reduce) {
  .v-fc::after, .v-fc__chip { animation: none; }
}
@media (max-width: 1180px) {
  /* Middle-column chips would land on the copy — drop them first. */
  .v-fc__chip--mid { display: none; }
}
@media (max-width: 860px) {
  .v-fc__chip { display: none; }
  .v-fc { min-height: 0; padding: clamp(4.5rem,10vw,7rem) 0; }
  .v-fc__btns { flex-direction: column; align-items: stretch; }
  .v-btn--edge { justify-content: space-between; }
}


/* =====================================================================
   Skip link — WCAG 2.4.1 Bypass Blocks.

   Off-screen until focused, then it lands over the nav. Without it a
   keyboard or screen-reader user tabs the whole navigation on every page
   before reaching the content. The target is the <main id="main"> that
   Site.mount already wraps every page body in.
   ===================================================================== */
.v-skip {
  position: fixed; top: 0; left: 50%;
  transform: translate(-50%, -120%);
  z-index: 2147483001;
  padding: 12px 26px; border-radius: 0 0 14px 14px;
  background: #04120c; color: #14E185;
  font-family: "Montserrat", Arial, sans-serif; font-weight: 700;
  font-size: 14px; letter-spacing: 0.06em; text-decoration: none;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  transition: transform 220ms cubic-bezier(.22, 1, .28, 1);
}
.v-skip:focus { transform: translate(-50%, 0); outline: 2px solid #14E185; outline-offset: -4px; }
@media (prefers-reduced-motion: reduce) { .v-skip { transition: none; } }


/* =====================================================================
   Header background — made explicit.

   The markup carried Tailwind's `bg-veltrix-bg/85`, but the veltrix-*
   palette was never registered (the config block ran before the Play
   CDN script, which only reads it afterwards), so the class resolved to
   nothing and the bar rendered fully transparent. That accident is what
   the note further up this file describes as the intended look: no
   background of its own, just the backdrop blur, so whatever scrolls
   underneath blurs through.

   Keeping that look, now as an actual rule rather than a broken class.
   ===================================================================== */
.v-header { background-color: transparent; }


/* =====================================================================
   Mobile burger.

   Was `text-veltrix-fg1` (#f0f0ee) on a transparent header. Every page
   is light behind the bar at scroll-top, so the icon rendered at
   1.06:1 against white — present, focusable, and invisible. Matches the
   nav links instead, which sit on the same backdrop.
   ===================================================================== */
.v-burger { color: #0e1f19; }
.v-burger:hover { background-color: rgba(14, 31, 25, 0.06); }


/* =====================================================================
   Nav breakpoint.

   Tailwind's tiers do not have one where this fits. The bar carries the
   logo, five links and the Contact CTA; at lg (1024px) "Partnership"
   collided with the button and the CTA wrapped to two lines — the same
   failure four links had at md (768px). Measured, the row needs ~1100px,
   so the switch happens at 1150 with headroom.

   Adding a sixth link? Re-check this number rather than assuming.
   ===================================================================== */
.v-nav__links,
.v-nav__cta { display: none; }
.v-burger { display: block; }
.v-nav__drawer { display: block; }

@media (min-width: 1150px) {
  .v-nav__links { display: flex; }
  .v-nav__cta   { display: inline-flex; }
  .v-burger,
  .v-nav__drawer { display: none; }
}
