/* Codertive design system. Plain hand-written CSS, no build step: this file
   is served exactly as written, so keep it valid CSS a browser can parse.

     1 Fonts        2 Tokens       3 Reset       4 Type
     5 Layout       6 Buttons      7 Header      8 Hero
     9 Ticker      10 Context     11 Chain      12 Services
    13 Process     14 Report      15 Cases      16 FAQ
    17 Contact     18 Footer      19 Consent    20 Legal
    21 Case study  22 Motion

   Only two weights exist (400 and 700) and neither family has a variable
   axis, so hierarchy is built from size and colour. Never set 500 or 600:
   the browser would synthesise the weight and it renders smeared. */

/* ---------------------------------------------------------------------- */
/* 1. Fonts, self-hosted, OFL (see fonts/*-OFL.txt)                        */
/* ---------------------------------------------------------------------- */

@font-face {
  font-family: 'Outfit';
  src: url('/fonts/outfit-400.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Outfit';
  src: url('/fonts/outfit-700.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Geist Mono';
  src: url('/fonts/geist-mono-400.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Geist Mono';
  src: url('/fonts/geist-mono-700.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---------------------------------------------------------------------- */
/* 2. Tokens                                                               */
/* ---------------------------------------------------------------------- */

:root {
  --paper: #fafbfc;
  --white: #ffffff;
  --ink: #0f1419;
  --ink-2: #16202a;

  /* Every value below clears WCAG AA at small text sizes on its own
     background; --text-4 sits at 4.58:1, which is the floor. */
  --text: #0f1419;
  --text-2: #3e4750;
  --text-3: #5b646e;
  --text-4: #6b747e;

  --on-dark: #f2f5f8;
  --on-dark-2: rgba(242, 245, 248, 0.7);
  --on-dark-3: rgba(242, 245, 248, 0.58);

  --accent: #1f5fc0;
  --accent-dark: #18519e;
  --accent-lit: #4da6f0;
  --accent-wash: rgba(31, 95, 192, 0.06);
  --accent-edge: rgba(31, 95, 192, 0.28);

  --line: rgba(15, 20, 25, 0.1);
  --line-2: rgba(15, 20, 25, 0.16);
  --line-dark: rgba(242, 245, 248, 0.14);
  --line-dark-2: rgba(242, 245, 248, 0.26);

  --font: 'Outfit', system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;
  --mono: 'Geist Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 28px;

  --shadow-1: 0 1px 2px rgba(15, 20, 25, 0.04);
  --shadow-2: 0 1px 2px rgba(15, 20, 25, 0.04), 0 28px 56px -44px rgba(15, 20, 25, 0.34);
  --shadow-3: 0 1px 2px rgba(15, 20, 25, 0.04), 0 34px 64px -40px rgba(15, 20, 25, 0.4);

  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  /* Single duration for every hover micro-interaction (buttons, cards,
     links). Premium UI research is consistent on two points: keep hover
     feedback inside roughly 150-300ms, and use the same timing everywhere
     rather than a different value per component, since matched timing
     reads as one coherent system and mismatched timing reads as sloppy
     even when no single value is "wrong". This replaced a scatter of
     0.18s/0.25s/0.28s/0.3s/0.32s values that had accumulated component by
     component. */
  --t-hover: 0.2s;
  --pad: clamp(20px, 4vw, 44px);
  --section-y: clamp(64px, 7vw, 110px);
  --header-h: 72px;
}

/* ---------------------------------------------------------------------- */
/* 3. Reset                                                                */
/* ---------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  color: var(--text);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img,
svg {
  display: block;
  max-width: 100%;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

button {
  background: none;
  border: none;
  cursor: pointer;
}

ul,
ol {
  list-style: none;
}

a {
  color: var(--accent);
  text-decoration: none;
}

*:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}

.bg-dark *:focus-visible {
  outline-color: var(--accent-lit);
}

::selection {
  background: var(--accent);
  color: #fff;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: #fff;
  padding: 12px 20px;
  border-radius: 0 0 var(--r-sm) 0;
  font-weight: 700;
}

.skip-link:focus {
  left: 0;
}

/* ---------------------------------------------------------------------- */
/* 4. Type                                                                 */
/* ---------------------------------------------------------------------- */

.h1 {
  font-size: clamp(33px, 4.6vw, 60px);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.035em;
}

.h2 {
  font-size: clamp(27px, 3.4vw, 44px);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.032em;
}

.h3 {
  font-size: clamp(19px, 1.6vw, 23px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.022em;
}

.lede {
  font-size: clamp(16.5px, 1.2vw, 18.5px);
  line-height: 1.62;
  color: var(--text-3);
  max-width: 62ch;
}

/* Sentence case, not uppercase and tracked out. Tracked-out caps labels
   read as decoration on every section, and there is one on most sections
   here. */
.eyebrow {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: 14px;
}

.bg-dark .eyebrow {
  color: var(--accent-lit);
}

.mono {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

.muted {
  color: var(--text-3);
}

.small {
  font-size: 14px;
}

/* ---------------------------------------------------------------------- */
/* 5. Layout                                                               */
/* ---------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: 1360px;
  margin-inline: auto;
  padding-inline: var(--pad);
}

.section {
  padding-block: var(--section-y);
}

.section--top {
  padding-top: 0;
}

.section-head {
  max-width: 720px;
  margin-bottom: clamp(40px, 4.5vw, 60px);
}

/* Fluid, like the heading it follows (.h1/.h2 both scale by viewport width),
   so the gap holds the same visual proportion to the headline at every size
   instead of a fixed 16px reading as generous next to a 27px mobile heading
   and cramped next to a 44px desktop one. */
.section-head .lede {
  margin-top: clamp(14px, 1.6vw, 20px);
}

.bg-dark {
  background: var(--ink);
  color: var(--on-dark);
}

.bg-dark .lede,
.bg-dark .muted {
  color: var(--on-dark-2);
}

/* The dark block reads as one slab even though it holds two sections. */
.slab-top {
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}

.slab-bottom {
  border-radius: 0 0 var(--r-xl) var(--r-xl);
}

/* ---------------------------------------------------------------------- */
/* 6. Buttons                                                              */
/* ---------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  border-radius: var(--r-md);
  padding: 15px 26px;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.012em;
  white-space: nowrap;
  transition: background-color var(--t-hover) var(--ease), border-color var(--t-hover) var(--ease),
    color var(--t-hover) var(--ease), box-shadow var(--t-hover) var(--ease),
    transform var(--t-hover) var(--ease);
}

.btn-primary {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 0 0 0 rgba(31, 95, 192, 0);
}

.btn-primary:hover {
  background: var(--accent-dark);
  box-shadow: 0 12px 26px -10px rgba(31, 95, 192, 0.55);
  transform: translateY(-2px);
}

.btn-primary:active {
  transform: translateY(1px);
  box-shadow: 0 4px 10px -8px rgba(31, 95, 192, 0.5);
}

.btn-quiet {
  border: 1px solid var(--line-2);
  color: var(--text);
  background: var(--white);
}

.btn-quiet:hover {
  border-color: var(--accent);
  color: var(--accent);
  transform: translateY(-2px);
  box-shadow: 0 10px 22px -14px rgba(15, 20, 25, 0.45);
}

.btn-quiet:active {
  transform: translateY(1px);
}

.bg-dark .btn-quiet {
  background: transparent;
  border-color: var(--line-dark-2);
  color: var(--on-dark);
}

.bg-dark .btn-quiet:hover {
  border-color: var(--accent-lit);
  color: var(--accent-lit);
}

.btn-ink {
  background: var(--ink);
  color: #fff;
}

.btn-ink:hover {
  background: var(--accent);
  transform: translateY(-2px);
  box-shadow: 0 12px 26px -10px rgba(31, 95, 192, 0.45);
}

.arrow {
  transition: transform var(--t-hover) var(--ease);
}

.btn:hover .arrow,
.textlink:hover .arrow {
  transform: translateX(4px);
}

.textlink {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 15.5px;
  color: var(--accent);
  transition: color var(--t-hover) var(--ease);
}

.textlink:hover {
  color: var(--accent-dark);
}

.bg-dark .textlink {
  color: var(--accent-lit);
}

/* ---------------------------------------------------------------------- */
/* 7. Header                                                               */
/* ---------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(250, 251, 252, 0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s var(--ease);
}

.site-header.is-scrolled {
  border-bottom-color: var(--line);
}

.site-header__bar {
  max-width: 1360px;
  margin-inline: auto;
  padding-inline: var(--pad);
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  flex-shrink: 0;
}

.brand__mark {
  width: 26px;
  height: 26px;
}

.brand__word {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.035em;
}

.primary-nav {
  display: none;
}

@media (min-width: 940px) {
  .primary-nav {
    display: block;
  }
}

.primary-nav ul {
  display: flex;
  align-items: center;
  gap: 28px;
}

.primary-nav a {
  font-size: 14.5px;
  color: var(--text-3);
  transition: color 0.2s var(--ease);
}

.primary-nav a:hover,
.primary-nav a[aria-current='page'] {
  color: var(--text);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.header-cta {
  display: none;
  padding: 11px 19px;
  font-size: 14.5px;
  border-radius: var(--r-sm);
}

@media (min-width: 940px) {
  .header-cta {
    display: inline-flex;
  }
}

.nav-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-2);
  color: var(--text);
}

@media (min-width: 940px) {
  .nav-toggle {
    display: none;
  }
}

.mobile-nav {
  position: fixed;
  inset: 0;
  z-index: 59;
  background: var(--paper);
  padding: calc(var(--header-h) + 24px) var(--pad) 32px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.mobile-nav[hidden] {
  display: none;
}

.mobile-nav a {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--text);
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.mobile-nav .btn {
  margin-top: 24px;
  align-self: flex-start;
}

/* ---------------------------------------------------------------------- */
/* 8. Hero                                                                 */
/* ---------------------------------------------------------------------- */

.hero {
  position: relative;
  overflow: hidden;
  padding-block: clamp(64px, 8vw, 120px) clamp(48px, 6vw, 84px);
}

/* One soft wash of brand colour. No particle field: a drifting constellation
   is the single most recognisable stock-tech background there is, and it
   says nothing about what this company does. */
.hero__glow {
  position: absolute;
  left: 50%;
  top: -280px;
  width: min(1100px, 130vw);
  height: 760px;
  transform: translateX(-50%);
  pointer-events: none;
  background: radial-gradient(
    ellipse at center,
    rgba(31, 95, 192, 0.09) 0%,
    rgba(31, 95, 192, 0.05) 34%,
    rgba(31, 95, 192, 0.015) 58%,
    rgba(31, 95, 192, 0) 74%
  );
}

/* Copy left, deliverable right. B2B homepage research is consistent that
   the hero should carry a real view of what the buyer receives rather than
   an abstract graphic, and that most reading time is spent here, so the
   space is given to the report and to proof rather than to decoration. */
.hero__grid {
  position: relative;
  display: grid;
  gap: clamp(40px, 5vw, 60px);
  align-items: center;
}

@media (min-width: 1040px) {
  .hero__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr);
    gap: clamp(48px, 5vw, 72px);
  }
}

.hero__in {
  position: relative;
  z-index: 2;
}

.hero__title {
  max-width: 15ch;
}

.hero__visual {
  position: relative;
  z-index: 2;
}

/* Numbers taken from the case studies, each linking to the one it came
   from, so the claim above the fold is checkable rather than asserted. */
.hero__proof {
  margin-top: clamp(32px, 3.5vw, 44px);
  padding-top: 24px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 14px 36px;
}

.hero__proof-item {
  color: inherit;
  display: block;
}

.hero__proof-n {
  display: block;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--accent);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.hero__proof-l {
  display: block;
  font-size: 13.5px;
  color: var(--text-3);
  border-bottom: 1px solid transparent;
  transition: border-color var(--t-hover) var(--ease), color var(--t-hover) var(--ease);
}

.hero__proof-item:hover .hero__proof-l {
  color: var(--text);
  border-bottom-color: var(--line-2);
}

.hero__sub {
  margin-top: clamp(18px, 2vw, 26px);
  font-size: clamp(17px, 1.5vw, 20px);
  line-height: 1.55;
  color: var(--text-3);
  max-width: 58ch;
}

.hero__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: clamp(28px, 3vw, 40px);
}

/* ---------------------------------------------------------------------- */
/* 9. Ticker                                                               */
/* ---------------------------------------------------------------------- */

.ticker {
  background: var(--ink);
  overflow: hidden;
  border-block: 1px solid var(--line);
}

/* Spacing lives on the children rather than in `gap`. With `gap`, the space
   between the two copies is counted once but split across the 50% mark, so
   translating -50% left an 11px jump at every loop. Giving each child its own
   trailing margin makes one copy exactly half the track.

   Speed: a single pass through TICKER renders at roughly 994px. Marquees
   like this commonly run close to 50px/s, with a full cycle landing
   somewhere in a 15-40s band; the site's original 46s (about 22px/s) sat
   past the slow end of that. 120s here is scaled for a half that's 4 copies
   of that pass (see generate.py for why it's repeated), which works out to
   about 33px/s, or close to 30s for one logical pass, inside that band.
   Mobile runs slower still below, which is the typical treatment since a
   fixed px/s speed reads as busier on a narrower screen. */
.ticker__track {
  display: flex;
  align-items: center;
  width: max-content;
  padding-block: 16px;
  animation: tickerRun 120s linear infinite;
}

@media (max-width: 640px) {
  .ticker__track {
    animation-duration: 170s;
  }
}

.ticker__track > * {
  margin-right: 22px;
}

@keyframes tickerRun {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

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

.ticker__item {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--on-dark);
  white-space: nowrap;
  line-height: 1;
}

.ticker__sep {
  color: var(--accent-lit);
  line-height: 1;
}

/* ---------------------------------------------------------------------- */
/* 10. Context                                                             */
/* ---------------------------------------------------------------------- */

.context-wrap {
  display: grid;
  gap: 40px;
}

@media (min-width: 960px) {
  .context-wrap {
    grid-template-columns: 0.85fr 1fr;
    gap: 72px;
  }
  .context-head {
    position: sticky;
    top: calc(var(--header-h) + 48px);
    align-self: start;
  }
}

.context-list {
  view-timeline-name: --context-progress;
}

.context-item {
  padding: 30px 0 30px 26px;
  border-left: 2px solid var(--line);
  transition: border-color 0.4s var(--ease);
}

.context-item:first-child {
  padding-top: 0;
}

.context-item.is-visible {
  border-left-color: var(--accent);
}

/* Same idea as the chain: each item's border lights up at the point in the
   list's own scroll-through where a reader would plausibly be reading it,
   rather than every item that has been scrolled past turning on at once
   and staying that way regardless of where the reader actually is now.
   Scrolling back up dims an item again, same as the chain's fill retreats,
   which is the part a one-shot IntersectionObserver trigger cannot do. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .context-item:nth-child(1) {
      animation: context-item-active linear both;
      animation-timeline: --context-progress;
      animation-range: cover 5% cover 15%;
    }
    .context-item:nth-child(2) {
      animation: context-item-active linear both;
      animation-timeline: --context-progress;
      animation-range: cover 27.5% cover 37.5%;
    }
    .context-item:nth-child(3) {
      animation: context-item-active linear both;
      animation-timeline: --context-progress;
      animation-range: cover 50% cover 60%;
    }
  }
}

@keyframes context-item-active {
  from {
    border-left-color: var(--line);
  }
  to {
    border-left-color: var(--accent);
  }
}

/* Dims the title and body of whichever items are not the one currently in
   focus, on the same per-item ranges as the border above, so the two move
   together: the item being read sits at full strength while the others
   recede rather than all three competing for attention at once. Left off
   the base rules below (full opacity there) so this only ever applies in
   browsers that support scroll-driven animation, where it can also undim
   on scroll-up; everyone else just gets the border cue. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .context-item:nth-child(1) .context-item__title,
    .context-item:nth-child(1) .context-item__body {
      animation: context-text-focus linear both;
      animation-timeline: --context-progress;
      animation-range: cover 5% cover 15%;
    }
    .context-item:nth-child(2) .context-item__title,
    .context-item:nth-child(2) .context-item__body {
      animation: context-text-focus linear both;
      animation-timeline: --context-progress;
      animation-range: cover 27.5% cover 37.5%;
    }
    .context-item:nth-child(3) .context-item__title,
    .context-item:nth-child(3) .context-item__body {
      animation: context-text-focus linear both;
      animation-timeline: --context-progress;
      animation-range: cover 50% cover 60%;
    }
  }
}

/* The small rightward settle (paired with the opacity fade above) is what
   gives the focused item a sense of coming forward rather than just
   getting brighter, the same "depth" cue used in editorial scrollytelling. */
@keyframes context-text-focus {
  from {
    opacity: 0.45;
    translate: -8px 0;
  }
  to {
    opacity: 1;
    translate: 0 0;
  }
}

.context-item__title {
  font-size: clamp(19px, 1.9vw, 24px);
  font-weight: 700;
  letter-spacing: -0.022em;
  line-height: 1.3;
  margin-bottom: 10px;
  opacity: 1;
}

.context-item__body {
  font-size: 16px;
  line-height: 1.62;
  color: var(--text-3);
  opacity: 1;
  max-width: 48ch;
}

/* ---------------------------------------------------------------------- */
/* 11. Chain                                                               */
/* ---------------------------------------------------------------------- */

.chain {
  position: relative;
  display: grid;
  gap: 0;
  grid-template-columns: repeat(5, 1fr);
  view-timeline-name: --chain-progress;
}

/* Stops at the centre of the first and last node rather than running on to
   the container edge, which read as an unfinished line. Each of the five
   columns is 20% wide and the 34px node sits at its left edge, so the
   centres are 17px in from the start and 20% minus 17px from the end. */
.chain__line {
  position: absolute;
  left: 17px;
  right: calc(20% - 17px);
  top: 17px;
  height: 2px;
  background: var(--line);
}

.chain__fill {
  height: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-lit));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.5s var(--ease);
}

.chain.is-visible .chain__fill {
  transform: scaleX(1);
}

/* Where the browser supports it, the fill tracks scroll position directly
   instead of playing a fixed-length animation the moment the chain enters
   view: scroll down and it grows, scroll back up and it retreats, exactly
   like Apple's own scroll-linked progress indicators. This reads from
   `--chain-progress`, a named timeline the .chain row declares on itself
   above, so the line (a thin 2px bar) tracks the row's transit through the
   viewport rather than its own, which would complete almost instantly.
   Falls back to the fixed transition above, unstyled here, in any browser
   that does not support scroll-driven animations yet. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .chain__fill {
      animation: chain-fill-progress linear both;
      animation-timeline: --chain-progress;
      animation-range: cover 10% cover 55%;
    }

    /* Each circle lights up at the point along that same timeline where the
       fill line visually reaches it, instead of every circle turning on
       together the moment the row scrolls into view with no relation to
       the bar's own progress. The five ranges are evenly spaced across the
       same 10%-55% cover window the fill itself runs across (one axis, so
       "halfway along the bar" and "halfway along a circle's window" mean
       the same scroll position), each about 10 points wide so a circle
       still eases into colour rather than snapping. */
    .chain-step:nth-child(2) .chain-step__num {
      animation: chain-step-active linear both;
      animation-timeline: --chain-progress;
      animation-range: cover 5% cover 15%;
    }
    .chain-step:nth-child(3) .chain-step__num {
      animation: chain-step-active linear both;
      animation-timeline: --chain-progress;
      animation-range: cover 16.25% cover 26.25%;
    }
    .chain-step:nth-child(4) .chain-step__num {
      animation: chain-step-active linear both;
      animation-timeline: --chain-progress;
      animation-range: cover 27.5% cover 37.5%;
    }
    .chain-step:nth-child(5) .chain-step__num {
      animation: chain-step-active linear both;
      animation-timeline: --chain-progress;
      animation-range: cover 38.75% cover 48.75%;
    }
    .chain-step:nth-child(6) .chain-step__num {
      animation: chain-step-active linear both;
      animation-timeline: --chain-progress;
      animation-range: cover 50% cover 60%;
    }
  }
}

@keyframes chain-fill-progress {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

@keyframes chain-step-active {
  from {
    background-color: var(--paper);
    border-color: var(--line-2);
    color: var(--text-4);
  }
  to {
    background-color: var(--accent);
    border-color: var(--accent);
    color: #fff;
  }
}

.chain-step {
  position: relative;
  padding-right: 28px;
}

.chain-step__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--paper);
  border: 1.5px solid var(--line-2);
  font-family: var(--mono);
  font-size: 13px;
  color: var(--text-4);
  position: relative;
  z-index: 2;
  margin-bottom: 18px;
  transition: background-color 0.35s var(--ease), border-color 0.35s var(--ease),
    color 0.35s var(--ease);
}

.chain-step.is-visible .chain-step__num {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.chain-step__title {
  font-size: 17.5px;
  font-weight: 700;
  letter-spacing: -0.022em;
  margin-bottom: 7px;
}

.chain-step__body {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--text-3);
}

@media (max-width: 1100px) {
  .chain {
    grid-template-columns: 1fr;
    border-top: 1px solid var(--line);
  }
  .chain__line {
    display: none;
  }
  .chain-step {
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr);
    gap: 4px 18px;
    padding: 22px 0;
    border-bottom: 1px solid var(--line);
  }
  .chain-step__num {
    grid-row: span 2;
    margin-bottom: 0;
  }
}

/* ---------------------------------------------------------------------- */
/* 12. Services                                                            */
/* ---------------------------------------------------------------------- */

.service-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  gap: 18px;
}

.service-card {
  border: 1px solid var(--line-dark);
  border-radius: var(--r-lg);
  padding: 34px 30px 30px;
  background: rgba(242, 245, 248, 0.02);
  transition: background-color var(--t-hover) var(--ease), border-color var(--t-hover) var(--ease),
    transform var(--t-hover) var(--ease);
}

.service-card:hover {
  background: rgba(77, 166, 240, 0.07);
  border-color: rgba(77, 166, 240, 0.42);
  transform: translateY(-3px);
}

.service-card__tag {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--accent-lit);
}

.service-card__title {
  margin: 26px 0 12px;
  font-size: 23px;
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.2;
}

.service-card__body {
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--on-dark-2);
}

.service-card__items {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line-dark);
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.service-card__items li {
  font-size: 12.5px;
  font-family: var(--mono);
  color: var(--on-dark-3);
  border: 1px solid var(--line-dark);
  border-radius: 999px;
  padding: 4px 11px;
}

/* ---------------------------------------------------------------------- */
/* 13. Process                                                             */
/* ---------------------------------------------------------------------- */

.phase-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

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

@media (min-width: 1150px) {
  .phase-grid {
    grid-template-columns: repeat(5, 1fr);
  }
}

.phase {
  position: relative;
  overflow: hidden;
  background: rgba(242, 245, 248, 0.03);
  border: 1px solid var(--line-dark);
  border-radius: var(--r-md);
  padding: 24px 20px 22px;
}

.phase--last {
  background: linear-gradient(135deg, rgba(31, 95, 192, 0.22), rgba(31, 95, 192, 0.05));
  border-color: rgba(77, 166, 240, 0.42);
}

.phase__num {
  position: absolute;
  top: -10px;
  right: 14px;
  font-family: var(--mono);
  font-size: 62px;
  font-weight: 700;
  color: rgba(242, 245, 248, 0.05);
  line-height: 1;
  pointer-events: none;
}

.phase--last .phase__num {
  color: rgba(77, 166, 240, 0.16);
}

.phase__title {
  position: relative;
  font-size: 17.5px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 9px;
}

.phase__body {
  position: relative;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--on-dark-2);
}

/* ---------------------------------------------------------------------- */
/* 14. Report                                                              */
/* ---------------------------------------------------------------------- */

/* align-items: stretch (grid's own default, stated explicitly here) makes
   every card in a row match the tallest one, which is what turns three
   cards of visibly different lengths into one even row instead of a
   staircase. */
.report-grid,
.deliverables {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  gap: 16px;
  align-items: stretch;
}

/* color is reset here rather than left to inherit. .bg-dark sets a light
   color for its own text, and a light-background card dropped inside it
   (the "Working together" cards on /work/) inherited that instead of
   picking up --text, so any child without its own explicit color, such as
   .report-side__title, rendered near-invisible: light text on a white
   card. */
.card {
  background: var(--white);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
}

.report-main {
  padding: 24px 26px 20px;
  box-shadow: var(--shadow-2);
  display: flex;
  flex-direction: column;
  margin: 0;
}

.report-main__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}

.report-main__title {
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.018em;
}

.report-pill {
  font-size: 12.5px;
  color: var(--text-3);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 12px;
  flex: none;
}

/* min-width:0 is load bearing. As a flex child this defaults to min-width:auto,
   which resolves to the grid's min-content width and pushes the card, its
   column and every sibling wider than a phone screen. body's overflow-x:hidden
   then hid the damage instead of preventing it. */
.rtable {
  overflow-x: auto;
  min-width: 0;
}

.hero__grid > * {
  min-width: 0;
}

/* Below this the four columns cannot hold their minimums without scrolling,
   so the numbers tighten rather than the table sliding sideways. */
@media (max-width: 560px) {
  .rrow {
    grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(34px, 0.58fr));
    gap: 0 8px;
  }
  .rrow--body {
    font-size: 13.5px;
    padding: 11px 8px;
  }
  .rrow--head {
    padding: 0 8px 9px;
    font-size: 10.5px;
  }
  .report-main {
    padding: 20px 18px 16px;
  }
}

.rrow {
  display: grid;
  grid-template-columns: minmax(140px, 1.7fr) repeat(3, minmax(52px, 0.62fr));
  gap: 0 12px;
  align-items: center;
}

/* Head and body share the same inline padding so the columns line up. The
   body rows previously pulled themselves 10px wider with a negative margin
   to bleed the highlight to the card edge, which made every row wider than
   the scroll container and clipped the last column at every width. The
   highlight is an inset rounded row instead. */
.rrow--head {
  padding: 0 10px 10px;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-3);
}

.rrow--body {
  padding: 12px 10px;
  border-radius: 9px;
  border-bottom: 1px solid rgba(15, 20, 25, 0.06);
  font-size: 14.5px;
  font-weight: 700;
  color: var(--text-2);
}

.rrow--hi {
  background: var(--accent-wash);
}

.rrow--hi .rrow__name,
.rrow--hi .rrow__sales {
  color: var(--accent);
}

.rrow__name {
  color: var(--text);
  letter-spacing: -0.012em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rrow__n {
  text-align: right;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

.report-foot {
  display: flex;
  align-items: center;
  gap: 9px;
  padding-top: 18px;
  font-size: 14px;
  color: var(--text-3);
}

/* Flex column so the grid stretch above (align-items: stretch) makes every
   card in a row match the tallest one, rather than each sizing to its own
   (slightly different) body copy length. */
.report-side {
  padding: 28px 26px 26px;
  display: flex;
  flex-direction: column;
}

.report-side__tag {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--accent);
  margin-bottom: 14px;
}

.report-side__title {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 9px;
}

.report-side__body {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--text-3);
}

.checklist {
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.checklist li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--text-2);
}

.checklist svg {
  flex: none;
  margin-top: 5px;
}

/* ---------------------------------------------------------------------- */
/* 15. Case cards                                                          */
/* ---------------------------------------------------------------------- */

.case-grid {
  display: grid;
  gap: 18px;
}

@media (min-width: 900px) {
  .case-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.case-card {
  display: flex;
  flex-direction: column;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 28px 26px 24px;
  box-shadow: var(--shadow-1);
  color: var(--text);
  transition: border-color var(--t-hover) var(--ease), box-shadow var(--t-hover) var(--ease),
    transform var(--t-hover) var(--ease);
}

.case-card:hover {
  border-color: var(--accent-edge);
  box-shadow: var(--shadow-3);
  transform: translateY(-3px);
}

.case-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-4);
  margin-bottom: 18px;
}

.case-card__title {
  font-size: clamp(19px, 1.7vw, 22px);
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.24;
}

.case-card__result {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 15px;
  color: var(--text-3);
}

.case-card__result b {
  color: var(--accent);
  font-weight: 700;
}

/* margin-bottom, not a margin-top on .case-card__go: that element's
   margin-top is auto, which pins it to the card's bottom edge across a
   stretched grid row (see its own comment). Flex auto-margins take every
   bit of free space in the column, so on a row where the taller card leaves
   no free space, that auto margin resolves to exactly 0, and the line ends
   up touching the chips with no breathing room at all. Flex siblings don't
   collapse margins the way block siblings do, so this gap survives as a
   floor no matter how the auto margin above it resolves. */
.case-card__stack {
  margin-top: 18px;
  margin-bottom: 20px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.chip {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-3);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 10px;
  background: var(--paper);
}

.case-card__go {
  padding-top: 18px;
  border-top: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--accent);
  margin-top: auto;
}

.case-card:hover .arrow {
  transform: translateX(4px);
}

/* ---------------------------------------------------------------------- */
/* 16. FAQ                                                                 */
/* ---------------------------------------------------------------------- */

.faq-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 0 64px;
}

.faq-item {
  border-bottom: 1px solid var(--line);
}

.faq-q {
  width: 100%;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  padding: 21px 0;
  text-align: left;
  font-size: 16.5px;
  font-weight: 700;
  letter-spacing: -0.018em;
  line-height: 1.4;
  color: var(--text);
  transition: color 0.2s var(--ease);
}

.faq-q:hover {
  color: var(--accent);
}

.faq-icon {
  position: relative;
  flex: none;
  width: 16px;
  height: 16px;
  margin-top: 5px;
}

.faq-icon::before,
.faq-icon::after {
  content: '';
  position: absolute;
  background: currentColor;
  border-radius: 1px;
}

.faq-icon::before {
  left: 0;
  right: 0;
  top: 7.5px;
  height: 1.5px;
}

.faq-icon::after {
  top: 0;
  bottom: 0;
  left: 7.5px;
  width: 1.5px;
  transition: transform 0.32s var(--ease);
}

.faq-item.is-open .faq-icon::after {
  transform: scaleY(0);
}

/* Collapsed only when JS is available to open it again. Without JS every
   answer stays visible rather than being sealed shut. */
.faq-panel {
  overflow: hidden;
}

html.js .faq-panel {
  height: 0;
}

.faq-panel p {
  padding-bottom: 22px;
  font-size: 15.5px;
  line-height: 1.62;
  color: var(--text-3);
  max-width: 60ch;
}

/* ---------------------------------------------------------------------- */
/* 17. Contact                                                             */
/* ---------------------------------------------------------------------- */

.contact-card {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--shadow-3);
}

.contact-card__aside {
  background: var(--accent);
  color: #fff;
  padding: clamp(32px, 4vw, 46px) clamp(24px, 3.5vw, 40px);
}

.contact-card__aside .h2 {
  font-size: clamp(25px, 2.6vw, 33px);
}

.contact-card__aside p {
  margin-top: 14px;
  font-size: 16px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.86);
}

.contact-card__details {
  margin-top: 26px;
  padding-top: 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.3);
  display: flex;
  flex-direction: column;
  gap: 7px;
  font-size: 14.5px;
  color: rgba(255, 255, 255, 0.86);
}

.contact-card__details a {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.contact-card__form {
  padding: clamp(28px, 3.5vw, 44px) clamp(24px, 3vw, 40px);
}

.form-grid {
  display: grid;
  gap: 15px;
}

@media (min-width: 560px) {
  .form-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .field--wide,
  .form-grid > button,
  .form-grid > .form-note {
    grid-column: 1 / -1;
  }
}

.field label {
  display: block;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text-3);
  margin-bottom: 6px;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  background: #f4f6f8;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 12px 14px;
  font-size: 15px;
  transition: border-color 0.18s var(--ease), background-color 0.18s var(--ease),
    box-shadow 0.18s var(--ease);
}

.field input::placeholder,
.field textarea::placeholder {
  color: #8b939c;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--white);
  box-shadow: 0 0 0 3px rgba(31, 95, 192, 0.12);
}

.field textarea {
  resize: vertical;
  min-height: 92px;
  line-height: 1.5;
}

.field select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1.5l5 5 5-5' stroke='%235B646E' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-right: 38px;
}

.field-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}

.form-note {
  font-size: 13.5px;
  color: var(--text-4);
}

/* ---------------------------------------------------------------------- */
/* 18. Footer                                                              */
/* ---------------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--line);
}

.site-footer__in {
  padding-block: 30px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px 26px;
  font-size: 14px;
  color: var(--text-3);
}

.site-footer__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  font-weight: 700;
}

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 26px;
}

.site-footer__links a,
.site-footer__links button {
  color: var(--text-3);
  font-size: 14px;
  transition: color 0.2s var(--ease);
}

.site-footer__links a:hover,
.site-footer__links button:hover {
  color: var(--text);
}

/* ---------------------------------------------------------------------- */
/* 19. Consent                                                             */
/* ---------------------------------------------------------------------- */

.consent-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  background: var(--ink);
  color: var(--on-dark);
  border-top: 1px solid var(--line-dark);
}

.consent-banner__inner {
  max-width: 1360px;
  margin-inline: auto;
  padding: 22px var(--pad);
}

.consent-summary {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

@media (min-width: 900px) {
  .consent-summary {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 32px;
  }
}

.consent-summary p {
  max-width: 74ch;
  font-size: 14.5px;
  color: var(--on-dark-2);
}

.consent-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
  flex: none;
}

.consent-actions .btn {
  padding: 10px 17px;
  font-size: 14px;
  border-radius: var(--r-sm);
}

.consent-preferences {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--line-dark);
}

.consent-grid {
  display: grid;
  gap: 18px;
}

@media (min-width: 760px) {
  .consent-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.consent-option .row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--mono);
  font-size: 13.5px;
}

.consent-option .hint {
  margin-top: 6px;
  font-size: 13.5px;
  color: var(--on-dark-3);
}

/* ---------------------------------------------------------------------- */
/* 20. Legal                                                               */
/* ---------------------------------------------------------------------- */

.doc-wrap {
  display: grid;
  gap: 40px;
  padding-block: clamp(48px, 6vw, 84px);
}

@media (min-width: 1000px) {
  .doc-wrap {
    grid-template-columns: 240px minmax(0, 1fr);
    gap: 64px;
  }
  .doc-toc {
    position: sticky;
    top: calc(var(--header-h) + 40px);
    align-self: start;
  }
}

.doc-toc__label {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-4);
  margin-bottom: 12px;
}

.doc-toc a {
  display: block;
  padding: 6px 0;
  font-size: 14px;
  color: var(--text-3);
}

.doc-toc a:hover {
  color: var(--accent);
}

.doc-head {
  padding-bottom: 32px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 8px;
}

.doc-head .lede {
  margin-top: 16px;
}

.doc-updated {
  margin-top: 18px;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--text-4);
}

.doc-section {
  padding-top: 34px;
  scroll-margin-top: calc(var(--header-h) + 24px);
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  gap: 0 12px;
}

.doc-section__n {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--accent);
  padding-top: 5px;
}

.doc-section h2 {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.022em;
}

.doc-section__body {
  grid-column: 2;
}

.doc-section__body p {
  margin-top: 12px;
  color: var(--text-3);
  max-width: 72ch;
}

@media (max-width: 560px) {
  .doc-section {
    grid-template-columns: 1fr;
  }
  .doc-section__n {
    padding-top: 0;
    margin-bottom: 6px;
  }
  .doc-section__body {
    grid-column: 1;
  }
}

.doc-close {
  margin-top: 44px;
  padding: 22px 24px;
  background: var(--accent-wash);
  border: 1px solid var(--accent-edge);
  border-radius: var(--r-md);
  font-size: 15px;
  color: var(--text-2);
}

.cookie-row {
  padding-block: 26px;
  border-bottom: 1px solid var(--line);
}

.cookie-row__head {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 17px;
  font-weight: 700;
}

.cookie-badge {
  font-family: var(--mono);
  font-size: 11.5px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 10px;
  color: var(--text-4);
}

.cookie-row p {
  margin-top: 8px;
  color: var(--text-3);
  max-width: 72ch;
}

/* ---------------------------------------------------------------------- */
/* 21. Case study                                                          */
/* ---------------------------------------------------------------------- */

.progress-bar {
  position: fixed;
  left: 0;
  top: 0;
  height: 2px;
  background: var(--accent);
  width: 100%;
  transform: scaleX(0);
  transform-origin: left;
  z-index: 80;
}

.case-hero {
  padding-block: clamp(40px, 5vw, 68px) clamp(32px, 4vw, 48px);
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--text-3);
  margin-bottom: 26px;
}

.back-link:hover {
  color: var(--accent);
}

.case-hero__sector {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--accent);
  margin-bottom: 16px;
}

.case-hero .h1 {
  max-width: 20ch;
}

.case-hero__lead {
  margin-top: clamp(20px, 2.6vw, 30px);
  padding: 20px 24px;
  background: var(--accent-wash);
  border: 1px solid var(--accent-edge);
  border-radius: var(--r-md);
  font-size: clamp(17px, 1.5vw, 20px);
  line-height: 1.45;
  color: var(--text-2);
  max-width: 58ch;
}

.case-hero__lead b {
  color: var(--accent);
}

.metric-grid {
  margin-top: clamp(28px, 3vw, 40px);
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
}

.metric {
  padding: 22px 22px 20px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
}

.metric__n {
  font-size: clamp(28px, 3vw, 38px);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.metric__label {
  margin-top: 10px;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--text-2);
}

.metric__note {
  margin-top: 4px;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-4);
}

.case-meta {
  margin-top: 30px;
  padding: 18px 22px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  display: grid;
  gap: 14px;
}

@media (min-width: 860px) {
  .case-meta {
    grid-template-columns: auto auto 1fr;
    gap: 32px;
    align-items: center;
  }
}

.case-meta__k {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-4);
  margin-bottom: 3px;
}

.case-meta__v {
  font-size: 14.5px;
  color: var(--text-2);
}

.case-meta__stack {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.case-layout {
  display: grid;
  gap: 40px;
  padding-bottom: clamp(56px, 6vw, 88px);
}

@media (min-width: 1100px) {
  .case-layout {
    grid-template-columns: 220px minmax(0, 1fr);
    gap: 64px;
  }
  .case-toc {
    position: sticky;
    top: calc(var(--header-h) + 36px);
    align-self: start;
  }
}

@media (max-width: 1099px) {
  .case-toc {
    display: none;
  }
}

.case-toc__label {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-4);
  margin-bottom: 12px;
}

.case-toc a {
  display: block;
  padding: 7px 0 7px 13px;
  border-left: 2px solid var(--line);
  font-size: 13.5px;
  color: var(--text-3);
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.case-toc a:hover {
  color: var(--text);
}

.case-toc a.is-active {
  color: var(--accent);
  border-left-color: var(--accent);
  font-weight: 700;
}

.case-body {
  max-width: 72ch;
}

.case-section {
  padding-top: clamp(36px, 4vw, 54px);
  scroll-margin-top: calc(var(--header-h) + 20px);
}

.case-section > .h2 {
  font-size: clamp(23px, 2.4vw, 31px);
  margin-bottom: 18px;
}

.case-section p {
  margin-top: 14px;
  color: var(--text-2);
  line-height: 1.7;
}

.callout {
  padding: 22px 26px;
  background: var(--accent-wash);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-md) var(--r-md) 0;
}

.callout p {
  margin-top: 0;
  color: var(--text-2);
}

.stack-list {
  margin-top: 22px;
  border-top: 1px solid var(--line);
}

.stack-list li {
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}

.stack-list__t {
  font-size: 16.5px;
  font-weight: 700;
  letter-spacing: -0.018em;
  margin-bottom: 5px;
}

.stack-list__d {
  font-size: 15px;
  line-height: 1.62;
  color: var(--text-3);
}

.phase-block {
  padding: 22px 0;
  border-bottom: 1px solid var(--line);
}

.phase-block__tag {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--accent);
  margin-bottom: 8px;
}

.phase-block__t {
  font-size: 17.5px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 8px;
}

.phase-block p {
  margin-top: 0;
  font-size: 15.5px;
  color: var(--text-3);
}

.code-chips {
  margin-top: 14px;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.code-chip {
  font-family: var(--mono);
  font-size: 12px;
  padding: 5px 10px;
  border-radius: 7px;
  background: #eef1f4;
  color: var(--text-2);
}

.code-chip--hi {
  background: var(--accent);
  color: #fff;
}

/* Diagram. Box and arrow flows are the standard way analytics and tag
   platforms document how events move; this is that convention, not an
   invented visual language. */
.diagram {
  margin-top: 28px;
  padding: 26px 24px 22px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
}

.diagram__label {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-4);
  margin-bottom: 20px;
}

.diagram__svg {
  width: 100%;
  max-width: 420px;
  height: auto;
  margin-inline: auto;
}

.diagram__notes {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.diagram__notes li {
  position: relative;
  padding-left: 18px;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--text-3);
}

.diagram__notes li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 9px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}

.diagram__caption {
  margin-top: 16px;
  font-size: 13.5px;
  color: var(--text-4);
  font-style: normal;
}

/* Vertical sequence, used where the story is an order of operations rather
   than a flow of data. */
.seq {
  margin-top: 26px;
}

.seq__item {
  position: relative;
  padding: 0 0 24px 32px;
}

.seq__item::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 18px;
  bottom: -4px;
  width: 1px;
  background: var(--line-2);
}

.seq__item:last-child::before {
  display: none;
}

.seq__dot {
  position: absolute;
  left: 0;
  top: 5px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--line-2);
}

.seq__item--done .seq__dot {
  background: var(--accent);
}

/* Block, so the milestone badge drops onto its own line instead of running
   into the step title beside it. */
.seq__t {
  display: block;
  font-size: 15.5px;
  font-weight: 700;
  color: var(--text-2);
}

.seq__badge {
  margin-top: 10px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 13px;
  background: var(--accent-wash);
  border: 1px solid var(--accent-edge);
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 700;
  color: var(--accent);
}

.result-metric {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}

.result-metric__n {
  font-size: clamp(26px, 2.6vw, 34px);
  font-weight: 700;
  letter-spacing: -0.04em;
  color: var(--accent);
  line-height: 1;
  flex: none;
  font-variant-numeric: tabular-nums;
}

.result-metric__l {
  font-size: 15.5px;
  font-weight: 700;
  color: var(--text-2);
}

.result-metric__note {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-4);
}

.ba-grid {
  margin-top: 26px;
  display: grid;
  gap: 16px;
}

@media (min-width: 700px) {
  .ba-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.ba-card {
  padding: 22px 22px 20px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--white);
}

.ba-card--after {
  background: var(--accent-wash);
  border-color: var(--accent-edge);
}

.ba-card__label {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-4);
  margin-bottom: 14px;
}

.ba-card--after .ba-card__label {
  color: var(--accent);
}

.ba-card li {
  position: relative;
  padding: 7px 0 7px 16px;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--text-2);
}

.ba-card li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 15px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--text-4);
}

.ba-card--after li::before {
  background: var(--accent);
}

.method {
  margin-top: 26px;
  padding: 20px 22px;
  border: 1px dashed var(--line-2);
  border-radius: var(--r-md);
}

.method__label {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-4);
  margin-bottom: 9px;
}

.method p {
  margin-top: 0;
  font-size: 14.5px;
  color: var(--text-3);
}

.nda {
  margin-top: 34px;
  display: flex;
  gap: 12px;
  padding: 18px 20px;
  background: #eef1f4;
  border-radius: var(--r-md);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--text-3);
}

.nda svg {
  flex: none;
  margin-top: 2px;
}

.case-nav {
  margin-top: 40px;
  padding-top: 28px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  align-items: center;
  justify-content: space-between;
}

.case-nav__next {
  max-width: 46ch;
  text-align: right;
}

.case-nav__k {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-4);
  margin-bottom: 6px;
}

.case-nav__t {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
  transition: color 0.2s var(--ease);
}

.case-nav a:hover .case-nav__t {
  color: var(--accent);
}

/* ---------------------------------------------------------------------- */
/* 22. Motion                                                              */
/* ---------------------------------------------------------------------- */

/* The one-time reveal runs as an `animation`, not a `transition`. It used to
   be a transition, but `transition` is a shorthand: whichever rule wins the
   cascade for an element replaces the *entire* property list, not just the
   parts it mentions. `[data-reveal]` is declared last in this file, so on
   any card that also has its own `:hover` transition (service-card,
   case-card, ...), it silently discarded that card's transition list and
   left hover changes to background, border and transform snapping instead
   of easing. `animation` is a separate CSS property from `transition`, so
   the one-time reveal and a component's own hover transition can both be
   active on the same element without either one overwriting the other.

   Uses `translate`, not `transform`, in the keyframes: a `transform` here
   would still collide with `.card:hover { transform }`, since both would be
   animating the same visual property even if not the same CSS property
   declaration.

   The offset is small and the animation is under 0.6s: content that travels
   far or lingers reads as decoration rather than the page responding to the
   reader, which is the opposite of the quick, purposeful reveals used by
   higher-end sites. Paired with the earlier IntersectionObserver trigger in
   motion.js, the motion is usually finished, or nearly so, by the time a
   section is actually scrolled into view. */
html.js [data-reveal] {
  opacity: 0;
  translate: 0 12px;
}

html.js [data-reveal].is-visible {
  animation: reveal 0.55s var(--ease) var(--d, 0ms) both;
}

@keyframes reveal {
  from {
    opacity: 0;
    translate: 0 12px;
  }
  to {
    opacity: 1;
    translate: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  html.js [data-reveal] {
    opacity: 1;
    translate: none;
  }
  html.js [data-reveal].is-visible {
    animation: none;
  }
  .btn:hover,
  .case-card:hover,
  .service-card:hover {
    transform: none;
  }
}
