/* ============================================================
   XQUBE GLOBAL — ABOUT (page 05)
   The firm's manifesto, and the longest read on the site.
   Layers on css/main.css: nothing there is redefined, only
   extended.

   THE COMPOSITOR'S PASS. The client's whole brief was "about us
   page bad rework", after two passes that each fixed one section
   in isolation. The diagnosis is that eleven sections were built
   from one brick — "[mono index] + [name] + [line]", seventeen
   times — and that two mega-scenes ran the same grammar for
   860vh. Two rules now run ACROSS the page rather than inside a
   section:

     1. WE ONLY NUMBER WHAT THE CLIENT NUMBERED. The deck numbers
        the five beliefs and the eight controls, and nothing else.
        The nine authored indices (archetypes 01-04, segments
        01-05) are deleted. 17 index leaves become 8.
     2. THE HEAD SITS IN A DIFFERENT PLACE IN EACH SECTION —
        run-in (how we think), marginal (who we work with),
        hanging above the block (data security) — with the
        head:body scale relationship INVERTED in the middle one.

   The devices that remain:
     · the roadmap   a pinned horizontal timeline the scroll
                     travels. 400vh -> 260vh, counter deleted.
     · the articles  "What we believe", NO LONGER PINNED: five
                     convictions at full weight with hung folios,
                     460vh -> a rest. The world still writes each
                     number, now off an IntersectionObserver.
     · the anchor    Delaware is the datum; the stations register
                     to it. UNTOUCHED — approved.
     · the spread    "Data security", NOT pinned: a lexicon book,
                     and now the page's ONLY numbered section.
     · the sheet     "The founders", NOT pinned: a publication
                     whose margin is wired to the clause that
                     awards each credential. UNTOUCHED.

   SIX OF ELEVEN SECTIONS NOW HAVE NO JS-CONDITIONAL OPACITY AT
   ALL (the name, how we think, what we believe, who we work with,
   data security, onward). .abt-live is load-bearing for exactly
   one dimmed object on the page — the roadmap's cards — plus the
   founders' credential promotion. This page's PE surface shrank
   for the third pass running.
   ============================================================ */

/* ---------- you are here ---------- */
.nav-links a.is-here { color: var(--text); }
.nav-links a.is-here::after { transform: scaleX(1); opacity: 0.6; }
.mobile-links a.is-here { color: var(--text); }
.mobile-links a.is-here .mono { color: var(--pink); }

/* ---------- the cold open belongs to the homepage ----------
   An inner page is arrived at, not opened on. The chrome stays. */
.js.cold .nav-inner,
.js.cold .scroll-progress { opacity: 1; }

/* a deck heading used as an eyebrow keeps its own casing in the DOM */
.abt-label { text-transform: uppercase; }


/* ============================================================
   SCENE — the title
   Holds the film's "prologue" slot so the number-field builds,
   then plays this page's own choreography (js/about.js).
   ============================================================ */
section[data-scene="prologue"].abt-title { height: 200vh; }

section[data-scene="prologue"].abt-title .abt-title-stage {
  justify-content: center;
  padding-top: calc(var(--nav-h) + var(--s6));
  padding-bottom: var(--s7);
}
.abt-title-stage .title-block { max-width: 1120px; }
.abt-h1 {
  font-size: clamp(36px, 5.8vw, 84px);
  line-height: 1.05;
  letter-spacing: -0.028em;
  max-width: 16ch;
  margin: 0 auto;
}
.abt-standfirst {
  margin: clamp(32px, 5.4vh, 60px) auto 0;
  max-width: 58ch;
  opacity: var(--subop, 1);
  transform: translateY(calc((1 - var(--subop, 1)) * 20px));
}


/* ============================================================
   REST — the manifesto
   The headline runs wide; the argument runs in one measured
   column, offset right, with the turn breaking out in pink.
   ============================================================ */
.abt-manifesto .h2-editorial { max-width: 18ch; }
.mf-body {
  margin: clamp(48px, 7vh, 84px) 0 0 auto;
  max-width: 720px;
  border-left: 1px solid var(--line);
  padding-left: clamp(24px, 4vw, 56px);
  display: flex; flex-direction: column;
  gap: var(--s4);
}
.mf-body .body-lg { max-width: 66ch; }
.mf-turn {
  margin: var(--s2) 0;
  font-size: clamp(22px, 2.5vw, 30px);
  line-height: 1.4;
  font-weight: 500;
  letter-spacing: -0.018em;
  color: var(--pink);
  text-shadow: 0 0 30px rgba(255, 77, 128, 0.25);
  max-width: 22ch;
}


/* ============================================================
   REST — THE NAME  ·  THE HELD BEAT

   The deck gives a heading and no body, and the previous answer
   put a 520px bordered glass plate here containing a PNG of the
   XQUBE mark — directly on top of the live number-field, which
   this page already points at 'xsmall' HERE so the world draws
   that same mark behind this section. Two renderings of one logo,
   forty pixels apart, in the section whose subject is the mark.

   The plate, the PNG and the aria-hidden fake wordmark are gone.
   Three approved words, centred, at display scale, over the mark
   the world is forming. It is the ONLY centred rest on the page,
   which is itself a differentiator, and it is a page-turn between
   the manifesto's close and the roadmap's opening.

   Zero JS, zero custom properties, no invented copy.
   ============================================================ */
.abt-name .wrap {
  min-height: clamp(280px, 46vh, 520px);
  display: grid;
  place-items: center;
  text-align: center;
}
.name-h {
  font-size: clamp(34px, 5.6vw, 78px);
  max-width: 100%;
}
/* the mark is the brightest formation this page draws (xsmall runs
   at nearly twice the alpha of the beliefs' digits), and three words
   of approved copy must never depend on the particles behind them
   being sparse. A radial wash, centred on the type, not a plate. */
.abt-name::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  /* widened when the name story moved in: the wash has to carry a two
     column argument now, not three centred words */
  background: radial-gradient(72% 58% at 50% 56%,
    rgba(10, 10, 14, 0.78), rgba(10, 10, 14, 0.4) 62%, transparent 82%);
}

/* ---------- THE NAME · X, CUBED ----------
   The client's own arithmetic, verbatim. The deck sets it in gold; here it
   takes the site's purple and pink and nothing else changes. The mark
   performs the sentence: one X, with three squares multiplying out of it —
   not added to it, not doubled. */
.name-story {
  position: relative; z-index: 1;
  width: min(1040px, 100%);
  margin-top: clamp(34px, 6vh, 76px);
  display: grid;
  grid-template-columns: 0.82fr 1.18fr;
  gap: clamp(32px, 5vw, 84px);
  align-items: center;
  text-align: left;
}
.name-mark-wrap {
  position: relative;
  display: grid; place-items: center;
  min-height: clamp(186px, 26vh, 288px);
  /* the three faces sit at real depths, so the pointer can push the cube
     around: --mx/--my are written by js/about.js, -1..1 from centre */
  perspective: 900px;
  --mx: 0; --my: 0;
  --spread: 1;
  touch-action: manipulation;
  -webkit-user-select: none; user-select: none;
}
.name-mark {
  position: relative; z-index: 2;
  font-size: clamp(84px, 10.5vw, 148px);
  font-weight: 700;
  line-height: 0.92;
  letter-spacing: -0.045em;
  color: var(--text);
}
.name-mark sup {
  font-size: 0.34em;
  vertical-align: super;
  margin-left: 0.04em;
  color: var(--pink-soft);
}
.name-cube {
  position: absolute; inset: 0; z-index: 1;
  display: grid; place-items: center;
  transform-style: preserve-3d;
  transform: rotateX(calc(var(--my) * -9deg)) rotateY(calc(var(--mx) * 9deg));
}
/* one square, and three of it: the same face stepped back twice, which is
   the figure the copy describes — not added to, multiplied by itself */
.name-cube i {
  position: absolute;
  width: clamp(146px, 16.5vw, 218px);
  aspect-ratio: 1;
  border: 1px solid rgba(148, 138, 255, 0.3);
  border-radius: 5px;
  transform: translate3d(calc(var(--dx) * var(--spread)), calc(var(--dy) * var(--spread)), var(--z));
}
.name-cube i:nth-child(1) { --dx: -15px; --dy: 15px;  --z: -44px; border-color: rgba(148, 138, 255, 0.16); }
.name-cube i:nth-child(2) { --dx: 0px;   --dy: 0px;   --z: 0px; }
.name-cube i:nth-child(3) { --dx: 15px;  --dy: -15px; --z: 44px;  border-color: rgba(255, 125, 161, 0.22); }

/* leaning on it pushes the faces further apart — the multiplication, felt */
.name-mark-wrap:hover { --spread: 1.5; }
.name-mark-wrap:hover .name-cube i:nth-child(1) { border-color: rgba(148, 138, 255, 0.3); }
.name-mark-wrap:hover .name-cube i:nth-child(3) { border-color: rgba(255, 125, 161, 0.42); }
.name-mark-wrap:hover .name-mark sup { color: var(--pink-soft); }

.name-h3 {
  font-size: clamp(26px, 3.2vw, 44px);
  line-height: 1.1;
  letter-spacing: -0.025em;
  color: var(--text);
  margin-bottom: clamp(15px, 2.4vh, 26px);
}
.name-copy p {
  color: var(--mist);
  font-size: 17px;
  line-height: 1.62;
  margin-bottom: clamp(12px, 1.8vh, 18px);
}
.name-copy p strong { color: var(--text); font-weight: 600; }
.name-copy .cube-emphasis {
  font-size: clamp(23px, 2.8vw, 35px);
  line-height: 1.28;
  letter-spacing: -0.02em;
  color: var(--faint);
  margin: clamp(24px, 3.4vh, 34px) 0;
}
.name-copy .cube-emphasis .cube-hl { color: var(--pink-soft); }

/* the squares multiply out of the letter as the section is read into place */
@media (prefers-reduced-motion: no-preference) {
  .js .name-cube i {
    transition: transform 1.05s cubic-bezier(0.22, 0.6, 0.2, 1), opacity 0.8s ease;
  }
  /* they arrive as ONE square and multiply out into three */
  .js .name-mark-wrap:not(.in) .name-cube i { transform: none; opacity: 0; }
  .js .name-cube i:nth-child(2) { transition-delay: 0.12s; }
  .js .name-cube i:nth-child(3) { transition-delay: 0.24s; }

  /* the cursor only promises what this state can deliver: js/about.js binds
     nothing at all under reduced motion, so the affordance lives here too */
  .js .name-mark-wrap { cursor: pointer; }
  /* the pointer tilt is smoothed, not lagged: short enough to feel direct */
  .js .name-cube { transition: transform 0.16s ease-out; }
  .js .name-mark {
    transform: translate3d(calc(var(--mx) * 7px), calc(var(--my) * 7px), 0);
    transition: transform 0.22s ease-out, color 0.4s ease;
  }

  /* click or tap and it multiplies itself out again, from one */
  @keyframes cube-remultiply {
    0%   { transform: translate3d(0, 0, 0) scale(0.84); }
    52%  { transform: translate3d(calc(var(--dx) * 2), calc(var(--dy) * 2), calc(var(--z) * 1.7)) scale(1.07); }
    100% { transform: translate3d(calc(var(--dx) * var(--spread)), calc(var(--dy) * var(--spread)), var(--z)) scale(1); }
  }
  @keyframes sup-multiply {
    0%   { transform: scale(0.5) translateY(6px); opacity: 0.25; }
    55%  { transform: scale(1.3) translateY(-2px); opacity: 1; }
    100% { transform: none; opacity: 1; }
  }
  .js .name-mark-wrap.pop .name-cube i {
    animation: cube-remultiply 0.86s cubic-bezier(0.22, 0.6, 0.2, 1) both;
  }
  .js .name-mark-wrap.pop .name-cube i:nth-child(2) { animation-delay: 0.07s; }
  .js .name-mark-wrap.pop .name-cube i:nth-child(3) { animation-delay: 0.14s; }
  .js .name-mark-wrap.pop .name-mark sup {
    display: inline-block;
    animation: sup-multiply 0.86s cubic-bezier(0.22, 0.6, 0.2, 1) 0.14s both;
  }
}


/* ============================================================
   SCENE — THE ROADMAP
   A horizontal timeline the scroll travels along. The gate holds
   still at the centre of the stage; the road moves through it.
   The "We are here" marker rides the Years 5–10 stage and lights
   when that stage reaches the gate.

   400vh -> 260vh, the same cost as the anchor, so no single scene
   dominates the page. The range is 160vh over map01(p, 0.06, 0.94),
   i.e. ~47vh per stage transition — comfortable for 40-word cards.
   The STAGE 01/04 tally is deleted: the eyebrow reads "FOUR STAGES
   · TWENTY YEARS" and every card carries its own approved "Years"
   label, so the counter duplicated approved copy — and it was one
   half of a grammar this page ran twice.
   ============================================================ */
.road-scene { height: 260vh; }
.road-stage { justify-content: flex-start; }

.road-head {
  position: relative; z-index: 3;
  text-align: center;
  max-width: 900px;
}
.road-head .chapter-line { margin-bottom: clamp(20px, 3vh, 34px); }
.road-h2 { font-size: clamp(30px, 4.4vw, 60px); }

.road-viewport {
  position: relative;
  z-index: 2;
  width: 100%;
  height: min(420px, 50vh);
  margin-top: clamp(22px, 4vh, 48px);
}

/* the gate: the fixed point the road passes through */
.road-gate {
  position: absolute;
  left: 50%; top: 14px; bottom: -14px;
  transform: translateX(-50%);
  width: 1px;
  z-index: 1;
  background: linear-gradient(180deg, transparent, rgba(148, 138, 255, 0.3) 14%, rgba(148, 138, 255, 0.3) 86%, transparent);
}
.road-gate i {
  position: absolute; left: 50%; top: 0; bottom: 0;
  width: 1px;
  transform: translateX(-50%);
  background: linear-gradient(180deg, transparent, var(--purple), transparent);
  opacity: var(--gateop, 0.3);
  box-shadow: 0 0 22px rgba(91, 76, 255, 0.7);
  transition: opacity 0.5s ease;
}

.road-track {
  --panel-w: min(600px, 80vw);
  position: absolute;
  left: 50%; top: 0;
  height: 100%;
  width: calc(var(--panel-w) * 4);
  display: flex; align-items: stretch;
  padding-top: 44px;
  z-index: 2;
  transform: translateX(calc(-12.5% - var(--k, 0) * 25%));
  will-change: transform;
}
.road-line {
  position: absolute;
  left: 6%; right: 6%; top: 22px;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(148, 138, 255, 0.35) 12%, rgba(255, 77, 128, 0.4) 88%, transparent);
}
.road-slot {
  flex: 0 0 25%;
  max-width: 25%;
  padding: 0 clamp(14px, 1.6vw, 26px);
  display: flex;
}
.road-card {
  position: relative;
  flex: 1 1 auto;
  display: flex; flex-direction: column;
  gap: var(--s1);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09);
  padding: clamp(24px, 2.4vw, 34px);
}
.js .road-card {
  opacity: 0.3;
  transform: translateY(14px) scale(0.965);
  transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.3, 0.7, 0.3, 1),
              border-color 0.5s ease, box-shadow 0.5s ease;
}
.js .road-slot.is-focus .road-card {
  opacity: 1;
  transform: none;
  border-color: rgba(148, 138, 255, 0.45);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09),
              0 22px 60px rgba(0, 0, 0, 0.5), 0 0 34px rgba(91, 76, 255, 0.14);
}
.js .road-slot.is-focus.is-now .road-card {
  border-color: rgba(255, 125, 161, 0.5);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09),
              0 22px 60px rgba(0, 0, 0, 0.5), 0 0 40px rgba(255, 77, 128, 0.2);
}
.road-node {
  position: absolute;
  top: -26px; left: 50%;
  transform: translateX(-50%);
  width: 9px; height: 9px;
  border-radius: 50%;
  background: #16161d;
  border: 1px solid var(--line-strong);
  transition: background 0.5s ease, border-color 0.5s ease, box-shadow 0.5s ease;
}
.road-slot.is-focus .road-node {
  background: var(--purple);
  border-color: var(--purple-soft);
  box-shadow: 0 0 16px rgba(91, 76, 255, 0.9);
}
.road-slot.is-now .road-node {
  background: var(--pink);
  border-color: var(--pink-soft);
  box-shadow: 0 0 16px rgba(255, 77, 128, 0.85);
}
.road-year {
  font-size: 12px; letter-spacing: 0.2em;
  color: var(--purple-soft);
}
.road-slot.is-now .road-year { color: var(--pink-soft); }
.road-name {
  font-size: clamp(23px, 2.4vw, 31px);
  font-weight: 600; letter-spacing: -0.02em; line-height: 1.15;
  margin-top: 4px;
}
.road-body {
  font-size: 14.5px; line-height: 1.72;
  color: var(--mist);
  margin-top: var(--s2);
}
.road-here {
  display: inline-flex; align-items: center; gap: 9px;
  align-self: flex-start;
  font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
  color: var(--pink-soft);
  border: 1px solid rgba(255, 77, 128, 0.45);
  border-radius: 100px;
  background: rgba(255, 77, 128, 0.08);
  padding: 6px 14px;
  margin-bottom: var(--s2);
}
.road-here-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--pink);
  box-shadow: 0 0 12px rgba(255, 77, 128, 0.95);
  animation: pulse 2.4s ease-in-out infinite;
}
/* the STAGE 01/04 tally lived here (.road-meta / .road-meta em) and is
   deleted along with #road-idx and its per-frame lookup in js/about.js */


/* ============================================================
   REST — HOW WE THINK  ·  THE COMPOSITE

   WAS four equal bordered glass cards on a 4-up grid, each an
   authored mono index over a 21px name over a 14px line — the
   same brick as "Who we work with" and "Data security".

   THE RUN-IN HEAD, the oldest of the three head positions in book
   design: the name sits INSIDE its own first line. h3 and p are
   both display:inline, so they flow as one paragraph, and the
   oversized inline name forces its own line box open. That
   leading mismatch is the entire composition — there is no
   border, no radius, no backdrop-filter, no grid, no separator,
   no index and no hover.

   The four approved verbs (Finds / Brings / Stands / Builds) take
   --text at weight 500 so four grey lines resolve into four
   predicates of one implied subject: "one firm, four characters",
   performed, with not a word authored.

   Zero JS, zero .reveal on any ancestor of an approved word.
   ============================================================ */
.arch-composite {
  position: relative;
  z-index: 1;
  max-width: 64ch;
  margin-top: clamp(40px, 6vh, 68px);
  display: flex; flex-direction: column;
  gap: clamp(26px, 3.4vh, 44px);
}
.arch-who {
  display: inline;
  font-size: clamp(23px, 2.5vw, 33px);
  font-weight: 600;
  letter-spacing: -0.022em;
  color: var(--text);
  white-space: nowrap;
  margin-right: 0.42em;
}
.arch-does {
  display: inline;
  font-size: clamp(15.5px, 1.35vw, 17.5px);
  line-height: var(--arch-lead, 1.95);
  color: var(--mist);
}
.arch-v { color: var(--text); font-weight: 500; }


/* the roadmap holds its cards at 0.3 until the scene lights them —
   not a readable resting state, so it needs the .abt-live guard.
   This is now the ONLY such rule on the page: the beliefs deck used
   to need one too (five absolutely-positioned cards at --bop: 0) and
   it is deleted along with the deck itself, rather than rewritten. */
.js:not(.abt-live) .road-card { opacity: 1; transform: none; }
/* ...and the track itself must unstack, not just un-dim. The roadmap is a
   horizontal track translated by --k, which ONLY js/about.js writes. If that
   file dies while the engine lives, --k never arrives, the track stays parked
   on panel 0, and three of the four approved stages sit outside the viewport —
   clipped at every scroll position, which is worse than no-JS because the
   no-JS path below unstacks them properly. Mirror that path here. */
/* padding-left: 0 undoes the phone gutter the spine lives in below — with
   --k dead there is no spine and no node column to leave room for. */
.js:not(.abt-live) .road-viewport { position: static; height: auto; margin-top: var(--s5); padding-left: 0; }
.js:not(.abt-live) .road-track {
  position: static; width: 100%;
  flex-direction: column; gap: var(--s3);
  padding-top: 0; transform: none;
}
.js:not(.abt-live) .road-slot { flex: 0 0 auto; max-width: 100%; padding: 0; }
.js:not(.abt-live) .road-gate,
.js:not(.abt-live) .road-line { display: none; }


/* ============================================================
   REST — WHAT WE BELIEVE  ·  THE ARTICLES

   WAS a 460vh pinned scene: an absolutely-stacked deck of five
   cards dealt one at a time on --bop/--bty against a mono 01/05
   tally, which held four of five approved convictions at opacity
   0 at any scroll position and needed SIX defensive blocks to be
   safe (a first-child fallback, a .js:not(.abt-live) rescue, two
   !important overrides at 980px, a no-JS block and a reduced-
   motion block). It also ran the roadmap's exact grammar sixty
   viewport-heights later, and the pair cost 860vh.

   Five convictions are simultaneous, not sequential — dealing
   them asserted an order the copy does not have.

   THE HUNG FOLIO. The deck's own 01-05 sit in a margin column
   under a SHORT hairline that spans only the folio: a
   compositor's mark, never a full-measure row rule, so it cannot
   read as the table "Who we work with" just stopped being. The
   statements rise to display scale because they are aphorisms and
   should be the loudest type in any rest on this page.

   THE PAYOFF SURVIVES: the world still writes each belief's
   number in particles. The driver is now an IntersectionObserver
   in js/about.js, not a 460vh per-frame scrub.

   PURE CASCADE. No scene, no custom property, no .reveal on any
   ancestor of an approved word, no .abt-live rule, no !important
   anywhere. All five are at full weight on the first painted
   frame in every state, so nothing below needs to rescue them.
   ============================================================ */
.bel-articles {
  position: relative;
  z-index: 1;
  margin-top: clamp(40px, 6vh, 68px);
  display: flex; flex-direction: column;
  gap: clamp(48px, 7vh, 88px);
}
.bel-article {
  display: grid;
  grid-template-columns: clamp(48px, 6vw, 84px) minmax(0, 1fr);
  column-gap: clamp(18px, 2.4vw, 34px);
  align-items: baseline;
}
/* the folio and its short rule: the rule stops at the folio column,
   so five stacked entries draw five 48-84px marks and never a table */
.bel-folio {
  grid-column: 1; grid-row: 1;
  font-size: 12px; letter-spacing: 0.24em;
  color: var(--purple-soft);
}
.bel-folio::after {
  content: "";
  display: block;
  height: 1px;
  margin-top: 12px;
  background: linear-gradient(90deg, rgba(148, 138, 255, 0.5), rgba(148, 138, 255, 0));
}
.bel-h3 {
  grid-column: 2; grid-row: 1;
  font-size: clamp(25px, 3vw, 40px);
  font-weight: 600; line-height: 1.14; letter-spacing: -0.022em;
  max-width: 24ch;
  text-wrap: balance;
}
.bel-body {
  grid-column: 2; grid-row: 2;
  margin-top: var(--s2);
  font-size: clamp(15.5px, 1.35vw, 17.5px);
  line-height: 1.82;
  color: var(--mist);
  max-width: 60ch;
}


/* ============================================================
   REST — WHO WE WORK WITH  ·  THE REGISTER

   WAS an <ol> of rows on a 64px / 300px / 1fr grid, each with a
   border-top, an authored index the deck never printed, and a
   hover padding-shift.

   THE MARGINAL HEAD, and the scale INVERTED. The segment name is
   small and ranged right in a narrow margin; its approved
   sentence is big, in --text, beside it; align-items: baseline
   drops the small head onto the big body's FIRST baseline, and
   that shared baseline is the whole gesture. It costs nothing and
   it is the exact inverse of the spread below (big head above a
   small block) and of the composite above (big head inside a
   small line) — a difference that survives at thumbnail size,
   which is how a page is judged "does this feel the same".

   No rule, no cell, no index, no hover shift, no JS.
   ============================================================ */
/* "Who we work with" is a plain grid of boxes — no scroller.
   It was a horizontal shelf, which meant the fifth segment lived off the
   right edge and only a trackpad could reach it comfortably; wheel and drag
   handlers got it moving but it still read as a clipped row rather than a
   section you could take in. Five boxes that simply wrap need no affordance,
   no handler and no explaining: three across, two under, everything on the
   page at once. Still deliberately dialled down — small label, ~15px --mist
   body — so the section supports the page rather than shouting over it. */
.aud-register {
  position: relative;
  z-index: 1;
  list-style: none;
  margin: clamp(30px, 4.5vh, 52px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: clamp(14px, 1.5vw, 22px);
}
.aud-entry {
  display: flex; flex-direction: column;
  gap: 10px;
  padding: clamp(16px, 1.5vw, 22px);
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.014);
}
.aud-who {
  text-align: left;
  font-size: clamp(10.5px, 0.9vw, 12px);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--purple-soft);
}
.aud-what {
  font-size: clamp(14px, 1.1vw, 16px);
  line-height: 1.56;
  letter-spacing: -0.004em;
  color: var(--mist);
  max-width: 100%;
}


/* ============================================================
   SCENE — THE ANCHOR  (where we work)

   Delaware is the datum. It does not arrive with the reader —
   an anchor is the fixed point, so it is already set and at full
   weight on the first frame. The two Indian stations are what
   move: they come in OFF REGISTER, adrift and tilted, and align
   themselves to Delaware's line as the scroll advances. When both
   have landed, the datum seals. "The standard every file must
   meet", performed instead of asserted.

   SAFETY — both custom properties DEFAULT TO THEIR FINISHED VALUE
   (--set: 1, --reg: 1). A missing js/main.js, a dead engine,
   reduced motion and narrow widths therefore all render the
   aligned state with every approved sentence at full weight.
   js/about.js only ever drives them DOWN from finished and back.
   ============================================================ */
.anch-scene { height: 260vh; }
.anch-stage { justify-content: center; }

.anch-head {
  position: relative; z-index: 2;
  width: min(1200px, 100%);
}
.anch-head .chapter-line { margin-bottom: clamp(14px, 2.4vh, 28px); }
.anch-h2 { font-size: clamp(28px, 3.6vw, 50px); }

.anch-body {
  position: relative; z-index: 2;
  width: min(1200px, 100%);
  margin-top: clamp(26px, 4.4vh, 54px);
  display: grid;
  grid-template-columns: minmax(0, 1.14fr) 1px minmax(0, 1fr);
  column-gap: var(--tie, clamp(30px, 4vw, 64px));
  align-items: stretch;
}

/* ---- the datum: the line everything else registers to ---- */
.anch-datum {
  position: relative;
  display: block;
  width: 1px;
  align-self: stretch;
  background: linear-gradient(180deg,
    transparent, rgba(255, 255, 255, 0.09) 10%,
    rgba(255, 255, 255, 0.09) 90%, transparent);
}
.anch-datum-line {
  position: absolute; left: 0; top: 0;
  width: 1px; height: 100%;
  transform-origin: 50% 0;
  transform: scaleY(var(--set, 1));
  background: linear-gradient(180deg,
    transparent, rgba(242, 243, 247, 0.5) 10%,
    rgba(242, 243, 247, 0.5) 90%, transparent);
  opacity: 0.8;
  transition: opacity 0.6s ease;
}
.anch-stage.registered .anch-datum-line { opacity: 1; }
.anch-seal {
  position: absolute; left: 50%; top: 0;
  width: 11px; height: 11px;
  margin: -5px 0 0 -5.5px;
  border-radius: 50%;
  border: 1px solid rgba(242, 243, 247, 0.5);
  background: #12121a;
  transform: scale(var(--set, 1));
  transition: background 0.6s ease, box-shadow 0.6s ease, border-color 0.6s ease;
}
.anch-stage.registered .anch-seal {
  background: var(--text);
  border-color: var(--text);
  box-shadow: 0 0 18px rgba(242, 243, 247, 0.75);
}

/* ---- the card, shared by the anchor and the two stations ---- */
.place {
  position: relative;
  display: flex; flex-direction: column;
  gap: var(--s2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(13, 13, 19, 0.55);
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09);
  padding: var(--s4) var(--s3) var(--s3);
  overflow: hidden;
}
.js .place {
  transition: opacity 0.75s cubic-bezier(0.22, 0.6, 0.2, 1),
              transform 0.4s cubic-bezier(0.3, 0.7, 0.3, 1),
              border-color 0.35s ease, box-shadow 0.35s ease;
}
.place:hover {
  transform: translateY(-5px);
  border-color: rgba(148, 138, 255, 0.45);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09),
              0 18px 46px rgba(0, 0, 0, 0.45), 0 0 30px rgba(91, 76, 255, 0.12);
}
.place-coord { font-size: 11px; letter-spacing: 0.12em; color: var(--faint); }
.place-name {
  font-size: clamp(21px, 2.1vw, 26px);
  font-weight: 600; letter-spacing: -0.018em; line-height: 1.2;
}
.place-dash { color: var(--faint); font-weight: 400; }
.place-role { color: var(--pink); font-weight: 500; }
.place-body { font-size: 14.5px; line-height: 1.75; color: var(--mist); }
.place-clock {
  display: flex; align-items: baseline; gap: 12px;
  margin-top: auto;
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
  font-size: 11px; letter-spacing: 0.18em;
  color: var(--faint);
}
.place-clock i {
  font-style: normal;
  font-size: 15px; letter-spacing: 0.04em;
  color: var(--text);
}
/* Only the LIVE value needs a script — "DELAWARE · ET", "MUMBAI · IST" and
   "AHMEDABAD · IST" are static approved copy that names the three offices.
   This rule used to be `.no-js .place-clock { display: none }`, which took
   all three down with the clock: the only place in the codebase where a
   fallback deleted approved copy instead of resting it. The row keeps its
   rule and its labels; the "--:--" placeholder and the empty offset span
   (both written by js/about.js) are what go. */
.no-js .place-clock .seam-tz,
.no-js .place-clock .place-offset { display: none; }

/* ---- the anchor itself ---- */
.place-anchor {
  border-color: rgba(255, 255, 255, 0.24);
  background: rgba(255, 255, 255, 0.045);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.13),
              0 0 54px rgba(255, 255, 255, 0.05);
}
/* the anchor is the one white thing on the page — it does not take
   the editorial pink, and it is no longer demoted to structural purple */
.place-anchor .place-role { color: var(--text); }
.anch-name {
  font-size: clamp(30px, 3.5vw, 46px);
  line-height: 1.06;
  letter-spacing: -0.026em;
}
.anch-name .place-dash,
.anch-name .place-role {
  font-size: clamp(16px, 1.45vw, 20px);
  letter-spacing: -0.008em;
}
.anch-cross {
  position: relative;
  display: inline-block;
  width: 13px; height: 13px;
  margin-right: 11px;
  vertical-align: -2px;
}
.anch-cross::before,
.anch-cross::after {
  content: "";
  position: absolute;
  background: var(--text);
  opacity: 0.85;
}
.anch-cross::before {
  left: 0; right: 0; top: 6px; height: 1px;
  transform: scaleX(var(--set, 1));
}
.anch-cross::after {
  top: 0; bottom: 0; left: 6px; width: 1px;
  transform: scaleY(var(--set, 1));
}
.anch-lede {
  color: var(--text);
  font-size: clamp(15px, 1.3vw, 16.5px);
}
.anch-territory {
  margin-top: auto;
  padding-top: clamp(16px, 2.4vh, 26px);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.anch-terr-label {
  font-size: 10px; letter-spacing: 0.26em;
  color: var(--faint);
  margin-bottom: 10px;
}
.anch-terr { font-size: 13.5px; line-height: 1.8; }
/* the eleven states are the anchor's territory: the sentence is
   verbatim, only the place names carry the weight */
.anch-st { color: var(--text); }
/* the territory block takes the card's slack; the reference clock
   sits straight under it rather than splitting the space with it */
.place-anchor .place-clock { margin-top: 0; }
.place-ref { margin-left: auto; color: var(--faint); font-size: 10px; letter-spacing: 0.2em; }

/* ---- the two stations ---- */
.anch-stations {
  display: flex; flex-direction: column;
  gap: var(--s2);
}
.place-station {
  --reg: 1;
  flex: 1 1 auto;
  overflow: visible;
  transform: translate(calc((1 - var(--reg)) * 56px), calc((1 - var(--reg)) * 20px))
             rotate(calc((1 - var(--reg)) * 0.85deg));
  opacity: calc(0.34 + 0.66 * var(--reg));
}
/* the register transform is scrubbed per frame — it must not be
   chased by a transition, but the chrome around it may settle */
.js .place.place-station {
  transition: border-color 0.4s ease, box-shadow 0.4s ease;
}
.place-station:hover {
  transform: translate(calc((1 - var(--reg)) * 56px), calc((1 - var(--reg)) * 20px - 5px))
             rotate(calc((1 - var(--reg)) * 0.85deg));
}
.place-station.in-register {
  border-color: rgba(255, 255, 255, 0.18);
}
/* the tie: drawn from the datum outward, closing only when the
   station has actually arrived */
.anch-tie {
  position: absolute;
  top: 30px; right: 100%;
  width: var(--tie, clamp(30px, 4vw, 64px));
  height: 1px;
  transform-origin: 0 50%;
  transform: scaleX(var(--reg, 1));
  background: linear-gradient(90deg, rgba(242, 243, 247, 0.55), rgba(148, 138, 255, 0.5));
}
.anch-mark {
  position: absolute;
  left: -4.5px; top: 26px;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: #16161d;
  border: 1px solid var(--line-strong);
  transition: background 0.45s ease, border-color 0.45s ease, box-shadow 0.45s ease;
}
.place-station.in-register .anch-mark {
  background: var(--text);
  border-color: var(--text);
  box-shadow: 0 0 16px rgba(242, 243, 247, 0.7);
}
.place-station .place-body { font-size: 14px; }
/* the offset is measured FROM the anchor — written live by js/about.js */
.place-offset {
  margin-left: auto;
  font-size: 10px; letter-spacing: 0.16em;
  color: var(--faint);
  white-space: nowrap;
}


/* ============================================================
   REST — DATA SECURITY  ·  THE LEXICON SPREAD

   Was a 480vh pinned scene (eight controls at opacity 0.26,
   armed one at a time against an nn/08 tally). The last pass cut
   the scroll but kept the device: ol > li > [index][name][line]
   with four brackets in a right gutter that vanished below
   1150px — i.e. #audience, two sections earlier, plus a chip.

   It is now a book of defined terms. The name is set at display
   scale, its approved line is broken at ITS OWN full stops into
   separately-set specifications, and the eight entries flow
   through a CSS multi-column text measure — a construction that
   exists nowhere else on this site (no other stylesheet declares
   `columns`). Two hairline gestures, exact opposites, carry the
   argument: the adjectives above are STRUCK OUT; each
   specification's leftover space is RULED OFF.

   NOTHING IS INVENTED. No taxonomy, no group, no kind, no chip,
   no bracket, no counter, no per-item promotion. The only
   sub-structure is the split at the copy's own periods, which
   falls out 3/2/1/2/2/2/2/1 — fifteen rules of fifteen different
   lengths, so no two entries share a silhouette.

   THE RULE IS LAYOUT, NOT MEASUREMENT. `width:100%` cancelled by
   `margin-right: calc(-100% - gap)` gives the rule ZERO advance,
   so it can never force a wrap or overflow; `overflow:hidden` on
   its own block clips it at the measure. Correct under wrapping,
   a late font swap, and any zoom. Nothing is measured in JS.

   PE — THE BOOK CARRIES NO .reveal, AND NEITHER DOES ANY
   ANCESTOR OF AN APPROVED WORD. css/main.css sets
   `.js .reveal:not(.in) { opacity: 0 }` and opacity applies to
   the whole subtree, so a .reveal wrapper would hold all eight
   controls at zero until the observer fired. Nothing here is
   driven at all now: the last moving part was keyed off the
   struck-adjectives band, and both are gone. No scene, no
   .abt-live rule, no custom property waiting on a script.

   AND IT IS NOW THE PAGE'S ONLY NUMBERED SECTION. Nine authored
   indices went out of "How we think" and "Who we work with"
   because the deck never printed them; these eight stay because
   the deck did. That is what makes them mean something, and it
   is why this section is otherwise left alone.
   ============================================================ */

/* #security sits over the LIVE globe region (js/about.js) and is
   deliberately absent from the shared wash rule below, because the
   old opaque plate supplied its own ground. The plate is gone, so
   the section supplies a local one. Scoped to .abt-secure only. */
.abt-secure::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    transparent, rgba(10, 10, 14, 0.62) 8%,
    rgba(10, 10, 14, 0.62) 92%, transparent);
}
.abt-secure {
  --nam-rule: rgba(148, 138, 255, 0.40);
  --nam-rule-on: rgba(242, 243, 247, 0.72);
  --nam-indent: clamp(14px, 1.6vw, 22px);
  --nam-gap: clamp(28px, 3.4vw, 46px);
  --nam-tail: 10px;                       /* clear space before a rule starts */
  --nam-reserve: clamp(26px, 3vw, 40px);  /* strip the text may not enter, but the rule may */
}

.nam-head { max-width: 1180px; }
.nam-intro {
  margin-top: clamp(22px, 3.4vh, 34px);
  max-width: 64ch;
  font-size: clamp(15.5px, 1.35vw, 17.5px);
  line-height: 1.78;
  color: var(--mist);
}

/* The .nam-struck / .nam-x band of struck-out marketing adjectives was
   removed from the markup an earlier pass ago — it put a competitive
   claim in the client's voice — but ~45 lines of CSS for it survived,
   including `.js .nam-struck:not(.in) ~ .nam-book .nam-sn::after
   { --rx: 0 }`, a selector that can never match. All of it is deleted
   here. --rx already falls back to 1, so the remainder rules render
   byte-identically; this is pure dead-code removal. */

/* ---- the spread ---- */
.nam-book { margin-top: clamp(26px, 3.6vh, 44px); }

/* the ONLY hairlines that cross the full measure: a head rule and
   a foot rule, the way a printed page carries them. There is no
   rule between entries — a separator per item is #audience's
   grammar and it is exactly what this section is escaping. */
.nam-guide {
  display: flex; justify-content: space-between; gap: 12px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 12px 0;
  font-size: 9.5px; letter-spacing: 0.26em;
  color: var(--faint);
}

.nam-flow { margin-top: clamp(24px, 3vh, 38px); max-width: 70ch; }

.nam-entry { margin-bottom: var(--nam-gap); }
.nam-entry:last-child { margin-bottom: 0; }

.nam-term {
  font-size: clamp(20px, 2.15vw, 27px);
  font-weight: 600;
  letter-spacing: -0.018em;
  line-height: 1.16;
  text-wrap: balance;
}
/* a superior reference figure fused to the first word — NOT an
   index cell. Eight stacked entries produce no column of digits. */
.nam-ref {
  font-size: 10.5px; letter-spacing: 0.16em;
  color: var(--purple-soft);
  vertical-align: 0.66em;
  margin-right: -0.14em;
}
.nam-stop { color: var(--purple-soft); transition: color 0.3s ease; }

/* the lexicon hanging indent: the term hangs left of its own
   specifications, so name and line do NOT share an x-origin. */
.nam-spec {
  margin: 9px 0 0 var(--nam-indent);
  font-size: clamp(13.5px, 1.05vw, 15px);
  line-height: 1.62;
  color: var(--mist);
}

/* ---- THE REMAINDER RULE ----
   Each specification is its own block; the hairline runs from the
   end of its last line to the right edge of the measure. A form
   where every blank is filled and the remainder ruled off, so
   nothing can be added later.

   LOAD-BEARING, three parts, none of them optional:
   1. `overflow: hidden` clips the rule at the PADDING box.
   2. `margin-right: calc(-100% - tail)` cancels the width, so the
      box advances 0px and can never wrap or force overflow.
   3. `padding-right: --nam-reserve` reserves a strip the text may
      not enter but the rule may. That is what guarantees the rule
      is never a 2px stub and never vanishes: its minimum length is
      (reserve - tail), i.e. 16-30px, at every measure and after
      every wrap. Without it the gesture flickers in and out as the
      window resizes, which is the one real defect this device had.
   ---- */
.nam-sn {
  display: block;
  overflow: hidden;
  padding-right: var(--nam-reserve);
  padding-bottom: 2px;
}
.nam-sn::after {
  content: "";
  display: inline-block;
  width: 100%;
  margin-left: var(--nam-tail);
  margin-right: calc(-100% - var(--nam-tail));
  height: 1px;
  vertical-align: 0.3em;
  background: var(--nam-rule);
  transform: scaleX(var(--rx, 1));
  transform-origin: 0 50%;
}

/* .nam-colophon ("EVERY STATEMENT ABOVE NAMES A THING. NONE OF THEM IS
   AN ADJECTIVE.") is deleted with its markup: an authored editorial
   claim in the client's voice, wearing mono chrome, which is the only
   reason it outlived the struck-adjectives band it argued for. */

/* the spread opens to two columns; the gutter is whitespace, never
   a column-rule, so it cannot read as a table divider. */
@media (min-width: 900px) {
  .nam-flow {
    max-width: none;
    columns: 2;
    column-gap: clamp(36px, 5vw, 76px);
  }
  .nam-entry { break-inside: avoid; -webkit-column-break-inside: avoid; }
}

/* The remainder rules used to draw in on arrival, keyed off the struck
   adjectives band's .in as a SIBLING. The band is gone, so the trigger
   is gone and --rx rests at its finished value of 1: the rules are
   simply drawn, statically, in every state. Only the hover tint below
   still needs a transition. */
.js .nam-sn::after { transition: background 0.3s ease; }

/* pointer only, self-scoped, and never a demotion: nothing outside
   the hovered entry changes and no word is dimmed anywhere. */
@media (hover: hover) {
  .nam-entry:hover .nam-sn::after { background: var(--nam-rule-on); }
  .nam-entry:hover .nam-stop { color: var(--text); }
}


/* ============================================================
   REST — THE FOUNDERS  ·  THE SHEET

   The deepest copy on the site used to be its flattest surface:
   27px names, 15.5px grey bios, a 01/03 counter, no device, and
   a hand-written full-bleed veil dimming the whole viewport just
   to make it legible. It is now set as a publication — one
   sheet, three profiles, display-scale names, a real deck
   paragraph, hung folios and breath marks, and the axis flipped
   on the second partner.

   The one live idea is the MARGIN TALKING BACK: each credential
   token is wired to the exact approved clause that awards it and
   takes weight when that clause crosses the reading line.

   SAFETY DOCTRINE — the un-earned token sits at var(--purple-soft),
   which is precisely the colour the credential string has today.
   Earning is a PROMOTION to var(--text); it is never a demotion
   from a readable resting state. And the pre-state is scoped
   .abt-live, so a dead js/about.js renders everything earned
   rather than stranded. Nothing here needs a
   .js:not(.abt-live) un-do rule.
   ============================================================ */
/* the long readings win the contrast fight, but the founders no
   longer need a curtain: the sheet below supplies its own ground,
   so this drops from a flat 0.72 blanket to a light wash that only
   has to carry the chapter head. The globe survives underneath a
   long read for the first time on this page. */
.abt-think::before,
.abt-beliefs::before,
.abt-audience::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    transparent, rgba(10, 10, 14, 0.55) 6%,
    rgba(10, 10, 14, 0.34) 26%,
    rgba(10, 10, 14, 0.34) 94%, transparent);
}
/* The cube sits centred behind this section, so the founders go back to a flat
   ground rather than the centred one the gutter braid wanted. Held at 0.46: a
   wireframe of twelve edges is sparse, so the read stays clean while the object
   is still plainly visible through it. */
.abt-founders::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    transparent, rgba(10, 10, 14, 0.46) 6%,
    rgba(10, 10, 14, 0.46) 94%, transparent);
}
/* the three card-less rests take the stronger wash. .abt-beliefs joins
   them in this pass: un-pinning the beliefs cost them their deck, and
   they now sit directly on 'digits' — the loudest formation this page
   draws under a long read. Approved copy must never depend on a card
   for legibility, and five convictions at display scale over a live
   particle field is exactly where that would show. */
.abt-think::before,
.abt-beliefs::before,
.abt-audience::before {
  background: linear-gradient(180deg,
    transparent, rgba(10, 10, 14, 0.6) 14%,
    rgba(10, 10, 14, 0.6) 86%, transparent);
}

/* ---- the sheet: paper held up against the world.
        Flat ground, NO backdrop-filter — this surface is several
        viewports tall and blur at that size over a live particle
        canvas is a compositing bill nobody should pay. Deliberate;
        do not "make it match .place". ---- */
.fnd-list {
  position: relative;
  z-index: 1;
  margin-top: clamp(44px, 7vh, 86px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(10, 10, 14, 0.82);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08),
              0 40px 120px -40px rgba(0, 0, 0, 0.7);
  padding: clamp(34px, 4.4vw, 78px) clamp(26px, 3.8vw, 70px);
}

.fnd + .fnd {
  border-top: 1px solid var(--line);
  margin-top: clamp(64px, 9vh, 128px);
  padding-top: clamp(64px, 9vh, 128px);
}

/* ---- the standing: name, then role, in the deck's own order ---- */
.fnd-slug { position: relative; width: fit-content; max-width: 100%; }
.fnd-name {
  font-size: clamp(28px, 3.3vw, 44px);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.032em;
  max-width: 14ch;
  text-wrap: balance;
}
/* "CA." hung as a mono folio in the gutter. It is approved copy, so
   it is NOT aria-hidden and it stays inside the heading's own text. */
.fnd-hon {
  font-family: var(--mono-f);
  font-size: 14px; letter-spacing: 0.24em; font-weight: 600;
  color: var(--muted);
  position: absolute;
  right: calc(100% + 0.7em); top: 0.66em;
  white-space: nowrap;
}
.fnd-role {
  font-size: clamp(14px, 1.2vw, 16px);
  color: var(--pink);
  margin-top: clamp(12px, 1.6vh, 18px);
}

/* "the set": the sorts start loose and close as the block arrives.
   Rides js/main.js's own .reveal observer — one CSS property, no
   JS, and it still plays if js/about.js is dead. */
.js .fnd-standing .fnd-name {
  letter-spacing: 0.1em;
  transition: letter-spacing 1.2s cubic-bezier(0.16, 0.84, 0.24, 1);
}
.js .fnd-standing.in .fnd-name { letter-spacing: -0.032em; }

/* the composing stick: two rules snap around the name, hold, release */
.fnd-stick { position: absolute; inset: 0; pointer-events: none; }
.fnd-stick i {
  position: absolute; left: 0; width: 100%; height: 1px;
  background: var(--purple-soft);
  transform: scaleX(0); transform-origin: 0 50%;
  opacity: 0;
}
.fnd-stick i:first-child { top: -0.32em; }
.fnd-stick i:last-child { bottom: -0.22em; }
.js .fnd-standing.in .fnd-stick i { animation: fnd-stick 1.5s cubic-bezier(0.3, 0.7, 0.3, 1) both; }
.js .fnd-standing.in .fnd-stick i:last-child { animation-delay: 0.07s; }
@keyframes fnd-stick {
  0%   { transform: scaleX(0); opacity: 0; }
  16%  { transform: scaleX(1); opacity: 0.6; }
  62%  { transform: scaleX(1); opacity: 0.6; }
  100% { transform: scaleX(1); opacity: 0; }
}

/* ---- the body: margin beside a measured reading column ---- */
.fnd-body {
  display: grid;
  grid-template-columns: minmax(190px, 260px) minmax(0, 1fr);
  gap: clamp(34px, 4.6vw, 88px);
  margin-top: clamp(32px, 4.6vh, 60px);
}
.fnd-flip .fnd-body { grid-template-columns: minmax(0, 1fr) minmax(190px, 260px); }
.fnd-flip .fnd-spec { grid-column: 2; grid-row: 1; }
.fnd-flip .fnd-read { grid-column: 1; grid-row: 1; }
.fnd-flip .fnd-standing { text-align: right; }
.fnd-flip .fnd-slug { margin-left: auto; }
/* The folio hangs on the LEADING edge of the name in both axes. Flipping it to
   the far side made the flipped partner render as "Ankit N. Shah CA." — the
   approved string "CA. Ankit N. Shah" reordered on screen. Visual order is
   reading order, and approved copy does not get reordered for symmetry. */
.fnd-flip .fnd-hon { right: calc(100% + 0.7em); left: auto; }
.fnd-flip .fnd-name { max-width: 100%; }

.fnd-spec {
  position: sticky;
  top: calc(var(--nav-h) + var(--s4));
  align-self: start;
}
/* ---------- the founder's own frame, out of the group negative ----------
   All three portraits are the same photograph at the same scale, moved
   sideways: --fx picks the man. Because they share one frame they share
   one light, so the three read as a set rather than three snapshots.
   The studio backdrop is the page's own black, so the picture is masked
   out at the foot instead of cut — he stands in the column. */
.fnd-face {
  display: block;
  aspect-ratio: 4 / 5;
  margin-bottom: var(--s3);
  background-image: url(../assets/founders.jpg?v=2);
  background-repeat: no-repeat;
  background-size: 380% auto;   /* tight enough that no neighbour intrudes */
  background-position: var(--fx, 50%) var(--fy, 0%);
  -webkit-mask-image: linear-gradient(180deg, #000 58%, rgba(0, 0, 0, 0.35) 84%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 58%, rgba(0, 0, 0, 0.35) 84%, transparent 100%);
}
/* --fx is a background-position, NOT the face's position in the negative, and
   the two are only equal at 50%. With background-size: 380%, a face sitting at
   F% of the photo lands at (3.8F - 2.8P)% of the frame — so Kalpesh, whose head
   is at 75.4% of the group shot, was landing at 31.7%: hard against the left of
   his own portrait. Solving 3.8F - 2.8P = 50 for each man centres him:
     Nilesh   F 48.4  ->  P 47.8
     Kalpesh  F 75.4  ->  P 84.5
   (Ankit is not here: he has his own studio frame and already lands at 48%.) */
.fnd[data-fnd="0"] .fnd-face { --fx: 47.8%; --fy: 12.5%; }  /* Nilesh  */
.fnd[data-fnd="2"] .fnd-face { --fx: 84.5%; --fy: 13%;   }  /* Kalpesh */

/* --fy lifts the two group crops. Only 43.9% of the negative's height fits
   this 4:5 frame, and both men sat low in it: ~29% of the frame was empty
   above their heads, against 12.7% for Ankit, who has his own studio
   portrait. That is why Kalpesh read as small in his own picture. The
   values above put both at ~13% headroom, so all three portraits now crop
   to the same shape. */

/* Ankit ships as his own studio portrait, not a crop of the group negative.
   ankit.jpg is already 4:5 — the exact frame — so it is zoomed only enough to
   bring his face to the same prominence the group-crops give Nilesh and
   Kalpesh, then positioned high so the mask fades the shirt, not the chin.
   The (0,3,0) selector clears both the base .fnd-face plate and the phone
   breakpoint's founders-1100 swap without needing !important. */
.fnd[data-fnd="1"] .fnd-face {
  --fx: 50%;
  background-image: url(../assets/ankit.jpg?v=2);
  background-size: 128% auto;
  background-position: 50% 6%;
}
.js .fnd[data-fnd="1"] .fnd-face:not(.in) { background-position: 50% 12%; }

/* the frame settles as it is read into place */
@media (prefers-reduced-motion: no-preference) {
  .js .fnd-face { transition: background-position 1.2s cubic-bezier(0.22, 0.6, 0.2, 1), opacity 0.75s ease; }
  .js .fnd-face:not(.in) { background-position: var(--fx, 50%) calc(var(--fy, 0%) + 8%); }
}

.fnd-creds {
  border-top: 1px solid var(--line);
  padding-top: var(--s2);
}
.fnd-creds dt {
  font-size: 9.5px; letter-spacing: 0.26em;
  color: var(--faint);
  margin-bottom: 12px;
}
.fnd-creds dd {
  margin: 0;
  font-size: 12.5px; letter-spacing: 0.06em; line-height: 2.3;
  color: var(--purple-soft);
}

/* the credential string is byte-identical: only the tokens are
   wrapped, and every " · " separator is a real text node, so the
   accessible string reads exactly as the deck wrote it. */
.cr {
  position: relative;
  /* inline-block + nowrap: a token must never fragment across a line,
     or its mark ends up beside the wrong half of its own name */
  display: inline-block;
  white-space: nowrap;
  font-weight: 400;
  padding-left: 16px;
  color: var(--purple-soft);
  transition: color 0.5s ease, text-shadow 0.5s ease;
}
.cr::before {
  content: "";
  position: absolute; left: 0; top: 0.42em;
  width: 6px; height: 6px;
  border-radius: 50%;
  border: 1px solid var(--purple-soft);
  background: transparent;
  transition: background 0.45s ease, box-shadow 0.45s ease,
              transform 0.45s cubic-bezier(0.3, 0.7, 0.3, 1);
}
.cr.is-earned { color: var(--text); text-shadow: 0 0 22px rgba(91, 76, 255, 0.35); }
.cr.is-earned::before {
  background: var(--purple-soft);
  box-shadow: 0 0 12px rgba(91, 76, 255, 0.7);
  transform: scale(1.15);
}
.cr.is-pointed { color: var(--text); }
.cr.is-pointed::before { background: var(--pink); box-shadow: 0 0 12px rgba(255, 77, 128, 0.8); }

/* the prose end of the same conversation: a hairline, never an
   underline, so it can never read as a link. */
.fnd-earn {
  transition: color 0.5s ease;
}
/* The clause that awards a credential used to carry a hairline under it,
   and a pink one while its token was pointed at. Both are gone: the bios
   read as prose, not as a page of part-underlined sentences. The link
   between token and clause survives — the clause still brightens to --text
   when you point at its credential, and the token still fills either way,
   so nothing about the device is lost except the rule. */
.fnd-earn.is-pointed { color: var(--text); }

/* the not-yet state is the ONLY thing scoped to .abt-live, and it is
   a colour the reader can already read. A dead js/about.js therefore
   renders the whole margin earned. */
@media (prefers-reduced-motion: no-preference) {
  .abt-live .cr:not(.is-earned) { color: var(--purple-soft); }
  .abt-live .cr:not(.is-earned)::before { background: transparent; box-shadow: none; }
}
/* ...except that was not actually true when written: `.cr` already rests at
   --purple-soft (line 1077), so the .abt-live rule above only repeats it and a
   dead js/about.js left every token un-earned rather than earned. Two verifiers
   caught the comment asserting behaviour the CSS did not have. This is the rule
   that makes the claim true — with the script dead, the whole margin reads as
   earned, at full weight, with its mark lit. */
.js:not(.abt-live) .cr { color: var(--text); }
.js:not(.abt-live) .cr::before {
  background: var(--purple-soft);
  box-shadow: 0 0 12px rgba(91, 76, 255, 0.7);
}

.fnd-read { display: flex; flex-direction: column; gap: clamp(24px, 3.4vh, 42px); }
/* the first paragraph is a real deck. Kalpesh's "Every young firm
   needs one person who has already seen everything." finally lands
   at 23px in --text instead of 15.5px grey. */
.fnd-deck {
  font-size: clamp(17px, 1.4vw, 20px);
  line-height: 1.62;
  letter-spacing: -0.008em;
  color: var(--text);
  max-width: 56ch;
}
.fnd-read p:not(.fnd-deck):not(.fnd-turn):not(.fnd-quote) {
  position: relative;
  font-size: clamp(15.5px, 1.35vw, 17.5px);
  line-height: 1.84;
  color: var(--mist);
  max-width: 64ch;
}
/* breath marks, hung in the gutter.
   The empty alt-text component keeps the glyph decorative: Chrome, Firefox and
   Safari all expose CSS generated content to the accessibility tree, so without
   it a screen reader reads an em dash into the middle of five approved
   paragraphs. `content: "—" / ""` renders the mark and announces nothing. */
.fnd-read p:not(.fnd-deck):not(.fnd-turn):not(.fnd-quote)::before {
  content: "—" / "";
  position: absolute; left: -1.6em; top: 0;
  font-size: 0.9em;
  color: var(--purple-soft);
  opacity: 0.45;
}
.fnd-flip .fnd-read p:not(.fnd-deck):not(.fnd-turn):not(.fnd-quote)::before { left: auto; right: -1.6em; }

/* the page's own summary of itself, verbatim, where the deck puts it */
/* THE PUNCHLINE GETS KALPESH'S TREATMENT.
   It was written as its own small italic line, and then lost: the body rule
   above is p:not(...):not(...), which scores (0,3,1) against this block's
   (0,1,0), so the quote inherited grey --mist at body size and picked up the
   hanging em-dash breath mark meant for ordinary paragraphs. Exempting it
   above and scoping this to .fnd-read gives it the same pink, weight and glow
   the closing line already carries, and [data-words] in the markup lights it
   the same way. */
.fnd-read .fnd-quote {
  margin-top: var(--s2);
  font-size: clamp(19px, 1.9vw, 25px);
  line-height: 1.42;
  font-weight: 500;
  letter-spacing: -0.014em;
  color: var(--pink);
  text-shadow: 0 0 34px rgba(255, 77, 128, 0.22);
  max-width: 32ch;
}
.fnd-read .fnd-turn {
  margin-top: var(--s2);
  font-size: clamp(21px, 2.2vw, 28px);
  line-height: 1.34;
  font-weight: 500;
  letter-spacing: -0.018em;
  color: var(--pink);
  text-shadow: 0 0 34px rgba(255, 77, 128, 0.22);
  max-width: 24ch;
}
/* the rule-off: the accountant's gesture for an account that is
   closed and balanced. Once on the page, under the closing line. */
.fnd-rule {
  display: block;
  height: 1px;
  width: min(360px, 100%);
  margin-top: clamp(26px, 3.4vh, 40px);
  background: linear-gradient(90deg, var(--pink), rgba(255, 77, 128, 0));
  transform: scaleX(var(--rule, 1));
  transform-origin: 0 50%;
}
.js .fnd-rule { transition: transform 0.9s cubic-bezier(0.3, 0.7, 0.3, 1) 0.5s; }
.js .fnd-read:not(.in) .fnd-rule { --rule: 0; }


/* ============================================================
   ONWARD  ·  THE SIGN-OFF

   Not a section: the last line of the founders' sheet. The page's
   real ending is Kalpesh's approved "Two countries. Three full
   careers. One standard." at 40px in pink, closed by .fnd-rule —
   the accountant's rule-off — and this used to draw a SECOND
   closing rule 130px later, under a "NEXT" label, around three
   pills floating in a void.

   The section gap collapses, the border-top and the label go, and
   .fnd-rule is the only closing rule on the page. The primary CTA
   keeps its pill (it is the site's standard CTA, and consistency
   in navigation chrome is correct even where consistency in
   content form is not); the other two drop to text links, so the
   beat reads as a colophon rather than a toolbar.
   ============================================================ */
.abt-close { padding-top: clamp(40px, 6vh, 72px); }
.onward-row {
  display: flex; align-items: center; gap: clamp(20px, 3vw, 44px);
  flex-wrap: wrap;
}
.onward-links {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s2) clamp(24px, 3.4vw, 46px);
}
.onward-link {
  display: inline-flex; align-items: baseline; gap: 10px;
  font-size: clamp(14.5px, 1.2vw, 16px);
  color: var(--mist);
  transition: color 0.3s ease;
}
.onward-link-arrow {
  color: var(--purple-soft);
  transition: transform 0.35s cubic-bezier(0.3, 0.7, 0.3, 1), color 0.3s ease;
}
.onward-link:hover { color: var(--text); }
.onward-link:hover .onward-link-arrow { color: var(--pink); transform: translateX(4px); }


/* ============================================================
   RESPONSIVE
   Pinned scenes with tall content go static at 980px. No device
   is allowed to cost the reader a sentence.
   ============================================================ */
@media (max-width: 1150px) {
  .fnd-body { grid-template-columns: minmax(170px, 220px) minmax(0, 1fr); gap: clamp(28px, 3.6vw, 56px); }
}

@media (max-width: 980px) {
  /* the title keeps its pinned stage — it is one screen of type */
  section[data-scene="prologue"].abt-title { height: 170vh; }
  section[data-scene="prologue"].abt-title .abt-title-stage {
    position: sticky; top: 0;
    height: 100svh; min-height: 0;
    overflow: hidden;
    justify-content: center;
    padding: calc(var(--nav-h) + var(--s5)) var(--pad) var(--s6);
  }
  section[data-scene="prologue"].abt-title .scroll-cue { display: flex; }
  .abt-h1 { font-size: clamp(31px, 8.2vw, 46px); max-width: 100%; }
  .abt-standfirst { font-size: 16px; }
  /* THE WORLD STEPS BACK — ONCE, NOT THREE TIMES.
     This was 0.42, written for the title stage ("a phone-width stage is
     mostly headline"). But .hero-canvas-wrap is position:fixed and covers
     the whole document, so the 0.42 was landing on all NINE formations this
     page registers — xsmall behind "The name", cube behind the founders,
     globe behind the anchor — and it compounded with two penalties the
     engine already applies on its own: js/main.js caps the field at 850
     particles instead of 2100 below 980px and draws half of them at 1.4px
     instead of 2.2px. The mark that "The name" is ABOUT was the casualty.
     0.72 is still comfortably less ink than the desktop the client
     approved (fewer particles, smaller particles, 72% opacity), and every
     long read on this page already carries its own ::before wash. */
  .hero-canvas-wrap { opacity: 0.72; }

  .mf-body {
    margin-left: 0;
    border-left: 0;
    padding-left: 0;
  }
  .mf-turn { max-width: 100%; }

  /* the roadmap becomes a vertical list: same four stages, in order */
  .road-scene { height: auto; }
  .road-stage {
    position: static;
    height: auto; min-height: 0;
    overflow: visible;
    padding-top: var(--section-gap);
    padding-bottom: 0;
    justify-content: flex-start;
    align-items: stretch;
  }
  .road-stage .ghost-center { display: none; }
  .road-head { max-width: 100%; text-align: left; margin: 0 auto; width: 100%; }
  /* THE ROAD IS ROTATED, NOT DELETED. The gate goes — nothing travels
     through it on a phone — but the spine, the four nodes and the pink
     "We are here" marker are the timeline itself, and they were being
     display:none'd, which left four unrelated cards where the section's
     whole subject is that they are one road. The line stands up and runs
     down the gutter; each node keeps its place on it. */
  .road-gate { display: none; }
  .road-viewport {
    position: relative;   /* the spine and the nodes hang off this box */
    height: auto;
    margin-top: clamp(32px, 5vh, 56px);
    padding-left: 26px;   /* the gutter the spine and the nodes live in */
  }
  /* the spine runs on the page's own gutter — the same x the eyebrow, the
     h2 and (below) the anchor's datum sit on, so the page has ONE vertical
     rule position on a phone rather than three near-misses */
  .road-line {
    display: block;
    left: 0; right: auto; top: 0; bottom: 0;
    width: 1px; height: auto;
    background: linear-gradient(180deg, transparent,
      rgba(148, 138, 255, 0.35) 8%, rgba(255, 77, 128, 0.4) 92%, transparent);
  }
  .road-track {
    position: static;
    width: 100%;
    display: flex; flex-direction: column;
    gap: var(--s3);
    padding-top: 0;
    transform: none;
  }
  .road-slot { flex: 1 1 auto; max-width: 100%; padding: 0; }
  .road-card { min-height: 0; }
  /* full weight is the RESTING state here: .is-focus below is a pure
     promotion, so a frame that never runs costs the reader nothing */
  .js .road-card { opacity: 1; transform: none; }
  /* the node steps off the TOP of the card and onto the spine at its LEFT.
     -31, not -26: `left` on an absolutely positioned child is measured from
     its containing block's PADDING box, and .road-card carries a 1px border,
     so 26 (the gutter) + 1 (that border) + 4.5 (half the dot) lands the 9px
     dot's centre on the 1px line. */
  .road-node { top: 30px; left: -31px; transform: none; }
  /* ...and with js/about.js dead the gutter collapses (line 518), so the
     nodes would hang off the page edge with no spine to sit on. They go
     with the spine. Scoped inside the phone block on purpose: the desktop
     fallback is left exactly as it is. */
  .js:not(.abt-live) .road-node { display: none; }

  /* the beliefs used to need a whole unstacking block here, including two
     !important overrides fighting the scene's own inline styles. It is a
     rest now, laid out in flow at every width, so the block is deleted
     rather than rewritten — a static section has nothing to unstack. */

  /* the anchor unstacks: Delaware first at full weight, then the
     two stations — and the REGISTRATION comes with them. */
  .anch-scene { height: auto; }
  .anch-stage {
    position: static;
    height: auto; min-height: 0;
    overflow: visible;
    padding-top: var(--section-gap);
    padding-bottom: 0;
    justify-content: flex-start;
    align-items: stretch;
  }
  .anch-stage .ghost-center { display: none; }
  .anch-head { width: 100%; }
  /* minmax(0, 1fr), NOT 1fr. `1fr` is minmax(AUTO, 1fr) and the auto
     minimum is the largest min-content in the track, which cannot shrink:
     .place-clock is a nowrap flex row ending in "+9:30 FROM DELAWARE"
     (and "+10:30 FROM DELAWARE" outside US daylight time), so at 360px
     the track resolved WIDER than the page and about.html was the one
     page on the site that scrolled sideways. */
  .anch-body {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--s3);
    width: 100%;
  }
  /* ...and the row itself has to be allowed to reflow, or .place clips it
     (the anchor) / .place-station spills it (the stations). Wrapped, the
     row's min-content collapses to its widest single child. */
  .place-clock { flex-wrap: wrap; row-gap: 6px; }

  /* THE DATUM IS ROTATED, NOT DELETED.
     The desktop datum is a COLUMN separator and a phone has one column, so
     the 1px grid track goes. What it MEANS does not: the two Indian
     stations arrive off register and square up to a standard that is
     already set. The standard is redrawn as a vertical rule down the left
     of the stations, sitting exactly on the page gutter, with each
     station's tie closing onto it and its mark lighting when it lands.
     Delaware keeps its full measure above, unindented, because Delaware is
     the thing being registered TO. */
  .anch-datum { display: none; }
  .anch-stations { position: relative; padding-left: 20px; }
  .anch-stations::before {
    content: "";
    position: absolute;
    left: 0; top: 30px; bottom: 30px;
    width: 1px;
    pointer-events: none;
    background: linear-gradient(180deg,
      rgba(242, 243, 247, 0.45), rgba(148, 138, 255, 0.3));
  }
  /* 21, not 20: `left` is measured from .place-station's PADDING box and the
     card carries a 1px border, so the tie starts on the rule and ends on the
     mark. transform-origin is already 0 50% and scaleX(--reg) is already the
     transform, so it still draws OUT OF the standard toward the card. */
  .anch-tie { display: block; right: auto; left: -21px; width: 21px; }
  .anch-mark { display: block; }
  /* the arrival, at a distance a 360px card can travel without pushing the
     page sideways (desktop moves 56px; the whole card is only ~290px wide
     here). --reg still DEFAULTS to 1 in the base rule, so a dead script,
     no-JS and reduced motion all render this squared up and at full
     weight — those three cases are handled in their own blocks below.
     The floor is 0.55, not the desktop 0.34: on a phone the card is the
     whole measure and a fast scroll must never meet grey copy. */
  .place-station,
  .place-station:hover {
    transform: translate(calc((1 - var(--reg)) * 18px), calc((1 - var(--reg)) * 10px))
               rotate(calc((1 - var(--reg)) * 0.5deg));
    opacity: calc(0.55 + 0.45 * var(--reg));
  }

  /* the boxes drop to a single column and tighten up. The margin-column
     rules that used to live here went with the hanging layout — .aud-entry
     is a flex box now, so grid-template-columns had nothing to act on. */
  .aud-register { grid-template-columns: 1fr; gap: 12px; }

  /* the beliefs' folio un-hangs for the same reason */
  .bel-article { grid-template-columns: 1fr; row-gap: 12px; }
  .bel-folio, .bel-h3, .bel-body { grid-column: 1; grid-row: auto; }
  .bel-folio::after { max-width: 64px; }

  /* the founders: the sheet dissolves and the composition is a plain
     stack. Every device added here is a desktop idea — the client's
     complaint was desktop — and none of it is load-bearing for a word. */
  .fnd-list {
    border: 0; border-radius: 0;
    background: none; box-shadow: none;
    padding: 0;
    margin-top: clamp(32px, 5vh, 56px);
  }
  /* display:BLOCK, and it is load-bearing for the rule after next. A sticky
     GRID ITEM is constrained to its own grid AREA, so in a one-column stack
     `position: sticky` on .fnd-spec would unstick the instant its row ended
     — i.e. the instant the bio began, which is the only place it is wanted.
     On desktop it works because spec and bio share ONE row. A block
     container gives it the whole profile to travel. */
  .fnd-body { display: block; }
  .fnd-flip .fnd-body { grid-template-columns: 1fr; }
  .fnd-flip .fnd-spec, .fnd-flip .fnd-read { grid-column: 1; grid-row: auto; }
  .fnd-flip .fnd-standing { text-align: left; }
  .fnd-flip .fnd-slug { margin-left: 0; }
  .fnd-flip .fnd-hon { right: calc(100% + 0.7em); left: auto; }

  /* THE MARGIN STILL TALKS BACK ON A PHONE.
     This was position:static, which parked the portrait and the CREDENTIALS
     list ABOVE the bio in a single column — so by the time the reader
     reached "Certified Public Accountant from the Washington State Board of
     Accountancy" the CPA token was several hundred pixels off the top of
     the screen and founderCredits() lit it to an empty viewport. Nilesh
     qualifies five times in one paragraph; the margin is the record of it,
     and the record has to be on screen while the sentence is read.
     It stays sticky and becomes a slim header bar instead: thumbnail, then
     the credential string beside it, both held under the nav for the length
     of the bio. Same device, same moment, at a height a phone can pay. */
  .fnd-spec {
    position: sticky;
    top: calc(var(--nav-h) + 4px);
    z-index: 2;
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr);
    gap: var(--s2);
    align-items: center;
    /* the bio scrolls UNDER this, so the ground cannot be sheer */
    background: var(--bg);
    /* a little air on the right: the credential string can land exactly at
       the column edge at some widths ("... Dip. IB" measured 299px in a
       299px column at 430), which reads as a clipped last token even though
       nothing is technically overflowing */
    padding: 8px 4px 10px 0;
    border-bottom: 1px solid var(--line);
  }
  /* and the string must always be free to wrap rather than run to the edge */
  .fnd-creds, .fnd-creds dd { min-width: 0; }
  .fnd-creds dd { line-height: 1.9; }
  .fnd-read { margin-top: var(--s3); }
  /* the portrait becomes a locator rather than a plate. Same one group
     negative, same --fx crop — background-position is a percentage, so the
     framing survives the scale change untouched.
     The 1100 renders at ~213 CSS px here rather than the full 1800: a third
     of the bytes for a picture no phone shows larger than 56px. */
  .fnd-face {
    max-width: 56px;
    margin-bottom: 0;
    background-image: url(../assets/founders-1100.jpg?v=2);
  }
  .fnd-creds { border-top: 0; padding-top: 0; }
  .fnd-creds dt { margin-bottom: 6px; }
  /* the name story stacks: the mark leads, the arithmetic follows */
  .name-story { grid-template-columns: 1fr; gap: clamp(22px, 4vh, 38px); }
  .name-mark-wrap { min-height: clamp(150px, 22vh, 210px); }
  /* the string is byte-identical; it is only set tighter, because it now
     costs the reader viewport for the whole length of a bio. Nilesh's five
     tokens land on two lines at 360px, which is what sets the bar height. */
  .fnd-creds dd { font-size: 11.5px; line-height: 1.9; }
  .cr { padding-left: 14px; }
  .fnd-name { font-size: clamp(27px, 6.6vw, 40px); max-width: 100%; }
  /* the folio un-hangs and sits inline — same text, same order */
  .fnd-hon { position: static; font-size: 0.34em; letter-spacing: 0.16em; margin-right: 0.35em; }
  .fnd-read p:not(.fnd-deck):not(.fnd-turn):not(.fnd-quote)::before,
  .fnd-flip .fnd-read p:not(.fnd-deck):not(.fnd-turn):not(.fnd-quote)::before { display: none; }
}

@media (max-width: 640px) {
  /* the run-in head still runs in, at a size a phone can hold. The
     leading mismatch that IS the composition survives — it is the ratio
     between the two, not the absolute size, that opens the line box. */
  .arch-who { font-size: clamp(21px, 5.6vw, 26px); }
  .arch-composite { --arch-lead: 1.85; }
  .bel-h3 { font-size: clamp(22px, 6vw, 27px); }
  .aud-what { font-size: clamp(14px, 3.8vw, 15.5px); max-width: 100%; }
  /* LOAD-BEARING, not polish: the spread is single-column here and
     the phone is this section's expensive case. These three lines
     are what keep it inside the scroll budget. */
  .abt-secure { --nam-gap: 24px; --nam-reserve: 26px; }
  .nam-term { font-size: clamp(19px, 5.3vw, 21px); }
  .nam-spec { line-height: 1.56; }
  .nam-guide { flex-direction: column; gap: 4px; padding: 10px 0; }
  .nam-book { margin-top: 24px; }
  .onward-links .btn { width: 100%; justify-content: center; }
  .onward-links { width: 100%; }
}

/* short desktop windows: the roadmap and the controls need the height */
@media (min-width: 981px) and (max-height: 820px) {
  section[data-scene="prologue"].abt-title .scroll-cue { display: none; }
  .road-head .chapter-line { margin-bottom: var(--s2); }
  .road-viewport { height: min(420px, 58vh); margin-top: var(--s3); }
  .road-card { padding: var(--s3); }
  .road-body { font-size: 13.5px; margin-top: var(--s1); }
  .road-here { margin-bottom: var(--s1); }
  .anch-head .chapter-line { margin-bottom: var(--s2); }
  .anch-body { margin-top: var(--s3); }
  .place { padding: var(--s3); }
  .anch-lede { font-size: 14.5px; }
  .anch-terr { font-size: 13px; line-height: 1.7; }
  .anch-territory { padding-top: var(--s2); }
  .place-station .place-body { font-size: 13.5px; line-height: 1.65; }
  .anch-stations { gap: var(--s1); }
}


/* ============================================================
   NO-JS — the whole page, statically
   ============================================================ */
.no-js section[data-scene="prologue"].abt-title { height: auto !important; }
.no-js section[data-scene="prologue"].abt-title .abt-title-stage {
  position: static;
  height: auto; min-height: 0;
  overflow: visible;
  padding-top: calc(var(--nav-h) + var(--s7));
  padding-bottom: var(--s7);
}
.no-js section[data-scene="prologue"].abt-title .scroll-cue { display: none; }

/* one fewer scene to stand down: "What we believe" is a rest now, and a
   rest has no stage, no height and nothing to strand. */
.no-js .road-scene,
.no-js .anch-scene { height: auto !important; }
.no-js .road-stage,
.no-js .anch-stage {
  position: static;
  height: auto; min-height: 0;
  overflow: visible;
  padding-top: var(--section-gap);
  padding-bottom: 0;
  justify-content: flex-start;
  align-items: stretch;
}
.no-js .road-gate,
.no-js .road-line,
.no-js .road-node { display: none; }
.no-js .road-head { text-align: left; max-width: 100%; width: 100%; }
.no-js .road-body { max-width: 72ch; }
/* "Data security" needs nothing at all without JS: it is a rest, the
   eight rows are at full weight by default, the brackets default to
   --d/--a of 1 so the whole margin is drawn, and the :has() hover is
   pure CSS. Same for the founders: the sheet, the deck, every bio and
   every credential token render from the cascade alone. Only the
   letter-spacing "set", the composing stick and the rule-off are
   .js-scoped, and all three are decoration. */
.no-js .cr { color: var(--text); }
.no-js .cr::before { background: var(--purple-soft); box-shadow: 0 0 12px rgba(91, 76, 255, 0.7); }
.no-js .road-viewport { position: static; height: auto; margin-top: var(--s5); padding-left: 0; }
.no-js .road-track {
  position: static; width: 100%;
  flex-direction: column; gap: var(--s3);
  padding-top: 0; transform: none;
}
.no-js .road-slot { max-width: 100%; padding: 0; }
.no-js .road-card { min-height: 0; }
/* the anchor without JS: the datum is drawn, both stations are already
   in register, and no approved sentence is left below full weight.
   minmax(0, 1fr) for the same reason as the phone block: `1fr` cannot
   shrink below the clock row's min-content and pushes the page sideways. */
.no-js .anch-body { grid-template-columns: minmax(0, 1fr); row-gap: var(--s3); width: 100%; }
.no-js .anch-datum,
.no-js .anch-tie,
.no-js .anch-mark { display: none; }
.no-js .anch-stations { padding-left: 0; }
.no-js .anch-stations::before { display: none; }
.no-js .place-station,
.no-js .place-station:hover { transform: none; opacity: 1; }
.no-js .anch-head { width: 100%; }
/* the standfirst is real approved copy — it must never depend on JS to be seen
   (this rule existed only under prefers-reduced-motion, leaving no-JS blank) */
.no-js .abt-standfirst { opacity: 1 !important; transform: none !important; }


/* ============================================================
   REDUCED MOTION — every device resolves to its finished state
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  section[data-scene="prologue"].abt-title { height: auto !important; }
  section[data-scene="prologue"].abt-title .abt-title-stage {
    position: static;
    height: auto; min-height: 0;
    overflow: visible;
    padding-top: calc(var(--nav-h) + var(--s7));
    padding-bottom: var(--s7);
  }
  .abt-standfirst { opacity: 1; transform: none; }

  .road-scene, .anch-scene { height: auto !important; }
  .road-stage, .anch-stage {
    position: static;
    height: auto; min-height: 0;
    overflow: visible;
    padding-top: var(--section-gap);
    padding-bottom: 0;
    justify-content: flex-start;
    align-items: stretch;
  }
  .road-gate, .road-line, .road-node { display: none; }
  .road-head { text-align: left; max-width: 100%; width: 100%; }
  .road-body { max-width: 72ch; }
  .road-viewport { position: static; height: auto; margin-top: var(--s5); padding-left: 0; }
  .road-track {
    position: static; width: 100%;
    flex-direction: column; gap: var(--s3);
    padding-top: 0; transform: none;
  }
  .road-slot { max-width: 100%; padding: 0; }
  .road-card, .js .road-card { min-height: 0; opacity: 1; transform: none; }
  .road-here-dot { animation: none; }

  /* "What we believe", "How we think", "Who we work with", "The name"
     and "Onward" need NOTHING here: they have no motion to disarm and
     no property to resolve. That is the point of the pass — the
     reduced-motion block got shorter, not longer. */

  /* the spread resolves to its finished state. js/main.js adds .in to
     EVERY .reveal immediately under reduced motion, so anything keyed
     to .in has to be disarmed by hand or it lands in its start state
     permanently. --rx already rests at 1 now that the struck band is
     gone; only the hover transition needs standing down. */
  .js .nam-sn::after { transition: none; }
  .js .fnd-standing .fnd-name,
  .js .fnd-standing.in .fnd-name { letter-spacing: -0.032em; transition: none; }
  .js .fnd-standing.in .fnd-stick i { animation: none; }
  .fnd-stick i { opacity: 0; }
  .js .fnd-rule, .js .fnd-read:not(.in) .fnd-rule { --rule: 1; transition: none; }
  /* nothing waits on the earn observer: every token is earned */
  .cr { color: var(--text); }
  .cr::before { background: var(--purple-soft); box-shadow: 0 0 12px rgba(91, 76, 255, 0.7); }

  /* the anchor resolves to its registered state */
  .anch-body { grid-template-columns: minmax(0, 1fr); row-gap: var(--s3); width: 100%; }
  .anch-datum, .anch-tie, .anch-mark { display: none; }
  .anch-stations { padding-left: 0; }
  .anch-stations::before { display: none; }
  .anch-head { width: 100%; }
  .place-station, .place-station:hover, .js .place-station {
    transform: none; opacity: 1;
  }
}


/* ============================================================
   GLASS, COSTED FOR A PHONE (page-scoped)
   css/main.css flattens its OWN glass components below 980px, but it cannot
   reach the devices this page declares. backdrop-filter re-blurs everything
   behind an element on every frame it moves, and these sit inside a
   scrolling film — it is the most expensive thing a handset does here.

   Blur removed below 980px. Where the surface was too sheer to survive
   without it (var(--surface) is 3.5% white) an opaque base is added as
   background-COLOR, not background, so any ruled gradient painted on top
   of it is left intact.
   ============================================================ */
@media (max-width: 980px) {
  .road-card {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--glass-flat);
  }
  /* .place kept its rgba(13,13,19,0.55) when the blur came off, which was
     survivable while the whole field was held at 0.42. It is at 0.72 now,
     and 55% of a sheer card over a live globe is not a ground a 14px line
     can sit on — so the stations take an opaque base, as background-COLOR
     so nothing painted on top of it is disturbed. */
  .place {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: rgba(13, 13, 19, 0.86);
  }
  /* the anchor stays the one lighter card on the page: with no blur left to
     lift a 4.5% white veil, the lift has to be in the colour itself */
  .place-anchor { background-color: rgba(26, 26, 34, 0.9); }
}
