/* ==========================================================================
   K1 Nova components
   ========================================================================== */

/* --------------------------------------------------------------
   Header
   -------------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  padding-block: 0.9rem;
  transition: background-color 0.4s var(--ease), border-color 0.4s var(--ease), padding 0.4s var(--ease);
  border-bottom: 1px solid transparent;
}
.site-header.is-stuck {
  background: rgba(7, 11, 20, 0.9);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  border-bottom-color: var(--line);
  padding-block: 0.6rem;
}
.site-header__inner {
  display: flex; align-items: center; gap: 1.25rem;
}

.brand { display: inline-flex; align-items: center; gap: 0.7rem; flex: none; }
.brand__icon { width: 38px; height: 38px; border-radius: 11px; }
.brand__wordmark { width: 118px; height: auto; }
.brand:hover .brand__icon { filter: brightness(1.12); }
.brand__icon, .brand__wordmark { transition: filter 0.3s var(--ease); }

.nav { margin-inline: auto; }
.nav__list {
  display: flex; align-items: center; gap: 0.15rem;
  list-style: none; padding: 0.3rem; margin: 0;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  backdrop-filter: blur(10px);
}
.nav__item { position: relative; }
.nav__link {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.5rem 0.95rem;
  border-radius: var(--r-pill);
  font-size: var(--fs-sm); font-weight: 500; color: var(--muted);
  background: none; border: 0; cursor: pointer;
  transition: color 0.25s var(--ease), background-color 0.25s var(--ease);
  white-space: nowrap;
}
.nav__link:hover { color: var(--white); background: rgba(255, 255, 255, 0.06); }
.nav__link.is-current { color: var(--white); background: rgba(59, 130, 246, 0.16); }
.nav__chev { width: 9px; height: 6px; transition: transform 0.3s var(--ease); opacity: 0.75; }
[aria-expanded='true'] .nav__chev { transform: rotate(180deg); }

.nav__menu {
  position: absolute; top: calc(100% + 0.7rem); left: 50%;
  transform: translateX(-50%);
  width: min(30rem, 84vw);
  z-index: 40;
}
.nav__menu-inner {
  display: grid; gap: 0.35rem; padding: 0.55rem;
  background: rgba(13, 20, 36, 0.96);
  backdrop-filter: blur(22px);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
  animation: menu-in 0.28s var(--ease-out);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-6px); } }
.nav__menu-link {
  display: grid; gap: 0.15rem; padding: 0.85rem 1rem;
  border-radius: var(--r-md);
  transition: background-color 0.25s var(--ease);
}
.nav__menu-link:hover, .nav__menu-link:focus-visible { background: rgba(59, 130, 246, 0.14); }
.nav__menu-link.is-current { background: rgba(59, 130, 246, 0.1); }
.nav__menu-title { font-weight: 600; font-size: var(--fs-sm); }
.nav__menu-blurb { font-size: var(--fs-xs); color: var(--muted); line-height: 1.5; }

.site-header__actions { display: flex; align-items: center; gap: 0.8rem; flex: none; }
.site-header__phone {
  display: inline-flex; align-items: center; min-height: 2.5rem; padding-inline: 0.35rem;
  font-size: var(--fs-sm); font-weight: 500; color: var(--muted);
  transition: color 0.25s var(--ease);
}
.site-header__phone:hover { color: var(--white); }

.burger {
  display: none; width: 44px; height: 44px;
  align-items: center; justify-content: center;
  flex-direction: column; gap: 5px;
  background: rgba(255, 255, 255, 0.05); border: 1px solid var(--line);
  border-radius: 13px; cursor: pointer;
}
.burger span {
  display: block; width: 17px; height: 1.6px; background: var(--white);
  border-radius: 2px; transition: transform 0.35s var(--ease), opacity 0.25s var(--ease);
}
.burger[aria-expanded='true'] span:first-child { transform: translateY(3.3px) rotate(45deg); }
.burger[aria-expanded='true'] span:last-child { transform: translateY(-3.3px) rotate(-45deg); }

.mobile-menu {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(7, 11, 20, 0.97);
  backdrop-filter: blur(24px);
  padding: 6.5rem var(--gutter) 2.5rem;
  overflow-y: auto;
  animation: fade-in 0.3s var(--ease-out);
}
@keyframes fade-in { from { opacity: 0; } }
.mobile-menu__inner { display: flex; flex-direction: column; min-height: 100%; gap: 1.5rem; }
.mobile-menu__list { list-style: none; padding: 0; margin: 0; display: grid; }
.mobile-menu__list a {
  display: block; padding: 1.05rem 0;
  font-family: var(--font-display); font-size: 1.6rem; font-weight: 500;
  letter-spacing: -0.02em;
  border-bottom: 1px solid var(--line);
}
.mobile-menu__list a:hover { color: var(--blue-light); }
.mobile-menu__foot { margin-top: auto; display: grid; gap: 1rem; padding-top: 1.5rem; }
.mobile-menu__phone { text-align: center; color: var(--muted); font-weight: 500; }
body.menu-open { overflow: hidden; }

/* --------------------------------------------------------------
   Hero
   -------------------------------------------------------------- */
.hero {
  position: relative;
  padding-top: clamp(2.5rem, 5vw, 4.5rem);
  padding-bottom: clamp(5rem, 9vw, 8.5rem);
  overflow: hidden;
  isolation: isolate;
}
.hero__glow-a {
  width: 60vw; height: 60vw; max-width: 900px; max-height: 900px;
  top: -22%; right: -14%;
}
.hero__glow-b {
  width: 40vw; height: 40vw; max-width: 620px; max-height: 620px;
  bottom: -18%; left: -12%;
}
.hero__inner {
  position: relative; z-index: 2;
  display: grid; gap: clamp(2.5rem, 5vw, 4rem);
  align-items: center;
}
.hero__eyebrow { margin-bottom: 1.4rem; }
.hero__heading {
  /* A shade smaller than .display, because this heading carries the full
     promise rather than three short lines, and at the display size it ran to
     five lines and pushed the buttons off the first screen. */
  font-size: clamp(2.2rem, 1.2rem + 3.5vw, 3.7rem);
  font-weight: 500; letter-spacing: -0.035em; line-height: 1.02;
  margin-bottom: 1.6rem;
  max-width: 16ch;
}
.hero__lead { max-width: 46ch; margin-bottom: 2.2rem; }
.hero__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-bottom: 2.6rem; }

.hero__recent {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.55rem 0.9rem;
  font-size: var(--fs-xs); color: var(--muted-dim);
}
.hero__recent-label {
  text-transform: uppercase; letter-spacing: 0.13em; font-weight: 600;
  color: var(--muted-dim);
}
.hero__recent-item {
  position: relative; padding-left: 0.95rem;
  color: var(--muted); font-weight: 500; letter-spacing: 0.01em;
}
.hero__recent-item::before {
  content: ''; position: absolute; left: 0; top: 50%;
  width: 4px; height: 4px; margin-top: -2px; border-radius: 50%;
  background: var(--blue); opacity: 0.8;
}

.hero__media { position: relative; }
.hero__video { width: 100%; height: auto; background: var(--ink-900); }
.hero__video--desktop {
  /* The clip has its own near black background, so a soft radial mask lets it
     dissolve into the page instead of sitting inside an obvious card. */
  -webkit-mask-image: radial-gradient(ellipse 62% 50% at 54% 50%, #000 24%, rgba(0, 0, 0, 0.55) 56%, transparent 100%);
  mask-image: radial-gradient(ellipse 62% 50% at 54% 50%, #000 24%, rgba(0, 0, 0, 0.55) 56%, transparent 100%);
}
.hero__video--mobile { display: none; }
.hero__scrim { display: none; }

@media (max-width: 899px) {
  .hero {
    min-height: 88svh;
    display: flex; align-items: flex-end;
    padding-top: clamp(3rem, 14vw, 6rem);
    padding-bottom: clamp(5.5rem, 14vw, 8rem);
  }
  .hero__media { display: none; }
  .hero__video--mobile {
    display: block; position: absolute; inset: 0; z-index: 0;
    width: 100%; height: 100%; object-fit: cover;
    opacity: 0.85;
  }
  .hero__scrim {
    display: block; position: absolute; inset: 0; z-index: 1;
    background:
      linear-gradient(180deg, rgba(7, 11, 20, 0.94) 0%, rgba(7, 11, 20, 0.55) 34%,
      rgba(7, 11, 20, 0.72) 62%, var(--ink-900) 97%);
  }
  .hero__glow-a { width: 100vw; height: 70vw; top: -10%; right: -30%; opacity: 0.7; }
  .hero__heading { letter-spacing: -0.032em; }
  .hero__lead { font-size: 1.02rem; max-width: 34ch; }
  .hero__actions { margin-bottom: 1.9rem; }
  .hero__actions .btn { flex: 1 1 auto; justify-content: center; min-width: 13rem; }
}

@media (min-width: 900px) {
  .hero__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .hero__media { position: relative; }
  .hero__video--desktop { width: 132%; max-width: none; margin-left: -12%; margin-block: -8%; }
}
@media (min-width: 1400px) {
  .hero__video--desktop { width: 142%; margin-left: -14%; }
}

/* --------------------------------------------------------------
   Trust strip
   -------------------------------------------------------------- */
.trust { padding-bottom: 0; }
.trust__panel { padding: clamp(1.75rem, 3.4vw, 2.75rem); }
.trust__panel::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.75) 30%, rgba(124, 58, 237, 0.55) 68%, transparent);
}
.trust__head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 1rem;
  padding-bottom: 1.6rem;
}
.trust__sub { font-size: var(--fs-sm); }
.trust__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1.5rem;
  border-top: 1px solid var(--line);
  padding-top: 1.75rem;
}
.trust__item { position: relative; }
.trust__index {
  display: block; font-family: var(--font-display); font-size: var(--fs-xs);
  font-weight: 500; letter-spacing: 0.14em; color: var(--blue);
  margin-bottom: 0.7rem;
}
.trust__title { font-family: var(--font-display); font-size: 1.06rem; font-weight: 500; letter-spacing: -0.015em; margin-bottom: 0.35rem; }
.trust__body { font-size: var(--fs-sm); color: var(--muted); line-height: 1.55; }

@media (min-width: 560px) {
  .trust__list { grid-template-columns: 1fr 1fr; gap: 1.5rem clamp(1.5rem, 4vw, 2.5rem); }
}
@media (min-width: 940px) {
  .trust__list { grid-template-columns: repeat(4, 1fr); gap: 0; }
  .trust__item { padding-inline: clamp(1rem, 2vw, 1.9rem); }
  .trust__item:first-child { padding-left: 0; }
  .trust__item:last-child { padding-right: 0; }
  .trust__item + .trust__item { border-left: 1px solid var(--line); }
}

/* --------------------------------------------------------------
   Section heads
   -------------------------------------------------------------- */
.section-head { max-width: 44rem; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.section-head .eyebrow { margin-bottom: 1.1rem; }
.section-head .lead { margin-top: 1.2rem; }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .lead { margin-inline: auto; }
.section-head--center .eyebrow { justify-content: center; }

/* --------------------------------------------------------------
   Problems
   -------------------------------------------------------------- */
.problems { background: var(--ink-800); }
.problems__grid { display: grid; gap: clamp(2.5rem, 5vw, 5rem); }
.problems__aside .eyebrow { margin-bottom: 1.1rem; }
.problems__heading { margin-bottom: 1.3rem; }
.problems__link { margin-top: 1.6rem; }
.problems__list { list-style: none; margin: 0; padding: 0; }
.problems__item {
  display: grid; grid-template-columns: auto 1fr; gap: 1.1rem;
  padding: 1.35rem 0.25rem 1.35rem 0;
  border-bottom: 1px solid var(--line);
  transition: padding-left 0.4s var(--ease);
}
.problems__item:first-child { border-top: 1px solid var(--line); }
.problems__item:hover { padding-left: 0.65rem; }
.problems__num {
  font-family: var(--font-display); font-size: var(--fs-xs); font-weight: 500;
  color: var(--blue); letter-spacing: 0.1em; padding-top: 0.42rem;
  transition: color 0.3s var(--ease);
}
.problems__item:hover .problems__num { color: var(--blue-light); }
.problems__title { margin-bottom: 0.3rem; }
.problems__body { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; max-width: 52ch; }

@media (min-width: 960px) {
  .problems__grid { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr); align-items: start; }
  .problems__aside { position: sticky; top: 8rem; }
}

/* --------------------------------------------------------------
   Feature (alternating service blocks)
   -------------------------------------------------------------- */
.feature { overflow: hidden; isolation: isolate; padding-block: clamp(4rem, 7.5vw, 7.5rem); }
.feature__glow {
  width: 46vw; height: 46vw; max-width: 720px; max-height: 720px;
  top: 8%; right: -16%; opacity: 0.75;
}
.feature--flip .feature__glow { right: auto; left: -16%; }
.feature__grid { position: relative; z-index: 2; display: grid; gap: clamp(2.5rem, 5vw, 4.5rem); align-items: center; }
.feature__heading { margin: 1.2rem 0 1.4rem; }
.feature__body + .feature__body { margin-top: 1rem; }
.feature__cta { margin-top: 2rem; }
.feature__media { min-width: 0; }

@media (min-width: 980px) {
  .feature__grid { grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr); }
  .feature--flip .feature__grid { grid-template-columns: minmax(0, 1.18fr) minmax(0, 0.82fr); }
  .feature--flip .feature__copy { order: 2; }
  .feature--flip .feature__media { order: 1; }
  .feature__copy { max-width: 34rem; }
  .feature:not(.feature--flip) .feature__copy { margin-left: auto; margin-right: 0; }
}

.checklist { list-style: none; margin: 2rem 0 0; padding: 0; display: grid; gap: 0.75rem 1.6rem; }
.checklist li {
  position: relative; padding-left: 1.7rem;
  font-size: var(--fs-sm); color: var(--muted); line-height: 1.5;
}
.checklist li::before {
  content: ''; position: absolute; left: 0; top: 0.34em;
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  background: rgba(59, 130, 246, 0.16);
  border: 1px solid rgba(59, 130, 246, 0.4);
}
.checklist li::after {
  content: ''; position: absolute; left: 0.32rem; top: 0.66em;
  width: 0.4rem; height: 0.22rem;
  border-left: 1.6px solid var(--blue-light);
  border-bottom: 1.6px solid var(--blue-light);
  transform: rotate(-45deg);
}
.checklist--tight { margin-top: 0; gap: 0.6rem; }
@media (min-width: 640px) { .checklist { grid-template-columns: 1fr 1fr; } }
.checklist--single { grid-template-columns: 1fr !important; }

/* --------------------------------------------------------------
   Device frames
   -------------------------------------------------------------- */
.frame { position: relative; border-radius: var(--r-lg); overflow: hidden; background: var(--ink-700); }
.frame--browser {
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-lift);
  border-radius: clamp(12px, 1.6vw, 20px);
}
.frame__bar {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.62rem 0.9rem;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02));
  border-bottom: 1px solid var(--line);
}
.frame__dots { display: inline-flex; gap: 0.36rem; }
.frame__dots i { width: 8px; height: 8px; border-radius: 50%; background: rgba(255, 255, 255, 0.18); }
.frame__url {
  flex: 1; text-align: center; font-size: 0.68rem; color: var(--muted-dim);
  letter-spacing: 0.02em;
}
.frame__screen { position: relative; background: #fff; }
.frame__screen img { width: 100%; }

.frame--phone {
  border-radius: 22px; padding: 5px;
  background: linear-gradient(160deg, #2b3550, #10182a);
  border: 1px solid var(--line-2);
  box-shadow: 0 30px 60px -22px rgba(0, 0, 0, 0.95);
}
.frame--phone .frame__screen { border-radius: 17px; overflow: hidden; }

.showcase, .project__media { position: relative; }
.showcase__phone, .project__phone {
  position: absolute; z-index: 3;
  bottom: -12%;
  /* Overhang is tied to the page gutter so the phone never leaves the viewport. */
  left: max(-7%, calc(var(--gutter) * -0.55));
  width: clamp(96px, 21%, 190px);
}
.seo-visual img {
  width: 100%; border-radius: var(--r-xl);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-lift);
}

/* --------------------------------------------------------------
   Services: the three things we do
   -------------------------------------------------------------- */
.services { overflow: hidden; isolation: isolate; }
.services__glow {
  width: 70vw; height: 40vw; max-width: 900px;
  top: -8%; left: 50%; transform: translateX(-50%); opacity: 0.55;
}
/* Three large horizontal cards stacked vertically, each revealing on its own
   as it scrolls into view. */
.services__stack {
  position: relative; z-index: 2;
  display: grid; gap: clamp(1.25rem, 2.5vw, 2rem);
}

.svc {
  position: relative; overflow: hidden;
  display: grid; gap: clamp(1.75rem, 3.5vw, 3.5rem);
  align-items: center;
  padding: clamp(1.6rem, 3.2vw, 3rem);
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--r-2xl);
  transition: border-color 0.4s var(--ease), background-color 0.4s var(--ease),
    transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
}
.svc::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.65) 35%, rgba(124, 58, 237, 0.45) 72%, transparent);
}
.svc:hover {
  border-color: rgba(59, 130, 246, 0.34);
  background: var(--ink-700);
  transform: translateY(-3px);
  box-shadow: 0 40px 90px -50px rgba(59, 130, 246, 0.5);
}

.svc__copy { min-width: 0; }
.svc__num {
  display: block; font-family: var(--font-display);
  font-size: var(--fs-xs); font-weight: 500; letter-spacing: 0.16em;
  color: var(--blue); margin-bottom: 0.9rem;
}
.svc__title { font-size: clamp(1.75rem, 1.2rem + 1.9vw, 2.6rem); margin-bottom: 0.7rem; }
.svc__lede {
  font-size: var(--fs-lead); color: var(--white); line-height: 1.45;
  letter-spacing: -0.012em; margin-bottom: 1.5rem; max-width: 34ch;
}
.svc__points { list-style: none; margin: 0 0 1.6rem; padding: 0; display: grid; gap: 0.65rem; }
.svc__points li {
  position: relative; padding-left: 1.35rem;
  font-size: var(--fs-sm); color: var(--muted); line-height: 1.5;
}
.svc__points li::before {
  content: ''; position: absolute; left: 0; top: 0.6em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--blue);
}

/* Media column. Nothing in here is allowed to size the card. */
.svc__media { min-width: 0; }
.svc__media > * { max-width: 100%; }
.svc__media img { width: 100%; height: auto; max-width: 100%; display: block; }
.svc__media .frame--browser { box-shadow: 0 30px 70px -34px rgba(0, 0, 0, 0.9); }
.seo-visual--card img { border-radius: var(--r-lg); }

.svc__store { position: relative; }

/* Scroll spacer used only by the deck layout below. */
.svc-gap { display: none; }

/* Platform badges. Small, fixed, and explicitly constrained so a large source
   file can never blow up the layout. */
.platforms {
  list-style: none; margin: 0.9rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.5rem;
}
.platform {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0.8rem;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--line-2);
  backdrop-filter: blur(8px);
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.01em;
  color: var(--muted); white-space: nowrap;
  transition: color 0.35s var(--ease), border-color 0.35s var(--ease);
}
.platform__mark {
  width: 20px; height: 20px;
  max-width: 24px; max-height: 24px;
  min-width: 20px;
  flex: none; object-fit: contain;
  color: var(--muted-dim);
  transition: color 0.35s var(--ease);
}
.svc:hover .platform { color: var(--white); border-color: var(--line-3); }
.svc:hover .platform__mark { color: var(--blue-light); }

@media (min-width: 900px) {
  .svc { grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr); }
  /* Alternate the media side so the stack does not read as three identical rows.
     Column widths swap along with the order, so the media always keeps the
     wider column and every card's visual is the same size. */
  .svc:nth-child(even) { grid-template-columns: minmax(0, 1.22fr) minmax(0, 0.78fr); }
  .svc:nth-child(even) .svc__copy { order: 2; }
  .svc:nth-child(even) .svc__media { order: 1; }
}

/* --------------------------------------------------------------
   Service cards as a scrolled deck.
   Each card pins to the middle of the screen and the next one rides up
   over it, so one card is in view at a time. The card underneath scales
   back a little so it reads as a stack rather than a jump cut.

   Sticky positioning does the work; the classes only add the depth. If
   the script never runs, or the viewport is short, or the visitor asked
   for reduced motion, this degrades to three cards stacked down the page.
   -------------------------------------------------------------- */
/* Whether the deck runs is decided in the script, not here, because it depends
   on the tallest card actually fitting the viewport under the header. A media
   query cannot measure that, and it varies with phone size and font settings.
   Everything below only applies once the script adds .is-deck. */
@media (prefers-reduced-motion: no-preference) {
  /* Sticky does not work inside a clipping ancestor, so the section drops its
     overflow while the deck is on. The ambient glow is still contained by the
     overflow-x on body. */
  .services:has(.is-deck) { overflow: visible; }

  .services__stack.is-deck {
    display: block;
    /* Cards pin against this box, so they stay pinned once stuck and the next
       card rides up over the one before it. */
    position: relative;
  }

  /* The scroll distance between cards has to be a sibling rather than a margin
     on the card. A sticky element stops sticking when its MARGIN box reaches the
     bottom of the containing block, so a margin here would cut each card's pin
     short. Padding on the stack does not help either: Chrome constrains sticky
     to the content box, not the padding box. A real empty sibling is content, so
     it extends the travel without shortening it. */
  .is-deck .svc-gap { display: block; height: 58vh; }

  .is-deck .svc {
    position: sticky;
    /* Clears the sticky header, then centres the tallest card. */
    top: max(5.5rem, calc((100svh - var(--deck-card-h, 30rem)) / 2));
    /* scale and opacity rather than transform, so the entrance animation on
       [data-reveal] keeps its own transform and the two do not fight. */
    transition:
      scale 0.5s var(--ease-out),
      opacity 0.5s var(--ease-out),
      filter 0.5s var(--ease-out),
      border-color 0.4s var(--ease),
      background-color 0.4s var(--ease);
    will-change: scale, opacity;
  }

  /* The card that has been scrolled past, now sitting under the incoming one. */
  .is-deck .svc.is-behind {
    scale: 0.945;
    opacity: 0.4;
    filter: saturate(0.7);
  }
  .is-deck .svc.is-behind:hover { transform: none; }
}

/* --------------------------------------------------------------
   Why K1 Nova
   -------------------------------------------------------------- */
.why { background: var(--ink-800); }
.why__grid { display: grid; gap: clamp(2.5rem, 5vw, 4.5rem); }
.why__aside .eyebrow { margin-bottom: 1.1rem; }
.why__heading { margin-bottom: 1.2rem; }
.why__cta { margin-top: 1.8rem; }
.why__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; }
.why__item {
  padding: 1.3rem 0;
  border-top: 1px solid var(--line);
}
.why__item:last-child { border-bottom: 1px solid var(--line); }
.why__title {
  font-family: var(--font-display); font-size: 1.06rem; font-weight: 500;
  letter-spacing: -0.015em; margin-bottom: 0.3rem;
}
.why__body { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; max-width: 54ch; }

@media (min-width: 900px) {
  .why__grid { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); align-items: start; }
  .why__aside { position: sticky; top: 8rem; }
  .why__list { grid-template-columns: 1fr 1fr; gap: 0 clamp(1.5rem, 4vw, 3rem); }
  .why__item:nth-child(2) { border-top: 1px solid var(--line); }
  .why__item:nth-last-child(2) { border-bottom: 1px solid var(--line); }
}

.feature__note {
  margin-top: 1.4rem; font-size: var(--fs-xs); color: var(--muted-dim);
  line-height: 1.6; max-width: 48ch;
}

/* --------------------------------------------------------------
   Before / after comparison
   -------------------------------------------------------------- */
.compare { overflow: hidden; isolation: isolate; background: var(--ink-800); }
.compare__glow { width: 60vw; height: 40vw; max-width: 900px; top: 6%; left: 50%; transform: translateX(-50%); opacity: 0.6; }
.compare__head { position: relative; z-index: 2; max-width: 44rem; margin-bottom: clamp(2rem, 4vw, 3rem); }
.compare__head .eyebrow { margin-bottom: 1.1rem; }
.compare__head .lead { margin-top: 1.2rem; }

.compare__stage { position: relative; z-index: 2; max-width: 1100px; margin-inline: auto; }
.compare__viewer {
  position: relative;
  aspect-ratio: var(--compare-ratio, 1440 / 1010);
  border-radius: clamp(14px, 1.8vw, 22px);
  overflow: hidden;
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-lift);
  background: #fff;
  touch-action: pan-y;
  cursor: ew-resize;
  --pos: 50%;
}
/* Nested inside a browser frame: the frame already supplies the chrome, the
   rounding and the clipping, so the viewer drops its own and fills the screen. */
.frame__screen .compare__viewer {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  width: 100%;
}
.frame--compare .frame__screen { overflow: hidden; }

.compare__img { position: absolute; inset: 0; }
.compare__img img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.compare__img--before { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }

.compare__tag {
  position: absolute; top: 0.9rem; z-index: 4;
  padding: 0.3rem 0.75rem; border-radius: var(--r-pill);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  background: rgba(7, 11, 20, 0.9); color: #fff;
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.16);
  pointer-events: none;
}
.compare__tag--before { left: 0.9rem; }
.compare__tag--after { right: 0.9rem; background: rgba(29, 78, 216, 0.94); border-color: rgba(255, 255, 255, 0.35); }

.compare__handle {
  position: absolute; top: 0; bottom: 0; left: var(--pos);
  width: 2px; margin-left: -1px; z-index: 5;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.5), #fff 22%, #fff 78%, rgba(255, 255, 255, 0.5));
  pointer-events: none;
}
.compare__grip {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(7, 11, 20, 0.9); color: #fff;
  border: 1.5px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(6px);
}
.compare__grip svg { width: 20px; height: 12px; }

.compare__range {
  position: absolute; inset: 0; z-index: 6;
  width: 100%; height: 100%; margin: 0; opacity: 0;
  cursor: ew-resize; -webkit-appearance: none; appearance: none; background: none;
}
.compare__range:focus-visible { opacity: 1; outline: 3px solid var(--blue-light); outline-offset: 4px; }
.compare__range::-webkit-slider-thumb { -webkit-appearance: none; width: 44px; height: 100%; }
.compare__range::-moz-range-thumb { width: 44px; height: 100%; border: 0; background: transparent; }

.compare__notes {
  position: relative; z-index: 2;
  max-width: 1100px; margin: clamp(2rem, 4vw, 3rem) auto 0;
}
.compare__notes-title { margin-bottom: 1.1rem; }
.compare__changes {
  list-style: none; margin: 0 0 1.4rem; padding: 0;
  display: grid; gap: 0.7rem 2rem;
  font-size: var(--fs-sm); color: var(--muted);
}
.compare__changes li { position: relative; padding-left: 1.35rem; line-height: 1.55; }
.compare__changes li::before {
  content: ''; position: absolute; left: 0; top: 0.62em;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--blue);
}
.compare__disclaimer { font-size: var(--fs-xs); color: var(--muted-dim); margin-bottom: 1.2rem; max-width: 60ch; }
@media (min-width: 760px) { .compare__changes { grid-template-columns: 1fr 1fr; } }

/* --------------------------------------------------------------
   Work / project blocks
   -------------------------------------------------------------- */
.work__list { display: grid; gap: clamp(4rem, 9vw, 8rem); }
.project { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
.project__index {
  display: block; font-family: var(--font-display); font-size: var(--fs-xs);
  font-weight: 500; letter-spacing: 0.16em; color: var(--blue); margin-bottom: 0.9rem;
}
.project__name { margin-bottom: 0.5rem; font-size: clamp(1.85rem, 1.25rem + 2vw, 2.8rem); }
.project__meta {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.11em;
  color: var(--muted-dim); margin-bottom: 1.2rem;
}
.project__summary { margin-bottom: 1.5rem; }

.tags { list-style: none; display: flex; flex-wrap: wrap; gap: 0.45rem; margin: 0 0 1.6rem; padding: 0; }
.tags li {
  padding: 0.32rem 0.75rem; border-radius: var(--r-pill);
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.03em;
  color: var(--muted); background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--line);
}

@media (min-width: 980px) {
  .project { grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.7fr); gap: clamp(3rem, 5vw, 5rem); }
  .project__copy { max-width: 30rem; }
  .project--flip .project__media { order: 2; }
  .project--flip .project__copy { order: 1; }
  .project--flip .project__phone { left: auto; right: max(-7%, calc(var(--gutter) * -0.55)); }
}

/* --------------------------------------------------------------
   Process
   -------------------------------------------------------------- */
.process { background: var(--ink-800); }
.process__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2.25rem; counter-reset: step; }
.process__step { position: relative; padding-top: 1.75rem; border-top: 1px solid var(--line); }
.process__step::after {
  content: ''; position: absolute; top: -1px; left: 0; width: 2.5rem; height: 2px;
  background: linear-gradient(90deg, var(--blue), var(--violet));
}
.process__num {
  display: block; font-family: var(--font-display); font-size: 2.2rem; font-weight: 500;
  letter-spacing: -0.03em;
  color: transparent; -webkit-text-stroke: 1px rgba(255, 255, 255, 0.22);
  margin-bottom: 0.8rem; line-height: 1;
}
.process__title { margin-bottom: 0.5rem; }
.process__body { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; }
@media (min-width: 860px) { .process__list { grid-template-columns: repeat(4, 1fr); gap: 2rem; } }

/* --------------------------------------------------------------
   Pricing
   -------------------------------------------------------------- */
.pricing { position: relative; overflow: hidden; isolation: isolate; }
.pricing__glow { width: 56vw; height: 40vw; max-width: 820px; top: 4%; left: 50%; transform: translateX(-50%); opacity: 0.7; }
.pricing__grid { position: relative; z-index: 2; display: grid; gap: 1.25rem; align-items: stretch; }

.price {
  display: flex; flex-direction: column;
  padding: clamp(1.6rem, 2.6vw, 2.25rem);
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  position: relative;
}
.price--featured {
  background: linear-gradient(178deg, rgba(59, 130, 246, 0.13), rgba(124, 58, 237, 0.07) 55%, transparent 88%), var(--ink-700);
  border-color: rgba(59, 130, 246, 0.42);
  box-shadow: 0 40px 90px -40px rgba(59, 130, 246, 0.55);
}
.price__flag {
  position: absolute; top: -0.75rem; left: clamp(1.6rem, 2.6vw, 2.25rem);
  padding: 0.28rem 0.8rem; border-radius: var(--r-pill);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase;
  background: linear-gradient(100deg, var(--blue-solid), #6D28D9); color: #fff;
}
.price__name { margin-bottom: 0.5rem; }
.price__tagline { font-size: var(--fs-sm); color: var(--muted); margin-bottom: 1.4rem; min-height: 2.9em; line-height: 1.5; }
.price__amount { display: flex; flex-direction: column; gap: 0.15rem; }
.price__from { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.13em; color: var(--muted-dim); }
.price__amount strong {
  font-family: var(--font-display); font-size: clamp(2.1rem, 4vw, 2.7rem);
  font-weight: 500; letter-spacing: -0.035em; line-height: 1.05;
}
/* Offer price with the regular price struck through beside it. */
.price__figures { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }
.price__was {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 2vw, 1.45rem);
  font-weight: 500; letter-spacing: -0.02em;
  color: var(--muted-dim);
  text-decoration-thickness: 1.5px;
  text-decoration-color: rgba(170, 180, 200, 0.75);
}
.price__offer-note { font-size: var(--fs-xs); color: var(--muted); margin-top: 0.3rem; }
.price__flag--offer { background: linear-gradient(100deg, #C2410C, #DC2626); }
.price__rule { margin: 1.4rem 0; }
.price .checklist { grid-template-columns: 1fr; }
.price__best {
  font-size: var(--fs-xs); color: var(--muted-dim); line-height: 1.55;
  margin: 1.4rem 0 1.6rem; padding-top: 1.2rem; border-top: 1px solid var(--line);
}
.price__cta { margin-top: auto; }
@media (min-width: 900px) { .pricing__grid { grid-template-columns: repeat(3, 1fr); gap: 1.5rem; } }

.pricing__note {
  position: relative; z-index: 2;
  margin-top: clamp(2rem, 4vw, 3rem);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 1rem 2rem;
  padding: 1.4rem clamp(1.4rem, 2.5vw, 2rem);
  background: rgba(255, 255, 255, 0.028);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.pricing__note p { color: var(--muted); font-size: var(--fs-sm); }
.pricing__note strong { color: var(--white); }

/* --------------------------------------------------------------
   Management
   -------------------------------------------------------------- */
.management__panel { display: grid; gap: 2rem; padding: clamp(1.75rem, 3.4vw, 2.75rem); }
.management__copy .eyebrow { margin-bottom: 1rem; }
.management__copy h2 { margin-bottom: 0.9rem; }
.management__body { font-size: var(--fs-sm); line-height: 1.65; max-width: 52ch; }
.management__points { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; align-content: center; }
.management__points li {
  position: relative; padding-left: 1.4rem; font-size: var(--fs-sm); color: var(--muted);
}
.management__points li::before {
  content: ''; position: absolute; left: 0; top: 0.62em; width: 6px; height: 6px;
  border-radius: 50%; background: linear-gradient(120deg, var(--blue), var(--violet));
}
@media (min-width: 860px) { .management__panel { grid-template-columns: 1.1fr 0.9fr; gap: 3rem; } }

/* --------------------------------------------------------------
   FAQ
   -------------------------------------------------------------- */
.faq__grid { display: grid; gap: clamp(2.5rem, 5vw, 4.5rem); }
.faq__aside .eyebrow { margin-bottom: 1.1rem; }
.faq__aside h2 { margin-bottom: 1.2rem; }
.faq__aside .link-arrow { margin-top: 1.5rem; }
.faq__list { display: grid; }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__item:first-child { border-top: 1px solid var(--line); }
.faq__q {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1.5rem;
  padding: 1.3rem 0; cursor: pointer; list-style: none;
  font-family: var(--font-display); font-size: 1.02rem; font-weight: 500;
  letter-spacing: -0.012em; line-height: 1.4;
  transition: color 0.25s var(--ease);
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { color: var(--blue-light); }
.faq__icon {
  position: relative; flex: none; width: 1.1rem; height: 1.1rem; margin-top: 0.2rem;
}
.faq__icon::before, .faq__icon::after {
  content: ''; position: absolute; left: 50%; top: 50%; background: var(--blue-light);
  transform: translate(-50%, -50%); transition: transform 0.3s var(--ease), opacity 0.3s var(--ease);
}
.faq__icon::before { width: 1.1rem; height: 1.5px; }
.faq__icon::after { width: 1.5px; height: 1.1rem; }
.faq__item[open] .faq__icon::after { transform: translate(-50%, -50%) rotate(90deg); opacity: 0; }
.faq__a { padding-bottom: 1.4rem; }
.faq__a p { font-size: var(--fs-sm); color: var(--muted); line-height: 1.68; max-width: 66ch; }
.faq__item[open] .faq__a { animation: faq-in 0.35s var(--ease-out); }
@keyframes faq-in { from { opacity: 0; transform: translateY(-6px); } }
@media (min-width: 960px) {
  .faq__grid { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); align-items: start; }
  .faq__aside { position: sticky; top: 8rem; }
}

/* --------------------------------------------------------------
   Final CTA
   -------------------------------------------------------------- */
.cta-panel {
  position: relative; overflow: hidden; isolation: isolate;
  border-radius: var(--r-3xl);
  border: 1px solid var(--line-2);
  background:
    radial-gradient(120% 130% at 12% 0%, rgba(59, 130, 246, 0.22), transparent 58%),
    radial-gradient(90% 120% at 96% 100%, rgba(124, 58, 237, 0.2), transparent 62%),
    var(--ink-800);
  padding: clamp(2.5rem, 6vw, 5rem);
  text-align: center;
}
.cta-panel__glow { width: 70%; height: 120%; top: -40%; left: 15%; opacity: 0.5; }
.cta-panel__content { position: relative; z-index: 2; max-width: 46rem; margin-inline: auto; }
.cta-panel__heading { margin-bottom: 1.2rem; }
.cta-panel__body { margin-inline: auto; margin-bottom: 2.2rem; }
.cta-panel__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; justify-content: center; }

/* --------------------------------------------------------------
   Page hero (interior pages)
   -------------------------------------------------------------- */
.page-hero {
  position: relative; overflow: hidden; isolation: isolate;
  padding-top: clamp(2.5rem, 5vw, 4rem);
  padding-bottom: clamp(3rem, 6vw, 5rem);
}
.page-hero__glow { width: 62vw; height: 46vw; max-width: 900px; top: -28%; right: -12%; }
.page-hero__grid { position: relative; z-index: 2; display: grid; gap: clamp(2.5rem, 5vw, 4rem); align-items: center; }
.page-hero__heading { margin: 1.2rem 0 1.4rem; }
.page-hero__lead { max-width: 52ch; }
.page-hero__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 2.2rem; }
@media (min-width: 980px) {
  .page-hero__grid--split { grid-template-columns: minmax(0, 1.06fr) minmax(0, 0.94fr); }
}

.crumbs { position: relative; z-index: 2; margin-bottom: 1.75rem; }
.crumbs ol {
  display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0; padding: 0;
  font-size: var(--fs-xs); color: var(--muted-dim);
}
.crumbs li + li::before { content: '/'; margin-right: 0.5rem; opacity: 0.5; }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--blue-light); }

/* --------------------------------------------------------------
   Footer
   -------------------------------------------------------------- */
.site-footer {
  position: relative;
  padding-top: clamp(3.5rem, 7vw, 6rem);
  padding-bottom: 2.5rem;
  border-top: 1px solid var(--line);
  background: var(--ink-850);
}
.site-footer::before {
  content: ''; position: absolute; inset: -1px 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.5) 25%, rgba(124, 58, 237, 0.4) 70%, transparent);
}
.site-footer__top { display: grid; gap: 3rem; padding-bottom: 3rem; }
.site-footer__brand img { width: 230px; height: auto; }
.site-footer__blurb { margin: 1.5rem 0; font-size: var(--fs-sm); color: var(--muted); max-width: 40ch; line-height: 1.65; }
.site-footer__contact { display: grid; gap: 0.35rem; font-size: var(--fs-sm); }
.site-footer__contact a { color: var(--muted); transition: color 0.25s var(--ease); }
.site-footer__contact a:hover { color: var(--blue-light); }

.site-footer__nav { display: grid; gap: 2.25rem; }
.site-footer__heading {
  font-family: var(--font-body); font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.13em; color: var(--white);
  margin-bottom: 1rem;
}
.site-footer__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.15rem; }
/* Footer links carry a comfortable tap area rather than sitting at text height. */
.site-footer__col a {
  display: inline-flex; align-items: center; min-height: 2.5rem;
  font-size: var(--fs-sm); color: var(--muted); transition: color 0.25s var(--ease);
}
.site-footer__col a:hover { color: var(--white); }
.site-footer__contact a { display: inline-flex; align-items: center; min-height: 2.5rem; }
.site-footer__legal a { display: inline-flex; align-items: center; min-height: 2.5rem; }
.site-footer__area { margin-top: 1rem; font-size: var(--fs-xs); color: var(--muted-dim); line-height: 1.6; max-width: 30ch; }

.site-footer__bottom {
  display: flex; flex-wrap: wrap; gap: 1rem 2rem; align-items: center; justify-content: space-between;
  padding-top: 2rem; border-top: 1px solid var(--line);
  font-size: var(--fs-xs); color: var(--muted-dim);
}
.site-footer__legal { display: flex; gap: 1.5rem; list-style: none; margin: 0; padding: 0; }
.site-footer__legal a { transition: color 0.25s var(--ease); }
.site-footer__legal a:hover { color: var(--white); }

@media (min-width: 760px) { .site-footer__nav { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1020px) { .site-footer__top { grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); gap: 4rem; } }

/* --------------------------------------------------------------
   Responsive header
   -------------------------------------------------------------- */
@media (max-width: 1079px) {
  .nav, .site-header__phone { display: none; }
  .burger { display: flex; }
  .site-header__inner { justify-content: space-between; }
}
@media (max-width: 479px) {
  .site-header__cta { display: none; }
  .brand__wordmark { width: 100px; }
}

/* --------------------------------------------------------------
   Forms
   -------------------------------------------------------------- */
.quote-form { display: grid; gap: 1.5rem; }
.honeypot { position: absolute; left: 0; top: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; clip-path: inset(50%); }
.quote-form__grid { display: grid; gap: 1.25rem; }
@media (min-width: 620px) { .quote-form__grid { grid-template-columns: 1fr 1fr; } }

/* align-content keeps a field's rows packed at the top. Without it a field
   sitting beside one that carries a hint line gets stretched to match the row,
   which made the email input taller than the website input next to it. */
.field { display: grid; gap: 0.45rem; min-width: 0; align-content: start; }
.field--full { grid-column: 1 / -1; }
.field__label {
  display: flex; align-items: baseline; gap: 0.5rem;
  font-size: var(--fs-sm); font-weight: 600; color: var(--white);
}
.field__req { color: var(--blue-light); }
.field__opt { font-size: 0.7rem; font-weight: 500; color: var(--muted-dim); letter-spacing: 0.03em; }
.field__input {
  width: 100%; padding: 0.85rem 1rem;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  color: var(--white); font-size: var(--fs-sm);
  transition: border-color 0.25s var(--ease), background-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.field__input::placeholder { color: rgba(170, 180, 200, 0.5); }
.field__input:hover { border-color: var(--line-3); }
.field__input:focus {
  outline: none; border-color: var(--blue);
  background: rgba(59, 130, 246, 0.07);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.22);
}
.field__input[aria-invalid='true'] { border-color: #F87171; }
.field__textarea { resize: vertical; min-height: 9rem; line-height: 1.6; font-family: inherit; }
.field__select { position: relative; }
.field__select select { appearance: none; -webkit-appearance: none; padding-right: 2.5rem; cursor: pointer; }
.field__select option { background: var(--ink-700); color: var(--white); }
.field__select svg {
  position: absolute; right: 1rem; top: 50%; transform: translateY(-50%);
  width: 10px; height: 6px; color: var(--muted); pointer-events: none;
}
.field__hint { font-size: var(--fs-xs); color: var(--muted-dim); }
.field__error { font-size: var(--fs-xs); color: #F87171; min-height: 0; }
.field__error:empty { display: none; }

.quote-form__foot { display: grid; gap: 0.9rem; justify-items: start; }
.quote-form__note { font-size: var(--fs-xs); color: var(--muted-dim); max-width: 42ch; }

.form__status { font-size: var(--fs-sm); padding: 0.85rem 1.1rem; border-radius: var(--r-md); }
.form__status:empty { display: none; }
.form__status.is-success { background: rgba(52, 211, 153, 0.12); border: 1px solid rgba(52, 211, 153, 0.35); color: #A7F3D0; }
.form__status.is-error { background: rgba(248, 113, 113, 0.1); border: 1px solid rgba(248, 113, 113, 0.32); color: #FCA5A5; }
.form__status.is-pending { background: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.32); color: var(--blue-light); }

/* --------------------------------------------------------------
   Prose (privacy, terms, case study body copy)
   -------------------------------------------------------------- */
.prose { max-width: 46rem; }
.prose > * + * { margin-top: 1.15rem; }
.prose h2 {
  font-size: var(--fs-h3); margin-top: 2.75rem; margin-bottom: 0.4rem;
  padding-top: 2rem; border-top: 1px solid var(--line);
}
.prose h3 { font-size: var(--fs-h4); margin-top: 1.9rem; margin-bottom: 0.2rem; }
.prose p, .prose li { color: var(--muted); line-height: 1.75; }
.prose strong { color: var(--white); font-weight: 600; }
.prose a { color: var(--blue-light); text-decoration: underline; text-underline-offset: 3px; }
.prose a:hover { color: #8DBBFF; }
.prose ul, .prose ol { padding-left: 1.2rem; display: grid; gap: 0.5rem; }
.prose li::marker { color: var(--blue); }
.prose .note {
  padding: 1.1rem 1.3rem; border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.03); border: 1px solid var(--line);
  font-size: var(--fs-sm);
}

/* --------------------------------------------------------------
   Split layout (heading left, body right)
   -------------------------------------------------------------- */
.split { display: grid; gap: clamp(1.5rem, 4vw, 3.5rem); }
.split__side .eyebrow { margin-bottom: 1rem; }
.split__note { font-size: var(--fs-sm); margin-top: 1rem; max-width: 34ch; }
.split__body > * + * { margin-top: 1.1rem; }
@media (min-width: 900px) {
  .split { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); align-items: start; }
}

/* --------------------------------------------------------------
   Numbered grid list
   -------------------------------------------------------------- */
.grid-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--r-xl); overflow: hidden; }
.grid-list__item { padding: clamp(1.4rem, 2.6vw, 2rem); background: var(--ink-850); }
.grid-list__num {
  display: block; font-family: var(--font-display); font-size: var(--fs-xs);
  font-weight: 500; letter-spacing: 0.14em; color: var(--blue); margin-bottom: 0.9rem;
}
.grid-list__item h3 { margin-bottom: 0.45rem; }
.grid-list__item p { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; }
@media (min-width: 700px) { .grid-list { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1060px) { .grid-list { grid-template-columns: repeat(4, 1fr); } }

.page-hero__visual { width: 100%; border-radius: var(--r-xl); border: 1px solid var(--line); }

/* --------------------------------------------------------------
   Background clips

   Artwork that sits under copy: the arc below the closing call to action, the
   lattice across the top of the interior pages. Markup comes from
   movingBackdrop() in lib/html.js, the clip is attached by site.js.

   The still and the clip are the same picture, laid exactly over each other at
   the same opacity, so only one of them is ever visible and the band can never
   double up and brighten. `is-bg-video` goes on the parent, and only once the
   clip is really playing: until then, and for anyone whose clip never arrives,
   the still is what is on screen and nothing looks unfinished.

   Both sit under a scrim rather than being dimmed on their own, because the
   text over them has to stay at its contrast whatever the clip is doing at that
   moment. `npm run check` measures that.
   -------------------------------------------------------------- */
.cta-band__art,
.cta-band__video,
.page-hero__art,
.page-hero__video {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.cta-band__art img,
.page-hero__art img { width: 100%; height: 100%; object-fit: cover; }
.cta-band__video,
.page-hero__video { object-fit: cover; opacity: 0; }

/* The still fades out as the clip fades in, both over the same moment. */
.cta-band__art img,
.page-hero__art img,
.cta-band__video,
.page-hero__video { transition: opacity 0.8s var(--ease-out); }

/* The arc is a curve across the bottom of its frame with the glow above it, so
   it is anchored to the bottom of the band: the light rises into the copy
   rather than being cropped away above it. */
.cta-band__art img,
.cta-band__video { object-position: 50% 100%; opacity: 0.55; }
.cta-band.is-bg-video .cta-band__art img { opacity: 0; }
.cta-band.is-bg-video .cta-band__video { opacity: 0.55; }
.cta-band__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, var(--ink-850) 0%, rgba(11, 16, 23, 0.72) 42%, rgba(11, 16, 23, 0.55) 100%);
}

/* The lattice is weighted to the right of its frame and falls away to black on
   the left, which is where the heading and the lead sit. Anchored right, it
   fills the empty half of the band and leaves the copy on plain dark. */
.page-hero__art img,
.page-hero__video { object-position: 100% 50%; opacity: 0.5; }
.page-hero.is-bg-video .page-hero__art img { opacity: 0; }
.page-hero.is-bg-video .page-hero__video { opacity: 0.5; }
.page-hero__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(90deg, var(--ink-900) 0%, rgba(7, 11, 20, 0.86) 38%, rgba(7, 11, 20, 0.45) 100%),
    linear-gradient(180deg, rgba(7, 11, 20, 0.5) 0%, transparent 30%, var(--ink-900) 100%);
}

/* On a narrow window the copy runs the full width of the band, so the lattice
   sits under the heading rather than beside it. That is why it is dimmer here,
   and it stays dimmer: this only makes it move at that weight.

   It used to be taken out altogether, which left the interior pages still on a
   phone while the arc under the closing call to action moved, and the two bands
   on one page disagreeing about whether the artwork was alive read as the top
   one being broken. The clip is 57KB, the scrim over it is what holds the
   text's contrast rather than the artwork being dark, and `npm run check`
   measures that at this width too. Reduced motion and a saved-data connection
   still get the still, which is the same picture. */
@media (max-width: 780px) {
  .page-hero__art img { opacity: 0.28; }
  .page-hero.is-bg-video .page-hero__video { opacity: 0.28; }
}

/* Anyone who asked for less motion keeps the still, which is the same picture.
   site.js never attaches a clip for them either; this is the belt to that
   brace, and it also covers a clip that was already playing when the setting
   was changed with the page open. */
@media (prefers-reduced-motion: reduce) {
  .cta-band__video,
  .page-hero__video { display: none; }
  .cta-band.is-bg-video .cta-band__art img { opacity: 0.55; }
  .page-hero.is-bg-video .page-hero__art img { opacity: 0.5; }
}

/* --------------------------------------------------------------
   Pricing teaser
   -------------------------------------------------------------- */
.pricing-teaser__panel {
  display: grid; gap: 1.75rem; align-items: center;
  padding: clamp(1.75rem, 3.4vw, 2.75rem);
  background:
    radial-gradient(110% 140% at 8% 0%, rgba(59, 130, 246, 0.14), transparent 62%),
    var(--ink-800);
}
.pricing-teaser__panel h2 { margin: 0.9rem 0 0.7rem; }
.pricing-teaser__body { font-size: var(--fs-sm); max-width: 52ch; line-height: 1.65; }
.pricing-teaser__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }
@media (min-width: 900px) {
  .pricing-teaser__panel { grid-template-columns: 1.4fr auto; gap: 3rem; }
}

/* --------------------------------------------------------------
   Three column explainer
   -------------------------------------------------------------- */
.cols-3 { display: grid; gap: 1.25rem; }
.col-card {
  padding: clamp(1.5rem, 2.8vw, 2.1rem);
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  position: relative;
}
.col-card::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.6), transparent);
}
.col-card__title { margin-bottom: 0.8rem; }
.col-card p { font-size: var(--fs-sm); color: var(--muted); line-height: 1.65; }
@media (min-width: 860px) { .cols-3 { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 700px) { .cols-3--four { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1060px) { .cols-3--four { grid-template-columns: repeat(4, 1fr); } }

.source-note { margin-top: 1.5rem; font-size: var(--fs-xs); color: var(--muted-dim); }
/* On a light band --muted-dim is too pale to clear AA at this size. */
.section--light .source-note { color: var(--muted-on-light); font-size: var(--fs-sm); }

/* --------------------------------------------------------------
   Expectations panel
   -------------------------------------------------------------- */
.expectations__panel {
  display: grid; gap: 2rem;
  padding: clamp(1.75rem, 3.4vw, 2.75rem);
  background:
    radial-gradient(120% 150% at 100% 0%, rgba(124, 58, 237, 0.13), transparent 60%),
    var(--ink-800);
}
.expectations__copy h2 { margin: 0.9rem 0 0.8rem; }
.expectations__body { font-size: var(--fs-sm); line-height: 1.65; max-width: 44ch; }
.expectations__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.85rem; }
.expectations__list li {
  position: relative; padding-left: 1.6rem;
  font-size: var(--fs-sm); color: var(--muted); line-height: 1.6;
}
.expectations__list li::before {
  content: ''; position: absolute; left: 0; top: 0.52em;
  width: 0.6rem; height: 0.6rem; border-radius: 2px;
  border: 1.5px solid rgba(124, 58, 237, 0.7);
  transform: rotate(45deg);
}
@media (min-width: 900px) { .expectations__panel { grid-template-columns: 0.9fr 1.1fr; gap: 3rem; } }

@media (min-width: 700px) { .cols-3--six { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .cols-3--six { grid-template-columns: repeat(3, 1fr); } }

/* --------------------------------------------------------------
   About panel
   -------------------------------------------------------------- */
.about-panel {
  padding: clamp(2rem, 4.5vw, 3.5rem);
  background:
    radial-gradient(110% 150% at 100% 0%, rgba(59, 130, 246, 0.16), transparent 62%),
    var(--ink-800);
  isolation: isolate;
}
.about-panel__glow { width: 60%; height: 140%; top: -30%; right: -10%; opacity: 0.5; }
.about-panel__content { position: relative; z-index: 2; max-width: 50rem; }
.about-panel__content h2 { margin: 0.9rem 0 1rem; }
.about-panel__body { margin-bottom: 2rem; }
.about-panel__contact { display: flex; flex-wrap: wrap; align-items: center; gap: 1.25rem 2rem; }
.about-panel__details { display: grid; gap: 0.2rem; font-size: var(--fs-sm); }
.about-panel__details a { display: inline-flex; align-items: center; min-height: 2.25rem; color: var(--muted); }
.about-panel__details a:hover { color: var(--blue-light); }

/* --------------------------------------------------------------
   Contact
   -------------------------------------------------------------- */
.page-hero--contact { padding-bottom: clamp(3rem, 6vw, 5rem); }
.contact__glow-b { width: 40vw; height: 40vw; max-width: 620px; bottom: -10%; left: -14%; }
.contact__grid { position: relative; z-index: 2; display: grid; gap: clamp(2.5rem, 5vw, 4rem); align-items: start; }
.contact__heading { margin: 1.2rem 0 1.3rem; }
.contact__intro .lead { max-width: 44ch; }

.contact__direct { display: grid; gap: 0.75rem; margin: 2.25rem 0; }
.contact__card {
  display: grid; gap: 0.15rem;
  padding: 0.95rem 1.2rem;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  transition: border-color 0.25s var(--ease), background-color 0.25s var(--ease), transform 0.3s var(--ease);
}
a.contact__card:hover { border-color: rgba(59, 130, 246, 0.45); background: rgba(59, 130, 246, 0.08); transform: translateY(-2px); }
.contact__card-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.13em; color: var(--muted-dim); font-weight: 600; }
.contact__card-value { font-weight: 600; font-size: var(--fs-sm); }
@media (min-width: 520px) { .contact__direct { grid-template-columns: 1fr 1fr; } }

.contact__steps { padding-top: 1.75rem; border-top: 1px solid var(--line); }
.contact__steps-title { margin-bottom: 1.1rem; }
.contact__steps-list { list-style: none; margin: 0 0 1.1rem; padding: 0; display: grid; gap: 0.8rem; }
.contact__steps-list li {
  display: flex; gap: 0.8rem; align-items: flex-start;
  font-size: var(--fs-sm); color: var(--muted); line-height: 1.55;
}
.contact__steps-list span {
  flex: none; display: grid; place-items: center;
  width: 1.5rem; height: 1.5rem; border-radius: 50%;
  background: rgba(59, 130, 246, 0.16); border: 1px solid rgba(59, 130, 246, 0.4);
  color: var(--blue-light); font-size: 0.72rem; font-weight: 700;
}
.contact__reassure { font-size: var(--fs-xs); color: var(--muted-dim); max-width: 46ch; }

.contact__form-panel { padding: clamp(1.5rem, 3vw, 2.5rem); border-radius: var(--r-xl); }
.contact__form-title { margin-bottom: 0.4rem; }
.contact__form-sub { font-size: var(--fs-xs); color: var(--muted-dim); margin-bottom: 1.75rem; }

@media (min-width: 1000px) {
  .contact__grid { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }
  .contact__form-wrap { position: sticky; top: 7rem; }
}

/* --------------------------------------------------------------
   Case study
   -------------------------------------------------------------- */
.case-hero__grid { position: relative; z-index: 2; display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: end; }
.case-hero__heading { margin: 1.1rem 0 1.3rem; }
.case-hero__lead { max-width: 52ch; }
.case-hero__tags { margin-top: 1.6rem; margin-bottom: 0; }

.case-facts { display: grid; gap: 0; margin: 0; border-top: 1px solid var(--line); }
.case-facts__row { display: grid; gap: 0.2rem; padding: 0.95rem 0; border-bottom: 1px solid var(--line); }
.case-facts dt { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.13em; color: var(--muted-dim); font-weight: 600; }
.case-facts dd { margin: 0; font-size: var(--fs-sm); color: var(--white); line-height: 1.5; }
@media (min-width: 980px) { .case-hero__grid { grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr); } }

.case-shot { max-width: 1200px; margin-inline: auto; }

.case-approach { display: grid; gap: 2rem; }
.case-approach__item { display: grid; grid-template-columns: auto 1fr; gap: 1.1rem; }
.case-approach__num {
  font-family: var(--font-display); font-size: var(--fs-xs); font-weight: 500;
  letter-spacing: 0.14em; color: var(--blue); padding-top: 0.35rem;
}
.case-approach__item h3 { margin-bottom: 0.4rem; }
.case-approach__item p { font-size: var(--fs-sm); color: var(--muted); line-height: 1.68; max-width: 60ch; }

.case-screens { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); align-items: center; }
.case-screens__phone { max-width: 260px; margin-inline: auto; }
@media (min-width: 900px) {
  .case-screens { grid-template-columns: minmax(0, 1fr) auto; }
  .case-screens--solo { grid-template-columns: minmax(0, 1fr); }
  .case-screens__phone { margin-inline: 0; }
}

.case-full__scroller {
  max-width: 900px; margin-inline: auto;
  max-height: min(80vh, 760px); overflow-y: auto;
  border-radius: var(--r-lg); border: 1px solid var(--line-2);
  box-shadow: var(--shadow-lift);
  background: #fff;
  scrollbar-width: thin;
}
.case-full__scroller img { width: 100%; }

.case-more { display: grid; gap: 1.5rem; }
.case-more__card {
  display: grid; gap: 0.9rem;
  padding: 1rem; border-radius: var(--r-xl);
  border: 1px solid var(--line); background: var(--ink-800);
  transition: border-color 0.3s var(--ease), transform 0.35s var(--ease), background-color 0.3s var(--ease);
}
.case-more__card:hover { border-color: rgba(59, 130, 246, 0.4); transform: translateY(-3px); background: var(--ink-700); }
.case-more__card picture img { border-radius: var(--r-md); width: 100%; }
.case-more__meta { display: grid; gap: 0.15rem; padding-inline: 0.4rem; }
.case-more__name { font-family: var(--font-display); font-size: 1.1rem; font-weight: 500; letter-spacing: -0.015em; }
.case-more__industry { font-size: var(--fs-xs); color: var(--muted-dim); }
.case-more__card .link-arrow { padding-inline: 0.4rem; padding-bottom: 0.4rem; }
@media (min-width: 760px) { .case-more { grid-template-columns: 1fr 1fr; } }

.showcase--phones { display: flex; gap: clamp(0.75rem, 2vw, 1.5rem); align-items: flex-start; justify-content: center; }
.showcase--phones .frame--phone { flex: 1 1 0; max-width: 250px; }
.showcase__phone-b { margin-top: clamp(1.5rem, 4vw, 3.5rem); }

.local-proof-links { display: flex; flex-wrap: wrap; gap: 1.5rem 2.5rem; margin-top: 1.75rem; justify-content: center; }

/* --------------------------------------------------------------
   Legal pages
   -------------------------------------------------------------- */
.page-hero--legal { padding-bottom: clamp(1.5rem, 3vw, 2.5rem); }
.legal__heading { margin: 1.1rem 0 0.6rem; }
.legal__updated { font-size: var(--fs-sm); }

/* --------------------------------------------------------------
   404
   -------------------------------------------------------------- */
.notfound {
  position: relative; overflow: hidden; isolation: isolate;
  min-height: 76svh; display: flex; align-items: center;
  padding-block: clamp(4rem, 10vw, 8rem);
}
.notfound__glow { width: 70vw; height: 60vw; max-width: 900px; top: -25%; left: 50%; transform: translateX(-50%); }
.notfound__inner { position: relative; z-index: 2; max-width: 46rem; }
.notfound__heading { margin: 1.2rem 0 1.4rem; }
.notfound__lead { margin-bottom: 2.2rem; }
.notfound__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-bottom: 2.75rem; }
.notfound__links {
  display: flex; flex-wrap: wrap; gap: 0.6rem;
  padding-top: 1.75rem; border-top: 1px solid var(--line);
}
.notfound__links a {
  display: inline-flex; align-items: center; min-height: 2.5rem;
  padding: 0.42rem 0.95rem; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: rgba(255, 255, 255, 0.035);
  font-size: var(--fs-xs); font-weight: 500; color: var(--muted);
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease), background-color 0.25s var(--ease);
}
.notfound__links a:hover { color: var(--white); border-color: var(--line-3); background: rgba(255, 255, 255, 0.07); }
.notfound__contact { margin-top: 1.75rem; font-size: var(--fs-sm); color: var(--muted); }
.notfound__contact a { color: var(--blue-light); }

/* --------------------------------------------------------------
   Section rhythm: lighter bands read as large rounded panels
   stacked on the page, and the closing CTA overlaps the footer.
   -------------------------------------------------------------- */
.surface-alt, .problems, .process, .compare {
  border-radius: clamp(26px, 3vw, 54px);
  position: relative;
  z-index: 1;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
/* Two lighter bands in a row should read as one, not two stacked cards. */
.surface-alt + .surface-alt,
.problems + .surface-alt { margin-top: calc(clamp(26px, 3vw, 54px) * -1); }

.cta-section { padding-bottom: 0; position: relative; z-index: 3; }
.cta-panel { margin-bottom: clamp(-4.5rem, -4vw, -2.5rem); }
.site-footer { padding-top: clamp(6.5rem, 11vw, 9.5rem); }

/* ==========================================================================
   The entry
   --------------------------------------------------------------------------
   A filmed desk, a monitor mid build, and a scroll that walks you into it.

   Measured off the footage, as a fraction of the 1280x720 frame, the monitor's
   screen sits at:

       first frame   left 27.188%  top 20.139%  width 46.484%  height 46.944%
       last frame    left 21.953%  top 15.972%  width 56.719%  height 57.361%

   The camera pushes straight in, so the centre barely moves: 50.43%, 43.61% at
   the start and 50.31%, 44.65% at the end. The overlay is pinned to that centre
   at the larger of the two sizes, and the zoom is computed from it. The clip
   plays and loops on its own, so the monitor is never at a known point in its
   dolly; that is fine, because the handover to the real screen is a dissolve,
   and by the time the panel is readable the filmed one is blurred behind it.

   The plain hero is the DEFAULT here, and `is-intro-active` on the root element
   is what turns the film on. It is set by the inline boot script in lib/seo.js
   before first paint, so a visitor with no JavaScript, one who asked for less
   motion, and one on a metered connection all get the hero at full size and
   never download the video at all -- and nobody sees the hero appear and then
   get covered by a film landing on top of it. --z is the zoom and --p is scroll
   progress; both only ever drive transforms and opacities, so nothing
   re-lays out.
   ========================================================================== */
.enter { position: relative; background: var(--ink-900); }
.enter__stage { position: relative; }
.enter__video,
.enter__vignette,
.enter__cue { display: none; }
.enter__screen { position: relative; min-height: 88svh; }
.enter__site { position: relative; min-height: 88svh; }

.enter__hero {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
  padding: clamp(1.25rem, 4vw, 3rem);
  text-align: center;
}
.enter__hero-glow {
  width: 60%; height: 60%;
  top: 8%; left: 50%; transform: translateX(-50%);
  opacity: 0.5;
  z-index: 1;
}
/* Wide enough that the heading's first line stays one line at its largest size.
   Sized in rem, not vw: for most of the entry this sits inside the screen box
   rather than the window. */
.enter__hero-inner { position: relative; z-index: 2; max-width: 52rem; width: 100%; }
.enter__wordmark { width: 132px; height: auto; margin: 0 auto 2.2rem; opacity: 0.9; display: block; }
.enter__eyebrow {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--blue-light);
  margin-bottom: 1.1rem;
}
.enter__heading {
  font-family: var(--font-display);
  font-size: clamp(2rem, 5.4vw, 4rem);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.03em;
  margin-bottom: 1.3rem;
}
.enter__line { display: block; }
.enter__sub {
  font-size: clamp(0.98rem, 1.25vw, 1.15rem);
  color: var(--muted);
  line-height: 1.6;
  max-width: 34rem;
  margin: 0 auto 2.2rem;
}
.enter__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; justify-content: center; }

/* ==========================================================================
   Live: the film, the zoom and the build
   ========================================================================== */
.is-intro-active .enter {
  /* The sticky child pins for one viewport; the rest is the scroll the zoom is
     spread over. Kept short on purpose. */
  height: calc(100vh + var(--enter-scroll, 170vh));
}
.is-intro-active .enter .enter__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  /* Both cuts cover the window, so this is only ever seen in the frame or two
     before the film has anything to draw, and on a window shaped like neither. */
  background:
    radial-gradient(90% 55% at 50% 40%, rgba(19, 33, 58, 0.55), transparent 70%),
    #05070A;
}

/* The film. A 16:9 box sized to cover the viewport, then scaled about the point
   the monitor's screen is centred on, so the push lands the screen dead centre
   whatever shape the window is. */
.is-intro-active .enter .enter__stage {
  position: absolute;
  left: 50%;
  top: 50%;
  /* Sized to cover and nothing more. It used to be 14% larger than that, so it
     could be hung off the monitor's centre rather than its own, and every one of
     those percent was the 720p source being magnified further. The frame is
     simply centred now, and the push still reads as going into the monitor
     because the ZOOM is anchored there: transform-origin is the glass, so the
     glass is the one thing that holds still while everything grows around it. */
  --stage-w: max(100vw, calc(100svh * 1280 / 720));
  background: #05070A;
  width: var(--stage-w);
  height: calc(var(--stage-w) * 720 / 1280);
  transform: translate(-50%, -50%) scale(var(--z, 1));
  transform-origin: 50.17% 44.00%;
  will-change: transform;
  /* First beat of the entrance. See "The entrance" below. */
  opacity: var(--in, 1);
  transition: --in 1.1s var(--ease-out);
}

.is-intro-active .enter .enter__video {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Softened and dimmed as the panel takes over, which is also what stops the
     filmed screen's set dressing from ever being legible. */
  filter: blur(calc(var(--film-soft, 0) * 14px)) brightness(calc(1 - var(--film-soft, 0) * 0.55));
}

.is-intro-active .enter .enter__vignette {
  display: block;
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(120% 90% at 50% 44%, transparent 38%, rgba(5, 7, 10, 0.55) 100%);
  opacity: calc(1 - var(--film-soft, 0));
}

/* The monitor's screen, and after the handover, the site. */
/* The screen sits in viewport pixels rather than inside the scaled stage, so it
   can start life as the monitor's glass and grow from there into the whole
   window. The script writes these four values every frame; they interpolate from
   wherever the monitor currently is to the full viewport as the panel takes
   over. That is what makes this work in portrait, where a 16:9 frame can never
   cover the window and the monitor would otherwise be a letterbox sliver. */
.is-intro-active .enter .enter__screen {
  position: absolute;
  left: var(--screen-x, 0px);
  top: var(--screen-y, 0px);
  width: var(--screen-w, 100%);
  height: var(--screen-h, 100%);
  min-height: 0;
  z-index: 2;
  overflow: hidden;
  /* The panel sizes its type off this box. Declared on the parent because cqw
     inside the container element resolves against the container outside it. */
  container-type: inline-size;
  border-radius: var(--screen-radius, 0px);
}

/* ---------- The site, coming up inside the screen ---------- */
.is-intro-active .enter .enter__site {
  position: absolute;
  inset: 0;
  min-height: 0;
  z-index: 3;
  background: var(--ink-900);
  opacity: var(--site-in, 0);
  overflow: hidden;
}
/* A transparent element still takes clicks, so without this the hero's two
   buttons sit invisibly over the film the whole way down. is-entered is set on
   the root element by the same driver that fades the site in. */
.is-intro-active .enter .enter__site { pointer-events: none; }
.is-entered .enter .enter__site { pointer-events: auto; }

/* Each part arrives when the code that writes it has been written. --part is
   set per element by the script; nothing here runs on a timer. */
.is-intro-active .enter [data-build-part] {
  opacity: var(--part, 0);
  transform: translateY(calc((1 - var(--part, 0)) * 0.9rem));
  filter: blur(calc((1 - var(--part, 0)) * 3px));
}

/* ---------- Whose desk this is ---------- */
.enter__brand { display: none; }
.is-intro-active .enter .enter__brand {
  display: grid;
  position: absolute;
  left: 50%;
  /* Sat just above the monitor's own top edge, which the script measures every
     frame, rather than at a percentage of a window whose shape it cannot know. */
  top: auto;
  bottom: calc(100% - var(--glass-top, 20%) + 2rem);
  transform: translateX(-50%) translateY(calc((1 - var(--in, 1)) * 0.75rem));
  z-index: 3;
  justify-items: center;
  gap: 0.6rem;
  width: min(19rem, 46vw);
  pointer-events: none;
  /* Hands over exactly as the build panel arrives, so the two never compete.
     The entrance rides in as a second multiplier rather than replacing this
     one, so the two never fight over the same property. */
  /* Two gates, not one. --handoff is how far the screen has let go of the
     monitor, and the lockup sits above the monitor, so it has to be gone before
     the growing box reaches it. --site-in only
     ever rises, so it is what keeps the lockup gone once it has handed over. */
  opacity: calc(
    clamp(0, calc(1 - var(--handoff, 0) * 1.6), 1)
    * clamp(0, calc(1 - var(--site-in, 0) * 2), 1)
    * var(--in, 1));
  transition: --in 0.9s var(--ease-out) 0.5s;
}
.enter__brand-halo {
  position: absolute;
  inset: -55% -35%;
  z-index: 0;
  background: radial-gradient(50% 50% at 50% 50%, rgba(36, 107, 253, 0.42), transparent 70%);
  filter: blur(14px);
}
.enter__brand-mark {
  position: relative;
  z-index: 1;
  width: 100%;
  height: auto;
  /* Two shadows rather than one: a tight halo that reads as the mark being lit,
     and a wide one that lifts it off a photograph busy enough to swallow it. */
  filter:
    drop-shadow(0 0 14px rgba(36, 107, 253, 0.85))
    drop-shadow(0 0 46px rgba(36, 107, 253, 0.5))
    drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
  animation: brand-breathe 4.5s var(--ease) infinite;
}
.enter__brand-line {
  position: relative;
  z-index: 1;
  margin: 0;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.3em;
  color: rgba(255, 255, 255, 0.82);
  text-shadow: 0 0 16px rgba(36, 107, 253, 0.75), 0 1px 10px rgba(0, 0, 0, 0.8);
}
@keyframes brand-breathe {
  0%, 100% { filter: drop-shadow(0 0 12px rgba(36, 107, 253, 0.7)) drop-shadow(0 0 38px rgba(36, 107, 253, 0.4)) drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55)); }
  50%      { filter: drop-shadow(0 0 20px rgba(36, 107, 253, 0.95)) drop-shadow(0 0 60px rgba(36, 107, 253, 0.6)) drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55)); }
}
@media (prefers-reduced-motion: reduce) { .enter__brand-mark { animation: none; } }

/* A short window leaves almost nothing above the monitor -- at 1600x700 the
   gap is 78px -- so the lockup gives up its tagline and shrinks to fit rather
   than sitting across the screen. */
@media (max-height: 760px) and (min-aspect-ratio: 1 / 1) {
  .is-intro-active .enter .enter__brand { width: min(13rem, 34vw); gap: 0.35rem; }
  .is-intro-active .enter .enter__brand-line { display: none; }
}

/* On a phone the mark sits above the monitor exactly as it does on a desk --
   the portrait film covers the window too, so there is no band edge to hang it
   off any more, only the rain behind the glass, which is the quietest part of
   the picture and the best place for it. */
@media (max-aspect-ratio: 1 / 1) {
  .is-intro-active .enter .enter__brand {
    width: min(17rem, 62vw);
    gap: 0.55rem;
  }
  /* The tagline is set wider than the mark it sits under, and on a 375px phone
     62vw is not enough for it: it broke after GROW. and the extra row was what
     pushed the lockup off the top. It keeps one line and is allowed to be wider
     than the mark, which is what centring it in the band is for. */
  .enter__brand-line {
    letter-spacing: clamp(0.12em, 0.9vw, 0.22em);
    white-space: nowrap;
  }
}

/* ---------- The cue ---------- */
.is-intro-active .enter .enter__cue {
  display: grid;
  position: absolute;
  left: 50%;
  bottom: clamp(1.25rem, 4vh, 2.5rem);
  transform: translateX(-50%) translateY(calc((1 - var(--in, 1)) * 0.6rem));
  z-index: 4;
  justify-items: center;
  gap: 0.7rem;
  margin: 0;
  /* Gone as soon as the visitor has taken the hint, and last to arrive on the
     way in. */
  opacity: calc(clamp(0, calc(1 - var(--p, 0) * 7), 1) * var(--in, 1));
  transition: --in 0.8s var(--ease-out) 1s;
  pointer-events: none;
}
.enter__cue-label {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: rgba(255, 255, 255, 0.72);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.75);
}
.enter__cue-line {
  width: 1px;
  height: 2.6rem;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.65), transparent);
  animation: cue-drop 2.1s var(--ease) infinite;
}
@keyframes cue-drop {
  0%, 100% { transform: scaleY(0.4); transform-origin: top; opacity: 0.35; }
  50%      { transform: scaleY(1);   transform-origin: top; opacity: 1; }
}

/* Taller than it is wide: a different film, not a crop of the wide one.

   This used to be the 16:9 frame shown whole as a band, with dark above and
   below it, because nothing that shape can cover a 9:19 window without cropping
   away everything but the middle of the monitor. The portrait cut is filmed
   720x1280, so it covers the window the way the wide one covers a desktop: rain
   at the top, the monitor across the middle, the desk below. Same rule as
   landscape, the film's own ratio and its own glass centre to push into. */
@media (max-aspect-ratio: 1 / 1) {
  .is-intro-active .enter .enter__stage {
    --stage-w: max(100vw, calc(100svh * 720 / 1280));
    height: calc(var(--stage-w) * 1280 / 720);
    top: 50%;
    transform-origin: 50.55% 49.82%;
  }
}

@media (max-width: 760px) {
  .enter__wordmark { width: 108px; margin-bottom: 1.6rem; }
}

/* ==========================================================================
   The entrance

   Nothing appears at once. The film arrives first, the wordmark half a second
   behind it, the scroll cue half a second behind that. Three beats instead of
   one, which reads as composed rather than busy.

   All of it is opacity and a small lift, on elements that are laid out from the
   first paint, so the entrance cannot move the page or cost a layout: CLS stays
   at zero.

   `is-curtain` is on the root element from before the first paint, set by the
   same inline boot script that decides on the film, so the page never flashes
   its contents and then hides them. Taking the class off is what starts the
   three transitions; site.js does that as soon as the film has frames to show,
   and the boot script does it on a short timer regardless, so a visitor whose
   video never loads is not left looking at black.

   The three elements already have their opacity driven by the scroll, so the
   entrance is a separate multiplier, --in, that the scroll never touches.
   Registering it is what lets it be transitioned at all; a browser that does
   not support @property gets the same page with no fade rather than a broken
   one.
   ========================================================================== */
@property --in {
  syntax: '<number>';
  inherits: false;
  initial-value: 1;
}

.is-intro-active.is-curtain .enter .enter__stage,
.is-intro-active.is-curtain .enter .enter__brand,
.is-intro-active.is-curtain .enter .enter__cue { --in: 0; }

/* The screen box is the one thing here whose position cannot be written in CSS:
   it starts life as the monitor's glass, and where that is depends on the drawn
   size of the film, which only the script can measure. Its declared default is
   the whole window, which is what it ends as. site.js is deferred, so between
   the first paint and the first measurement the browser has a box at the wrong
   end of that journey, and it paints it often enough to cost a layout shift of
   about 0.32 on its own.

   Held back until it has been measured, it simply appears where it belongs.
   Visibility rather than opacity, because an element that was never visible has
   nothing to shift from. The curtain is already on the root from before the
   first paint and site.js takes it off in the same task that writes the four
   values, so there is no frame where this is visible and unmeasured. If site.js
   never arrives the boot script's timer lifts the curtain anyway, and the box
   appears at its declared default, which is the full-size hero. */
.is-intro-active.is-curtain .enter .enter__screen { visibility: hidden; }

/* The same rhythm for a visitor who never gets the film at all: reduced data,
   or JavaScript that never arrived. `both` means that if the animations never
   run, the hero is simply there, which is the one outcome that must never
   break. Anyone who asked for less motion gets it immediately and still. */
@media (prefers-reduced-motion: no-preference) {
  html:not(.is-intro-active) .enter__light,
  html:not(.is-intro-active) .enter__hero-glow {
    animation: enter-fade 1.1s var(--ease-out) both;
  }
  html:not(.is-intro-active) .enter__hero .enter__wordmark,
  html:not(.is-intro-active) .enter__hero .enter__eyebrow,
  html:not(.is-intro-active) .enter__hero .enter__heading,
  html:not(.is-intro-active) .enter__hero .enter__sub {
    animation: enter-rise 0.9s var(--ease-out) 0.5s both;
  }
  html:not(.is-intro-active) .enter__hero .enter__actions {
    animation: enter-rise 0.8s var(--ease-out) 1s both;
  }
}
@keyframes enter-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes enter-rise {
  from { opacity: 0; transform: translateY(0.75rem); }
  to   { opacity: 1; transform: none; }
}

/* The header is held back while the visitor is still outside the screen, then
   arrives once they are inside. `is-intro-active` is set by the inline boot
   script before first paint, so without JavaScript the header is simply always
   visible. */
.is-intro-active .site-header {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-0.5rem);
  transition: opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out);
}
.is-intro-active.is-entered .site-header {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}

/* ==========================================================================
   Homepage sections
   ========================================================================== */

/* ==========================================================================
   The card deck
   --------------------------------------------------------------------------
   One card at a time, slid left and right, used by both the paths and the
   services. The track is a plain scroll-snapping row: swipe, trackpad,
   shift-wheel and the keyboard all work with no script at all, and the arrows
   and dots ship hidden so a browser that never runs the script is left with a
   row that works rather than controls that do not.

   --near is written onto each slide per frame from scroll position: 1 when it
   is centred, 0 when it is a card's width away. It carries the scale and the
   fade, and it deliberately has no transition on it, so the cards move with the
   finger rather than easing along behind it.
   ========================================================================== */
.deck {
  --deck-gap: clamp(0.55rem, 2.2vw, 1.35rem);
  --deck-ink: var(--white);
  --deck-line: var(--line-2);
  --deck-dot: rgba(255, 255, 255, 0.24);
  --deck-dot-on: var(--blue);
  position: relative;
}
.section--light .deck {
  --deck-ink: var(--ink-text);
  --deck-line: var(--line-on-light-2);
  --deck-dot: rgba(8, 11, 16, 0.16);
  --deck-dot-on: var(--blue-ink);
}

.deck__track {
  /* Positioned, so a slide's offsetLeft is a coordinate inside the track
     and the script can measure where cards sit without transforms in the
     way. See paint() in site.js. */
  position: relative;
  /* Narrow enough that a sliver of the next card always shows. Centred, the
     peek either side is (track - --slide) / 2 - gap, less whatever the shrink
     on the off-centre card takes off it, so slide width, gap and that shrink
     are all the same dial: how obvious it is that these cards slide. */
  --slide: min(76vw, 20rem);
  display: flex;
  /* Each card is its own height. Stretched to match the tallest, the short
     ones open a hole between their last line and their button, which on a
     slider is a hole in the middle of the only card you can see. */
  align-items: flex-start;
  gap: var(--deck-gap);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* The first and last card centre like every other one. */
  padding-inline: max(0px, calc((100% - var(--slide)) / 2));
  /* Room for a card to lift and cast a shadow without being clipped off. */
  padding-block: 0.5rem 1rem;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.deck__track::-webkit-scrollbar { display: none; }
.deck__track:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; border-radius: var(--r-lg); }

.deck__slide {
  flex: 0 0 var(--slide);
  display: flex;
  scroll-snap-align: center;
  transform-origin: center center;
  transform: scale(calc(0.94 + 0.06 * var(--near, 1)));
  opacity: calc(0.5 + 0.5 * var(--near, 1));
  will-change: transform, opacity;
}
.deck__slide > * { flex: 1; min-width: 0; }

/* ---------- Arrows and dots ---------- */
.deck__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.8rem, 3vw, 1.4rem);
  margin-top: clamp(0.9rem, 2.5vw, 1.5rem);
}
.deck__arrow {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--deck-line);
  background: transparent;
  color: var(--deck-ink);
  cursor: pointer;
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease),
    opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.deck__arrow svg { width: 1rem; height: 1rem; }
.deck__arrow:hover { border-color: var(--blue); background: rgba(36, 107, 253, 0.12); }
.deck__arrow:active { transform: scale(0.94); }
.deck__arrow[disabled] { opacity: 0.3; cursor: default; }
.deck__arrow[disabled]:hover { border-color: var(--deck-line); background: transparent; }

.deck__dots { display: flex; align-items: center; }
/* The dot people see is the ::before. The button around it is thumb sized and
   invisible, because a 8px tap target is not one. */
.deck__dot {
  display: grid;
  place-items: center;
  /* 44 square is the smallest a thumb reliably hits. The visible dot is the
     ::before and stays the size it was; only the invisible button grew. */
  min-width: 2.75rem;
  height: 2.75rem;
  padding: 0 0.4rem;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.deck__dot::before {
  content: '';
  display: block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--r-pill);
  background: var(--deck-dot);
  transition: width 0.4s var(--ease-out), background-color 0.4s var(--ease);
}
.deck__dot[aria-current="true"]::before { width: 1.7rem; background: var(--deck-dot-on); }

/* Sliding is the whole point, so when it is not wanted the cards simply sit
   at full size and the row scrolls without the scale and the fade. */
@media (prefers-reduced-motion: reduce) {
  .deck__track { scroll-behavior: auto; }
  .deck__slide { transform: none; opacity: 1; }
  .deck__dot::before { transition: none; }
}

/* ---------- Choose your path ---------- */
.paths__kicker { color: var(--muted-on-light); letter-spacing: 0.16em; }
.path-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: clamp(1.6rem, 3vw, 2.4rem);
  background: var(--paper-2);
  border: 1px solid var(--line-on-light);
  border-radius: var(--r-xl);
  color: var(--ink-text);
  transition: border-color 0.3s var(--ease), box-shadow 0.4s var(--ease);
}
.path-card:hover {
  border-color: rgba(36, 107, 253, 0.32);
  box-shadow: 0 30px 60px -34px rgba(8, 16, 32, 0.4);
}
.path-card__top { display: flex; align-items: center; gap: 0.8rem; margin-bottom: 0.4rem; }
.path-card__icon { width: 28px; height: 28px; color: var(--blue); flex: none; }
.path-card__label {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--blue-ink);
}
.path-card__q {
  font-family: var(--font-display); font-size: clamp(1.3rem, 1.05rem + 1vw, 1.75rem);
  font-weight: 500; letter-spacing: -0.02em; line-height: 1.2;
}
.path-card__body { font-size: var(--fs-sm); color: var(--muted-on-light); line-height: 1.6; }
.path-card__go {
  display: inline-flex; align-items: center; gap: 0.5rem;
  margin-top: 0.8rem;
  font-size: var(--fs-sm); font-weight: 600; color: var(--blue-ink);
}
.path-card__go svg { width: 0.8rem; height: 0.8rem; transition: transform 0.35s var(--ease); }
.path-card:hover .path-card__go svg { transform: translateX(4px); }

/* Below the width where the paths stop sliding, the track runs edge to edge
   rather than sitting inside the page's gutter. The gutter is 20px a side on a
   phone, which is most of the room a peek needs, and cards sliding off the edge
   of the screen reads better than cards sliding into a margin. */
@media (max-width: 879px) {
  .deck__track {
    margin-inline: calc(50% - 50vw);
    padding-inline: max(0px, calc((100vw - var(--slide)) / 2));
  }
}

/* Three paths fit a wide screen exactly, so there is nothing to slide: the
   track stops scrolling and the script takes the controls away with it. */
@media (min-width: 880px) {
  .deck--paths .deck__track {
    --slide: calc((100% - 2 * var(--deck-gap)) / 3);
    padding-inline: 0;
  }
}

/* ---------- What we do ---------- */
.services-detail { overflow: hidden; isolation: isolate; background: var(--ink-900); }
.services-detail__glow { width: 60vw; height: 40vw; max-width: 900px; top: 0; right: -18%; opacity: 0.5; }

.deck--svc .deck__track { --slide: min(84vw, 40rem); }

.svc-card {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: clamp(1.25rem, 3.2vw, 2.25rem);
  border: 1px solid var(--line-2);
  border-radius: var(--r-xl);
  background:
    radial-gradient(120% 80% at 0% 0%, rgba(36, 107, 253, 0.14), transparent 60%),
    var(--ink-850);
  transition: border-color 0.3s var(--ease);
}
/* The number sits on the kicker's line rather than above it: as its own row it
   cost more height than it was worth on a card that has to fit a phone. */
.svc-card__meta { display: flex; align-items: baseline; gap: 0.6rem; }
.svc-card__n {
  font-family: var(--font-display);
  font-size: 1.05rem; font-weight: 500; line-height: 1; letter-spacing: 0.02em;
  color: var(--blue);
}
.svc-card__kicker {
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted-dim);
}
.svc-card__heading { margin: 0; max-width: 22ch; }
.svc-card__body { font-size: var(--fs-sm); color: var(--muted); line-height: 1.55; }
.svc-card__note { font-size: var(--fs-xs); color: var(--muted-dim); line-height: 1.5; }
.svc-card__platforms { margin-block: 0.1rem; }

.svc-card__groups { display: grid; gap: 0.7rem; margin-top: 0.25rem; }
.svc-card__group-title {
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--blue-light);
  margin-bottom: 0.4rem;
}
.svc-card__chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; }
.svc-card__chips li {
  font-size: 0.75rem; line-height: 1.25; color: var(--muted);
  padding: 0.26rem 0.6rem;
  border: 1px solid var(--line-2);
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.035);
}
.svc-card__link { align-self: flex-start; margin-top: 0.5rem; }

@media (min-width: 620px) {
  .svc-card__groups { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem 1.4rem; }
  .svc-card__groups:has(> :nth-child(3)) > :first-child { grid-column: 1 / -1; }
}

/* ---------- Automation flow ---------- */
.flow__steps {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0;
  counter-reset: flow;
}
.flow__step { position: relative; padding: 0 0 2.25rem 2.5rem; }
.flow__step:last-child { padding-bottom: 0; }
/* The connector between steps, drawn as it scrolls in. */
.flow__step::before {
  content: ''; position: absolute; left: 0.47rem; top: 1.5rem; bottom: 0;
  width: 2px; background: linear-gradient(180deg, var(--blue), rgba(36, 107, 253, 0.15));
  transform-origin: top; transform: scaleY(0);
  transition: transform 0.7s var(--ease-out) 0.15s;
}
.flow__step:last-child::before { display: none; }
.flow__step.is-visible::before { transform: scaleY(1); }
.flow__dot {
  position: absolute; left: 0; top: 0.25rem;
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  border: 2px solid var(--blue); background: var(--paper);
  display: grid; place-items: center;
}
.flow__dot i {
  width: 0.35rem; height: 0.35rem; border-radius: 50%; background: var(--blue);
  transform: scale(0); transition: transform 0.45s var(--ease-out) 0.1s;
}
.flow__step.is-visible .flow__dot i { transform: scale(1); }
.flow__n {
  display: block; font-family: var(--font-display); font-size: var(--fs-xs);
  font-weight: 500; letter-spacing: 0.16em; color: var(--blue-ink); margin-bottom: 0.3rem;
}
.flow__title {
  font-family: var(--font-display); font-size: clamp(1.15rem, 1rem + 0.7vw, 1.45rem);
  font-weight: 500; letter-spacing: -0.02em; margin-bottom: 0.3rem;
}
.flow__body { font-size: var(--fs-sm); color: var(--muted-on-light); line-height: 1.6; max-width: 46ch; }
@media (min-width: 940px) {
  .flow__steps { grid-template-columns: repeat(5, 1fr); gap: 0; }
  .flow__step { padding: 3rem 1.25rem 0 0; }
  .flow__step::before {
    left: 1.05rem; right: 0; top: 0.5rem; bottom: auto; width: auto; height: 2px;
    background: linear-gradient(90deg, var(--blue), rgba(36, 107, 253, 0.15));
    transform-origin: left; transform: scaleX(0);
    transition: transform 0.7s var(--ease-out) 0.15s;
  }
  .flow__step.is-visible::before { transform: scaleX(1); }
  .flow__dot { top: 0; left: 0; }
}

/* ---------- Work, dark ---------- */
.work-dark { background: var(--ink-850); overflow: hidden; isolation: isolate; }
.work-dark__glow { width: 55vw; height: 40vw; max-width: 820px; top: 4%; left: -14%; opacity: 0.55; }
.work-dark__grid { position: relative; z-index: 2; display: grid; gap: 1.5rem; }
.wcard {
  display: grid; gap: 1.25rem;
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: 1rem 1rem 1.6rem;
  transition: border-color 0.35s var(--ease), transform 0.4s var(--ease);
}
.wcard:hover { border-color: rgba(36, 107, 253, 0.34); transform: translateY(-3px); }
.wcard__media { display: block; border-radius: var(--r-md); overflow: hidden; background: #fff; }
.wcard__media img { width: 100%; display: block; }
/* Every card shows the same window on its project, whether that window holds a
   single screenshot or a comparison, so a row of three stays level. The source
   images have different proportions; the card decides, not the file. */
.wcard__media { position: relative; aspect-ratio: 16 / 10; }
.wcard__media img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.wcard__media--compare { background: var(--ink-900); }
.wcard__media--compare .compare__viewer {
  position: absolute; inset: 0;
  aspect-ratio: auto; width: 100%; height: 100%;
  border: 0; border-radius: inherit; box-shadow: none;
}
.wcard__media--compare .compare__tag { font-size: 0.66rem; padding: 0.24rem 0.55rem; }
.wcard__media--compare .compare__grip { width: 34px; height: 34px; }
.wcard__hint {
  position: absolute; left: 50%; bottom: 0.6rem; transform: translateX(-50%);
  z-index: 3; margin: 0; padding: 0.28rem 0.7rem;
  font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.1em;
  color: #fff; background: rgba(8, 11, 16, 0.72);
  border-radius: 999px; pointer-events: none;
  transition: opacity 0.3s var(--ease);
}
.wcard__media--compare:has(.is-used) .wcard__hint,
.compare__viewer.is-used ~ .wcard__hint { opacity: 0; }
.wcard__body { padding-inline: 0.5rem; }
.wcard__name { font-family: var(--font-display); font-size: 1.3rem; font-weight: 500; letter-spacing: -0.02em; margin-bottom: 0.25rem; }
.wcard__meta { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.11em; color: var(--muted-dim); margin-bottom: 0.9rem; }
.wcard__summary { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; margin-bottom: 1.1rem; }
@media (min-width: 1000px) { .work-dark__grid { grid-template-columns: repeat(3, 1fr); } }

/* ---------- Ongoing support ---------- */
.ongoing { background: var(--ink-900); }
.ongoing__grid { display: grid; gap: 1.25rem; }
.plan {
  padding: clamp(1.6rem, 2.8vw, 2.25rem);
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  position: relative;
}
.plan--featured {
  background: linear-gradient(178deg, rgba(36, 107, 253, 0.14), transparent 70%), var(--ink-700);
  border-color: rgba(36, 107, 253, 0.4);
}
.plan__name { font-family: var(--font-display); font-size: 1.3rem; font-weight: 500; letter-spacing: -0.02em; margin-bottom: 0.4rem; }
.plan__line { font-size: var(--fs-sm); color: var(--muted); line-height: 1.55; margin-bottom: 1.4rem; min-height: 3em; }
.plan__list { list-style: none; margin: 0; padding: 1.3rem 0 0; display: grid; gap: 0.6rem; border-top: 1px solid var(--line); }
.plan__list li { position: relative; padding-left: 1.3rem; font-size: var(--fs-sm); color: var(--muted); line-height: 1.5; }
.plan__list li::before {
  content: ''; position: absolute; left: 0; top: 0.6em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--blue);
}
.ongoing__note {
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.5rem;
  font-size: var(--fs-sm); color: var(--muted);
}
@media (min-width: 900px) { .ongoing__grid { grid-template-columns: repeat(3, 1fr); gap: 1.5rem; } }

/* ---------- Closing CTA over the horizon art ---------- */
.horizon { position: relative; overflow: hidden; isolation: isolate; background: var(--ink-900); }
.horizon__art { position: absolute; inset: 0; z-index: 0; }
.horizon__art img { width: 100%; height: 100%; object-fit: cover; object-position: center 60%; }
/* The art is busy, so the copy gets its own ground rather than relying on luck. */
.horizon__scrim {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(180deg, rgba(8, 11, 16, 0.92) 0%, rgba(8, 11, 16, 0.72) 45%, rgba(8, 11, 16, 0.9) 100%),
    radial-gradient(70% 60% at 50% 50%, rgba(8, 11, 16, 0.55), transparent 75%);
}
.horizon__inner { position: relative; z-index: 2; }
.horizon__content { max-width: 44rem; text-align: center; margin-inline: auto; }
.horizon__heading { margin-bottom: 1.2rem; }
.horizon__body { margin: 0 auto 2.2rem; }
.horizon__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; justify-content: center; }

/* Five process steps rather than four. */
@media (min-width: 860px) { .process__list { grid-template-columns: repeat(5, 1fr); gap: 1.75rem; } }

/* On the intro page the header must not occupy layout space, or the sticky
   intro starts below a strip of page background. Fixed behaves the same as
   sticky once scrolling begins, and takes the strip away. */
.has-intro .site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
}

/* A .glow is absolutely positioned with z-index 0. Inside a stacking context
   that puts it ABOVE static content, so any container holding text next to one
   has to be positioned too or the blur washes over the headings. */
.services-detail > .page,
.work-dark > .page-wide,
.paths > .page,
.ongoing > .page {
  position: relative;
  z-index: 2;
}

/* Keep the arrow on the baseline of every card regardless of how far the
   question wraps. */
.path-card { align-content: start; }
.path-card__go { margin-top: auto; }

.site-footer__tagline {
  margin-top: 1.25rem;
  font-family: var(--font-display);
  font-size: 1.05rem; font-weight: 500; letter-spacing: -0.01em;
  color: var(--white);
}
.site-footer__tagline + .site-footer__blurb { margin-top: 0.5rem; }

/* The supplied blue light art, kept very low so the hero copy stays the
   brightest thing on the screen. */
.enter__light { position: absolute; inset: 0; z-index: 0; }
.enter__light img {
  width: 100%; height: 100%; object-fit: cover; opacity: 0.32;
  transition: opacity 0.8s var(--ease-out);
}

/* The moving version, laid exactly over the still and crossfaded with it once
   it is actually playing. Both sit at the same third of full opacity, and only
   one of them is ever visible, so the band never doubles up and brightens. */
.enter__light-video {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0;
  transition: opacity 0.8s var(--ease-out);
}
.is-light-video .enter__light-video { opacity: 0.32; }
.is-light-video .enter__light img { opacity: 0; }

/* Workspace photograph behind the ongoing support section, heavily scrimmed so
   it reads as texture rather than as an image competing with the text. */
.ongoing { position: relative; overflow: hidden; isolation: isolate; }
.ongoing__art { position: absolute; inset: 0; z-index: 0; }
.ongoing__art img { width: 100%; height: 100%; object-fit: cover; object-position: center 35%; opacity: 0.22; }
.ongoing__scrim {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(180deg, var(--ink-900) 0%, rgba(8, 11, 16, 0.86) 35%, rgba(8, 11, 16, 0.94) 100%);
}

/* ---------- Services mega menu ---------- */
.nav__menu--mega { width: min(56rem, 92vw); }
.nav__mega {
  display: grid;
  gap: 1.75rem;
  padding: 1.6rem 1.75rem;
  background: rgba(17, 22, 29, 0.97);
  backdrop-filter: blur(22px);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
  animation: menu-in 0.28s var(--ease-out);
}
.nav__mega-title {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--blue-light); margin-bottom: 0.35rem;
}
.nav__mega-blurb { font-size: var(--fs-xs); color: var(--muted-dim); line-height: 1.5; margin-bottom: 0.9rem; }
.nav__mega-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.1rem; }
.nav__mega-link {
  display: block; padding: 0.5rem 0.6rem; margin-inline: -0.6rem;
  border-radius: var(--r-sm);
  font-size: var(--fs-sm); font-weight: 500; color: var(--text);
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}
.nav__mega-link:hover, .nav__mega-link:focus-visible { background: rgba(36, 107, 253, 0.16); }
.nav__mega-link.is-current { color: var(--blue-light); }
@media (min-width: 880px) { .nav__mega { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2.25rem; } }

/* Five footer columns rather than three. */
@media (min-width: 760px) { .site-footer__nav { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1020px) { .site-footer__nav { grid-template-columns: repeat(5, 1fr); gap: 1.75rem; } }

/* ==========================================================================
   Shared page blocks
   ========================================================================== */

/* Titled groups of short points. Used across every service page. */
.pcols { display: grid; gap: 1.75rem; align-content: start; }
.pcol__title {
  font-family: var(--font-display); font-size: 1rem; font-weight: 500;
  letter-spacing: -0.012em; margin-bottom: 0.45rem;
}
.pcol__note { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; margin-bottom: 0.8rem; max-width: 42ch; }
.pcol__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.pcol__list li {
  position: relative; padding-left: 1.2rem;
  font-size: var(--fs-sm); color: var(--muted); line-height: 1.55;
}
.pcol__list li::before {
  content: ''; position: absolute; left: 0; top: 0.62em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--blue);
}
@media (min-width: 720px) { .pcols--2, .pcols--3, .pcols--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) {
  .pcols--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.5rem; }
  .pcols--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.5rem; }
}

/* Numbered tier: one kind of website, one SEO stage, one automation group. */
.tiers { overflow: hidden; isolation: isolate; }
.tiers > .page, .tiers > .page-wide { position: relative; z-index: 2; }
.tiers__glow { width: 55vw; height: 40vw; max-width: 850px; top: 2%; right: -16%; opacity: 0.5; }
.tiers__list { display: grid; gap: clamp(2.75rem, 5vw, 4.5rem); }
.tier {
  display: grid; gap: clamp(1.5rem, 3vw, 2.75rem);
  padding-top: clamp(1.75rem, 3.5vw, 2.75rem);
  border-top: 1px solid var(--line);
}
.tier__n {
  display: block; font-family: var(--font-display); font-size: var(--fs-xs);
  font-weight: 500; letter-spacing: 0.18em; color: var(--blue-light); margin-bottom: 0.8rem;
}
.tier__kicker {
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted-dim); margin-bottom: 0.8rem;
}
.tier__heading { margin-bottom: 0.9rem; max-width: 22ch; }
/* Body copy is capped near 70 characters so it stays readable on a wide screen. */
.tier__body { font-size: var(--fs-lead); color: var(--muted); line-height: 1.68; max-width: 68ch; }
.tier__body + .tier__body { margin-top: 0.9rem; }
.tier__head > .link-arrow { margin-top: 1.2rem; }
.tier__note {
  margin-top: 1.1rem; font-size: var(--fs-xs); color: var(--muted-dim);
  line-height: 1.6; max-width: 56ch;
}
@media (min-width: 900px) { .tier { grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); } }

/* Contextual closing band. Lighter than the full cinematic CTA, used to end
   interior pages with one clear next step. */
.cta-band { position: relative; overflow: hidden; isolation: isolate; background: var(--ink-850); }
.cta-band__glow { width: 55vw; height: 38vw; max-width: 800px; top: -20%; left: 50%; transform: translateX(-50%); opacity: 0.55; }
.cta-band__inner {
  position: relative; z-index: 2;
  display: grid; gap: 1.75rem; align-items: center;
}
.cta-band__heading { max-width: 20ch; }
.cta-band__body { margin-top: 0.9rem; max-width: 60ch; }
.cta-band__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; }
@media (min-width: 900px) {
  .cta-band__inner { grid-template-columns: minmax(0, 1.25fr) auto; gap: 3rem; }
  .cta-band__actions { justify-content: flex-end; }
}

/* Frontend / backend explainer. Two plain-language cards, no icons. */
.explain { display: grid; gap: 1.25rem; }
.explain__item {
  padding: clamp(1.6rem, 3vw, 2.4rem);
  background: var(--paper-2);
  border: 1px solid var(--line-on-light);
  border-radius: var(--r-xl);
}
.explain__label {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--blue-ink); margin-bottom: 0.7rem;
}
.explain__title {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 1.05rem + 0.9vw, 1.6rem);
  font-weight: 500; letter-spacing: -0.02em; line-height: 1.25;
  margin-bottom: 0.8rem;
}
.explain__body {
  font-size: var(--fs-body); color: var(--muted-on-light);
  line-height: 1.7; max-width: 62ch; margin-bottom: 1.5rem;
}
.explain .pcols { border-top: 1px solid var(--line-on-light); padding-top: 1.25rem; }
@media (min-width: 900px) { .explain { grid-template-columns: 1fr 1fr; gap: 1.5rem; } }

.custom-note {
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.5rem;
  font-size: var(--fs-sm); color: var(--muted);
}

/* ==========================================================================
   The team
   --------------------------------------------------------------------------
   Photographs are not taken yet. The circle holds initials until one exists,
   and is sized and cropped so dropping an <img> in its place changes nothing
   about the layout. See teamMember() in components/blocks.js.
   ========================================================================== */
.team__grid { display: grid; gap: 1.5rem; }
.person {
  display: grid;
  /* Pack to the top. The cards stretch to match the tallest, and without this
     a shorter bio pushes its own name and photo down out of line with the rest
     of the row. */
  align-content: start;
  gap: 1.1rem;
  justify-items: center;
  text-align: center;
  padding: clamp(1.5rem, 3vw, 2.25rem);
  background: var(--paper-2);
  border: 1px solid var(--line-on-light);
  border-radius: var(--r-xl);
}
.person__photo {
  display: grid;
  place-items: center;
  width: 104px;
  height: 104px;
  border-radius: 50%;
  overflow: hidden;
  /* The tint a photo will sit on, and what shows through until there is one. */
  background: linear-gradient(160deg, rgba(36, 107, 253, 0.16), rgba(36, 107, 253, 0.05));
  border: 1px solid rgba(36, 107, 253, 0.26);
}
.person__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.person__initials {
  font-family: var(--font-display);
  font-style: normal;
  font-size: 2rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--blue-ink);
}
.person__name {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  margin-bottom: 0.2rem;
}
.person__role {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--blue-ink);
  margin-bottom: 0.9rem;
}
.person__bio { font-size: var(--fs-sm); color: var(--muted-on-light); line-height: 1.65; margin: 0; }

@media (min-width: 820px) {
  .team__grid { grid-template-columns: repeat(3, 1fr); gap: 1.75rem; }
  .person { padding-block: 2.5rem; }
}

/* ---------- The strip on the homepage ---------- */
.team-strip__grid { display: grid; gap: 1rem; }
.person--compact {
  grid-template-columns: auto 1fr;
  justify-items: start;
  text-align: left;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.25rem;
  background: var(--ink-800);
  border-color: var(--line);
}
.person--compact .person__photo { width: 60px; height: 60px; }
.person--compact .person__initials { font-size: 1.15rem; color: var(--blue-light); }
.person--compact .person__name { font-size: 1.02rem; margin-bottom: 0.1rem; }
.person--compact .person__role { color: var(--muted); margin-bottom: 0; letter-spacing: 0.1em; }
.team-strip__more { margin: 1.75rem 0 0; text-align: center; }

@media (min-width: 820px) {
  .team-strip__grid { grid-template-columns: repeat(3, 1fr); gap: 1.25rem; }
}

/* ==========================================================================
   How we work
   ========================================================================== */
.how-work__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem; counter-reset: none; }
.how-work__step { position: relative; padding-top: 1.1rem; border-top: 1px solid var(--line); }
.section--light .how-work__step { border-top-color: var(--line-on-light); }
.how-work__n {
  display: block;
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  color: var(--blue-light);
  margin-bottom: 0.65rem;
}
.section--light .how-work__n { color: var(--blue-ink); }
.how-work__title {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 500;
  letter-spacing: -0.015em;
  margin-bottom: 0.4rem;
}
.how-work__body { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; margin: 0; }
.section--light .how-work__body { color: var(--muted-on-light); }

@media (min-width: 860px) {
  .how-work__list { grid-template-columns: repeat(4, 1fr); gap: 1.75rem; }
}

/* Where we are, under the hero actions. Quiet on purpose: it is reassurance,
   not a headline. */
.enter__place {
  margin: 1.5rem 0 0;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-dim);
}

/* How soon we answer, beside the form that asks. */
.contact__reply {
  margin: 0.35rem 0 1.4rem;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--blue-light);
}

/* ==========================================================================
   Case study navigation
   --------------------------------------------------------------------------
   Back to the index on one side, the next project on the other, so the three
   studies can be read as a loop rather than a series of dead ends.
   ========================================================================== */
.case-nav { padding-block: clamp(1.75rem, 4vw, 2.75rem); border-top: 1px solid var(--line); }
.case-nav .page {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
}
.link-arrow--back svg { transform: rotate(180deg); }
.case-nav__next { display: grid; gap: 0.2rem; text-align: right; margin-left: auto; }
.case-nav__label {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted-dim);
}
.case-nav__name {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  /* The project name is two words and belongs on one line; without this the
     inline arrow takes its share of the width and breaks "Chief Camper". */
  white-space: nowrap;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--text);
}
/* The arrow carries no size of its own outside .link-arrow, so it renders at
   whatever the viewBox says, which is far larger than the type beside it. */
.case-nav__name svg { width: 0.85rem; height: 0.85rem; flex: none; }
.case-nav__next:hover .case-nav__name { color: var(--blue-light); }
.case-nav__next svg { transition: transform 0.3s var(--ease); }
.case-nav__next:hover svg { transform: translateX(3px); }

/* ---------- Services, as an expandable group on a phone ----------
   A <details>, so it opens without JavaScript and the keyboard handles it
   natively. The list inside is the same data the desktop mega menu uses. */
.mobile-menu__details { border-bottom: 1px solid var(--line); }
.mobile-menu__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 0;
  cursor: pointer;
  list-style: none;
  /* Matches a top level link, because that is what it is. */
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--text);
}
.mobile-menu__summary:hover { color: var(--blue-light); }
.mobile-menu__summary::-webkit-details-marker { display: none; }
.mobile-menu__chev {
  width: 9px;
  height: 9px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  transition: transform 0.25s var(--ease);
  flex: none;
}
.mobile-menu__details[open] .mobile-menu__chev { transform: rotate(-135deg); }
.mobile-menu__sub {
  list-style: none;
  margin: 0 0 0.9rem;
  padding: 0 0 0 0.9rem;
  display: grid;
  border-left: 1px solid var(--line);
}
/* Subordinate, not another row of top level links. The generic rule above gives
   every anchor in this menu display type and a divider; inside the group they
   get neither. */
.mobile-menu__list .mobile-menu__sub a {
  font-family: inherit;
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--muted);
  padding: 0.5rem 0 0.5rem 0.9rem;
  border-bottom: 0;
}
.mobile-menu__list .mobile-menu__sub a:hover { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .mobile-menu__chev { transition: none; } }

/* ===================================================================
   Depth: glow on the display type, and cards that sit in space
   =================================================================== */

/* The headline picks up the light coming off the mesh behind it. Two
   shadows rather than one: a tight one to lift the letterforms off the
   grid, and a wide soft one for the bloom. */
.enter__heading {
  text-shadow:
    0 0 1px rgba(10, 16, 28, 0.55),
    0 2px 28px rgba(36, 107, 253, 0.42),
    0 4px 90px rgba(36, 107, 253, 0.28);
}

/* The second line steps back, which is what gives the pair its depth.
   The reference outlines it; outlined Poppins at this weight turns to
   wire, so this fades it instead and lets the glow carry the drama. */
.enter__line + .enter__line {
  color: color-mix(in srgb, var(--paper) 72%, var(--blue-light));
}

}

/* ===================================================================
   Deck, as a rotating stack
   The same scrolling row of cards, turned into space. Nothing about how
   it works changes: it is still a scroll-snapping track that swipes,
   tabs and runs without script, and --near still means what it meant.
   What is added is --side, the same distance keeping its sign, which is
   what lets a card know whether to turn its face left or right.
   =================================================================== */
.deck--stack .deck__track {
  perspective: 1200px;
  perspective-origin: 50% 46%;
  /* Room for the turned cards to lean without the track clipping their
     corners, and for the drop shadow under the near one. */
  padding-block: clamp(1.4rem, 4vw, 3rem);
  /* Half a track either side, so the first and the last card can reach the
     middle at all. Without it goTo() asks the first card to centre, the
     track is already at scrollLeft 0 with nowhere left to go, and the card
     that is supposed to be the one you are reading sits off to the left.
     That was the "doesn't show in the middle" bug, and it was never in the
     maths -- goTo measures the delta correctly, there was simply no room
     to travel it. */
  padding-inline: max(0px, calc((100% - var(--slide)) / 2));

  /* The browser's own snapping is off here. CSS scroll snap aligns a card's
     *transformed* border box, and on the arc each card is shifted sideways by
     an amount derived from how far off centre it is -- so mandatory snap
     chases the same moving target the script used to, and parked the card
     28.5px short of the middle no matter how long it was left. The script
     settles the track instead, against the laid-out position, which the
     transform cannot move. */
  scroll-snap-type: none;
}

.deck--stack .deck__slide {
  /* An arc rather than a row. The pull toward the middle is what closes the
     gaps and laps the cards over each other, the turn faces each one at the
     centre, and the push back sinks the outer ones behind the near one. All
     three come off --side, which carries how far round the card is and
     which way. */
  transform:
    translateX(calc(var(--side, 0) * -19%))
    rotateY(calc(var(--side, 0) * -34deg))
    translateZ(calc((var(--near, 1) - 1) * 130px))
    scale(calc(0.86 + 0.14 * var(--near, 1)));
  opacity: calc(0.46 + 0.54 * var(--near, 1));
  /* Nearest card on top, so the arc laps the right way round. */
  z-index: calc(10 - 10 * (1 - var(--near, 1)));
}

/* The near card lifts off the ones behind it. */
.deck--stack .deck__slide > * {
  box-shadow: 0 calc(var(--near, 1) * 34px) calc(var(--near, 1) * 70px - 10px)
              rgba(4, 8, 16, calc(var(--near, 1) * 0.55));
  transition: box-shadow 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
}

/* Hover says "this does something" and stops there. It was a blue glow,
   which lit the card up like it had been selected. */
.deck--stack .deck__slide > *:hover {
  border-color: rgba(169, 196, 255, 0.34);
}

/* Turning a card edge-on is motion, and some people have asked for less
   of it. They get the flat row the deck has always been. */
@media (prefers-reduced-motion: reduce) {
  .deck--stack .deck__track { perspective: none; }
  .deck--stack .deck__slide { transform: none; opacity: 1; }
  .deck--stack .deck__slide > * { box-shadow: none; transition: none; }
}

/* ===================================================================
   The services stage
   Words at the side, faces that turn in the middle, chips along the
   bottom. One service is current at a time and all three regions are
   showing the same one.
   =================================================================== */
.svc-stage__grid {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3.2rem);
  align-items: center;
  margin-top: clamp(1.5rem, 4vw, 2.6rem);
}
/* A grid item will not shrink below the width its contents demand unless it
   is told it may, and the deck is a flex row of five cards, so what it
   demands is all five side by side. The two column rule below says
   minmax(0, ...) and was fine; the single column a phone gets does not, and
   the deck came out 905px wide inside a 375px window. The page did not grow,
   because the body clips sideways overflow -- it just quietly cut the words
   off instead, which is the kind of thing an overflow test cannot see. */
.svc-stage__grid > * { min-width: 0; }
@media (min-width: 900px) {
  .svc-stage__grid { grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); }
}

/* Held at a height so swapping a short service for a long one does not
   jolt the stack sideways on the way past. */
.svc-stage__side { min-height: 17rem; }
@media (min-width: 900px) { .svc-stage__side { min-height: 19rem; } }

.svc-side__meta {
  display: flex; align-items: baseline; gap: 0.7rem;
  margin-bottom: 0.7rem;
}
.svc-side__n {
  font-family: var(--font-display);
  font-size: 1.05rem; font-weight: 600; color: var(--blue-light);
  letter-spacing: 0.02em;
}
.svc-side__kicker {
  font-size: var(--fs-xs); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted);
}
.svc-side__heading { margin-bottom: 0.85rem; }
.svc-side__body { color: var(--muted); line-height: 1.65; max-width: 34rem; }
.svc-side__note { color: var(--muted); font-size: var(--fs-sm); margin-top: 0.7rem; }
.svc-side__link { margin-top: 1.3rem; }

/* ---------- The faces that turn ---------- */
.deck--svc .deck__track { --slide: min(58vw, 14.5rem); }

.svc-face {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.6rem;
  aspect-ratio: 3 / 4;
  padding: clamp(1rem, 3vw, 1.5rem);
  border: 1px solid var(--deck-line);
  border-radius: var(--r-lg, 18px);
  background:
    linear-gradient(165deg, rgba(36, 107, 253, 0.16), rgba(124, 58, 237, 0.07) 46%, transparent 76%),
    var(--ink-800);
  overflow: hidden;
}
.svc-face__n {
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 7vw, 3.4rem);
  font-weight: 600;
  line-height: 1;
  color: rgba(169, 196, 255, 0.30);
}
.svc-face__rule {
  height: 1px; width: 2.4rem;
  background: linear-gradient(90deg, var(--blue), transparent);
}
.svc-face__kicker {
  font-size: var(--fs-sm);
  font-weight: 500;
  line-height: 1.3;
  color: var(--white);
}
/* A slow highlight across the glass, so a turned card still reads as a
   surface catching light rather than as a flat rectangle. */
.svc-face__sheen {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.10), transparent 42%);
  opacity: calc(0.35 + 0.65 * var(--near, 1));
}

/* ---------- The chips underneath ---------- */
.svc-stage__detail {
  margin-top: clamp(1.6rem, 4vw, 2.8rem);
  padding-top: clamp(1.3rem, 3vw, 2rem);
  border-top: 1px solid var(--deck-line);
  min-height: 9rem;
}

/* The entrance a panel plays when it becomes the current one. Short, and
   only ever on the panel arriving. */
@keyframes svc-swap {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.svc-side.is-swapped,
.svc-detail.is-swapped { animation: svc-swap 0.42s var(--ease-out) both; }

@media (prefers-reduced-motion: reduce) {
  .svc-side.is-swapped, .svc-detail.is-swapped { animation: none; }
}

/* ===================================================================
   Scroll-driven motion
   --enter runs 0 to 1 as an element rises into the window; --drift is
   a few pixels of lag on the soft colour washes. Both are written by
   the scroll module and mean nothing until a rule below reads them.
   Both default to the finished state, so nothing depends on the script
   having run.
   =================================================================== */

/* The stack tilts up off the page as the section arrives. */
.svc-stage__deck {
  transform:
    perspective(1600px)
    rotateX(calc((1 - var(--enter, 1)) * 16deg))
    translateY(calc((1 - var(--enter, 1)) * 34px));
  transform-origin: 50% 100%;
  opacity: calc(0.35 + 0.65 * var(--enter, 1));
  will-change: transform, opacity;
}

/* The washes lag the page. --drift is zero until the module writes one. */
.glow { translate: 0 var(--drift, 0px); }

@media (prefers-reduced-motion: reduce) {
  .svc-stage__deck { transform: none; opacity: 1; }
  .glow { translate: none; }
}

/* ===================================================================
   The lead flow, as a network
   A dark panel inset into a light section. The glow this look needs
   has to have something dark to glow against, and turning the whole
   section dark would have put three dark bands in a row.
   =================================================================== */
.flownet {
  /* This is a dark panel sitting inside a light section, and .section--light
     rebinds the ink tokens for dark-on-light: --white becomes #0B0F15 and
     --muted a mid grey. Everything in here inherited those, so the titles
     were near-black text on a near-black tile -- 1.06 against a needed 4.5,
     which the contrast audit caught and the eye nearly did not, because it
     reads as "dim styling" rather than as broken. Putting the dark-surface
     values back on the panel fixes every child at once, including anything
     added in here later. */
  --white: #FFFFFF;
  --muted: #8E97A6;
  --blue-light: #A9C4FF;

  position: relative;
  isolation: isolate;
  margin-top: clamp(1.6rem, 4vw, 2.8rem);
  padding: clamp(1.5rem, 4vw, 3rem) clamp(1.1rem, 3vw, 2.4rem);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: clamp(16px, 2vw, 24px);
  background:
    radial-gradient(70% 90% at 50% 50%, rgba(36, 107, 253, 0.16), transparent 70%),
    linear-gradient(180deg, #0D131D, #080B10);
  overflow: hidden;
}

/* The faint ruled ground from the reference. Two gradients, no image. */
.flownet::before {
  content: '';
  position: absolute; inset: 0; z-index: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(75% 75% at 50% 50%, #000, transparent 78%);
}

.flownet__wires {
  /* Across the whole panel, not the padding box, and sized explicitly.
     An SVG is a replaced element: left to width/height auto it keeps the
     viewBox's own 1:1 ratio, which made this 206% of the panel's height
     and threw every wire off the bottom. The wire coordinates below are
     percentages of the panel for the same reason. */
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  display: block;
  z-index: 1;
  pointer-events: none;
}
.flownet__wire {
  stroke: rgba(99, 150, 255, 0.55);
  stroke-width: 1.4;
  stroke-linecap: round;
  /* pathLength="1" on the element, so the whole wire is one unit long and
     the offset below is simply how much of it is still missing. Each wire
     carries its own --d, so they arrive one after another instead of all
     at once. Defaults to fully drawn when nothing writes --enter. */
  /* pathLength="1" on each wire makes its whole length one user unit, and
     the viewBox is written 1:1 with the panel's pixels, so one user unit is
     one px here. The units matter: stroke-dashoffset takes a length or a
     percentage and never a bare number, so the first version of this -- a
     plain `1 - clamp(...)` -- was invalid and silently computed to 0, which
     looked exactly like a wire that had finished drawing. */
  stroke-dasharray: 1px;
  stroke-dashoffset: calc(1px * (1 - clamp(0, (var(--enter, 1) - var(--d, 0)) * 2.4, 1)));
  filter: drop-shadow(0 0 4px rgba(36, 107, 253, 0.55));
}
/* The hub's outline needs no vector-effect either: the hex has its own
   square viewBox and is never stretched. */
.flownet__wires {
  overflow: visible;
}

.flownet__steps {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr) minmax(0, 1.2fr);
  /* Explicit, because the incoming tile spans them. With no rows declared
     there is only one explicit line, so `1 / -1` meant `1 / 1` and the
     tile sat in the top row instead of beside the whole stack. */
  grid-template-rows: repeat(4, auto);
  gap: clamp(0.6rem, 1.6vw, 1rem) 0;
  align-items: center;
  margin: 0; padding: 0; list-style: none;
}
.flownet__node--in { grid-column: 1; grid-row: 1 / -1; align-self: center; }
.flownet__node--out { grid-column: 3; }

.flownet__node {
  /* Opaque on purpose: every wire ends underneath a tile rather than at its
     edge, which is what frees the geometry from having to be exact. */
  background: linear-gradient(165deg, rgba(36, 107, 253, 0.12), transparent 60%), #0F1621;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 14px;
  padding: clamp(0.85rem, 2.2vw, 1.15rem);
}
.flownet__n {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--blue-light);
  margin-bottom: 0.35rem;
}
.flownet__title {
  font-size: var(--fs-h4);
  font-weight: 500;
  color: var(--white);
  margin-bottom: 0.3rem;
}
.flownet__body {
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--muted);
}

/* ---------- The hub ---------- */
.flownet__hub {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  z-index: 3;
  width: clamp(76px, 11vw, 118px);
  display: grid;
  place-items: center;
}
.flownet__hex { grid-area: 1 / 1; width: 100%; height: auto; display: block; }
.flownet__hex-fill { fill: url(#none), rgba(36, 107, 253, 0.16); }
.flownet__hex-line { stroke: rgba(130, 175, 255, 0.75); stroke-width: 1.5; }
.flownet__hub::after {
  content: '';
  grid-area: 1 / 1;
  width: 150%; aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(36, 107, 253, 0.5), transparent 68%);
  filter: blur(14px);
  z-index: -1;
}
.flownet__mark {
  grid-area: 1 / 1;
  width: 46%; height: auto;
  border-radius: 10px;
}

/* ---------- Stacked, on a narrow window ----------
   The network reads as a network only when there is width for it to
   fan out across. Below that it is an ordered list with a rail, which
   is what the content is anyway. */
@media (max-width: 860px) {
  .flownet__wires, .flownet__hub { display: none; }
  .flownet__steps { grid-template-columns: minmax(0, 1fr); }
  .flownet__node--in, .flownet__node--out { grid-column: 1; grid-row: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .flownet__wire { stroke-dashoffset: 0; }
}

/* ===================================================================
   The line dropping into the mark
   A thread hanging the K1 badge above the heading, the way the
   reference hangs its shield. Draws itself as the section arrives.
   =================================================================== */
.svc-stage__head { position: relative; }
.svc-drop {
  display: flex;
  flex-direction: column;
  /* Hung over the start of the heading, not the middle of the section.
     The reference centres its badge because the heading under it is
     centred too; ours is ranged left, so centring left it stranded in
     open space with nothing beneath it. */
  align-items: flex-start;
  gap: 0;
  margin-bottom: clamp(0.9rem, 2.5vw, 1.4rem);
}
.svc-drop__line { margin-left: clamp(16px, 2.6vw, 21px); }
.svc-drop__line {
  width: 1px;
  height: clamp(28px, 6vw, 54px);
  background: linear-gradient(180deg, transparent, var(--blue-light));
  /* Grows down out of nothing as the section rises. Falls back to its
     full height when no script writes --enter. */
  transform: scaleY(var(--enter, 1));
  transform-origin: 50% 0;
}
.svc-drop__mark {
  display: grid;
  place-items: center;
  width: clamp(34px, 6vw, 44px);
  aspect-ratio: 1;
  border-radius: 11px;
  background: var(--ink-800);
  border: 1px solid rgba(169, 196, 255, 0.28);
  box-shadow: 0 0 22px rgba(36, 107, 253, 0.38);
}
.svc-drop__mark img { width: 72%; height: auto; border-radius: 7px; }

.section--light .svc-drop__mark {
  background: var(--paper-2);
  border-color: rgba(36, 107, 253, 0.26);
}

@media (prefers-reduced-motion: reduce) {
  .svc-drop__line { transform: none; }
}

/* ===================================================================
   The chips underneath, as columns
   One column per group with a rule above them, which is how the
   reference lays its four compliance notes out.
   =================================================================== */
.svc-detail .svc-card__groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  gap: clamp(1rem, 3vw, 2.2rem);
  align-items: start;
}
.svc-detail .svc-card__group-title {
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue-light);
  margin-bottom: 0.6rem;
}
