/* ==========================================================================
   VIBENCODE
   ---------------------------------------------------------------------------
   This is v4 with six things fixed, not a new site. What changed and why:

   1. ONE MARK. v4 drew an invented chevron glyph in the header and a separate
      pleated "V" in three sections, and never used the real logo. All of that
      is gone; the supplied logo is the only mark, placed as-is.

   2. A REAL TYPEFACE. v4 was Arial plus one Georgia italic. A services agency
      lives on its typography, so: Bricolage Grotesque throughout, IBM Plex
      Mono for every label, annotation and number. Two voices, no more.

   3. THE HERO IS A TRANSLATION, AND NOTHING HERE IS ILLUSTRATED.
      v4's hero art was an abstract shape wobbling behind the type. A later
      attempt replaced it with invented "deliverable sheets", which was worse:
      an agency drawing a diagram of work it has not done is drawing stock
      art. The motif is now a pair of sentences: what a client says, and what
      it becomes once engineered, and there is no artwork on the site at all.

   4. THE POSTER COMPOSES AT EVERY WIDTH. v4 pinned every hero element into a
      fixed-height box with absolute coordinates, so it only resolved at one
      or two viewport widths. It is now a grid in normal flow, and the tagline
      finally does work: WHAT IF heads the ask, HOW TO heads the answer, and
      the typeface switches with it: grotesk asks, mono answers.

   5. SIX SERVICES NOW LOOK LIKE SIX SERVICES. Each carries its own pair,
      instead of the same ghosted shape at 16% opacity.

   6. THE DELIVERY PATH LINES UP, AND SCROLL DRIVES IT. v4 rotated a rule
      13deg and offset the steps with hardcoded margins; the two were computed
      independently and never met. The path is now measured from the step
      positions, and a single scroll-progress value drives the sharpening
      brief, the inking of the numbers and the drawing of the path together.
   ========================================================================== */

/* --------------------------------------------------------------------------
   CACHE: every page links this as /assets/site.css?v=NNNNNNNN. There is no
   build step to fingerprint the file, and a plain static server sends no
   Cache-Control, so a browser will happily keep an old copy and pair it with
   new markup - which shows up as whole sections rendering as bare text.
   BUMP THE ?v= IN EVERY HTML FILE WHENEVER THIS FILE OR A SCRIPT CHANGES.
   -------------------------------------------------------------------------- */

:root {
  --lilac: #dcd2f2;
  --lilac-deep: #cabce9;
  --paper: #f4f1eb;
  --sheet: #fcfaf6;
  --ink: #24202b;
  --orange: #f4512e;
  /* A lighter vermillion, used ONLY as the section-four ground. Ink on the
     accent orange is 4.61:1 - it clears AA by 0.11, which is no margin at all
     for 11px numerals and 14.5px paragraphs. On this it is 5.44:1. The accent
     stays punchy everywhere it is used small: buttons, numerals, marks. */
  --orange-ground: #f76b4a;
  --rust: #a8321a;     /* orange on lilac is 2.4:1: this is the text-safe one */
  --purple: #5c4676;
  --line: #c9c1cf;
  --muted: #68616f;

  /* How far a hovered row's tint carries past its own text - just enough
     that the numeral is not sitting on the edge of the band. The page
     gutter is 21px on a 375px screen, so this stays well inside it and
     needs no breakpoint of its own. */
  --tint-bleed: 6px;

  --display: "Bricolage Grotesque", "Helvetica Neue", Arial, sans-serif;
  --mono: "IBM Plex Mono", "Courier New", ui-monospace, monospace;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; scrollbar-gutter: stable; }
body {
  margin: 0;
  color: var(--ink);
  background: var(--paper);
  font-family: var(--display);
  font-size: 17px;
  line-height: 1.62;
  font-variation-settings: "opsz" 14, "wdth" 100;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
button, input, textarea, select { font: inherit; }
button, a, summary { -webkit-tap-highlight-color: transparent; }
button { cursor: pointer; color: inherit; }
a { color: inherit; text-decoration: none; }
svg { display: block; max-width: 100%; }
/* The hidden attribute is display:none from the UA sheet, which any author
   display rule outranks, and both heroes set one. Without this the switch
   shows both at once. */
[hidden] { display: none !important; }
h1, h2, h3, p { margin-top: 0; }
::selection { background: var(--orange); color: var(--ink); }
:focus-visible { outline: 3px solid var(--orange); outline-offset: 5px; }
section[id] { scroll-margin-top: 24px; }

.wrap { width: min(1320px, 87%); margin-inline: auto; }
.mono {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.6;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.eyebrow { display: flex; align-items: center; gap: 13px; }
.eyebrow::before {
  content: "";
  flex: none;
  width: 15px;
  height: 15px;
  background: currentColor;
  clip-path: polygon(0 0, 50% 65%, 100% 0, 100% 35%, 50% 100%, 0 35%);
}
.muted { color: var(--muted); }
.skip {
  position: fixed;
  top: -70px;
  left: 15px;
  z-index: 100;
  background: var(--ink);
  color: white;
  padding: 15px;
}
.skip:focus { top: 15px; }

.link {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  padding: 13px 0;
  font-size: 13px;
  font-weight: 600;
  border-bottom: 1px solid currentColor;
  transition: color .2s;
}
.link:hover { color: var(--orange); }
.button {
  display: inline-flex;
  justify-content: space-between;
  align-items: center;
  gap: 32px;
  background: var(--ink);
  color: var(--paper);
  padding: 17px 22px;
  min-height: 54px;
  font-size: 13px;
  font-weight: 600;
  border: 0;
  clip-path: polygon(0 0, calc(100% - 13px) 0, 100% 13px, 100% 100%, 0 100%);
  transition: background .2s, transform .2s, color .2s;
}
.button:hover { background: var(--orange); color: var(--ink); transform: translateY(-2px); }

/* Small persistent chapter index: unchanged from v4. */
.chapter-index {
  position: fixed;
  left: 18px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 20;
  display: grid;
  gap: 15px;
}
.chapter-index a {
  width: 25px;
  height: 25px;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 9px;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid #24202b25;
}
.chapter-index a.active { background: var(--orange); border-color: var(--orange); }
/* Over the orange section an orange active chip disappears into the ground.
   Ink is the only active colour that reads on all four grounds the page uses,
   so the rail swaps to it exactly there. */
.chapter-index.on-orange a { border-color: rgba(36, 32, 43, .45); }
.chapter-index.on-orange a.active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* A sixth chip, set apart from the five numbered ones by a wider gap and a
   quieter opacity: it isn't a chapter to jump to, it's the plain-text exit
   for a crawler or a copy-pasting reader, sitting in the one rail that is
   fixed on screen for the whole page rather than hunted for in a section
   that scrolls past. Never gets .active - it has no matching chapter id for
   updateChapter() to compare against, so the scroll-spy simply ignores it. */
.chapter-index .rail-ai {
  margin-top: 11px;
  opacity: .55;
  transition: opacity .2s, transform .2s ease;
}
.chapter-index .rail-ai:hover { opacity: 1; }

/* ======================================================
   ACT 01 / The poster, rebuilt in flow
   ====================================================== */

.opening { background: var(--lilac); position: relative; overflow: hidden; }

.header {
  min-height: 88px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 25px;
  border-bottom: 1px solid #24202b35;
}
.brand { display: flex; align-items: center; gap: 11px; }
.brand img { width: 34px; height: 34px; display: block; flex: none; }
.brand span {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -.04em;
  font-variation-settings: "opsz" 40, "wdth" 96;
}
.nav { display: flex; align-items: center; gap: 30px; font-size: 13px; font-weight: 600; }
.nav a { position: relative; padding-bottom: 3px; }
.nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 100%; bottom: 0;
  height: 2px;
  background: var(--orange);
  transition: right .25s ease;
}
.nav a:hover::after { right: 0; }
.nav-contact { border-bottom: 1px solid currentColor; }

.hero-meta {
  padding-top: 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 25px;
  color: var(--purple);
}

/* THE HERO IS A TRANSLATION.
   Left column: what a client actually says in the first meeting. Right
   column: what it becomes once it has been engineered. The tagline stops
   being an abstract slogan and becomes the two column headers: WHAT IF over
   the ask, HOW TO over the answer, and the typeface switches with it:
   grotesk asks, mono answers, all the way down both columns.

   There is no illustration, because an agency that invents a diagram of work
   it has not done ends up looking like stock art. Language is the motif. */
.hero-poster {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 60px;
  row-gap: 22px;
  padding: 34px 0 0;
}
.hero-title {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 60px;
  margin: 0;
  font-weight: 400;
}
.hero-word {
  font-size: clamp(46px, 6.5vw, 102px);
  line-height: .88;
  letter-spacing: -.05em;
  font-weight: 700;
  font-variation-settings: "opsz" 96, "wdth" 92;
}
.hero-word i { font-style: normal; }
.hero-word .w-plain { display: none; }
.meta-tagline { display: none; }
.hero-word.answer {
  font-family: var(--mono);
  font-weight: 600;
  letter-spacing: -.05em;
}

/* The badge sits in the gutter, on the row where the translation happens, it marks the moment, instead of floating in the middle of the container. */
.hero-meets {
  grid-row: 2;
  grid-column: 1;
  justify-self: end;
  align-self: start;
  margin: 4px -50px 0 0;
  padding: 8px 14px;
  background: var(--ink);
  color: var(--lilac);
  font-family: var(--mono);
  font-size: clamp(12px, 1.15vw, 16px);
  font-weight: 500;
  letter-spacing: .02em;
  transform: rotate(-4.5deg);
  z-index: 2;
}
.hero-meets i { font-style: normal; }
.hero-meets .w-becomes { display: none; }

/* Quotes are drawn, not stored, so a variant can enlarge the opening one
   without the string changing. Inline by default: identical to before. */
.said::before { content: "“"; }
.said::after { content: "”"; }
.said {
  grid-row: 2;
  grid-column: 1;
  margin: 0;
  max-width: 470px;
  /* Reserves two lines. Each of the six asks sets to one or two, so the index
     row below never jumps as the pair changes. */
  min-height: 2.7em;
  font-size: clamp(23px, 3vw, 43px);
  line-height: 1.2;
  letter-spacing: -.035em;
  font-weight: 600;
}
.became {
  grid-row: 2;
  grid-column: 2;
  margin: 0;
  max-width: 470px;
  min-height: 8.6em;
  font-family: var(--mono);
  font-size: clamp(12.5px, 1.1vw, 15.5px);
  line-height: 1.78;
  letter-spacing: -.01em;
}
.became .word { display: inline-block; }

/* ===========================================================================
   HERO VARIANTS
   A hero is the whole first section: header, meta line, the pair, the
   supporting copy and the call to action, so these are section treatments,
   not swapped middle blocks. One set of markup; the variant is a data
   attribute on <body>, which is also why the fold below can respond to it.
   =========================================================================== */

/* --- BECOMES ----------------------------------------------------------------
   The second attempt at "a visitor cannot see the logic", after a first one
   that answered it with boxes and an arrow and turned the poster into a
   diff-tool widget.

   Rethought: the missing information was never DIRECTION: English already
   reads left to right. It is WHO IS SPEAKING. Once you know the left is a
   client talking and the right is us answering, the logic is self-evident.
   So this changes two things and touches nothing else.

   1. THE BADGE NAMES THE OPERATION. It already sits between the columns doing
      a connector's job; it was just saying the wrong word. "meet" is left
      over from v4, where the line was "what if MEETS how to", a verb with no
      subject once the layout became two columns. Say "becomes" and the screen
      reads as one sentence:
          "Make support smarter."  becomes  A retrieval assistant over your...
   2. A DISPLAY QUOTE MARK OVER THE ASK. One oversized glyph in the rubric and
      the left column is unmistakably someone talking: from across the room,
      with nothing to read. An editorial device rather than a UI one, which is
      the whole difference from the version this replaces.
   --------------------------------------------------------------------------- */
body[data-hero="becomes"] .hero-meets .w-meet { display: none; }
body[data-hero="becomes"] .hero-meets .w-becomes { display: inline; }
/* The word is wider than the 60px gutter, so it must overlap one column. It
   overlaps the ask, which has whitespace to spare: at -74px it sat on the
   first line of the answer. */
body[data-hero="becomes"] .hero-meets { margin-right: -32px; }

body[data-hero="becomes"] .said::before {
  display: block;
  font-size: 2.5em;
  line-height: .62;
  color: var(--orange);
  margin-bottom: 4px;
}
/* With a display mark opening the quote, a closing one at text size reads as
   an orphan. The block does the bounding instead. */
body[data-hero="becomes"] .said::after { content: none; }
body[data-hero="becomes"] .said { min-height: 3.6em; }

/* --- THE STREAM -------------------------------------------------------------
   Both lilac sections carry a field of tokens drawn from all six pairs,
   revealed only inside a soft circle around the cursor. The text is always
   there; moving the pointer is what uncovers it.

   It reads from all six services rather than from the one on screen, on
   purpose: what the cursor turns up should show the range of the practice,
   not repeat the pair already in front of you.
   --------------------------------------------------------------------------- */
.hero-fx {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--fx-top, 0px);
  bottom: var(--fx-bottom, 0px);
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

/* Text keeps its own ground. Each block paints the section colour behind
   itself and feathers it outwards, so the stream fades off as it approaches
   any real text and survives only in the gaps between blocks. A halo rather
   than a panel: at this blur there is no visible edge, just a falloff. */
.opening .hero-meta,
.opening .said,
.opening .became,
.opening .hero-index,
.opening .hero-description,
.opening .hero-cta,
.contact .eyebrow,
.contact h2,
.contact-bottom p,
.contact-actions {
  position: relative;
  z-index: 1;
  background: var(--lilac);
  box-shadow: 0 0 22px 7px var(--lilac);
}
/* Section content has to sit above the ground layer. */
.opening > .header, .opening > .wrap, .contact > .wrap { position: relative; z-index: 1; }

.fx-stream {
  position: absolute;
  inset: 0;
  padding: 0;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.95;
  letter-spacing: .01em;
  color: var(--purple);
  opacity: .38;
  word-break: break-word;
  -webkit-mask-image: radial-gradient(circle 170px at var(--mx, -400px) var(--my, -400px), #000 0, rgba(0,0,0,.55) 46%, transparent 76%);
  mask-image: radial-gradient(circle 170px at var(--mx, -400px) var(--my, -400px), #000 0, rgba(0,0,0,.55) 46%, transparent 76%);
}
.fx-stream b { font-weight: 500; color: var(--rust); opacity: .8; }
@media (prefers-reduced-motion: reduce) { .fx-stream { display: none; } }


/* Micro-interactions. Each is tied to something the reader is actually doing;
   none of them run on their own. */
.service-choice .name { transition: transform .22s ease; }
.service-choice:hover .name { transform: translateX(5px); }
.chapter-index a { transition: transform .2s ease, background .25s, border-color .25s; }
.chapter-index a:hover { transform: scale(1.18); }
.delivery-step h3 { transition: transform .5s ease; }
.delivery-step.reached h3 { transform: translateX(4px); }

/* --- NAMED ------------------------------------------------------------------
   Copy only. Nothing moves; the two words change.

   The reason A is hard to read at a glance is not visual, it is that
   "WHAT IF." / "HOW TO." is a SLOGAN being asked to do a LABEL's job. A
   slogan is evocative, a label is literal, and no amount of styling makes an
   evocative phrase behave like a literal one, which is why a badge word and
   a display quote mark were only decoration.

   To decode A a visitor must infer four things: that the columns are related,
   that the left is a client speaking, that the right is our answer, and that
   the right is a transformation of the left. "YOU SAY." over a quotation and
   "WE BUILD." over a specification states all four and leaves nothing to
   infer. The slogan is not lost, it moves up to the meta line, where a
   slogan belongs.
   --------------------------------------------------------------------------- */
body[data-hero="named"] .hero-word .w-slogan { display: none; }
body[data-hero="named"] .hero-word .w-plain { display: inline; }
body[data-hero="named"] .hero-meets { display: none; }
body[data-hero="named"] .meta-default { display: none; }
body[data-hero="named"] .meta-tagline { display: block; }

/* Lives outside both heroes, so the switch cannot take the navigation with
   it when it swaps them. */
.hero-index {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  border-top: 1px solid #24202b28;
  padding-top: 14px;
  margin-top: 22px;
}
.pair-dot {
  border: 0;
  background: transparent;
  padding: 8px 10px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .1em;
  color: var(--purple);
  border-bottom: 2px solid transparent;
  transition: color .2s, border-color .2s;
}
.pair-dot:hover { color: var(--ink); }
.pair-dot[aria-pressed="true"] { color: var(--ink); border-bottom-color: var(--orange); }
.pair-jump {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rust);
  border-bottom: 1px solid currentColor;
  padding-bottom: 3px;
}

.hero-bottom {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: 40px;
  padding: 34px 0 58px;
  border-top: 1px solid #24202b28;
  margin-top: 30px;
}
.hero-description {
  font-size: 19px;
  line-height: 1.58;
  max-width: 620px;
  margin: 0;
}
.hero-description strong { font-weight: 700; }
.hero-cta { display: flex; flex-direction: column; align-items: start; gap: 10px; }
.hero-cta .mono { color: var(--purple); font-size: 9px; }

/* A physical fold, not a generic section divider.

   Painted inside out. It used to be an ink strip with two lilac triangles cut
   into it, sitting between the two sections - which meant the chevron was a
   flat block of colour and the cursor stream stopped dead at the straight
   edge above it. Now the element lives inside .opening, paints only the ink,
   and leaves the chevron transparent: the lilac showing through is the
   section's own ground, so the stream reaches the point of the V. */
.fold-divider {
  position: relative;
  z-index: 1;
  height: clamp(33px, 4.7vw, 96px);   /* holds ~5.4deg from 1400 up */
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: transparent;
}
.fold-divider::before,
.fold-divider::after { content: ""; background: var(--ink); }
.fold-divider::before { clip-path: polygon(0 0,100% 100%,0 100%); }
.fold-divider::after { clip-path: polygon(100% 0,100% 100%,0 100%); }

/* ======================================================
   ACT 02 / Services as an editorial stage
   ====================================================== */

.services {
  background: var(--ink);
  color: var(--paper);
  padding: 58px 0 90px;
  position: relative;
  overflow: hidden;
}
.section-heading {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 45px;
  align-items: start;
  margin-bottom: 52px;
}
.section-heading > * { min-width: 0; }
h2 {
  font-size: clamp(40px, 5.2vw, 72px);
  line-height: 1.02;
  letter-spacing: -.045em;
  font-weight: 700;
  font-variation-settings: "opsz" 72, "wdth" 94;
  margin-bottom: 20px;
}
.section-copy { max-width: 470px; font-size: 16px; line-height: 1.75; margin: 0; }
.services .eyebrow { color: var(--lilac); }
.services .section-copy { color: #c2b8cb; }

.service-stage {
  display: grid;
  grid-template-columns: .85fr 1.45fr;
  gap: 60px;
  border-top: 1px solid #ffffff35;
  padding-top: 25px;
}
.service-menu { display: flex; flex-direction: column; }
.service-choice {
  display: grid;
  grid-template-columns: 27px 1fr 20px;
  align-items: center;
  gap: 12px;
  text-align: left;
  padding: 19px 0;
  border: 0;
  border-bottom: 1px solid #ffffff25;
  color: #c1b7cb;
  background: none;
  transition: color .2s;
}
.service-choice .name {
  font-size: clamp(17px, 1.6vw, 22px);
  font-weight: 600;
  letter-spacing: -.03em;
}
.service-choice .arrow { opacity: 0; transform: translateX(-8px); transition: opacity .2s, transform .2s; }
/* Section 2's one piece of scroll motion: the six rows arrive in sequence as
   the stage enters. The class is added by JavaScript, so without it every row
   is simply present. */
.service-menu.staged .service-choice {
  opacity: .2;
  transform: translateY(12px);
  transition: opacity .55s ease, transform .55s ease, color .2s;
}
.service-menu.staged .service-choice.in-view { opacity: 1; transform: none; }
.service-menu.staged .service-choice:nth-child(2) { transition-delay: .07s; }
.service-menu.staged .service-choice:nth-child(3) { transition-delay: .14s; }
.service-menu.staged .service-choice:nth-child(4) { transition-delay: .21s; }
.service-menu.staged .service-choice:nth-child(5) { transition-delay: .28s; }
.service-menu.staged .service-choice:nth-child(6) { transition-delay: .35s; }
.service-choice:hover { color: white; }
.service-choice[aria-pressed="true"] { color: var(--lilac); }
.service-choice[aria-pressed="true"] .arrow { opacity: 1; transform: translateX(0); }
.service-choice[aria-pressed="true"] .mono { color: var(--orange); }

.service-feature { min-width: 0; min-height: 500px; padding-top: 15px; }
.feature-code { color: #b9a4d4; margin-bottom: 24px; }
.service-feature h3 {
  font-size: clamp(32px, 3.8vw, 52px);
  font-weight: 700;
  letter-spacing: -.045em;
  line-height: 1.06;
  font-variation-settings: "opsz" 60, "wdth" 95;
  max-width: 580px;
  margin-bottom: 26px;
}
.feature-copy {
  color: #d0c8d6;
  font-size: 16px;
  line-height: 1.78;
  min-height: 84px;
  max-width: 620px;
  margin: 0;
}

/* The hero device again, held still. */
.feature-translation {
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1fr);
  gap: 34px;
  margin: 30px 0 6px;
  padding: 24px 0 26px;
  border-top: 1px solid #ffffff28;
  border-bottom: 1px solid #ffffff28;
}
.ft-label { color: var(--orange); margin-bottom: 12px; }
.ft-said {
  margin: 0;
  font-size: clamp(19px, 1.75vw, 25px);
  line-height: 1.3;
  letter-spacing: -.03em;
  font-weight: 600;
  color: var(--lilac);
  min-height: 2.6em;
}
.ft-became {
  margin: 0;
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.8;
  letter-spacing: -.01em;
  color: #cfc7dc;
  min-height: 7.2em;
}
.output-label { margin-top: 25px; color: #b9a4d4; }
.output-list {
  list-style: none;
  padding: 0;
  margin: 12px 0 22px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.output-list li {
  padding: 8px 11px;
  border-bottom: 1px solid #ffffff45;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .03em;
}
.service-feature .link { color: var(--lilac); }
.service-feature .link:hover { color: var(--orange); }

.feature-enter { animation: feature-in .35s ease both; }
@keyframes feature-in {
  from { opacity: .25; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

/* A sector band on the seam between the dark services block and the pale
   practice one. It moves with the scroll rather than on a timer, so it is the
   reader driving it and it holds still when they do. */
.sectors {
  display: flex;
  align-items: center;
  gap: 26px;
  padding: 0 0 0 6%;
  height: 62px;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}
.sectors-label { color: var(--rust); flex: none; }
.sectors-window { flex: 1; overflow: hidden; min-width: 0; }
.sectors-track {
  display: flex;
  width: max-content;
  color: var(--muted);
  will-change: transform;
}
.sectors-track span { flex: none; padding-right: 34px; }
.sectors-track span::after { content: "◇"; padding-left: 34px; opacity: .5; }

/* ======================================================
   ACT 03 / The path through an engagement
   ====================================================== */

/* overflow:clip, not hidden: hidden would make this a scroll container and
   silently kill the sticky brief below. clip crops without doing that. */
.practice { position: relative; padding: 85px 0 90px; overflow: clip; }
.practice .section-copy { color: var(--muted); }
.practice .section-heading { margin-bottom: 46px; }
.practice .eyebrow { color: var(--purple); }

/* THE SCROLL TRANSFORMATION.
   One sentence, sharpening as you scroll the four steps. It is the hero
   device again, stretched over a scroll: the vague ask arrives first, and
   each stage of the engagement adds the precision that stage is responsible
   for. Nothing moves except opacity, the transformation is the language. */
/* Pinned. The earlier version read as broken because steps slid under a
   pinned block with no edge to slide under, so it now carries an opaque
   ground and a rule, and the path below is padded to clear it. */
.brief {
  position: sticky;
  top: 20px;
  z-index: 3;
  background: var(--paper);
  padding: 6px 0 20px;
  border-bottom: 1px solid rgba(36, 32, 43, .11);
  box-shadow: 0 12px 16px -14px rgba(36, 32, 43, .09);
}
.brief-label {
  color: var(--purple);
  margin-bottom: 15px;
  max-width: 1010px;
  display: flex;
  justify-content: space-between;
  gap: 20px;
}
.brief-count { color: var(--rust); }
.brief-line {
  margin: 0;
  max-width: 1010px;
  font-size: clamp(18px, 1.7vw, 24px);
  line-height: 1.44;
  letter-spacing: -.025em;
  font-weight: 600;
}
.brief-part {
  font-family: var(--mono);
  font-size: .76em;
  font-weight: 400;
  letter-spacing: -.01em;
}
/* Without JavaScript every clause is simply present and readable. */
.brief.staged .brief-part { opacity: .14; transition: opacity .55s ease; }
.brief.staged .brief-part.on { opacity: 1; }

/* v4 rotated a rule 13deg and hoped it met the steps. This draws the path from
   the step markers themselves, so it always connects them. */
.delivery-path {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 30px;
  padding: 34px 0 60px;
}
.delivery-line {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}
.delivery-line svg { width: 100%; height: 100%; }
.delivery-step {
  position: relative;
  z-index: 1;
  align-self: start;
  min-width: 0;
}
.delivery-step:nth-of-type(2) { margin-top: 74px; }
.delivery-step:nth-of-type(3) { margin-top: 148px; }
.delivery-step:nth-of-type(4) { margin-top: 222px; }

.step-number {
  display: inline-block;
  font-size: 92px;
  font-weight: 700;
  letter-spacing: -.06em;
  line-height: 1;
  font-variation-settings: "opsz" 96, "wdth" 88;
  color: transparent;
  -webkit-text-stroke: 1.4px var(--purple);
  background: var(--paper);
  transition: color .6s, -webkit-text-stroke-color .6s;
  margin-bottom: 18px;
}
/* Filled by scroll position, not by a one-shot entrance: the numbers ink in
   as the brief reaches the stage they are responsible for. */
.delivery-step.reached .step-number { color: var(--orange); -webkit-text-stroke-color: var(--orange); }
.delivery-step h3 {
  font-size: 25px;
  font-weight: 700;
  letter-spacing: -.035em;
  margin-bottom: 12px;
}
.delivery-step p {
  font-size: 14.5px;
  line-height: 1.75;
  color: var(--muted);
  max-width: 250px;
  margin-bottom: 0;
  background: var(--paper);
}
.practice-note {
  border-top: 1px solid var(--line);
  padding-top: 22px;
  display: flex;
  justify-content: space-between;
  gap: 25px;
  color: var(--muted);
}

/* ======================================================
   ACT 04 / The agency relationship
   ====================================================== */

.engagements {
  background: var(--orange-ground);
  padding: 72px 0;
  position: relative;
  overflow: hidden;
}
.engagements .eyebrow { color: var(--ink); }
.engagements .section-copy { max-width: 450px; }
.engagement-line {
  display: grid;
  grid-template-columns: 50px 1.1fr 1fr 52px;
  align-items: center;
  gap: 30px;
  padding: 30px 0;
  border-top: 1px solid #24202b70;
}
.engagement-line:last-of-type { border-bottom: 1px solid #24202b70; }
.engagement-line h3 {
  font-size: clamp(30px, 3.9vw, 54px);
  line-height: 1.02;
  font-weight: 700;
  letter-spacing: -.05em;
  font-variation-settings: "opsz" 60, "wdth" 94;
  margin: 0;
}
.engagement-line p { font-size: 14.5px; line-height: 1.72; margin: 0; max-width: 435px; }
.engagement-line .engagement-arrow {
  display: grid;
  place-items: center;
  justify-self: end;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 1.5px solid rgba(36, 32, 43, .55);
  font-size: 19px;
  line-height: 1;
  color: var(--ink);
  transition: background .25s, color .25s, border-color .25s, transform .25s;
}
.engagement-line:hover .engagement-arrow,
.engagement-line:focus-visible .engagement-arrow {
  background: var(--ink);
  border-color: var(--ink);
  /* Paper, not orange. An orange glyph on ink is 4.61:1 - the same thin ratio
     as everything else in this section - and an active control is the last
     place to spend the margin on palette tidiness. Paper on ink is 14.15:1. */
  color: var(--paper);
  transform: translate(3px, -3px);
}
.engagement-line:hover h3 { text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: 7px; }

/* ======================================================
   ACT 05 / The opening question returns
   ====================================================== */

.contact {
  position: relative;
  background: var(--lilac);
  overflow: hidden;
  padding: 70px 0 45px;
}
.contact-content { position: relative; z-index: 1; }
.contact h2 {
  font-size: clamp(58px, 8.4vw, 118px);
  line-height: .96;
  max-width: 950px;
  margin: 42px 0 34px;
}
.contact-bottom {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 35px;
  margin-bottom: 62px;
}
.contact-bottom p {
  max-width: 420px;
  font-size: 17px;
  line-height: 1.68;
  margin-bottom: 0;
}
.contact-actions { display: flex; flex-direction: column; align-items: start; gap: 12px; }
.contact-actions .mono { color: var(--purple); font-size: 9px; }

.footer {
  position: relative;
  border-top: 1px solid #24202b45;
  padding-top: 23px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 25px;
  flex-wrap: wrap;
}
.footer .mono { font-size: 9px; color: var(--purple); }

/* Progressive entrances: no content is hidden by default. */
.reveal.in-view { animation: rise .65s ease both; }
@keyframes rise {
  from { opacity: .4; transform: translateY(22px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (max-width: 1160px) {
  .chapter-index { display: none; }
  .service-stage { gap: 38px; }
  .hero-poster { column-gap: 44px; }
  .hero-title { column-gap: 44px; }
  .hero-meets { margin-right: -38px; }
}
@media (max-width: 920px) {
  .wrap { width: 89%; }
  .nav { gap: 20px; font-size: 12px; }

  /* One column, and the stack has to interleave: WHAT IF over the ask, then
     the badge, then HOW TO over the answer. The two words live inside the h1,
     so the h1 gives up its own box (display:contents) and lets its children
     take part in the poster grid directly: otherwise both headings would
     stack at the top and HOW TO would end up labelling the ask. */
  .hero-poster { grid-template-columns: 1fr; row-gap: 14px; padding-top: 22px; }
  .hero-title { display: contents; }
  .hero-word { font-size: clamp(52px, 12.6vw, 96px); order: 1; }
  .said { order: 2; }
  .hero-meets { order: 3; }
  .hero-word.answer { order: 4; margin-top: 10px; }
  .became { order: 5; }
  .said, .became, .hero-meets {
    grid-row: auto;
    grid-column: 1;
    justify-self: start;
    max-width: 620px;
  }
  .said { min-height: 0; margin-top: 8px; }
  .became { min-height: 0; }
  .hero-meets { margin: 4px 0 2px; }
  /* Stacked, the offset would read as a mistake, so the planes go full width
     and the break becomes vertical: the flow still runs off the first plane. */
  .hero-planes { height: auto; display: grid; gap: 0; margin-top: 22px; }
  .plane { position: static; width: auto; height: auto; min-height: 210px; }
  .plane-a { clip-path: polygon(0 0, 100% 0, 100% 92%, 0 100%); }
  .plane-b {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
    margin: 26px 0 0 auto;
    width: 92%;
    filter: drop-shadow(-10px -10px 20px rgba(36, 32, 43, .26));
  }
  .plane-word { font-size: clamp(40px, 11vw, 72px); }
  .flow { right: -26%; bottom: 34%; width: 62%; }
  .plane-seam { left: auto; right: 8%; top: auto; bottom: 0; transform: translateY(40%); }
  .def-senses li { grid-template-columns: 26px minmax(0, 1fr); gap: 12px; }
  .decoder-row { grid-template-columns: minmax(0, 1fr) 24px minmax(0, 1fr); gap: 12px; }
  .objection-said { max-width: none; }
  .objection-answer { grid-template-columns: 1fr; gap: 14px; }
  .intake-out { grid-template-columns: 1fr; gap: 22px; min-height: 0; }
  .intake-grid { grid-template-columns: 1fr; gap: 22px; }
  .intake-actions { gap: 18px; }
  .t-line { grid-template-columns: 58px minmax(0, 1fr); gap: 14px; }
  .board-row { grid-template-columns: 1fr; gap: 12px; padding: 20px 0; }
  .hero-wall { grid-template-columns: 1fr; gap: 26px; }
  .wall-asks { max-height: 250px; }
  .wall-answer { position: static; }
  .statement { max-width: none; }
  .statement-notes { position: static; width: auto; grid-auto-flow: column; margin-top: 26px; }
  .ledger th:nth-child(3), .ledger td:nth-child(3) { display: none; }
  .ledger th, .ledger td { width: auto; }
  /* Split stacks: two 195px columns on a phone squeezed WHAT IF. to nothing.
     The two grounds become two bands instead, and the ink one is painted on
     the answer items themselves so it stays glued to its content. */
  body[data-hero="becomes"] .hero-meets { margin-right: 0; }
  body[data-hero="becomes"] .said { min-height: 0; }

  .feature-translation { grid-template-columns: 1fr; gap: 24px; }
  .ft-said, .ft-became { min-height: 0; }
  .brief { position: static; }
  .hero-bottom { grid-template-columns: 1fr; align-items: start; gap: 25px; }
  .hero-description { font-size: 17px; }
  .hero-cta { flex-direction: row; align-items: center; gap: 25px; flex-wrap: wrap; }

  .section-heading { grid-template-columns: 1fr; gap: 25px; margin-bottom: 35px; }
  .section-copy { max-width: 570px; }
  .service-stage { grid-template-columns: 1fr; gap: 30px; }
  .service-menu { display: grid; grid-template-columns: 1fr 1fr; column-gap: 25px; }
  .service-feature { min-height: auto; }
  .feature-copy { min-height: auto; }
  .services { padding-bottom: 60px; }

  .delivery-path { gap: 22px; }
  .step-number { font-size: 74px; }
  .delivery-step h3 { font-size: 22px; }
  .engagement-line { gap: 20px; grid-template-columns: 30px 1fr 1fr 52px; }
  .contact-bottom { align-items: start; flex-direction: column; gap: 25px; }
}
@media (max-width: 580px) {
  .header { min-height: 74px; }
  .brand span { font-size: 23px; }
  .brand img { width: 29px; height: 29px; }
  .nav { gap: 17px; font-size: 11px; }
  .nav a:nth-child(2) { display: none; }
  .hero-meta { padding-top: 21px; }
  /* There was a `.hero-meta > span:last-child { display: none }` here, to keep
     the row to two items on a narrow screen. It was written while hero A was
     the default, where the last span is .meta-tagline and is already hidden -
     so it did nothing, and looked harmless. Under hero "named" the last span
     is the one that SHOULD show, and this rule (equal specificity, later in
     the file) beat it: the tagline vanished below 580px and the hero was left
     with a single line. The row is already two items in every variant, because
     .meta-default and .meta-tagline are mutually exclusive by definition, so
     nothing needs to be dropped here. */
  .hero-meta .mono { font-size: 9px; }
  .hero-word { font-size: 15.4vw; }
  .hero-meets { font-size: 15px; padding: 7px 12px; }
  .hero-bottom { padding-bottom: 40px; }
  .hero-cta { flex-direction: column; align-items: start; gap: 12px; }

  h2 { font-size: 40px; }
  .services { padding-top: 40px; }
  .service-menu { grid-template-columns: 1fr; }
  .service-choice { padding: 16px 0; }
  .service-choice .name { font-size: 20px; }
  .service-feature h3 { font-size: 34px; }
  .feature-copy { font-size: 15px; }

  .decoder-row { grid-template-columns: 1fr; gap: 4px; }
  .decoder-arrow { display: none; }
  .practice { padding: 55px 0 45px; }
  .delivery-path { display: block; padding: 10px 0 20px; }
  .delivery-line { display: none; }
  .delivery-step,
  .delivery-step:nth-of-type(n) {
    margin-top: 0;
    display: grid;
    grid-template-columns: 68px 1fr;
    column-gap: 18px;
    padding: 24px 0;
    border-top: 1px solid var(--line);
  }
  .step-number { font-size: 58px; grid-row: span 2; margin: 0; }
  .delivery-step h3 { margin-top: 4px; }
  .delivery-step p { max-width: none; font-size: 14px; }
  .practice-note { flex-direction: column; gap: 12px; }

  .engagements { padding: 50px 0; }
  .engagement-line { grid-template-columns: 25px 1fr 46px; gap: 13px; padding: 25px 0; }
  .engagement-line h3 { font-size: 33px; }
  .engagement-line p { grid-column: 2; grid-row: 2; font-size: 14px; }
  .engagement-arrow { grid-column: 3; grid-row: 1; }

  .contact { padding-top: 50px; }
  .contact h2 { font-size: clamp(52px,13vw,76px); margin-top: 35px; }
  .contact-bottom p { font-size: 16px; max-width: 320px; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* ==========================================================================
   INNER PAGES
   The home page is a five-act poster. Inner pages are documents: one lilac
   head carrying the same cursor stream, then paper, then the ink footer. The
   grounds and the two voices do the identifying, so nothing new is invented
   here - these are the same parts, arranged for reading rather than for
   arrival.
   ========================================================================== */

.page-head {
  position: relative;
  overflow: hidden;
  background: var(--lilac);
}
.page-head-body { padding: 54px 0 76px; }
.page-head .eyebrow { color: var(--purple); margin-bottom: 26px; }
.page-title {
  margin: 0;
  max-width: 15ch;
  font-size: clamp(44px, 6.4vw, 104px);
  line-height: .96;
  letter-spacing: -.05em;
  font-weight: 700;
  font-variation-settings: "opsz" 96, "wdth" 92;
}
.page-lede {
  margin: 30px 0 0;
  max-width: 620px;
  font-size: clamp(17px, 1.55vw, 21px);
  line-height: 1.62;
}
/* Same halo as the home page: the stream falls off as it nears real text. */
.page-head .eyebrow,
.page-title,
.page-lede,
.page-head-meta {
  position: relative;
  z-index: 1;
  background: var(--lilac);
  box-shadow: 0 0 22px 7px var(--lilac);
}
/* The job board is the one thing most visitors came for, and on the careers
   page it used to be five sections down. It sits in the head as well now, and
   as the same button rather than a quieter version - a hero action styled to
   look provisional reads as the less real of the two. No halo on it: unlike
   the text around it, a filled button already occludes the token stream. */
.page-head-actions { position: relative; z-index: 1; margin-top: 36px; }
.page-head-actions + .page-head-meta { margin-top: 28px; }

.page-head-meta {
  display: flex;
  gap: 30px;
  flex-wrap: wrap;
  margin-top: 34px;
  color: var(--purple);
}

/* --- a paper section on an inner page ------------------------------------ */
.page-section { padding: 78px 0; }
.page-section + .page-section { padding-top: 0; }
/* An adjacent-sibling selector does not care that an element is display:none,
   so a hidden section still strips the top padding from whatever follows it -
   which jammed the enquiry form flush against the lilac head the moment the
   (hidden) booking section was added above it. */
.page-section[hidden] + .page-section { padding-top: 78px; }
.page-section h2 {
  font-size: clamp(32px, 3.9vw, 56px);
  margin-bottom: 26px;
}
.page-section .lede { max-width: 680px; margin-bottom: 32px; font-size: 17px; line-height: 1.72; color: var(--muted); }
/* In a split section the column is its own measure; capping it again left the
   paragraph 180px short of the list under it. */
.page-section.split .lede { max-width: none; }
.rule-head {
  display: flex;
  align-items: baseline;
  gap: 20px;
  padding-bottom: 16px;
  margin-bottom: 34px;
  border-bottom: 1px solid var(--line);
  color: var(--purple);
}
.rule-head span + span { margin-left: auto; }

/* --- the six, as a list -------------------------------------------------- */
.service-rows { border-top: 1px solid var(--line); }
.service-row {
  display: grid;
  grid-template-columns: 62px minmax(0, .9fr) minmax(0, 1.25fr) 52px;
  gap: 34px;
  align-items: start;
  padding: 38px 0;
  border-bottom: 1px solid var(--line);
  transition: background .25s, box-shadow .25s;
}
/* --- row tints run past the text -----------------------------------------
   A hovered row used to be tinted exactly to the width of its own content, so
   the numeral sat hard on the left edge of the band and the whole thing read
   as cropped. The tint now carries 20px past the text on both sides.

   It is done with two offset box-shadows rather than negative margins or a
   pseudo-element: these rows are grids, so an extra ::before would become a
   grid item and take a column, and negative margins would drag the hairline
   borders out with them. A shadow is the same box painted behind, twice,
   shifted - so only the fill extends and the rules stay where they are.

   The distance is a shared variable, so the three rows cannot drift apart.
   -------------------------------------------------------------------------- */
.service-row:hover {
  background: rgba(36, 32, 43, .035);
  box-shadow: calc(var(--tint-bleed) * -1) 0 0 rgba(36, 32, 43, .035),
              var(--tint-bleed) 0 0 rgba(36, 32, 43, .035);
}
.service-row .n { font-family: var(--mono); font-size: 11px; letter-spacing: .1em; color: var(--rust); padding-top: .5em; }
.service-row h3 {
  margin: 0;
  font-size: clamp(24px, 2.5vw, 36px);
  letter-spacing: -.04em;
  line-height: 1.06;
}
/* Deliberately NOT .said / .became: those carry the home page's grid
   placement and quote pseudo-elements, which doubled the quotation marks
   here and would have fought the row layout. */
.service-row .row-ask {
  display: block;
  margin-top: 14px;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -.02em;
  line-height: 1.32;
  color: var(--purple);
}
.service-row .row-built {
  display: block;
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.78;
  color: var(--muted);
}
.service-row .go {
  display: grid;
  place-items: center;
  justify-self: end;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1.5px solid rgba(36, 32, 43, .3);
  font-size: 18px;
  transition: background .25s, color .25s, border-color .25s, transform .25s;
}
.service-row:hover .go {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
  transform: translate(3px, -3px);
}

/* --- the pair, stated on a service page ---------------------------------- */
.pair-block {
  display: grid;
  grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
  gap: 46px;
  padding: 40px 0 44px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.pair-block .label { color: var(--purple); margin-bottom: 14px; }
.pair-block .ask {
  margin: 0;
  font-size: clamp(22px, 2.5vw, 34px);
  font-weight: 600;
  letter-spacing: -.03em;
  line-height: 1.24;
}
.pair-block .built {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(13px, 1.15vw, 16px);
  line-height: 1.8;
  color: var(--muted);
}

/* --- a link that does not become a button --------------------------------
   The pull quote is the only place an inner page raises its voice, so the
   way out of it is a line of text with a rule that finishes drawing when you
   point at it - not a button. Rust rather than orange: orange on lilac is
   2.4:1 and unreadable at this size. */
.quiet-link {
  position: relative;
  white-space: nowrap;
  color: var(--rust);
  transition: color .25s ease;
}
.quiet-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 1px;
  background: currentColor;
  transform: scaleX(.28);
  transform-origin: 0 50%;
  transition: transform .4s cubic-bezier(.19,.72,.26,1);
}
.quiet-link:hover::after,
.quiet-link:focus-visible::after { transform: scaleX(1); }
.quiet-link span { display: inline-block; transition: transform .3s ease; }
.quiet-link:hover span { transform: translateX(4px); }

/* --- deliverables -------------------------------------------------------- */
.deliverables { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.deliverables li {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  gap: 20px;
  align-items: baseline;
  padding: 22px 0;
  border-bottom: 1px solid var(--line);
  font-size: 19px;
  letter-spacing: -.02em;
}
.deliverables li:first-child { border-top: 1px solid var(--line); }
.deliverables .n { font-family: var(--mono); font-size: 11px; letter-spacing: .1em; color: var(--rust); }

/* --- next / related ------------------------------------------------------ */
.next-rows { border-top: 1px solid var(--line); }
/* Set smaller than it was: five links to sibling pages were running at very
   nearly the size of the page's own title, and taking a screen and a half to
   do it. This is a list of where else to go, not a second headline. */
.next-rows a {
  display: flex;
  align-items: baseline;
  gap: 18px;
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
  font-size: clamp(18px, 1.6vw, 22px);
  letter-spacing: -.035em;
  font-weight: 700;
  transition: color .2s, padding-left .25s;
}
.next-rows a:hover { color: var(--rust); padding-left: 8px; }
.next-rows .n {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .1em;
  color: var(--purple);
  transition: color .25s ease;
}
.next-rows a:hover .n { color: var(--orange); }
.next-rows .go {
  margin-left: auto;
  font-size: 19px;
  transition: transform .3s cubic-bezier(.19,.72,.26,1);
}
.next-rows a:hover .go { transform: translate(4px, -4px); }

/* --- closing call ------------------------------------------------------- */
.page-cta { background: var(--lilac); padding: 74px 0; }
.page-cta h2 { font-size: clamp(38px, 5.4vw, 76px); max-width: 16ch; margin-bottom: 28px; }
.page-cta .row { display: flex; align-items: center; gap: 30px; flex-wrap: wrap; }
.page-cta .mono { color: var(--purple); }
/* What the two days actually are, under the action rather than inside it -
   a button that has to explain itself is not a button. */
.page-cta .cta-note { margin: 24px 0 0; max-width: 72ch; color: var(--purple); }
/* The quiet link's rule sits at 28% until hovered, which reads as an
   affordance under a pull quote and as a broken underline beside a button.
   In the closing call it starts whole and only warms. */
.page-cta .quiet-link::after { transform: scaleX(1); opacity: .4; transition: opacity .3s ease; }
.page-cta .quiet-link:hover::after,
.page-cta .quiet-link:focus-visible::after { opacity: 1; }
/* Offered where the friction appears: directly under the four phases, which
   is the moment a reader finds out a full engagement runs for weeks. */
.sprint-note { margin: 34px 0 0; }

/* --- the booker, while it is still coming ---------------------------------
   620px of reserved space with nothing in it reads as a broken section, so
   it says what it is doing. The bar is indeterminate on purpose: we cannot
   know how far along a third-party iframe is, and a progress bar that
   invents a percentage is a small lie.

   This is the one animation on the site that runs without being asked for,
   and it earns it by reporting something true - it is only ever on screen
   while something really is loading, and it removes itself. */
.cal-frame { position: relative; min-height: 620px; }
.cal-loading {
  position: absolute;
  inset: 0;
  margin: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 18px;
  color: var(--purple);
  text-align: center;
}
.cal-loading .link { margin-top: 4px; }
.cal-bar {
  position: relative;
  display: block;
  width: 168px;
  height: 2px;
  overflow: hidden;
  background: var(--line);
}
.cal-bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--orange);
  transform: scaleX(.45);
  transform-origin: 0 50%;
  animation: cal-sweep 1.25s cubic-bezier(.45, 0, .2, 1) infinite;
}
@keyframes cal-sweep {
  0%   { transform: translateX(-110%) scaleX(.4); }
  50%  { transform: translateX(0) scaleX(.75); }
  100% { transform: translateX(110%) scaleX(.4); }
}

@media (max-width: 620px) {
  .cal-frame { min-height: 560px; }
}

/* --- the booker, and a second button that knows it is second ------------
   The calendar is inline only on the enquiry page, where it is the point of
   the page; everywhere else the sprint button opens it over the page the
   reader was already being persuaded by. Min-height reserves the space so
   the section does not jump when Cal finishes loading. */
#cal-inline {
  margin-top: 6px;
  border-top: 1px solid var(--line);
  padding-top: 26px;
}
/* Two buttons in a row need one of them to be the quieter one, or the reader
   has to read both to find out which is which. */
/* .button carries a clip-path that notches its top-right corner, and an
   inset box-shadow is clipped by it - so an outlined variant came out with
   its border sliced off along the diagonal. A quieter fill keeps the notch
   intact, because a fill is exactly what clip-path is good at. */
/* An 11% ink wash on lilac read as a disabled button rather than a second
   one. Paper is the site's other ground, so on lilac it reads as a real
   raised element - and it is a fill, which is what the notch needs. */
.button-quiet { background: var(--paper); color: var(--ink); }
.button-quiet:hover { background: var(--ink); color: var(--paper); }

@media (max-width: 620px) {
}

/* --- devices without a pointer -------------------------------------------
   Two things go wrong on a touch screen and neither is visible on a desktop.

   A tapped element keeps :hover applied until something else is tapped, so
   every one of these decorations stays switched on behind the reader - a row
   they touched on the way past stays lit for the rest of the session. All of
   them are decoration, so on a device without a pointer they simply do not
   happen. Colour changes are left alone: those read as "you were just here",
   which is fair.

   And the inline text links measure 15 to 28px tall, well under the 44px a
   finger needs. A transparent ::before enlarges what is pressable without
   moving anything, since ::after is already carrying the underline.
   -------------------------------------------------------------------------- */
@media (hover: none) {
  .service-row:hover,
  .case-row:hover,
  .failures > div:hover { background: transparent; box-shadow: none; }
  .stages > *:hover { background: transparent; }
  a.client-mark:hover { background: transparent; }
  .people article:hover { transform: none; border-color: var(--line); }
  .next-rows a:hover,
  .faq summary:hover { padding-left: 0; }
  .service-row:hover .go,
  .case-row:hover .go { background: transparent; color: inherit; border-color: rgba(36, 32, 43, .3); transform: none; }

  .quiet-link::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
  }
}

/* --- the home page on a phone ---------------------------------------------
   No fold here: home does not load pages.js, and its own script belongs to
   the hero. Everything below is either a duplicate the phone cannot afford or
   a layout that only made sense with a second column to put things in.

   THE ECHO. .feature-translation repeats the hero's pair word for word - the
   same "Make support smarter." and the same thirty-four word answer under it.
   On a wide screen that is the motif restated in a place you never see at the
   same time as the hero. On a phone it is the identical sentence twice inside
   two screens of scroll, which is not a motif, it is a repeat. The block is
   the single largest thing on the section and it says nothing new.

   THE APHORISM. .practice-note is two mono lines that restate the section
   heading in different words. Ornament, and ornament is what a phone drops
   first.

   THE MENU. Six choices stacked one per row is half a screen of chrome before
   the reader reaches what the choices are for. Two columns halve it, and the
   hover arrow goes with them - there is no hover on a phone, and it was only
   ever drawn on hover anyway. */
@media (max-width: 920px) {
  .feature-translation,
  .practice-note { display: none; }

  .service-menu {
    display: grid;
    /* minmax(0, …) or the longest service name floors its column and the
       two come out 175 and 155 rather than equal. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 18px;
  }
  .service-choice {
    grid-template-columns: 22px 1fr;
    gap: 9px;
    padding: 15px 0;
  }
  .service-choice .arrow { display: none; }
}

/* --- the phone fold -------------------------------------------------------
   pages.js marks the overflow items and inserts the control; which viewport
   is a phone is decided here and only here. That way a rotation is handled by
   the browser as it happens, and there is no listener that can miss an event
   and leave half a section hidden on a desktop.

   So on a wide screen the marks are inert and the control is not rendered at
   all - the page is exactly what it was.

   The clamp is five lines: enough for the opening paragraph to say what its
   section is about, which is all a reader skimming on a phone asks of it.

   The control is a real button rather than a link dressed as one, because it
   changes what is on the page rather than going anywhere, and it carries
   aria-expanded so a screen reader is told which state it is in. */
.fold-more { display: none; }

@media (max-width: 920px) {
  /* Scoped to the closed section rather than to the item, so an open section
     carries no display rule at all and each element goes back to whatever its
     own stylesheet said. Overriding it here instead - with revert, or with a
     guessed block - would have broken .next-rows a and .ships li, which set
     their own. The !important is because those same rules out-specify a lone
     utility class. */
  section:not(.fold-open) .fold-extra { display: none !important; }

  .fold-clamp {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 5;
    line-clamp: 5;
    overflow: hidden;
  }
  .fold-open .fold-clamp { display: block; overflow: visible; }

  .fold-more {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    margin-top: 22px;
    padding: 11px 0;
    border: 0;
    border-bottom: 1px solid var(--line);
    background: none;
    font: inherit;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--purple);
    cursor: pointer;
  }
  .fold-more:hover,
  .fold-more:focus-visible { color: var(--rust); border-bottom-color: currentColor; }
}

/* --- the footer every page shares --------------------------------------- */
.site-footer { background: var(--ink); color: var(--paper); padding: 64px 0 40px; }
.footer-top {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) repeat(3, minmax(0, 1fr));
  gap: 40px;
  padding-bottom: 44px;
  border-bottom: 1px solid var(--on-ink-line);
}
.footer-brand .brand span { color: var(--paper); }
.footer-brand p {
  margin: 20px 0 0;
  max-width: 320px;
  font-size: 14.5px;
  line-height: 1.72;
  color: var(--on-ink-muted);
}
.footer-col h4 {
  margin: 0 0 16px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--orange);
}
.footer-col a, .footer-col p {
  display: block;
  margin: 0 0 10px;
  font-size: 14px;
  line-height: 1.6;
  color: #cfc7dc;
  transition: color .2s;
}
.footer-col a:hover { color: var(--paper); }
/* The only link on the site that leaves it. Nothing marks it as leaving but
   the arrow - the same mark the header's contact link carries - which is held
   quieter than the word and lifts on hover, as every other arrow here does. */
.footer-col a.out { white-space: nowrap; }
.footer-col a.out span {
  display: inline-block;
  margin-left: 5px;
  font-size: 12px;
  opacity: .55;
  transition: transform .25s cubic-bezier(.19,.72,.26,1), opacity .25s;
}
.footer-col a.out:hover span { opacity: 1; transform: translate(2px, -2px); }
.footer-bottom {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  padding-top: 26px;
  color: var(--on-ink-muted);
}

@media (max-width: 920px) {
  .page-head-body { padding: 40px 0 56px; }
  .page-section { padding: 56px 0; }
  .page-section[hidden] + .page-section { padding-top: 56px; }
  .service-row { grid-template-columns: 44px minmax(0, 1fr) 44px; gap: 18px; }
  .service-row > div:nth-of-type(2) { grid-column: 2 / -1; }
  .pair-block { grid-template-columns: 1fr; gap: 26px; }
  .footer-top { grid-template-columns: 1fr 1fr; gap: 32px; }


  /* .mono is the site's label size and 11px is under what a phone should be
     asked to read. One step up, phones only. */
  .mono { font-size: 12px; }
}
@media (max-width: 580px) {
  .page-title { max-width: none; }
  .service-row { grid-template-columns: 36px minmax(0, 1fr); }
  .service-row .go { display: none; }
  .footer-top { grid-template-columns: 1fr 1fr; gap: 30px 22px; }
  .footer-brand,
  .footer-col:last-child { grid-column: 1 / -1; }
  .rule-head { flex-wrap: wrap; gap: 8px; }
  .rule-head span + span { margin-left: 0; }
}

.not-this {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .62fr);
  gap: 44px;
  align-items: start;
  padding: 30px 0 34px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.not-this p {
  margin: 0;
  font-size: clamp(18px, 1.85vw, 25px);
  line-height: 1.48;
  letter-spacing: -.025em;
}
.not-this em { font-style: italic; color: var(--rust); }
.not-this .link { color: var(--ink); }

@media (max-width: 920px) {
  .not-this { grid-template-columns: 1fr; gap: 22px; }
}

/* ==========================================================================
   INNER PAGE: READING AIDS AND DEEPER SECTIONS
   ========================================================================== */

/* A rule across the top showing how much of a long page is left. */
.read-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 50;
  background: var(--orange);
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}

/* Rows arrive as they are reached. The class comes from script, so a page
   without script shows everything.

   Named rows-in, NOT "staged": home.js already uses .staged for the brief's
   clause reveal in section three, and a global `.staged > *` rule matched the
   brief line itself and made it invisible - on a page that never loads this
   script and so could never reveal it again. */
.rows-in > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .5s ease, transform .5s ease;
}
.rows-in > *.row-in { opacity: 1; transform: none; }

/* --- what goes wrong ----------------------------------------------------- */
.failures { display: grid; gap: 0; }
.failures > div {
  display: grid;
  grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
  gap: 36px;
  padding: 24px 0;
  border-bottom: 1px solid var(--line);
}
.failures > div:first-child { border-top: 1px solid var(--line); }
.failures .what {
  margin: 0;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.025em;
  line-height: 1.36;
}
.failures .fix {
  margin: 0;
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.78;
  color: var(--muted);
}
.failures .fix::before { content: "→ "; color: var(--rust); }

/* --- inputs we need ------------------------------------------------------ */
.needs { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 28px; }
.needs li { border-top: 2px solid var(--ink); padding-top: 16px; }
.needs b { display: block; font-size: 17px; letter-spacing: -.02em; margin-bottom: 8px; }
.needs span { font-size: 14.5px; line-height: 1.66; color: var(--muted); }

/* --- questions ----------------------------------------------------------- */
.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  list-style: none;
  cursor: pointer;
  padding: 22px 0;
  transition: color .2s, padding-left .25s cubic-bezier(.19,.72,.26,1);
  display: flex;
  align-items: baseline;
  gap: 18px;
  font-size: clamp(17px, 1.7vw, 22px);
  font-weight: 600;
  letter-spacing: -.03em;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover,
.faq summary:focus-visible { color: var(--rust); padding-left: 6px; }
.faq summary::after {
  content: "+";
  margin-left: auto;
  font-family: var(--mono);
  font-size: 20px;
  color: var(--rust);
  transition: transform .25s;
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq .answer {
  margin: 0;
  padding: 0 0 24px;
  max-width: 760px;
  font-size: 16px;
  line-height: 1.75;
  color: var(--muted);
}

@media (max-width: 920px) {
  .failures > div { grid-template-columns: 1fr; gap: 12px; }
}

/* --- three responses, all on things you can actually press ---------------
   The nav already draws its underline on hover; the footer now gets the same
   gesture, a question's number warms as you reach for it, and its answer
   arrives rather than appearing. Nothing here responds to a pointer passing
   over text that cannot be pressed. */
/* Block, so the links keep stacking - inline-block ran them together on one
   line - but only as wide as the text, so the rule underlines the words
   rather than the whole column. */
.footer-col a { position: relative; display: block; width: fit-content; }
.footer-col a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 1px;
  background: var(--orange);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .3s cubic-bezier(.19,.72,.26,1);
}
.footer-col a:hover::after,
.footer-col a:focus-visible::after { transform: scaleX(1); }

.faq summary::before { transition: color .2s ease; }
.faq summary:hover::before { color: var(--rust); }
.faq details[open] .answer { animation: answer-in .38s cubic-bezier(.19,.72,.26,1) both; }
@keyframes answer-in {
  from { opacity: 0; transform: translateY(-7px); }
  to { opacity: 1; transform: none; }
}

/* --- the enquiry form ---------------------------------------------------- */
.enquiry {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 26px 34px;
  max-width: 900px;
}
.enquiry .wide { grid-column: 1 / -1; }
.field { display: flex; flex-direction: column; gap: 9px; }
.field label {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--purple);
}
.field input,
.field select,
.field textarea {
  font-family: var(--display);
  font-size: 17px;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--line);
  padding: 10px 0;
  transition: border-color .2s;
}
.field textarea {
  border: 2px solid var(--line);
  padding: 14px 16px;
  line-height: 1.6;
  resize: vertical;
}
.field input:focus,
.field select:focus,
.field textarea:focus { outline: none; border-color: var(--orange); }
.field input::placeholder,
.field textarea::placeholder { color: #9d92b2; }
.field select { cursor: pointer; }
.actions { flex-direction: row; align-items: center; gap: 26px; flex-wrap: wrap; }
.actions .mono { color: var(--muted); max-width: 420px; }

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

/* --- case rows ----------------------------------------------------------- */
.case-rows { border-top: 1px solid var(--line); }
.case-row {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) minmax(0, 1fr) 48px;
  gap: 34px;
  align-items: start;
  padding: 34px 0;
  border-bottom: 1px solid var(--line);
  transition: background .25s, box-shadow .25s;
}
.case-row:hover {
  background: rgba(36, 32, 43, .035);
  box-shadow: calc(var(--tint-bleed) * -1) 0 0 rgba(36, 32, 43, .035),
              var(--tint-bleed) 0 0 rgba(36, 32, 43, .035);
}
.case-row .n { color: var(--rust); padding-top: .55em; }
.case-row .client { display: block; color: var(--purple); margin-bottom: 12px; }
.case-row h3 {
  margin: 0 0 12px;
  font-size: clamp(21px, 2.1vw, 29px);
  letter-spacing: -.04em;
  line-height: 1.1;
}
.case-row .case-ask {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -.02em;
  line-height: 1.34;
  color: var(--purple);
}
.case-row .case-got {
  margin: 0 0 14px;
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.78;
  color: var(--muted);
}
.case-row .case-tag { color: var(--rust); }
.case-row .go {
  display: grid;
  place-items: center;
  justify-self: end;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 1.5px solid rgba(36, 32, 43, .3);
  font-size: 17px;
  transition: background .25s, color .25s, border-color .25s, transform .25s;
}
.case-row:hover .go {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
  transform: translate(3px, -3px);
}
.template-note {
  margin: 26px 0 0;
  max-width: 640px;
  color: var(--rust);
  line-height: 1.8;
}

@media (max-width: 920px) {
  .case-row { grid-template-columns: 40px minmax(0, 1fr); gap: 16px; }
  .case-row > div:nth-of-type(2) { grid-column: 2; }
  .case-row .go { display: none; }
}

/* ==========================================================================
   INNER PAGES: THE APPARATUS

   The document pages were correct and dull: every section resolved to rows
   separated by hairlines, so six different kinds of information all looked
   like the same table. What follows gives each kind its own shape without
   inventing any new vocabulary, the parts are still lines, mono numerals,
   circles and the two grounds. What changes is that the lines now DRAW, and
   what has been passed reads differently from what has not.

   One rule governs everything here: script may only ever improve the page.
   Every dimmed or undrawn state is written so that its absence is the
   readable one, and the classes that dim are added by script alone.
   ========================================================================== */

/* --- section heads -------------------------------------------------------
   The rule under a section head is drawn rather than printed: it arrives
   with the section. Without script it is simply there. */
.rule-head { position: relative; border-bottom-color: transparent; }
.rule-head::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: var(--line);
  transform-origin: 0 50%;
}
.js .rule-head::after {
  transform: scaleX(0);
  transition: transform .85s cubic-bezier(.19,.72,.26,1);
}
.js .rule-head.ruled::after { transform: scaleX(1); }
.rule-head span:first-child::before {
  content: "";
  display: inline-block;
  width: 14px;
  height: 1px;
  margin-right: 11px;
  vertical-align: middle;
  background: var(--orange);
}

/* --- a section split down the page ---------------------------------------
   The document pages had one rhythm and used it eleven times: a head, a
   hairline, and full-width rows beneath it. This is the second rhythm, the
   head moves into a left-hand column and stays there while its own section
   scrolls past, the way a running head works in a printed report. Nothing
   new is drawn; the same two spans simply stack. */
.page-section.split > .wrap {
  display: grid;
  /* Capped, or the prose and the list run past 110 characters a line on a
     wide monitor. 880px is what the column already measures at 1400, so
     nothing changes below that - it simply stops growing. */
  grid-template-columns: minmax(0, 278px) minmax(0, 880px);
  gap: 0 64px;
  align-items: start;
}
.page-section.split .rule-head {
  position: sticky;
  top: 40px;
  flex-direction: column;
  align-items: flex-start;
  gap: 15px;
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: 0;
  line-height: 1.75;
}
/* The drawn rule is painted by ::after, so it has to be called off too. */
.page-section.split .rule-head::after { display: none; }
.page-section.split .rule-head span:first-child::before { display: none; }
.page-section.split .rule-head span + span { margin-left: 0; color: var(--muted); }

@media (max-width: 1000px) {
  .page-section.split > .wrap { grid-template-columns: 1fr; }
  .page-section.split .rule-head {
    position: static;
    flex-direction: row;
    align-items: baseline;
    gap: 20px;
    margin-bottom: 34px;
  }
  .page-section.split .rule-head span + span { margin-left: auto; }
}

/* --- a section on the lilac ground ---------------------------------------
   Third rhythm, and the cheapest one: the same section, on the other ground.
   A service page ran four screens of paper without a break in it. The band
   redefines --line rather than hard-coding a second hairline colour, so
   everything inside it picks up a rule that is actually visible on lilac. */
.page-section.band {
  background: var(--lilac);
  padding: 76px 0 80px;
  --line: rgba(92, 70, 118, .3);
  /* --muted is mixed for paper and measures 4.12:1 on lilac, which is under
     AA. This is the same colour a little further down, at 5.46:1. Redefined
     rather than overridden per selector, so anything moved onto a band gets
     legible body copy without being asked. */
  --muted: #554f5d;
}
.page-section + .page-section.band { padding-top: 76px; }
.page-section.band + .page-section { padding-top: 78px; }

/* The band kept its desktop padding on a phone - 76 and 80 against the 56
   every section around it gets - so one section in the middle of the page sat
   in visibly more air than its neighbours, and the section after it inherited
   a 78px gap for the same reason.

   This has to sit after the three rules above rather than with the other
   phone overrides: @media adds no specificity, so the later of two equally
   specific rules wins and the earlier one never applied at all. */
@media (max-width: 920px) {
  .page-section.band { padding: 56px 0 60px; }
  .page-section + .page-section.band,
  .page-section.band + .page-section { padding-top: 56px; }
}

/* --- what the engagement ships ------------------------------------------
   Three named outputs, which used to be a section of their own with a head
   and five rows of chrome around three short phrases. They belong to the
   description of the work, so they sit under it. */
.ships { margin-top: 36px; padding-top: 22px; border-top: 2px solid var(--ink); }
.ships .label { display: block; margin-bottom: 15px; color: var(--purple); }
.ships ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(238px, 1fr));
  gap: 22px 46px;
}
/* Three nouns told the reader nothing they could not have guessed. Each now
   says what is actually inside it. */
.ships li b { display: block; font-size: 17px; font-weight: 600; letter-spacing: -.025em; }
.ships li span { display: block; margin-top: 7px; font-size: 14px; line-height: 1.66; color: var(--muted); }
.ships li::before {
  content: "";
  display: block;
  width: 13px;
  height: 1px;
  margin-bottom: 11px;
  background: var(--orange);
}

/* --- is this the one you need -------------------------------------------
   Two sections became one. "When you need this" and "What this is not" were
   asking and answering the same question at opposite ends of the page, so
   they are now set side by side: the list you read down, and the one case
   that belongs to another service, on a panel of ordinary paper because
   that is where it is going. */
.fit {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr);
  gap: 54px;
  align-items: start;
}
.fit .label { display: block; margin-bottom: 16px; color: var(--purple); }
.fit-list { list-style: none; margin: 0; padding: 0; }
.fit-list li {
  padding: 17px 0;
  border-bottom: 1px solid var(--line);
  font-size: clamp(17px, 1.7vw, 21px);
  line-height: 1.42;
  letter-spacing: -.028em;
}
.fit-list li:first-child { border-top: 1px solid var(--line); }
.fit-no { background: var(--paper); padding: 26px 30px 22px; }
.fit-no p { margin: 0; font-size: 15.5px; line-height: 1.74; color: var(--muted); }
.fit-no em { font-style: italic; color: var(--rust); }
.fit-no .link { color: var(--ink); }

@media (max-width: 920px) {
  .fit { grid-template-columns: 1fr; gap: 30px; }
}

/* The input cards draw their own 2px rule across the top of each column, so
   the head's hairline was landing 34px above four heavier ones and the pair
   read as one doubled line. Where the content below opens with rules of its
   own, the head does not need to supply another. The hairline is painted by
   ::after here, so that is the one to call off. */
.rule-head:has(+ .needs)::after,
/* Same doubling under .failures, which draws a hairline across the top of
   every card - it was only ever noticed on .needs because those rules are
   heavier. Two pages use it: about and careers. */
.rule-head:has(+ .failures)::after { display: none; }

/* --- a drawn timeline ----------------------------------------------------
   Used once, on the About page. The spine fills as you scroll and each year
   lights as the fill reaches it, so the history is read at the speed you
   move through it. Everything behind you is bright; everything ahead is not
   yet. */
.timeline {
  --draw: 0;
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}
.timeline::before,
.timeline::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 10px;
  bottom: 10px;
  width: 1px;
}
.timeline::before { background: var(--line); }
.timeline::after {
  background: var(--orange);
  transform: scaleY(var(--draw));
  transform-origin: 50% 0;
}
.timeline li {
  position: relative;
  padding: 0 0 40px 34px;
}
.timeline li:last-child { padding-bottom: 0; }
.timeline li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--line);
  transition: background .4s ease, border-color .4s ease, transform .4s ease;
}
.timeline .year {
  display: block;
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--purple);
  margin-bottom: 9px;
  transition: color .4s ease;
}
.timeline h4 {
  margin: 0 0 9px;
  font-size: clamp(21px, 2.3vw, 30px);
  letter-spacing: -.035em;
  line-height: 1.1;
}
.timeline p { margin: 0; max-width: 46ch; font-size: 15px; line-height: 1.72; color: var(--muted); }
.js .timeline.drawing li { opacity: .5; transition: opacity .5s ease; }
.js .timeline.drawing li.lit { opacity: 1; }
.timeline li.lit::before { background: var(--orange); border-color: var(--orange); transform: scale(1.35); }
.timeline li.lit .year { color: var(--rust); }
@media (min-width: 820px) {
  .timeline li { display: grid; grid-template-columns: 165px minmax(0, 1fr); gap: 34px; }
  .timeline .year { margin: 0; padding-top: .5em; }
}

/* The enquiry page's four steps are a sequence in time from the reader's
   side - two days, then a call, then a scope - so they get the drawn spine.
   Slightly tighter than the journey on About, which has years to carry. */
.page-section .timeline li { padding-bottom: 30px; }
@media (min-width: 820px) {
  .page-section .timeline li { grid-template-columns: 190px minmax(0, 1fr); }
}

/* --- stages --------------------------------------------------------------
   Four phases of an engagement. This was a drawn track, a line that filled
   as you scrolled, with a lit node per phase, and it is deliberately not
   one any more. That figure now belongs to exactly one place on the site,
   the journey on the About page; repeating it on nine other pages spent its
   meaning for nothing.

   What replaces it is a hairline grid, which is the one shape a document
   page here did not already contain: everything else resolves to stacked
   full-width rows. The order is carried by the ordinal in front of each
   phase label, so no line has to carry it. Nothing moves; nothing dims. */
.stages {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  counter-reset: stage;
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
}
.stages > * {
  padding: 28px 34px 32px;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  transition: background .3s ease;
}
.stages > *:hover { background: rgba(220, 210, 242, .38); }
.stages .when {
  display: block;
  margin-bottom: 14px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--purple);
}
.stages .when::before {
  counter-increment: stage;
  content: counter(stage, decimal-leading-zero) "\00a0\00a0/\00a0\00a0";
  color: var(--rust);
}
.stages h4 {
  margin: 0 0 10px;
  font-size: clamp(20px, 1.9vw, 25px);
  letter-spacing: -.035em;
  line-height: 1.14;
}
.stages p { margin: 0; max-width: 44ch; font-size: 14.5px; line-height: 1.72; color: var(--muted); }

/* Each cell draws two sides of the grid, so sliding the cells up on arrival
   would take the grid visibly apart for half a second. They fade in place. */
.stages.rows-in > * { transform: none; }

@media (max-width: 720px) {
  .stages { grid-template-columns: 1fr; border-left: 0; }
  .stages > * { padding: 24px 0 26px; border-right: 0; }
}

/* --- inputs, as numbered cards ------------------------------------------- */
.needs { counter-reset: need; }
.needs li {
  position: relative;
  border-top: none;
  padding: 20px 60px 0 0;
}
.needs li::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--ink);
  transform-origin: 0 50%;
}
.needs.rows-in li::before {
  transform: scaleX(0);
  transition: transform .7s cubic-bezier(.19,.72,.26,1);
}
.needs.rows-in li.row-in::before { transform: scaleX(1); }
.needs li::after {
  counter-increment: need;
  content: counter(need, decimal-leading-zero);
  position: absolute;
  top: 14px;
  right: 0;
  font-family: var(--mono);
  font-size: 46px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.05em;
  color: var(--lilac);
  pointer-events: none;
}
.needs b, .needs span { position: relative; z-index: 1; }
.page-section.band .needs li::after { color: var(--lilac-deep); }

/* --- what goes wrong -----------------------------------------------------
   The problem carries a struck circle, the answer an arrow in one. Hovering
   a row fills both, which is the same gesture the home page uses to say
   "this is a thing you can act on". */
.failures > div { transition: background .3s ease, box-shadow .3s ease; }
.failures > div:hover {
  background: rgba(36, 32, 43, .028);
  box-shadow: calc(var(--tint-bleed) * -1) 0 0 rgba(36, 32, 43, .028),
              var(--tint-bleed) 0 0 rgba(36, 32, 43, .028);
}
.failures.struck .what { position: relative; padding-left: 36px; }
.failures.struck .what::before {
  content: "\00d7";
  position: absolute;
  left: 0;
  top: 1px;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 500;
  color: var(--rust);
  transition: background .3s ease, border-color .3s ease, color .3s ease;
}
.failures.struck > div:hover .what::before {
  background: var(--rust);
  border-color: var(--rust);
  color: var(--paper);
}
.failures .fix::before {
  content: "\2192";
  display: inline-grid;
  place-items: center;
  width: 21px;
  height: 21px;
  margin-right: 11px;
  vertical-align: -5px;
  border: 1px solid var(--line);
  border-radius: 50%;
  font-size: 12px;
  color: var(--rust);
  transition: background .3s ease, border-color .3s ease, color .3s ease, transform .3s ease;
}
.failures > div:hover .fix::before {
  background: var(--orange);
  border-color: var(--orange);
  color: var(--ink);
  transform: translateX(3px);
}

/* --- the founders --------------------------------------------------------
   A monogram in a ring. The ring is the only round form on the site apart
   from the logo's own brackets, which is deliberate: it rhymes without
   redrawing anything. */
.people { display: grid; grid-template-columns: repeat(auto-fit, minmax(238px, 1fr)); gap: 24px; }
.people article {
  padding: 28px 26px 30px;
  border: 1px solid var(--line);
  background: rgba(220, 210, 242, .24);
  transition: transform .45s cubic-bezier(.19,.72,.26,1), background .45s ease, border-color .45s ease;
}
/* The card lifts and the monogram fills; the ground deliberately does not
   change. Taking it to full lilac looked better and measured 4.12:1 on the
   body copy, which is below AA - the lift and the filled ring are affordance
   enough without spending contrast on it. */
.people article:hover {
  transform: translateY(-5px);
  border-color: var(--purple);
}
.people .mark {
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  margin-bottom: 22px;
  border: 1px solid var(--purple);
  border-radius: 50%;
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .06em;
  color: var(--purple);
  transition: background .45s ease, color .45s ease, border-color .45s ease;
}
.people article:hover .mark { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.people .role {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--rust);
  margin-bottom: 10px;
}
.people h3 { margin: 0 0 12px; font-size: 22px; letter-spacing: -.035em; }
.people p { margin: 0; font-size: 14.5px; line-height: 1.72; color: var(--muted); }

/* --- questions, numbered ------------------------------------------------- */
.faq { counter-reset: q; }
.faq summary::before {
  counter-increment: q;
  content: counter(q, decimal-leading-zero);
  flex: none;
  margin-right: 20px;
  padding-top: .45em;
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .12em;
  color: var(--purple);
}

/* --- pull quote ----------------------------------------------------------
   One sentence per page, set large on lilac, so a reader scrolling fast
   still collects the argument. It is the only place an inner page raises
   its voice. */
.pull {
  margin: 0;
  padding: 58px 0 62px;
  background: var(--lilac);
}
.pull-line {
  margin: 0;
  font-size: clamp(26px, 3.6vw, 50px);
  font-weight: 600;
  letter-spacing: -.04em;
  line-height: 1.12;
  max-width: 20ch;
}
.pull figcaption {
  margin-top: 22px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .05em;
  color: var(--purple);
}

@media (max-width: 700px) {
  .needs li { padding-right: 46px; }
  .needs li::after { font-size: 36px; }
}

/* ==========================================================================
   CASE STUDIES

   A case page is shorter than a service page and argues in a different
   order: the pair, then the diagnosis, then the build, then what it does
   now. Nothing new is drawn, the parts are the same hairlines, mono
   numerals and two grounds: but the arrangement changes at every step so
   that four sections do not read as one table: a two-column pair, a split
   with the head held in the margin, a numbered list, then the lilac ground
   for the outcome.

   The one figure reserved elsewhere stays reserved: no timeline here.
   ========================================================================== */

/* --- the three facts in a case head --------------------------------------
   Client, sector and service, stated rather than implied. The label stays
   in the head's purple; the value steps up to ink so the pair reads as a
   label and a fact rather than as one long mono string. */
.page-head-meta b { font-weight: 600; color: var(--ink); margin-left: 9px; }

/* --- prose ---------------------------------------------------------------
   The diagnosis is the one part of a case page that is genuinely an
   argument, so it is set as paragraphs rather than as rows. The first one
   carries the finding at display size; the rest support it. */
.prose p { margin: 0 0 19px; max-width: 64ch; font-size: 17.5px; line-height: 1.72; }
.prose p:last-child { margin-bottom: 0; }
.prose p:first-child {
  font-size: clamp(20px, 2.05vw, 27px);
  font-weight: 600;
  letter-spacing: -.03em;
  line-height: 1.34;
  max-width: 34ch;
  margin-bottom: 26px;
}

/* --- what it does now ----------------------------------------------------
   The closing section, and the only one on the lilac. A statement on the
   left, and on the right the parts of the job the system took over, a
   short ledger, hairline-ruled, deliberately unlike the .ships grid that
   closes the build section a screen earlier. */
.outcome {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
  gap: 60px;
  align-items: start;
}
.outcome .now {
  margin: 0;
  font-size: clamp(22px, 2.45vw, 33px);
  font-weight: 600;
  letter-spacing: -.035em;
  line-height: 1.26;
}
.outcome .now + p {
  margin: 24px 0 0;
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.78;
  color: var(--muted);
}
.outcome .label { display: block; margin-bottom: 15px; color: var(--purple); }
.took { list-style: none; margin: 0; padding: 0; }
.took li {
  padding: 15px 0;
  border-bottom: 1px solid var(--line);
  font-size: 16.5px;
  line-height: 1.5;
  letter-spacing: -.022em;
}
.took li:first-child { border-top: 1px solid var(--line); }

@media (max-width: 920px) {
  .outcome { grid-template-columns: 1fr; gap: 34px; }
}

/* --- the client wall -----------------------------------------------------
   Real marks, not a decorative strip: these are the logos the practice
   already publishes as clients. They arrive in six different colours and
   six different optical weights, and forcing them to one ink was tried and
   abandoned, two of the six carry raster icons that flatten into black
   squares. So the grid does the normalising instead: one cell height, one
   hairline, and each mark scaled by hand to the same optical size through
   --h. The mono caption under each is what makes it a ledger rather than a
   logo strip, and it names the marks that do not spell themselves. */
/* Nine marks never divide evenly into the column count at every width, so the
   last row is usually partial. The frame therefore lives on the wrapper, and
   the cells draw only their right and bottom edges; the grid is pulled 1px
   into the frame so the last column and row sit ON the frame rather than
   beside it. A half-filled bottom row then reads as an unfilled row of a
   table, which is what it is, instead of leaving the lattice hanging open. */
.client-wall {
  border: 1px solid var(--line);
  overflow: hidden;
}
/* Five across, so the nine marks fall as 5 + 4 and the tenth slot is left
   deliberately empty rather than the grid ending mid-row. Explicit column
   counts rather than auto-fit: the whole point is that the count divides the
   ten cells, which auto-fit cannot promise at an arbitrary width. */
.client-wall-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  margin: 0 -1px -1px 0;
}
@media (max-width: 880px) {
  .client-wall-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .client-wall-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.client-mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 19px;
  min-height: 134px;
  padding: 28px 20px;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  transition: background .25s ease;
}
/* Height is set per mark through --h so that marks of different optical
   weights look the same size. max-width keeps the wide lockups inside a
   narrow cell rather than overflowing it. */
.client-mark img { width: auto; height: var(--h, 26px); max-width: 100%; }
.client-mark span { color: var(--purple); transition: color .25s ease; }
a.client-mark:hover { background: rgba(36, 32, 43, .04); }
a.client-mark:hover span { color: var(--rust); }

/* The tenth cell. Nine marks in a five-wide grid leave one gap, so the gap is
   given away rather than padded out: an empty slot where a mark would go, and
   the line the practice already uses for this on its own site. The slot is a
   drawn rule like everything else here - it is the shape of an absent mark,
   not a picture of one. */
.client-next .slot {
  display: block;
  width: 48px;
  height: 26px;
  border: 1px dashed var(--line);
}
.client-next span { color: var(--rust); }
a.client-next:hover span { color: var(--ink); }
a.client-next:hover .slot { border-color: var(--rust); }

@media (max-width: 560px) {
  .client-mark { min-height: 112px; padding: 22px 12px; gap: 15px; }
}

/* --- the index rows, once they are real ---------------------------------
   The placeholder rows pointed at /contact/ and needed no visited state.
   Real ones are a list somebody reads through, so a row already read keeps
   its numeral in ink rather than rust. */
.case-row:visited .n { color: var(--muted); }

/* The one line on a case page that points back at the services. It sits at
   the foot of the outcome band rather than in a navigation section of its
   own, because "which of the six was this" is a question the reader has
   just earned the answer to, and because a second nav block at the end of
   a short page is the repetition this section set out to avoid.
   Set in the mono face but not in the mono CLASS: .mono uppercases, and
   three lines of uppercased monospace is a paragraph nobody reads. Same
   treatment the pull quote's caption uses. */
.band-note {
  margin: 42px 0 0;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: .01em;
  line-height: 1.85;
  max-width: 68ch;
  color: var(--purple);
}
.band-note .link { color: var(--ink); }

/* ==========================================================================
   ICONS

   Drawn from the same parts as everything else on the site: one stroke
   weight, no fills, currentColor, geometric rather than pictorial. They earn
   their place in exactly two lists on the careers page - six values and six
   disciplines - where a reader scans rather than reads. Anywhere a reader is
   reading, they would be decoration.
   ========================================================================== */
.ico {
  display: block;
  flex: none;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* On the values grid the icon sits under the rule and above the name, so the
   cell reads rule, mark, claim, detail. */
.needs .ico { color: var(--rust); margin-bottom: 14px; }

/* --- disciplines ---------------------------------------------------------
   Two columns of hairline rows. Not .deliverables: those are numbered steps
   in an order, and these are a set with no order at all. */
.roles {
  list-style: none;
  margin: 0 0 34px;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: 0 48px;
}
.roles li {
  display: flex;
  /* Aligned to the first line rather than centred: a row that wraps to two
     lines would otherwise float its mark in the gap between them. */
  align-items: flex-start;
  gap: 15px;
  padding: 15px 0;
  border-bottom: 1px solid var(--line);
  font-size: 16.5px;
  letter-spacing: -.022em;
}
.roles .ico { width: 20px; height: 20px; margin-top: 4px; color: var(--rust); }
.roles b { font-weight: 600; }
.roles span { color: var(--muted); font-size: 14.5px; }

/* ==========================================================================
   LEGAL DOCUMENTS

   Terms and privacy. One readable column, no apparatus: these are read
   linearly by somebody checking one clause, so nothing here scrolls, draws
   or reveals. The only borrowed part is the mono numeral, which numbers the
   sections so a clause can be pointed at.
   ========================================================================== */
.doc { max-width: 76ch; }
.doc h2 {
  margin: 52px 0 18px;
  padding-top: 26px;
  border-top: 1px solid var(--line);
  font-size: clamp(20px, 2vw, 26px);
  letter-spacing: -.03em;
  line-height: 1.2;
}
.doc h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.doc h2 .n {
  display: block;
  margin-bottom: 11px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .1em;
  color: var(--rust);
}
.doc p { margin: 0 0 18px; font-size: 16.5px; line-height: 1.78; }
.doc p:last-child { margin-bottom: 0; }
/* Defined terms. A description list rather than bolded run-ins, because the
   terms page is mostly definitions and they should be findable. */
.doc dl { margin: 0 0 18px; }
.doc dt { font-size: 16.5px; font-weight: 600; letter-spacing: -.02em; }
.doc dd { margin: 5px 0 17px; font-size: 16.5px; line-height: 1.78; color: var(--muted); }
.doc dd:last-child { margin-bottom: 0; }
/* .link is built for standalone links - 13px, padded, inline-flex with an
   arrow. Inside a paragraph it has to be a word in a sentence instead, so
   everything structural about it is undone here. */
.doc a {
  display: inline;
  padding: 0;
  gap: 0;
  font-size: inherit;
  font-weight: 600;
  color: var(--rust);
  border-bottom: 1px solid currentColor;
}
.doc a:hover { color: var(--ink); }
/* Same problem in the head: the contact address is a word in a mono line. */
.page-head-meta a { border-bottom: 1px solid currentColor; }
.page-head-meta a:hover { color: var(--rust); }

/* --- the registration line and the legal links --------------------------- */
.footer-col .cin {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .04em;
  line-height: 1.7;
  color: var(--on-ink-muted);
}
.footer-legal { display: flex; gap: 24px; flex-wrap: wrap; }
.footer-bottom a { color: var(--on-ink-muted); transition: color .2s; }
.footer-bottom a:hover { color: var(--paper); }

/* --- one type scale and one tap size, on phones ---------------------------
   Placed last on purpose. Every rule it corrects sets its size on its own
   selector - .output-list li, .footer .mono, .next-rows .n and the rest - and
   @media adds no specificity, so an override written anywhere earlier in this
   file loses to whichever of them comes after it.

   THE SIZES. A phone was rendering mono at 9, 10, 11 and 12px at once, and
   the mixture read as carelessness more than any one size did. 9px was the
   worst of it and sat in the two places people actually land: the home hero's
   two labels, and the line under every closing button. Everything mono now
   meets 12px below 920px. Desktop keeps its own scale, where the type is
   further from the eye and 9px is a caption rather than a hurdle.

   THE TAPS. "Our services" and "Work with us" measured 39px and the brand
   37px, on every page - the three most-tapped things on the site, all under
   the 44px a finger needs. Padding rather than a bigger font, so the header
   grows by five pixels and nothing else moves. */
@media (max-width: 920px) {
  .mono,
  .hero-meta .mono,
  .hero-cta .mono,
  .contact-actions .mono,
  .footer .mono,
  .footer-col .cin,
  .output-list li,
  .people .role,
  .stages .when,
  .next-rows .n,
  .deliverables .n,
  .service-row .n,
  .case-row .n,
  .pair-jump { font-size: 12px; }

  .nav { gap: 20px; font-size: 12px; }
  .nav a { padding-top: 13px; padding-bottom: 13px; }
  /* The underline is positioned from the bottom of the box, so the new
     padding would have dropped it away from the word it underlines.
     13px puts it back at the 3px it sits at on a desktop. */
  .nav a::after { bottom: 13px; }
  .brand { padding: 5px 0; }
}
