/* ============================================================
   MOSAEK — Stylesheet
   ============================================================ */

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--page-base);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--lh-body);
  letter-spacing: var(--track-normal);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 500; text-wrap: balance; }
p { text-wrap: pretty; }

/* ---------- Focus (never removed) ---------- */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}
.on-dark :focus-visible { outline-color: var(--brand-light); }

/* ---------- Screen-reader-only ---------- */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- Skip link ---------- */
.skip-link {
  position: absolute; left: var(--space-4); top: var(--space-4);
  transform: translateY(-200%);
  background: var(--brand-dark); color: var(--brand-light);
  padding: var(--space-3) var(--space-5);
  z-index: 999; text-decoration: none;
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
}
.skip-link:focus { transform: translateY(0); }

/* ---------- Layout primitives ---------- */
.wrap {
  width: 100%; max-width: var(--wrap-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.section { padding-block: var(--section-y); }
section[id] { scroll-margin-top: 88px; }
.bleed { width: 100%; }

/* 12-column editorial grid */
.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--space-5) var(--space-6);
}

/* Vivium's signature move: tiny label in the left column,
   substance in an offset right column. */
.editorial-row {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--space-4) var(--space-6);
  padding-block: var(--space-7);
  border-top: 1px solid var(--divider);
}
.editorial-row > .row-label { grid-column: 1 / 4; }
.editorial-row > .row-body  { grid-column: 4 / 11; }
.editorial-row > .row-full  { grid-column: 1 / -1; }
.on-dark .editorial-row { border-top-color: var(--divider-on-dark); }

/* ---------- Type utilities ---------- */
.label {
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-muted);
  line-height: 1.4;
}
.on-dark .label { color: var(--on-dark-muted); }

.display {
  /* Two steps down from --step-6. At full size the line ran to the gutter,
     and the second pass took it down again so the headline sits in the page
     rather than filling it. */
  font-size: clamp(1.8rem, 1rem + 3.1vw, 3.25rem);
  line-height: var(--lh-tight);
  letter-spacing: var(--track-tight);
  font-weight: 500;
}
.h2 {
  font-size: var(--step-5);
  line-height: var(--lh-snug);
  letter-spacing: var(--track-tight);
}
.h3 {
  font-size: var(--step-3);
  line-height: var(--lh-snug);
  letter-spacing: -0.02em;
}
.h4 { font-size: var(--step-2); line-height: 1.25; letter-spacing: -0.015em; }
.lede { font-size: var(--step-1); line-height: var(--lh-loose); max-width: var(--measure); }
.muted { color: var(--ink-muted); }
.on-dark .muted { color: var(--on-dark-muted); }
.measure { max-width: var(--measure); }
/* Opts out of the reading measure where a line is meant to stay whole. */
.lede-wide { max-width: none; }
/* Two-line heading: the claim, then a quieter second line that carries what
   used to be a separate lede. Only the first line takes the word reveal. */
.h2-stack { display: grid; gap: 0.06em; }
.h2-stack > span { display: block; }
/* 58%: at 46% the second line measured 2.54:1, under the 3:1 large-text
   floor it needs at heading size. */
.h2-sub { color: color-mix(in srgb, var(--brand-dark) 58%, transparent); }
.on-dark .h2-sub { color: color-mix(in srgb, var(--brand-light) 62%, transparent); }

/* ---------- Scroll word reveal ---------- */
.rw {
  opacity: 0.16;
  transition: opacity 300ms var(--ease-out);
}
.rw.is-lit { opacity: 1; }
/* Survey. Weed. Seed. — each word settles a step lighter than the one before
   it, so the line reads as the ink running out across the three stages
   rather than three words arriving at the same weight. */
.h2-fade .rw:nth-child(1).is-lit { opacity: 1; }
.h2-fade .rw:nth-child(2).is-lit { opacity: 0.78; }
/* 0.58 is the floor for the last word, not a taste call: it is a heading at
   large-text size, so it has to hold 3:1 on the page ground on its own. */
.h2-fade .rw:nth-child(3).is-lit { opacity: 0.58; }
@media (prefers-reduced-motion: reduce) {
  .h2-fade .rw:nth-child(2) { opacity: 0.78 !important; }
  .h2-fade .rw:nth-child(3) { opacity: 0.58 !important; }
}
@media (prefers-reduced-motion: reduce) {
  .rw { opacity: 1 !important; transition: none; }
}

/* ---------- Custom cursor ---------- */
@media (hover: hover) and (pointer: fine) {
  .has-custom-cursor,
  .has-custom-cursor a,
  .has-custom-cursor button,
  .has-custom-cursor [role='tab'] { cursor: none; }
  /* Text controls keep the native caret — the cursor shape is the
     affordance that tells you the field is editable. */
  .has-custom-cursor input,
  .has-custom-cursor textarea { cursor: text; }
  .has-custom-cursor select { cursor: pointer; }
}
.cursor-ring {
  position: fixed; top: 0; left: 0;
  width: 36px; height: 36px;
  border-radius: var(--radius-round);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 45%, transparent);
  color: var(--brand-dark);
  pointer-events: none;
  z-index: 9999;
  opacity: 0;
  display: flex; align-items: center; justify-content: center;
  transition: opacity 200ms var(--ease-out),
              width 220ms var(--ease-out),
              height 220ms var(--ease-out),
              background 220ms var(--ease-out),
              border-color 220ms var(--ease-out);
  will-change: transform;
}
.cursor-ring.is-on { opacity: 1; }
.cursor-ring.on-dark-bg {
  border-color: color-mix(in srgb, var(--brand-light) 65%, transparent);
  color: var(--brand-light);
}
.cursor-ring.on-dark-bg.is-active {
  background: var(--brand-light);
  border-color: var(--brand-light);
  color: var(--brand-dark);
}
.cursor-plus {
  display: flex; opacity: 0;
  transform: scale(0.5) rotate(-45deg);
  transition: opacity 200ms var(--ease-out), transform 260ms var(--ease-out);
}
.cursor-ring.is-active {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
  color: var(--brand-light);
}
.cursor-ring.is-active .cursor-plus { opacity: 1; transform: scale(1) rotate(0deg); }
/* Over the dock the filled disc sat right on top of the label the hover was
   there to reveal. It drops to a small outline instead: the item already
   swells and names itself, so the cursor only has to say "clickable". */
.cursor-ring.on-dock {
  width: 18px; height: 18px;
  background: transparent;
  border-color: color-mix(in srgb, var(--brand-dark) 55%, transparent);
}
.cursor-ring.on-dock.on-dark-bg {
  background: transparent;
  border-color: color-mix(in srgb, var(--brand-light) 70%, transparent);
}
.cursor-ring.on-dock .cursor-plus { opacity: 0; }
.cursor-ring.is-down { transform-origin: center; border-width: 2px; }
.cursor-ring.is-text { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .cursor-ring, .cursor-plus { transition: opacity 120ms linear; }
}

/* ---------- Links & buttons ---------- */
a { color: inherit; }
.link {
  display: inline-block;
  padding-block: 5px;        /* lifts these past the 24px target minimum */
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 1px;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.link:hover { opacity: .68; }
.on-dark .link { color: var(--accent-on-dark); }

.btn {
  display: inline-flex; align-items: center; gap: var(--space-3);
  min-height: 52px;
  padding: var(--space-3) var(--space-6);
  /* Neue Montreal, not the mono label face. At label size and tracking these
     read as two more captions in a page already full of them; set in the
     display face they read as the two things you are meant to press. */
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--step-1);
  letter-spacing: -0.008em;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.btn-primary { background: var(--brand-dark); color: var(--brand-light); }
.btn-primary:hover { background: var(--surface-alt); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--divider); }
.btn-ghost:hover { border-color: var(--brand-dark); }
.on-dark .btn-primary { background: var(--brand-light); color: var(--ink); }
.on-dark .btn-primary:hover { background: #DCC9B8; }
.on-dark .btn-ghost { color: var(--on-dark); border-color: var(--divider-on-dark); }
.on-dark .btn-ghost:hover { border-color: var(--brand-light); }
/* One arrow leaves to the right, another arrives from the left, both inside
   a slot the width of a single glyph. The first pass let them travel outside
   the button, so at mid-hover you saw two arrows at once. */
.btn .arrow {
  position: relative;
  display: inline-block;
  /* rem, not em: font-size is zeroed on this element to hide the literal
     glyph in the markup, and an em box measured against a zeroed font-size
     collapsed the slot to 0x0 everywhere the hero did not override it. */
  width: 1.05rem; height: 1.2rem;
  overflow: hidden;
  font-size: 0;                 /* the literal glyph in the markup is hidden */
}
.btn .arrow::before, .btn .arrow::after {
  content: '\2192';
  position: absolute; top: 0; left: 0;
  font-size: 1rem; line-height: 1.2;
  transition: transform 380ms var(--ease-out);
}
.btn .arrow::after { transform: translateX(-150%); }
.btn:hover .arrow::before,
.btn:focus-visible .arrow::before { transform: translateX(150%); }
.btn:hover .arrow::after,
.btn:focus-visible .arrow::after { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .btn .arrow::before, .btn .arrow::after { transition: none; }
}
/* The ghost fills from the left on hover rather than only firming its edge. */
.btn-ghost { position: relative; overflow: hidden; isolation: isolate; }
.btn-ghost::before {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background: color-mix(in srgb, var(--brand-dark) 8%, transparent);
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--dur-base) var(--ease-out);
}
.btn-ghost:hover::before, .btn-ghost:focus-visible::before { transform: scaleX(1); }

/* ---------- Dark passages ----------
   These sections no longer paint a background: the document gradient
   supplies the colour and ramps into it across their padding, so the page
   reads as one continuous field. They keep their light text scheme.
   ------------------------------------------------------------------ */
.on-dark {
  background: var(--brand-dark);
  color: var(--on-dark);
  border-radius: var(--radius-section) var(--radius-section) 0 0;
  position: relative;
  z-index: 1;
}
/* Extra room below the last line, so it clears the incoming curve. */
.on-dark > .section { padding-bottom: calc(var(--section-y) + var(--radius-section) + 1rem); }
.on-dark h1, .on-dark h2, .on-dark h3, .on-dark h4 { color: var(--brand-light); }
/* The light section that follows curves back over the dark one. */
.on-dark + section:not(.on-dark) {
  background: var(--page-base);
  border-radius: var(--radius-section) var(--radius-section) 0 0;
  margin-top: calc(-1 * var(--radius-section));
  padding-top: calc(var(--section-y) * 0.55 + var(--radius-section));
  position: relative;
  z-index: 2;
}

/* ---------- Floating chrome (nav) ----------
   Fixed pill group rather than a full-width bar: the page scrolls under it
   behind a blur, so the gradient tints the chrome as it passes.
   ------------------------------------------------------------------ */
.chrome {
  position: fixed;
  top: var(--space-4);
  left: var(--gutter);
  right: var(--gutter);
  z-index: var(--z-nav);
  display: flex;
  /* Centred, not top-aligned: the brand pill is deliberately taller than the
     nav, and flex-start left them hanging off a shared top edge. */
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  pointer-events: none;          /* only the pills themselves catch clicks */
}
.chrome > *, .chrome-right > * { pointer-events: auto; }
.chrome-right { display: flex; align-items: center; gap: var(--space-3); }

/* ---- Brand: icon only, unravels to icon + wordmark on hover ---- */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0;
  height: var(--chrome-h);
  padding: 0 10px;
  text-decoration: none;
  color: var(--brand-dark);
  background: var(--glass-light);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 8%, transparent);
  /* A lit top edge and a soft drop are what read as glass; the blur alone
     just reads as a translucent rectangle. */
  box-shadow: 0 8px 22px color-mix(in srgb, var(--brand-dark) 9%, transparent);
  border-radius: var(--radius-pill);
  overflow: hidden;
  transition: gap var(--dur-base) var(--ease-out),
              padding var(--dur-base) var(--ease-out);
}
.brand-mark {
  /* 34, not 26. The monogram's counters close up below ~30px and it reads
     as a blob; thickening the strokes would close them further. */
  flex: none;
  /* Pulled back from 36 so the monogram and the wordmark read as one lockup
     rather than a mark with a caption. */
  width: 32px; height: 27px;
  background-color: currentColor;
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
}
/* The wordmark unravels by animating the grid track, so the pill grows from
   the mark outwards rather than popping to full size. */
.brand-word {
  display: grid;
  grid-template-columns: 0fr;
  transition: grid-template-columns var(--dur-slow) var(--ease-out);
}
.brand-word > span {
  overflow: hidden;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 400;
  letter-spacing: -0.015em;
  line-height: 1;
  white-space: nowrap;
  /* Optically centred against the monogram: see .brand-card-word. */
  translate: 0 -0.06em;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
}
/* The wordmark is out when you land and ravels back to the mark as soon as
   you start reading, so the full name registers once and then gets out of
   the way. Hover brings it back at any point. */
.brand:hover, .brand:focus-visible, .brand.is-open { gap: 9px; padding: 0 18px 0 12px; }
.brand:hover .brand-word,
.brand:focus-visible .brand-word,
.brand.is-open .brand-word { grid-template-columns: 1fr; }
.brand:hover .brand-word > span,
.brand:focus-visible .brand-word > span,
.brand.is-open .brand-word > span { opacity: 1; transition-delay: 80ms; }
/* The mark is a registered wordmark, so the ™ has to be legible rather than
   a speck. Set off the word instead of tucked against the k. */
.brand-tm {
  font-size: 0.5em;
  vertical-align: super;
  letter-spacing: 0;
  margin-left: 0.22em;
}

/* ---- Nav pills ---- */
.pills {
  display: flex;
  align-items: center;
  gap: 0;
  height: var(--chrome-nav-h);
  padding: 0 4px;
  background: var(--glass-light);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  /* No outline: with one, the active item's own fill ran the full height of
     the bar and looked like a second, misaligned edge inside the first. */
  border: 0;
  box-shadow: 0 10px 26px color-mix(in srgb, var(--brand-dark) 12%, transparent);
  border-radius: var(--radius-pill);
}
/* A dock rather than a row of words. Each item is an icon that swells as the
   cursor nears it, the way a macOS dock does, and the section you are
   currently in stays swollen with its name spelled out — so there is always
   one label on screen telling you where you are, and the rest name
   themselves on hover. Icons alone would have been a wayfinding cost on a
   site where the sections are abstract; keeping the active label visible
   pays that back. Magnification is driven from JS as --m (0..1). */
.dock-item {
  position: relative;
  display: inline-flex; align-items: center; gap: 0;
  height: 26px;
  padding: 0 calc(7px + var(--m, 0) * 3px);
  border-radius: 10px;
  text-decoration: none;
  color: var(--ink-muted);
  transition: background var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}
.dock-ico {
  display: block;
  flex: none;
  width: calc(19px + var(--m, 0) * 7px);
  height: calc(19px + var(--m, 0) * 7px);
  transform: translateY(calc(var(--m, 0) * -1px));
}
/* Phosphor Light (MIT), pulled via Iconify. The weights are drawn into the
   path geometry rather than set as a stroke, so "lighter" is a variant
   choice, not a stroke-width — these replace hand-drawn 1.4px outlines that
   sat heavier than everything around them. */
.dock-ico svg { display: block; width: 100%; height: 100%; }
/* The name is laid out but collapsed to nothing until it is wanted, so
   expanding it costs no reflow of the page around the bar. */
.dock-name {
  display: block;
  overflow: hidden;
  max-width: 0;
  opacity: 0;
  white-space: nowrap;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 13px;
  letter-spacing: -0.005em;
  transition: max-width 320ms var(--ease-out), opacity 220ms var(--ease-out),
              margin-left 320ms var(--ease-out);
  margin-left: 0;
}
.dock-item:hover { color: var(--brand-dark); background: color-mix(in srgb, var(--brand-dark) 8%, transparent); }
.dock-item:hover .dock-name,
.dock-item:focus-visible .dock-name,
.dock-item[aria-current='true'] .dock-name,
/* Every label is out on landing so the menu reads as a menu once, then
   ravels back to icons on the first scroll — the active one stays. */
.dock.is-open .dock-name {
  max-width: 8rem;
  opacity: 1;
  margin-left: 6px;
}
.dock-item[aria-current='true'] {
  background: var(--brand-light);
  color: var(--ink);
}
.dock-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Hairline between wayfinding and the action. */
.dock-sep {
  width: 1px;
  align-self: stretch;
  margin: 7px 5px;
  background: color-mix(in srgb, var(--brand-dark) 14%, transparent);
}
.chrome[data-right-dark='true'] .dock-sep { background: color-mix(in srgb, var(--brand-light) 22%, transparent); }
.contact-pill { display: inline-flex; align-items: center; }

@media (prefers-reduced-motion: reduce) {
  .dock-item, .dock-ico, .dock-name { transition: none; }
}

/* ---- Contact pill ----
   The drawer is absolutely positioned so it can be wider than the trigger
   without stretching the button itself.
   ------------------------------------------------------------------ */
.contact-pill { position: relative; }
/* Folded into the dock rather than sitting beside it in its own pill: one
   bar, one edge. It keeps the solid burgundy fill so it still reads as the
   action among the nav's outline icons, but drops its own border and sizes
   itself to the dock items around it. */
.contact-trigger {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0;
  height: 32px;
  width: 32px;
  min-width: 0;
  padding: 0;
  background: var(--brand-dark);
  border: 0;
  border-radius: 10px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 13px;
  letter-spacing: -0.005em;
  text-decoration: none;
  color: var(--brand-light);
  white-space: nowrap;
  transition: width var(--dur-base) var(--ease-out), padding var(--dur-base) var(--ease-out);
}
/* Named while the bar is open, then ravels to the glyph with the rest. */
.contact-name {
  display: block;
  overflow: hidden;
  max-width: 0;
  opacity: 0;
  transition: max-width 320ms var(--ease-out), opacity 220ms var(--ease-out),
              margin-left 320ms var(--ease-out);
  margin-left: 0;
}
.dock.is-open .contact-trigger { width: auto; padding: 0 12px 0 13px; }
.dock.is-open .contact-name { max-width: 6rem; opacity: 1; margin-left: 7px; }
/* Icon only, square on the nav rail. The ring behind it pulses so the one
   action on the page that matters keeps asking to be pressed. */
.contact-trigger { position: relative; }
/* Phosphor Light in the same 19px box as the dock icons, so the mail mark
   sits in the same weight and size as Stakes, Services and the rest. It was
   a hand-drawn 1px-stroke envelope, which read lighter and smaller than
   everything beside it. */
.contact-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  position: relative;
  z-index: 1;
  width: 19px; height: 19px;
}
.contact-glyph svg { display: block; width: 100%; height: 100%; }
.contact-trigger::after {
  content: '';
  position: absolute; inset: -3px;
  z-index: -1;
  border-radius: calc(var(--radius-pill) + 3px);
  background: color-mix(in srgb, var(--brand-dark) 40%, transparent);
  opacity: 0;
  animation: contact-ping 3.2s ease-out infinite;
  pointer-events: none;
}
/* Nothing pulses on landing. The button is already carrying its name at that
   point, which is invitation enough; the halo starts once the bar has
   ravelled back to icons and the label is gone. */
.dock.is-open .contact-trigger::after { animation: none; opacity: 0; }
.chrome[data-right-dark='true'] .contact-trigger::after {
  background: color-mix(in srgb, var(--brand-light) 46%, transparent);
}
/* A soft halo breathing out from behind the button. The glyph itself stays
   still: moving the icon read as a twitch rather than an invitation. */
@keyframes contact-ping {
  0%   { transform: scale(0.94); opacity: 0; }
  26%  { opacity: 0.34; }
  100% { transform: scale(1.34); opacity: 0; }
}
.contact-pill.is-open .contact-trigger::after { animation: none; opacity: 0; }
@media (prefers-reduced-motion: reduce) { .contact-trigger::after { animation: none; opacity: 0; } }
.contact-arrow { display: inline-flex; transition: transform var(--dur-base) var(--ease-out); }
/* Click, not hover: a form inside a hover drawer closes the moment the
   pointer leaves it to reach a field. */
.contact-pill.is-open .contact-arrow { transform: rotate(90deg); }

.contact-drawer {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 372px;
  max-height: calc(100vh - 96px);
  display: grid;
  grid-template-rows: 0fr;
  background: var(--brand-dark);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: grid-template-rows var(--dur-slow) var(--ease-out);
  z-index: 2;
}
.contact-drawer-inner { overflow: hidden auto; max-height: inherit; }
.contact-pill.is-open .contact-drawer { grid-template-rows: 1fr; }

/* Compact version of the section form. Same fields, same validation, and a
   link straight down to the full one. */
.mini-form { padding: var(--space-5) var(--space-5) var(--space-4); }
.mini-title {
  font-family: var(--font-display);
  font-size: var(--step-2);
  letter-spacing: -0.018em;
  color: var(--brand-light);
  margin-bottom: var(--space-3);
}
/* The reassurance people actually want before they type anything. */
.mini-nda {
  font-size: var(--step-label);
  line-height: 1.7;
  letter-spacing: 0.02em;
  /* Quiet through size, not colour: dimming it further measured 4.33:1. */
  color: var(--on-dark-muted);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--divider-on-dark);
}
.mini-field > span i { font-style: normal; color: var(--accent-on-dark); }
.mini-field { display: grid; gap: 5px; margin-bottom: var(--space-3); }
.mini-field > span {
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--on-dark-muted);
}
/* The native list is an OS-drawn panel we cannot style, so the closed control
   at least carries our own chevron and the open list inherits the form's
   colours where the browser allows it. */
/* The visible control is now .sel-btn; it takes the same box as the inputs. */
.mini-field .sel-btn {
  font-size: var(--step--1);
  color: var(--on-dark);
  background: color-mix(in srgb, var(--brand-light) 8%, transparent);
  border: 1px solid var(--divider-on-dark);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
  min-height: 34px;
}
.mini-field .sel.is-open .sel-btn,
.mini-field .sel-btn:focus-visible { border-color: var(--brand-light); }
.mini-field[data-invalid='true'] .sel-btn { border-color: var(--accent-on-dark); }
.mini-field input,
.mini-field select,
.mini-field textarea {
  width: 100%;
  font: inherit;
  font-size: var(--step--1);
  color: var(--on-dark);
  background: color-mix(in srgb, var(--brand-light) 8%, transparent);
  border: 1px solid var(--divider-on-dark);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
  min-height: 34px;
  resize: vertical;
}
.mini-field input:focus,
.mini-field select:focus,
.mini-field textarea:focus { border-color: var(--brand-light); }
.mini-field[data-invalid='true'] input,
.mini-field[data-invalid='true'] select,
.mini-field[data-invalid='true'] textarea { border-color: var(--accent-on-dark); }

/* The gate, compressed to one row. The full disclosure text lives in the
   section form; this links straight to it. */
/* Accept on the left, a "+" that opens the terms in place on the right —
   reading them should not throw you out of the form you are filling in. */
.mini-gate {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--divider-on-dark);
}
.mini-terms-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: none;
  background: none;
  border: 1px solid var(--divider-on-dark);
  border-radius: var(--radius-sm);
  color: var(--on-dark-muted);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.mini-terms-toggle:hover { color: var(--brand-light); border-color: var(--brand-light); }
.mini-chev { display: inline-flex; transition: transform var(--dur-slow) var(--ease-out); }
.mini-terms-toggle[aria-expanded='true'] .mini-chev { transform: rotate(135deg); }
.mini-terms {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-slow) var(--ease-out);
}
.mini-terms[data-open='true'] { grid-template-rows: 1fr; }
.mini-terms-inner { overflow: hidden; }
/* These were <h4>, which put three headings ahead of the page's own <h1> in
   document order. They are labels inside a disclosure, not page sections. */
.mini-terms .mini-terms-h {
  line-height: 1.4;
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--brand-light);
  margin: var(--space-4) 0 var(--space-2);
}
/* Set back: this is the small print behind a disclosure, not something the
   reader is meant to work through before they can act. */
.mini-terms p:not(.mini-terms-h) {
  font-size: 0.6875rem;
  line-height: 1.6;
  /* Held at the full muted tone, not faded further: at 80% this measured
     3.87:1 at 11px, and the small print is exactly the text that has to stay
     legible. The size drop is what sets it back. */
  color: var(--on-dark-muted);
}
.mini-terms-inner > :last-child { margin-bottom: var(--space-5); }
.mini-accept {
  display: flex; align-items: center; gap: var(--space-3);
  flex: 1 1 auto;
  padding: 4px 0;
  background: none; border: 0; cursor: pointer;
  text-align: left;
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  line-height: 1.5;
  color: var(--on-dark-muted);
}
.mini-tick {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; flex: none;
  border: 1px solid var(--divider-on-dark);
  border-radius: var(--radius-sm);
  color: transparent;
}
.mini-accept[aria-pressed='true'] { color: var(--brand-light); }
.mini-accept[aria-pressed='true'] .mini-tick {
  background: var(--brand-light);
  border-color: var(--brand-light);
  color: var(--ink);
}
.mini-accept a { color: inherit; }
/* The display face, not mono: this and the hero's two calls are the only
   things on the page anyone is asked to press, and in the label face they
   read as one more caption. */
.mini-submit {
  display: flex; align-items: center; justify-content: center; gap: var(--space-3);
  width: 100%;
  min-height: 46px;
  margin-top: var(--space-3);
  background: var(--brand-light);
  color: var(--ink);
  border: 1px solid var(--brand-light);
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-weight: 400;
  /* A step under body: at --step-0 a five-word label filled the drawer's
     full width and read as a heading rather than a control. */
  font-size: var(--step--1);
  letter-spacing: -0.006em;
  cursor: pointer;
}
.mini-submit .arrow, .btn-submit .arrow {
  position: relative;
  display: inline-block;
  width: 1.05em; height: 1.2em;
  overflow: hidden;
  font-size: 0;
}
.mini-submit .arrow::before, .mini-submit .arrow::after,
.btn-submit .arrow::before, .btn-submit .arrow::after {
  content: '\2192';
  position: absolute; top: 0; left: 0;
  font-size: 1rem; line-height: 1.2;
  transition: transform 380ms var(--ease-out);
}
.mini-submit .arrow::after, .btn-submit .arrow::after { transform: translateX(-150%); }
.mini-submit:hover .arrow::before, .btn-submit:hover .arrow::before { transform: translateX(150%); }
.mini-submit:hover .arrow::after,  .btn-submit:hover .arrow::after  { transform: none; }
.mini-submit[aria-disabled='true'] .arrow, .btn-submit[aria-disabled='true'] .arrow { opacity: .5; }
.mini-submit:hover { background: #DCC9B8; border-color: #DCC9B8; }
.mini-submit[aria-disabled='true'],
.mini-submit[aria-disabled='true']:hover {
  background: color-mix(in srgb, var(--brand-light) 10%, transparent);
  border: 1px dashed color-mix(in srgb, var(--brand-light) 45%, transparent);
  /* 88% measured 4.44:1 at 10px; the dashed edge and tooltip carry the
     disabled signal instead of a dimmer label. */
  color: var(--brand-light);
  cursor: not-allowed;
}
.mini-status {
  margin-top: var(--space-3);
  font-size: var(--step-label);
  line-height: 1.5;
  color: var(--on-dark-muted);
}
.mini-status:empty { display: none; }
/* Both routes on one line, each behind a small chip so they read as two
   equal ways in rather than a stacked list. */
/* Three across on one line. The addresses themselves do not fit three-up in
   a 372px drawer, so the links carry verbs instead — the address is one tap
   away in the mail client anyway, and the row's job here is to offer the
   three ways out of the form, not to be read. */
.mini-meta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, auto));
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--divider-on-dark);
}
.mini-meta a {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-size: var(--step--1);
  line-height: 1.6;
  color: var(--brand-light);
  text-decoration: none;
  padding-block: 4px;
  transition: color var(--dur-fast) var(--ease-out);
}
.mini-meta-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: none;
  border-radius: var(--radius-round);
  background: color-mix(in srgb, var(--brand-light) 14%, transparent);
  color: var(--brand-light);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.mini-meta a:hover .mini-meta-icon { background: var(--brand-light); color: var(--brand-dark); }

/* Same row pattern as the drawer's, sitting inside the contact card: the
   form already carries the card's own padding, so this only needs the rule
   above it and the vertical air. */
.form-direct {
  /* One row here, not the drawer's two-by-two grid: the card is wide enough
     to take the label and both links on a single line. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2) var(--space-5);
  padding-inline: 0;
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  padding-bottom: 0;
}
.form-direct-label {
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--on-dark-muted);
  margin-right: var(--space-2);
}

.nav-toggle { display: none; }

/* Chrome inverts over dark ground. The two halves carry separate states —
   the nav can be over a burgundy card while the lockup is still over the
   page — so [data-over-dark] drives the left and [data-right-dark] the
   right. Inverting them together put one side or the other at 1.2:1. */
.chrome[data-over-dark='true'] .brand,
.chrome[data-right-dark='true'] .pills,
.chrome[data-right-dark='true'] .nav-toggle {
  background: var(--glass-dark);
  border-color: color-mix(in srgb, var(--brand-light) 20%, transparent);
  color: var(--brand-light);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.18);
}
.chrome[data-right-dark='true'] .dock-item { color: var(--on-dark-muted); }
.chrome[data-right-dark='true'] .dock-item:hover {
  background: color-mix(in srgb, var(--brand-light) 14%, transparent);
  color: var(--brand-light);
}
/* Solid, like the light-mode active state. A 30% sand tint with sand type on
   it measured 3.32:1 once the bar was over a burgundy section. */
.chrome[data-right-dark='true'] .dock-item[aria-current='true'] {
  background: var(--brand-light);
  color: var(--ink);
}
/* The visible pill background lives on .contact-trigger (the drawer was
   made absolute), so the inversion has to target the trigger — setting it
   on .contact-pill left burgundy text on a burgundy button. */
.chrome[data-right-dark='true'] .contact-trigger {
  background: var(--brand-light);
  color: var(--ink);
}
/* Over a burgundy section the drawer drops to the deeper burgundy so it still
   separates from what is behind it. It never flips to sand: every label, field
   and button inside is styled for a dark ground, and inverting the panel alone
   left all of them at 1.24:1. */
.chrome[data-right-dark='true'] .contact-drawer { background: var(--surface-alt); }
.chrome[data-right-dark='true'] .contact-drawer-inner > * { color: var(--ink); }
.chrome[data-right-dark='true'] .contact-drawer-inner > *:last-child { color: var(--ink-muted); }

/* ---------- Hero: the cycling word ----------
   The word in the headline changes on a timer. It was a fake select for a
   while — an open list of the alternatives sitting under the line — and that
   read as a form control dropped into a headline. Then a rule beneath it
   filled across the dwell, which was a second object competing with the
   word. Now the word IS the indicator: it comes up muted and the ink wipes
   across it left to right over the dwell, so it reads as a value settling
   rather than a caption being underlined. The box morphs to each word's own
   width so the sentence never carries a gap for the short ones.
   ------------------------------------------------------------------ */
.pick {
  position: relative;
  display: inline-block;
  /* Width is written by the script from a measurement of each word. */
  transition: width 520ms var(--ease-out);
}
.pick-word {
  display: inline-block;
  color: var(--brand-dark);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .pick-word {
    /* 55% is the floor, not a taste call: the unfilled half is real text at
       large size, so it has to hold 3:1 on the hero ground on its own. This
       measures 3.16:1 and is as light as the word can go. */
    background-image: linear-gradient(90deg,
      var(--brand-dark) 0 50%,
      color-mix(in srgb, var(--brand-dark) 55%, transparent) 50% 100%);
    background-size: 200% 100%;
    background-position: 100% 0;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    animation: pick-fill var(--dwell, 3200ms) linear infinite;
  }
}
@keyframes pick-fill {
  from { background-position: 100% 0; }
  to   { background-position: 0 0; }
}
.pick-word.is-swap { animation: pick-swap 460ms var(--ease-out), pick-fill var(--dwell, 3200ms) linear infinite; }
@keyframes pick-swap {
  0%   { opacity: 0; transform: translateY(0.16em); }
  100% { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .pick-word, .pick-word.is-swap {
    animation: none;
    background-image: none;
    color: var(--brand-dark);
  }
}

/* ---------- Hero ---------- */
/* Tighter foot: the hero's own bottom padding plus the Problem section's top
   padding stacked to 192px of empty ground between the two. */
/* Deeper top pad: the chrome floats over the page, and at the old height
   the first line of the headline sat right under it. */
.hero { position: relative; padding-block: clamp(8rem, 5.6rem + 5.8vw, 11rem) clamp(4.5rem, 3.4rem + 3.4vw, 7rem); }
/* Claim on one line rag-left, a full empty line, then the answer on one line
   pushed to the right edge. The step down and across leads into the globe,
   which is centred under both. */
.hero h1 {
  /* Sized to hold the longer of the two lines without wrapping at the wrap's
     max-width; the lines are nowrap so a bad break can't creep back in. */
  font-size: clamp(2rem, 0.2rem + 4.3vw, 4.5rem);
  display: grid;
  row-gap: 1em;                 /* the empty paragraph line between them */
  line-height: 1.18;
  margin-bottom: clamp(2rem, 1.2rem + 2.6vw, 3.5rem);
}
.hero .head-claim, .hero .head-answer { display: block; white-space: nowrap; }
.hero .head-answer { text-align: right; }
/* A live indicator ahead of the answer line: the narrative is running now,
   not something we set up once. Decorative — the sentence says it too. */
/* Live indicator, sized for a mono button label and set on the accent so it
   reads as a status light rather than a stray bullet. */
.live-dot {
  position: relative;
  display: inline-block;
  flex: none;
  width: 7px; height: 7px;
  margin-right: 2px;
  border-radius: 50%;
  background: var(--accent);
}
.live-dot::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  background: var(--accent);
  animation: live-ping 2s cubic-bezier(.2,.65,.3,1) infinite;
}
@keyframes live-ping {
  0%   { transform: scale(1);   opacity: 0.55; }
  75%  { transform: scale(3.2); opacity: 0; }
  100% { transform: scale(3.2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .live-dot::before { animation: none; opacity: 0.25; transform: scale(2); }
}
.hero .line-solid { color: var(--brand-dark); }
/* 62%, not 30%. At 30% the faded line measured 1.76:1 over the hero's
   painted background — below the 3:1 large-text floor. 62% holds 3.4:1
   across the full animation cycle and still reads a clear step down from
   the solid line's 10:1. */
.hero .line-faded { color: color-mix(in srgb, var(--brand-dark) 62%, transparent); }

/* Glimmer: a highlight sweeps along the clause, cycling between the faded
   and full brand colour. Falls back to the faded colour where
   background-clip:text is unsupported, and holds still for reduced motion. */
.line-glimmer { color: color-mix(in srgb, var(--brand-dark) 62%, transparent); }
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .line-glimmer {
    background-image: linear-gradient(
      100deg,
      color-mix(in srgb, var(--brand-dark) 62%, transparent) 0%,
      color-mix(in srgb, var(--brand-dark) 62%, transparent) 38%,
      var(--brand-dark) 50%,
      color-mix(in srgb, var(--brand-dark) 62%, transparent) 62%,
      color-mix(in srgb, var(--brand-dark) 62%, transparent) 100%);
    background-size: 260% 100%;
    background-position: 130% 0;
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    animation: glimmer 3.2s ease-in-out infinite;
  }
}
@keyframes glimmer {
  0%   { background-position: 130% 0; }
  70%  { background-position: -30% 0; }
  100% { background-position: -30% 0; }
}

/* ---------- Orbiting platforms ----------
   Rings of platform chips circling a particle globe. Each arm rotates and
   the chip counter-rotates by the same amount, so labels stay upright.
   Decorative: the platform names also exist as real text below.
   ------------------------------------------------------------------ */
@keyframes orbit-cw {
  from { transform: rotate(var(--start-angle)); }
  to   { transform: rotate(calc(var(--start-angle) + 360deg)); }
}
@keyframes orbit-ccw {
  from { transform: rotate(var(--start-angle)); }
  to   { transform: rotate(calc(var(--start-angle) - 360deg)); }
}
@keyframes counter-cw {
  from { transform: rotate(var(--counter-offset)); }
  to   { transform: rotate(calc(var(--counter-offset) - 360deg)); }
}
@keyframes counter-ccw {
  from { transform: rotate(var(--counter-offset)); }
  to   { transform: rotate(calc(var(--counter-offset) + 360deg)); }
}

.orbit {
  /* The gap from the headline's second line down to the diagram. Set against
     the 117px above the headline and the 49px between its two lines, so the
     three read as a progression rather than three near-identical bands. */
  margin-top: clamp(3.75rem, 2.3rem + 4.8vw, 7rem);
  /* Rings are centred on the container's bottom edge, so the container has
     to be at least the largest ring's radius tall or the top clips. Both
     are driven off the same variable so they cannot drift apart. */
  --ring-3: clamp(560px, 64vw, 940px);
  /* Wider spacing between rings so the set reads open rather than squashed. */
  --ring-2: calc(var(--ring-3) * 0.68);
  --ring-1: calc(var(--ring-3) * 0.36);
  /* One unit of chip scale, defined so that 1 unit == ring-3 / 688. Every
     chip dimension is written as a multiple of it, which keeps the whole set
     locked to the ring geometry: the radial gap between rings is 0.16 of
     ring-3, i.e. 110 units, and the widest mark is 108, so a mark can never
     reach across into its neighbour's band however the rings scale.
     Mirrors clamp(560px, 64vw, 940px) / 688, so the floor here and the ring
     set's floor land together — pin one without the other and the marks keep
     their size on a ring that has stopped shrinking, which is exactly where
     they start crossing. The top end is pulled in so the marks do not keep
     growing on very wide screens. */
  --mark-u: clamp(0.814px, 0.0930vw, 1.15px);
  position: relative;
  /* How far the box extends BELOW the shared centre line of the rings and the
     globe. Both are anchored to that line rather than to the container's
     bottom edge, so this reveals more of the sphere without moving it off
     centre — which is what pushed it up into ring 1's chips. */
  --orbit-reveal: calc(var(--ring-1) * 0.17);
  height: calc(var(--ring-3) / 2 + 34px + var(--orbit-reveal));
  margin-bottom: clamp(2.25rem, 1.4rem + 2.6vw, 4rem);
  overflow: hidden;
  display: flex;
  justify-content: center;
  /* No rule: the whole visual fades out at the bottom and returns to the
     page ground, so there is nothing to cut against. */
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 90%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 90%, transparent 100%);
}
.orbit-globe {
  position: absolute;
  bottom: var(--orbit-reveal); left: 50%;
  transform: translate(-50%, 50%);
  /* Tied to the inner ring rather than the viewport: the two used to be
     sized independently, so at narrow widths the globe outgrew ring 1 and
     the chips landed on top of the sphere. */
  width: calc(var(--ring-1) * 0.88);
  aspect-ratio: 1;
  pointer-events: none;
  z-index: 2;
}
.orbit-ring {
  position: absolute;
  bottom: var(--orbit-reveal); left: 50%;
  transform: translate(-50%, 50%);
  border-radius: var(--radius-round);
  border: 1px solid var(--divider);
  aspect-ratio: 1;
}
.orbit-r1 { width: var(--ring-1); }
.orbit-r2 { width: var(--ring-2); }
.orbit-r3 { width: var(--ring-3); }

.orbit-arm {
  position: absolute;
  top: 0; left: 50%;
  height: 50%;
  width: 0;              /* zero-width arm: the chip centres on the ring line */
  margin-left: 0;
  transform-origin: bottom center;
  display: flex;
  justify-content: center;
  animation: orbit-cw var(--dur, 30s) linear infinite;
}
.orbit-ring[data-dir='ccw'] .orbit-arm { animation-name: orbit-ccw; }

/* No pill. The capsule was doing two jobs — separating the mark from the
   ring line, and hiding how unevenly the wordmarks were sized — and with the
   marks now set to a common optical height it was only adding furniture. The
   marks sit straight on the page ground. */
.orbit-chip {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  height: calc(30 * var(--mark-u));
  padding: 0;
  margin-top: calc(-15 * var(--mark-u));
  background: transparent;
  border: 0;
  white-space: nowrap;
  animation: counter-cw var(--dur, 30s) linear infinite;
}
.orbit-ring[data-dir='ccw'] .orbit-chip { animation-name: counter-ccw; }

/* Chips carry the platform's own mark rather than its name set in mono, and
   each keeps an .sr-only label so nothing is lost when the image is the only
   content. Wordmarks only: the lockups that came with a symbol attached
   (Trustpilot's star, reddit's alien, the ChatGPT swirl, Tripadvisor's owl)
   were cropped back to the word, because at this size the symbol was most of
   what the eye got. Every mark is a transparent alpha silhouette trimmed to
   its own ink, built by tools/orbitmark.py. */
.orbit-mark {
  display: block;
  /* Sized per logo rather than into one shared box. A shared box scaled every
     mark by its bounding rectangle, so WIKIPEDIA (a long all-caps word) came
     out at half the letter height of Forbes. Each mark carries a box built
     from its own densest-row span, but taken to the square root rather than
     used straight: that span is cap height for an all-caps mark and x-height
     for a lowercase one, and equalising them outright made FORBES and BBC
     tiny beside Google. The half power splits the difference, which is what
     brings LinkedIn level with Google and Forbes level with Quora. Widths
     stay inside 108 units, the clearance between rings. */
  width: calc(var(--mw) * var(--mark-u));
  height: calc(var(--mh) * var(--mark-u));
  background-color: var(--ink-muted);
  /* Every mask is levelled to full opacity by tools/orbitmark.py, so the
     colour here is the colour every mark comes out. Before that, sources
     drawn in grey painted as a washed-out burgundy next to the black ones
     and the ring read as logos in a handful of different tints. */
  -webkit-mask: var(--logo) center / contain no-repeat;
          mask: var(--logo) center / contain no-repeat;
}
.orbit-mark[data-logo='bbc']           { --logo: url('../img/orbit/bbc.png');           --mw: 55.8; --mh: 15.9; }
.orbit-mark[data-logo='bloomberg']     { --logo: url('../img/orbit/bloomberg.png');     --mw: 89.2; --mh: 17.8; }
.orbit-mark[data-logo='chatgpt']       { --logo: url('../img/orbit/chatgpt.png');       --mw: 74.4; --mh: 13.3; }
.orbit-mark[data-logo='claude']        { --logo: url('../img/orbit/claude.png');        --mw: 64.8; --mh: 15.9; }
.orbit-mark[data-logo='entrepreneur']  { --logo: url('../img/orbit/entrepreneur.png');  --mw: 71.6; --mh: 16.6; }
.orbit-mark[data-logo='facebook']      { --logo: url('../img/orbit/facebook.png');      --mw: 80.4; --mh: 15.9; }
.orbit-mark[data-logo='forbes']        { --logo: url('../img/orbit/forbes.png');        --mw: 56.9; --mh: 14.2; }
.orbit-mark[data-logo='gemini']        { --logo: url('../img/orbit/gemini.png');        --mw: 52.1; --mh: 13.6; }
.orbit-mark[data-logo='glassdoor']     { --logo: url('../img/orbit/glassdoor.png');     --mw: 71.4; --mh: 14.3; }
.orbit-mark[data-logo='google']        { --logo: url('../img/orbit/google.png');        --mw: 56.5; --mh: 18.7; }
.orbit-mark[data-logo='indeed']        { --logo: url('../img/orbit/indeed.png');        --mw: 61.9; --mh: 16.6; }
.orbit-mark[data-logo='instagram']     { --logo: url('../img/orbit/instagram.png');     --mw: 79.8; --mh: 17.7; }
.orbit-mark[data-logo='khaleej-times'] { --logo: url('../img/orbit/khaleej-times.png'); --mw: 85.4; --mh: 15.6; }
/* Nudged up hardest of the set: the boxed "in" is a solid block beside open
   letterforms, and undersized it was the one mark the eye kept landing on. */
.orbit-mark[data-logo='linkedin']      { --logo: url('../img/orbit/linkedin.png');      --mw: 71.2; --mh: 17.4; }
.orbit-mark[data-logo='perplexity']    { --logo: url('../img/orbit/perplexity.png');    --mw: 82.0; --mh: 16.9; }
.orbit-mark[data-logo='quora']         { --logo: url('../img/orbit/quora.png');         --mw: 57.7; --mh: 16.2; }
.orbit-mark[data-logo='reddit']        { --logo: url('../img/orbit/reddit.png');        --mw: 53.0; --mh: 15.7; }
/* The two symbol marks. The height rule was built for wordmarks, where
   the box is mostly padding around a cap height; a symbol fills its own box,
   so the same numbers would have drawn it at roughly twice the weight of the
   words beside it. These are set to sit at the wordmarks' cap height. */
.orbit-mark[data-logo='techcrunch']    { --logo: url('../img/orbit/techcrunch.png');    --mw: 32.1; --mh: 15.9; }
.orbit-mark[data-logo='x']             { --logo: url('../img/orbit/x.svg');             --mw: 15.2; --mh: 15.5; }
.orbit-mark[data-logo='tiktok']        { --logo: url('../img/orbit/tiktok.png');        --mw: 57.0; --mh: 13.0; }
.orbit-mark[data-logo='tripadvisor']   { --logo: url('../img/orbit/tripadvisor.png');   --mw: 92.5; --mh: 16.7; }
.orbit-mark[data-logo='trustpilot']    { --logo: url('../img/orbit/trustpilot.png');    --mw: 68.7; --mh: 15.2; }
/* Pulled back: an all-caps serif at full width was the widest thing on the
   rings by a distance. */
.orbit-mark[data-logo='wikipedia']     { --logo: url('../img/orbit/wikipedia.png');     --mw: 91.8; --mh: 13.2; }
/* Both cropped back to the word and then pushed up: at the shared size the
   short marks were the ones getting lost on the rings. */
.orbit-mark[data-logo='yelp']          { --logo: url('../img/orbit/yelp.png');          --mw: 46.7; --mh: 19.9; }
.orbit-mark[data-logo='youtube']       { --logo: url('../img/orbit/youtube.png');       --mw: 50.7; --mh: 15.7; }

/* The same halo the contact button wears, borrowed for the chips: each one
   breathes once as it comes round, so the ring reads as live surfaces being
   watched rather than a decorative carousel. Delays are staggered by position
   so the set never pulses in unison. */
/* An opaque backing that dissolves at its own edges, so the ring's hairline
   stops running straight through the wordmarks without a chip being drawn
   around them. Soft-edged on purpose: a hard capsule was the furniture the
   pills had been. */
.orbit-chip::after {
  content: '';
  position: absolute; inset: calc(-9 * var(--mark-u)) calc(-13 * var(--mark-u));
  z-index: -1;
  border-radius: var(--radius-round);
  /* Part-transparent and warmed toward the section's own ground rather than
     a flat page-base disc, which read as a white plate laid on the gradient.
     At 78% the ring's hairline is down to a quarter strength behind a mark —
     enough that the line no longer runs through the letters, not so much
     that a shape appears. */
  background: color-mix(in srgb,
    color-mix(in srgb, var(--page-base) 94%, var(--brand-light)) 78%, transparent);
  -webkit-mask-image: radial-gradient(closest-side, #000 44%, transparent 100%);
          mask-image: radial-gradient(closest-side, #000 44%, transparent 100%);
  pointer-events: none;
}
.orbit-chip::before {
  content: '';
  position: absolute; inset: calc(-10 * var(--mark-u)) calc(-14 * var(--mark-u));
  z-index: -2;
  /* A soft bloom rather than the capsule the pill version used: without a
     background to sit behind, a hard rounded rectangle swelling out of the
     word read as a box, not a pulse. */
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--brand-dark) 24%, transparent), transparent 100%);
  opacity: 0;
  animation: orbit-ping 4.4s ease-out infinite;
  animation-delay: var(--ping, 0s);
  pointer-events: none;
}
@keyframes orbit-ping {
  0%   { transform: scale(0.7); opacity: 0; }
  12%  { opacity: 0.7; }
  42%  { transform: scale(1.25); opacity: 0; }
  100% { transform: scale(1.25); opacity: 0; }
}
.orbit-arm:nth-child(1) .orbit-chip::before { --ping: 0s; }
.orbit-arm:nth-child(2) .orbit-chip::before { --ping: 0.55s; }
.orbit-arm:nth-child(3) .orbit-chip::before { --ping: 1.1s; }
.orbit-arm:nth-child(4) .orbit-chip::before { --ping: 1.65s; }
.orbit-arm:nth-child(5) .orbit-chip::before { --ping: 2.2s; }
.orbit-arm:nth-child(6) .orbit-chip::before { --ping: 2.75s; }
.orbit-arm:nth-child(7) .orbit-chip::before { --ping: 3.3s; }
.orbit-arm:nth-child(8) .orbit-chip::before { --ping: 3.85s; }
.orbit-r2 .orbit-chip::before { animation-duration: 5.2s; }
.orbit-r3 .orbit-chip::before { animation-duration: 6.1s; }


/* No hover pause: the rings keep turning under the pointer. The data-paused
   hook stays for the reduced-motion and scripted cases. */
.orbit[data-paused='true'] .orbit-arm,
.orbit[data-paused='true'] .orbit-chip { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .orbit-arm, .orbit-chip { animation: none; }
  .orbit-chip::before { animation: none; opacity: 0; }
}

/* Platform names as readable text beneath the orbit */
.lives {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-6);
  margin-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}
.lives-col { border-top: 1px solid var(--divider); padding-top: var(--space-4); }
.lives-name {
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--brand-dark);
  margin-bottom: var(--space-3);
}
.lives-list { font-size: var(--step--1); line-height: 1.6; color: var(--ink-muted); }

@media (max-width: 1024px) {
  .lives { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
}
@media (max-width: 860px) {
  /* The claim-then-answer, left-then-right arrangement is the point, so it
     survives to phone widths: the halves wrap internally but the second one
     stays rag-right under the first. */
  .hero h1 { row-gap: 0.8em; }
  .hero .head-claim, .hero .head-answer { white-space: normal; }
  .hero .head-claim { max-width: 62%; }
  .hero .head-answer { text-align: right; margin-left: auto; max-width: 64%; }
}
@media (max-width: 560px) {
  /* Twelve tracks and two gutters leave 0px per column at phone widths, so
     the grid children overflowed their tracks (clipped by body's overflow-x
     rather than scrolling, which hid it). One column below this. */
  .grid { grid-template-columns: 1fr; }
  .grid > * { grid-column: 1 / -1 !important; }
  .lives { grid-template-columns: 1fr; }
  /* Tied to the viewport, not a fixed 420px: at 375px the outer ring put the
     chips at its 3 and 9 o'clock positions half off-screen, sliced mid-word. */
  /* 70vw, not 80: at 80 the chips at the ring's 3 and 9 o'clock positions
     ran past the gutter and were sliced mid-word. */
  .orbit {
    --ring-3: min(430px, 74vw);
    /* Rings are pushed further apart at phone widths: the set shrinks faster
       than the chips do, so the desktop ratios leave no clearance. */
    --ring-2: calc(var(--ring-3) * 0.64);
    --ring-1: calc(var(--ring-3) * 0.28);
    --mark-u: clamp(0.45px, 0.0968vw, 0.56px);
  }
  /* Half the marks come out at phone widths. Dropping every second one keeps
     what is left evenly spaced around its ring, which hiding a tail would
     not: the arms are laid out at equal angles, so evens and odds are each a
     complete, balanced set. */
  .orbit-arm:nth-child(even) { display: none; }
}

/* ---------- Hero foot ---------- */
/* A rule down the side of the standfirst, heavy enough to read as a bracket
   holding the two paragraphs together rather than a hairline. */
.hero-left { display: grid; align-content: center; }
.hero-intro { padding-left: 0; }
/* The bracket holds the two paragraphs and stops there. Running it past the
   heading as well put a heavy rule beside the ring mark, and the two marks
   were doing one job; against the body copy alone it reads as what it is,
   a hold on the prose. It shares its left edge with the ring mark above. */
.hero-intro-copy {
  border-left: 5px solid var(--brand-dark);
  padding-left: var(--space-5);
}

/* Drawn in the ring stroke, inset from the gutters, spanning both columns. */
/* Full column width, no inset. At 32px in it sat inside both the chrome
   above it and the rules below it, so it was the only horizontal line on the
   page that did not share their ends. */
.hero-foot-rule {
  grid-column: 1 / -1;
  height: 1px;
  margin-inline: 0;
  /* Carries the gap that used to be the grid's row-gap, and matches the gap
     the row-three blocks take above themselves, so row two sits with equal
     air above and below it. */
  margin-bottom: var(--space-7);
  background: var(--divider);
}
/* Stretched rather than centred with its neighbours, so it runs the full
   height of the taller half instead of hovering in the middle of it. */
.hero-intro       { grid-column: 1; grid-row: 2; }
.hero-actions-row { grid-column: 2; grid-row: 2; }

/* Both halves of row two sit centred between the hairline above them and
   the rules below, so whichever is shorter is not left hanging at the top of
   the taller one's height. */
.hero-intro,
.hero-actions-row { align-self: center; }
/* The block sat 48px under its rule and 128px off the section below, which
   read as a paragraph that had drifted up into the diagram. Roughly even
   now, with a little more below to allow for the curve of the next section
   eating into the space. */
.hero-foot {
  display: grid;
  /* No divider track: the standfirst and the buttons are one movement, and a
     rule down the middle of them was dividing a thing that is not divided. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: auto auto auto;
  column-gap: var(--space-8);
  row-gap: 0;
  align-items: start;
  position: relative;
  padding-top: var(--space-7);
}
.hero-intro { max-width: 62ch; }
/* Set in the brand face, sentence case: it reads as a line of writing
   rather than a system label. */
/* A signal going out, drawn as the hero's own rings: three of them leave the
   core one after another and fade at the edge. It is the orbit above this
   block reduced to 34px, which is the only mark that could sit here without
   introducing a new idea. */
/* The box is one line of the heading tall, not 34px, so flex-start puts it on
   the first line rather than below it; the artwork is centred inside that box
   and allowed to overflow. Sizing the box to the mark instead left the mark
   riding low against a 25px line. */
.eyebrow-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 1.25em; flex: none;
  overflow: visible;
}
.eyebrow-mark svg { display: block; width: 34px; height: 34px; overflow: visible; }
.em-core { fill: var(--brand-dark); }
.em-ring {
  fill: none;
  stroke: var(--brand-dark);
  stroke-width: 1.6;
  transform-origin: 20px 20px;
  animation: em-out 3.6s cubic-bezier(.22,.7,.35,1) infinite;
}
.em-2 { animation-delay: 1.2s; }
.em-3 { animation-delay: 2.4s; }
@keyframes em-out {
  0%   { transform: scale(0.55); opacity: 0; }
  12%  { opacity: 0.7; }
  100% { transform: scale(2.5); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .em-ring { animation: none; opacity: 0.28; }
  .em-2 { transform: scale(1.7); }
  .em-3 { transform: scale(2.4); }
}
.hero-eyebrow {
  display: flex;
  align-items: flex-start;
  /* The mark is centred on the bracket below it and the heading follows.
     The rule is 5px wide at the column's left edge, so its centre is at
     2.5px; a 34px mark centred there starts at -14.5px. The gap is then
     whatever puts the heading text on the body copy's left edge: the copy
     sits at 5px of border plus 24px of padding, so 29 - (-14.5 + 34). */
  margin-left: -14.5px;
  gap: 9.5px;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 500;
  letter-spacing: -0.018em;
  line-height: 1.25;
  /* Full brand colour: at 58% it measured 3.43:1 and failed AA anyway. */
  color: var(--brand-dark);
  margin-bottom: var(--space-4);
}
.hero-intro-body + .hero-intro-body { margin-top: var(--space-4); }
/* The one hard commitment above the fold, so it is built as a small block
   rather than a sentence: the scope sits in the label, which leaves the
   promise itself short enough to be set a step up from the paragraph and
   read as a statement. The seal is the solid cut-out mark, not the outline
   one, at 40px — a guarantee that whispers is not a guarantee. */
/* The two hard commitments, side by side under the buttons and above the
   three conditions: promises first, terms second. Each is built as a block
   rather than a sentence — the scope sits in the label, which leaves the
   promise short enough to be set a step up from the body and read as a
   statement. The seal is the solid cut-out mark, not the outline one: a
   guarantee that whispers is not a guarantee. */

/* The badge sits opposite the guarantee rather than under it: one is a
   promise, the other is the third-party record that backs it, and side by
   side they read as a pair. Height is set to the guarantee's own two-row
   stack so both halves fill the same band. */

/* A hairline between the pair, in the gutter rather than under them: they
   are two separate commitments, not one statement in two halves. */
/* A seal, drawn to the same rules as the ethics stamp further down: a ring,
   a fine tooth band, the laurel inside it, and the tick as the only heavy
   stroke on the mark. The first pass was a bare wreath with a tick laid over
   it, which read as two drawings sharing a box. */
/* Both blocks run on one rhythm: two 20px rows with a 4px gap, and the
   content centred in each row. The two sides carry mirrored hierarchies —
   small then large on the left, large then small on the right — so their
   glyph boxes can never share both a top and a bottom. Sizing the small
   halves up narrows that gap to about a pixel, and centring in a fixed row
   splits what remains evenly above and below. */
/* Auto rows, not two fixed 20px ones: the fixed pair was set when this block
   had to line up with the conditions opposite row for row, and a term that
   wrapped to two lines then ran straight through the line under it. */

.hero-intro-body {
  /* Back to body size. The half opposite grew a third button, so the copy is
     no longer the heavier of the two, and at the smaller step it was sitting
     short of the rules below it. */
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--ink-muted);
}
/* No panel any more. A tinted card around the calls and their notes was one
   box too many on a page already built out of boxes; the block holds together
   on its own, with a rule down the gutter separating it from the standfirst.
   The old .hero-actions-row wrapper went when the two halves were flattened into
   one grid, so everything below hangs off the button row itself. */
/* A line each at every width. Side by side they had to share the row with a
   34-character label, which left the second call looking like an afterthought
   pinned to the end of the first; stacked, all three read as full-weight
   choices. */
.hero-actions-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
/* Three steps down the same burgundy. Sand type cannot follow the fill past
   about 90% — at 80% it measures 3.46:1 — so the two lighter bars carry ink
   instead, which is what keeps the descent legible rather than merely
   decorative. */
/* Solid, then two outlines told apart by the weight of their edge rather
   than by a fill. A tint heavy enough to be seen against this ground reads
   as a grey slab, and two tints a few percent apart read as one weight
   twice; the border carries the descent at a fraction of the ink. */
.hero-actions-row .btn-ghost {
  background: transparent;
  border-color: color-mix(in srgb, var(--brand-dark) 38%, transparent);
  color: var(--brand-dark);
}
.hero-actions-row .btn-ghost::before { content: none; }
.hero-actions-row .btn-ghost:hover,
.hero-actions-row .btn-ghost:focus-visible {
  background: color-mix(in srgb, var(--brand-dark) 6%, transparent);
  border-color: color-mix(in srgb, var(--brand-dark) 52%, transparent);
}
.hero-actions-row .btn-third {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 38%, transparent);
  color: var(--brand-dark);
}
.hero-actions-row .btn-third:hover,
.hero-actions-row .btn-third:focus-visible {
  background: color-mix(in srgb, var(--brand-dark) 6%, transparent);
  border-color: color-mix(in srgb, var(--brand-dark) 52%, transparent);
}
/* Brand ink rather than --ink-muted on the tinted bars: the muted tone is
   lighter than the brand colour, and once the fill came up to 40% it was the
   sub-label that failed first, not the label. */
.hero-actions-row .btn-ghost .btn-sub,
.hero-actions-row .btn-third .btn-sub {
  color: color-mix(in srgb, var(--brand-dark) 88%, transparent);
}
.hero-actions-row .btn-third .arrow { background: color-mix(in srgb, var(--brand-dark) 14%, transparent); }
.hero-actions-row .btn-third:hover .arrow,
.hero-actions-row .btn-third:focus-visible .arrow { background: color-mix(in srgb, var(--brand-dark) 22%, transparent); }
/* A step under the page's other calls: at --step-1 a label this long filled
   the bar edge to edge with nothing to breathe against. */
.hero-actions-row .btn {
  justify-content: flex-start;
  min-height: 62px;
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  font-size: var(--step--1);
}
/* The assurance that answers each button sits on the button. Under NDA is
   the thing a reader wants to know at the moment they reach for the first
   one, not three rows further down, and an ethics line does more work
   beside the method than floating in a list of conditions. */
.hero-actions-row .btn-text { display: grid; gap: 2px; min-width: 0; text-align: left; }
.hero-actions-row .btn-sub {
  font-family: var(--font-label);
  font-size: 0.625rem;
  letter-spacing: 0.02em;
  line-height: 1.45;
}
.hero-actions-row .btn-primary .btn-sub {
  color: color-mix(in srgb, var(--brand-light) 82%, var(--brand-dark));
}
/* Phosphor Light, the same family and weight as the nav marks. A padlock for
   the confidential conversation and the broadcast mark for the engine — the
   latter is a dot inside widening arcs, which is the orbit above this block
   drawn small. */
.hero-actions-row .btn-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  flex: none;
  opacity: 0.82;
}
.hero-actions-row .btn-ico svg { display: block; width: 100%; height: 100%; }
.hero-actions-row .btn > .arrow { margin-left: auto; }
/* The arrow gets a disc of its own, pinned to the far end. The slide-swap
   underneath is unchanged — the two glyphs now travel across a circle
   instead of a bare 1em box, which is what makes it read as a control
   rather than a piece of punctuation. */
.hero-actions-row .btn .arrow {
  width: 28px; height: 28px;
  border-radius: 50%;
  flex: none;
  transition: background var(--dur-base) var(--ease-out);
}
.hero-actions-row .btn .arrow::before,
.hero-actions-row .btn .arrow::after {
  display: flex; align-items: center; justify-content: center;
  inset: 0;
  width: 100%; height: 100%;
  font-size: 0.875rem;
  line-height: 1;
}
.hero-actions-row .btn-primary .arrow {
  background: color-mix(in srgb, var(--brand-light) 18%, transparent);
}
.hero-actions-row .btn-primary:hover .arrow,
.hero-actions-row .btn-primary:focus-visible .arrow {
  background: color-mix(in srgb, var(--brand-light) 32%, transparent);
}
.hero-actions-row .btn-ghost .arrow {
  background: color-mix(in srgb, var(--brand-dark) 14%, transparent);
}
.hero-actions-row .btn-ghost:hover .arrow,
.hero-actions-row .btn-ghost:focus-visible .arrow {
  background: color-mix(in srgb, var(--brand-dark) 16%, transparent);
}
/* Three outcomes across one line, under the buttons. The copy opposite says
   what the work is; these say what it is for, next to the thing you press. */
/* The tick gets a column of its own so it sits on the term's line rather
   than floating against the wrapped gloss underneath it. */
/* Content-sized tracks, centred in the third: at 1fr the pair sat against
   the left of each column and the row read as left-weighted under a rule
   that runs the full width. */
/* Ranged left in a two-column block: with four items on two rows there is a
   common left edge to hang off, which centring would throw away. */
/* Was the mono face, which put the small half of this pair in one typeface
   and the small half of the guarantee opposite in the other — the two blocks
   flank the same divider, so the mirror read as a mistake. Both pairs now run
   in the display face, told apart by weight and colour. */

@media (max-width: 1024px) {
  .slots { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
  .slots-pause { top: -44px; }
}
@media (max-width: 720px) {
  .slots { grid-template-columns: 1fr; }
  /* One column: the explicit row and column placements that keep the two
     rules level on desktop have to be released here, or the children keep
     reaching for a third column that no longer exists. Order is restored by
     row rather than DOM order, so the copy still runs into its guarantees
     before the calls. */
  .hero-foot {
    /* minmax(0, …), never a bare 1fr. A bare 1fr is minmax(auto, 1fr), and
       the auto floor is the min-content of the widest item in the track.
       The promise band holds a marquee whose track is measured in thousands
       of pixels, so the single column grew to 2674px on a 390px phone and
       dragged the rule, the copy and the buttons off the screen with it. */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto;
    gap: var(--space-6);
    align-items: start;
  }
  /* Grid and flex items default to min-width: auto, which is the same trap
     one level down. */
  .hero-foot > * { min-width: 0; }
  .hero-proof,
  .hero-marquee { min-width: 0; max-width: 100%; }
  .hero-intro,
  .hero-actions-row,
  .hero-proof { grid-column: 1; }
  .hero-intro      { grid-row: 2; margin-top: 0; }
  .hero-actions-row { grid-row: 3; margin-top: 0; }
  .hero-proof { grid-row: 4; margin-top: var(--space-6); }
}

/* The method is a change of subject, not a continuation, so it gets a
   full section unit of air on both sides of the join. */
#what-we-do { padding-bottom: calc(var(--section-y) * 0.75); }
/* Case studies needs air before the Fractional Placements band starts. */
#work { padding-bottom: calc(var(--section-y) * 0.6); }
#services { padding-top: calc(var(--section-y) * 0.75); padding-bottom: calc(var(--section-y) * 1.5); }
.svc-intro { margin-bottom: var(--space-6); }
.svc-intro .label { margin-bottom: var(--space-4); }
.svc-intro .h2 { color: var(--brand-dark); }

/* ---------- Services showcase ----------
   Split still/motion media with a right rail: what the service is,
   and beneath it what it actually includes.
   ------------------------------------------------------------------ */
.svc-stage { position: relative; }
.svc-pane { position: relative; }
.svc-pane:not(.is-active) {
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-slow) var(--ease-out), visibility 0s linear var(--dur-slow);
}
.svc-pane.is-active { opacity: 1; visibility: visible; transition: opacity var(--dur-slow) var(--ease-out); }

/* Motion on the left, cards on the right, as a real two-column pane. The
   cards used to float over a two-up image, which forced them narrow and made
   the type shrink to fit. */
.svc-pane {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-4);
  align-items: stretch;
}
/* Cards first, panel second. The panel is the loud half, and leading with it
   meant the eye met the animation before it knew what stage it was watching.
   Reordered here rather than in the markup so the reading order stays
   media-then-detail for anything that ignores the grid. */
.svc-pane > .svc-media { order: 2; }
.svc-pane > .svc-rail { order: 1; }
@media (max-width: 1024px) {
  .svc-pane > .svc-media { order: 1; }
  .svc-pane > .svc-rail { order: 2; }
}
.svc-media {
  border-radius: var(--radius-media);
  overflow: hidden;
  min-height: 460px;
}
.svc-media .ph {
  border-radius: 0; border: 0; aspect-ratio: auto; height: 100%;
  /* Clears the ratio badge, which sits at the same top-left corner. */
  justify-content: flex-start; padding-top: calc(var(--space-5) + 26px);
}
.svc-media .ph::before { top: var(--space-5); left: var(--space-5); right: auto; }

/* ---------- Method motion ----------
   Three short looping sequences standing in for the film. Built in the DOM
   rather than generated as video: each one has to show legible content —
   sources being flagged, items struck out, a placement climbing the ranks —
   and a generated clip renders type as garbled shapes. This way the text is
   real, the palette is exact, it weighs nothing, and it fetches nothing.
   The search sequence is deliberately our own interface rather than a mock
   of a real engine's results page.
   ------------------------------------------------------------------ */
.svc-motion {
  height: 100%;
  min-height: 460px;
  display: flex; flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6) var(--space-6) calc(var(--space-6) + 54px);
  background: var(--surface);
  background-image: repeating-linear-gradient(
    45deg, transparent 0 11px,
    color-mix(in srgb, var(--brand-dark) 3%, transparent) 11px 12px);
}
.mo-head { display: flex; align-items: center; gap: var(--space-3); }
.mo-count {
  margin-left: auto;
  font-family: var(--font-label); font-size: 9px;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-muted);
}
/* A pass running left to right under the header, so the panel reads as
   something actively working rather than a static list. */
.mo-scan {
  height: 2px; border-radius: 2px;
  background: color-mix(in srgb, var(--brand-dark) 8%, transparent);
  overflow: hidden;
}
.mo-scan > i {
  display: block; height: 100%; width: 34%;
  border-radius: 2px;
  background: color-mix(in srgb, var(--brand-dark) 45%, transparent);
  animation: mo-scan 5.4s ease-in-out infinite;
}
@keyframes mo-scan {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(320%); }
}
.mo-dot {
  position: relative;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  animation: mo-pulse 2.6s ease-in-out infinite;
}
@keyframes mo-pulse { 0%,100% { opacity: .35; } 50% { opacity: 1; } }
/* Alert state: the panel goes hot the moment it has something live, and the
   indicator picks up a radar ring on top of the resting pulse. */
.mo-dot::after {
  content: ''; position: absolute; inset: -3px;
  border-radius: 50%; border: 1px solid var(--brand-dark);
  opacity: 0;
}
.svc-motion.is-hot .mo-dot { background: var(--brand-dark); animation-duration: 1.3s; }
.svc-motion.is-hot .mo-dot::after { animation: mo-ring 2.1s ease-out infinite; }
@keyframes mo-ring {
  0%   { transform: scale(.7); opacity: .7; }
  100% { transform: scale(2.6); opacity: 0; }
}
.svc-motion.is-hot .mo-scan > i { animation-duration: 3s; background: color-mix(in srgb, var(--brand-dark) 62%, transparent); }
.mo-title {
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--ink-muted);
}
.mo-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; flex: 1 1 auto; align-content: start; }
.mo-row {
  position: relative;
  overflow: hidden;
  padding: 9px var(--space-4);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--brand-dark) 4%, transparent);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 8%, transparent);
  font-size: 12px; line-height: 1.3;
  color: var(--ink-muted);
  transition: background 320ms var(--ease-out), border-color 320ms var(--ease-out),
              opacity 320ms var(--ease-out), transform 420ms var(--ease-out);
}
/* The row proper is a wrapper now, so a flagged item can drop a breakdown
   underneath its own line without breaking the column grid above it. */
.mo-line {
  display: grid; grid-template-columns: 20px 46px minmax(0, 1fr) auto;
  align-items: center; gap: var(--space-3);
}
.mo-row.has-caret .mo-line { grid-template-columns: 20px 46px minmax(0, 1fr) auto 12px; }
.mo-mark {
  width: 20px; height: 16px; border-radius: 4px;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 22%, transparent);
  display: grid; place-items: center;
  font-family: var(--font-label);
  font-size: 8px; line-height: 1; letter-spacing: 0;
  color: var(--ink-muted);
}
/* Severity as a meter, not the word. Three bars: one lit is low, two medium,
   all three high — sortable at a glance in a way 8px capitals never were. */
.mo-sev {
  display: grid; grid-auto-flow: column; align-items: end; gap: 2px;
  width: 20px; height: 13px;
}
/* The lit bars climb in weight as well as height, so a high-severity row
   reads as heavier ink and not just a taller stack. 62% is the floor: below
   that a lit bar drops under 3:1 on the row ground and stops counting as a
   graphic that carries meaning. */
.mo-sev {
  --sev-on: var(--brand-dark);
  --sev-off: color-mix(in srgb, var(--brand-dark) 14%, transparent);
}
.mo-sev i {
  width: 4px; border-radius: 1px;
  background: var(--sev-off);
  transition: background 260ms var(--ease-out);
}
.mo-sev i:nth-child(1) { height: 5px;  --lit: color-mix(in srgb, var(--sev-on) 62%, transparent); }
.mo-sev i:nth-child(2) { height: 9px;  --lit: color-mix(in srgb, var(--sev-on) 80%, transparent); }
.mo-sev i:nth-child(3) { height: 13px; --lit: var(--sev-on); }
.mo-sev[data-sev='LOW']  i:nth-child(1),
.mo-sev[data-sev='MED']  i:nth-child(-n+2),
.mo-sev[data-sev='HIGH'] i { background: var(--lit); }

/* Breakdown drawer: why the alert fired. Only the newest one is open, so
   the panel grows by one row's worth at most. */
.mo-caret {
  width: 12px; height: 12px; justify-self: end;
  background-color: currentColor;
  -webkit-mask: var(--caret) center / 10px 10px no-repeat;
          mask: var(--caret) center / 10px 10px no-repeat;
  --caret: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 15.5 5.5 9l1-1 5.5 5.5L17.5 8l1 1z'/%3E%3C/svg%3E");
  opacity: .55;
  transition: transform 320ms var(--ease-out), opacity 320ms var(--ease-out);
}
.mo-row.is-open .mo-caret { transform: rotate(180deg); opacity: .95; }
.mo-sub {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows 380ms var(--ease-out);
}
.mo-row.is-open .mo-sub { grid-template-rows: 1fr; }
.mo-sub > ul {
  overflow: hidden; min-height: 0;
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 2px;
}
.mo-row.is-open .mo-sub > ul { padding-top: 7px; }
.mo-sub li {
  position: relative;
  padding-left: 14px;
  margin-left: calc(20px + var(--space-3));
  font-size: 10.5px; line-height: 1.5;
  color: var(--ink-muted);
}
.mo-sub li::before {
  content: ''; position: absolute; left: 4px; top: .62em;
  width: 4px; height: 1px;
  background: color-mix(in srgb, var(--brand-dark) 40%, transparent);
}

/* Removal in progress: a bar draining across the foot of the row. */
.mo-prog {
  position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  transform: scaleX(0); transform-origin: left center;
  background: var(--brand-dark);
  opacity: 0;
}
.mo-row.is-busy .mo-prog { opacity: 1; animation: mo-prog 1900ms linear forwards; }
@keyframes mo-prog { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* The platform each item came from, drawn from the same mask set the orbit
   uses. Naming the surface is what makes the sequence read as real work on
   real places rather than a generic animated list. */
.mo-logo {
  display: block;
  width: 46px; height: 11px;
  background-color: currentColor;
  opacity: .78;
  -webkit-mask: var(--logo) left center / contain no-repeat;
          mask: var(--logo) left center / contain no-repeat;
}
.mo-logo[data-logo='reddit'] { --logo: url('../img/orbit/reddit.png'); }
.mo-logo[data-logo='trustpilot'] { --logo: url('../img/orbit/trustpilot.png'); }
.mo-logo[data-logo='google'] { --logo: url('../img/orbit/google.png'); }
.mo-logo[data-logo='chatgpt'] { --logo: url('../img/orbit/chatgpt.png'); }
.mo-logo[data-logo='forbes'] { --logo: url('../img/orbit/forbes.png'); }
.mo-logo[data-logo='linkedin'] { --logo: url('../img/orbit/linkedin.png'); }
.mo-logo[data-logo='quora'] { --logo: url('../img/orbit/quora.png'); }
.mo-logo[data-logo='wikipedia'] { --logo: url('../img/orbit/wikipedia.png'); }
.mo-logo[data-logo='bloomberg'] { --logo: url('../img/orbit/bloomberg.png'); }

.mo-tag {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  font-family: var(--font-label); font-size: 8px;
  text-transform: uppercase; letter-spacing: 0.08em;
  padding: 3px 7px; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
  color: var(--ink-muted);
  white-space: nowrap;
  min-width: 74px; text-align: center;
  transition: background 260ms var(--ease-out), border-color 260ms var(--ease-out), color 260ms var(--ease-out);
}
/* Status chips in the monitoring mock. 8px holds on a desktop screen; on a
   phone it goes up a step, with the count beside it. After the rule it
   overrides, because at equal specificity the later declaration wins. */
@media (max-width: 560px) {
  .mo-tag { font-size: 9.5px; min-width: 68px; }
  .mo-count { font-size: 10px; }
}
/* Rows with no status carry no pill: an empty outlined capsule read as a
   control that had failed to load. */
.mo-tag:empty { display: none; }
/* Working on it: the row a removal is running against. Loud on purpose —
   this is the stage where something is actively being taken down, so it
   takes the full burgundy field and the tag keeps a blinking indicator. */
.mo-row.is-busy {
  background: color-mix(in srgb, var(--brand-dark) 12%, transparent);
  border-color: var(--brand-dark);
  color: var(--brand-dark);
  animation: mo-urgent 700ms ease-in-out infinite alternate;
}
@keyframes mo-urgent {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--brand-dark) 26%, transparent); }
  to   { box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-dark) 0%, transparent); }
}
.mo-row.is-busy .mo-tag {
  background: var(--brand-dark); border-color: var(--brand-dark);
  color: var(--brand-light);
}
.mo-row.is-busy .mo-tag::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--brand-light);
  animation: mo-blink 460ms steps(1, end) infinite;
}
@keyframes mo-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: .15; } }
.mo-row.is-done .mo-tag {
  background: color-mix(in srgb, var(--brand-dark) 10%, transparent);
  border-color: transparent;
}
/* Cleared rows fade out as a set; the meter dims with them but stays
   legible rather than vanishing. */
.mo-row.is-done .mo-sev { --sev-on: color-mix(in srgb, var(--brand-dark) 48%, transparent); }
/* Never reached by the seed panel, which numbers its rows instead of rating
   them — kept so a burgundy row can never carry burgundy bars. */
.mo-row.is-own .mo-sev {
  --sev-on: var(--brand-light);
  --sev-off: color-mix(in srgb, var(--brand-light) 32%, transparent);
}
.mo-row.is-own .mo-sub li { color: var(--on-dark-muted); }
.mo-row.is-own .mo-sub li::before { background: color-mix(in srgb, var(--brand-light) 55%, transparent); }
/* flagged: found by the survey */
.mo-row.is-flag { background: color-mix(in srgb, var(--accent) 12%, transparent); border-color: color-mix(in srgb, var(--accent) 34%, transparent); color: var(--brand-dark); }
.mo-row.is-flag .mo-sev { --sev-off: color-mix(in srgb, var(--brand-dark) 26%, transparent); }
/* Burgundy, not the bronze accent: a sand glyph on bronze was 3.04:1. */
.mo-row.is-flag .mo-mark { background: var(--brand-dark); border-color: var(--brand-dark); color: var(--brand-light); }
.mo-row.is-flag .mo-tag { background: var(--brand-dark); border-color: var(--brand-dark); color: var(--brand-light); }
/* The instant an alert lands. Two beats, then it settles into the flagged
   state so the panel is not permanently strobing. */
.mo-row.is-alarm { animation: mo-alarm 450ms ease-out 2; }
@keyframes mo-alarm {
  0%   { background: color-mix(in srgb, var(--brand-dark) 26%, transparent); }
  100% { background: color-mix(in srgb, var(--accent) 12%, transparent); }
}
/* The row the pass is on right now. */
.mo-row.is-scanning { border-color: color-mix(in srgb, var(--brand-dark) 34%, transparent); }
.mo-row.is-scanning::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--brand-dark) 9%, transparent), transparent);
  animation: mo-sweep 720ms var(--ease-out) 1;
  pointer-events: none;
}
@keyframes mo-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
/* struck out: removed by the weeding */
.mo-row.is-cut { opacity: .34; }
.mo-row.is-cut .mo-text { text-decoration: line-through; }
.mo-row.is-gone { opacity: 0; transform: translateX(-12px); }
/* Taken out of flow once it has faded, so the list closes over the gap
   rather than leaving a hole where the item used to be. */
.mo-row[hidden] { display: none; }
/* owned: placed by the seeding */
.mo-row.is-own { background: var(--brand-dark); border-color: var(--brand-dark); color: var(--brand-light); }
.mo-row.is-own .mo-mark { background: var(--brand-light); border-color: var(--brand-light); color: var(--brand-dark); }
.mo-row.is-own .mo-tag { border-color: color-mix(in srgb, var(--brand-light) 40%, transparent); color: var(--brand-light); }
.mo-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mo-foot {
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--ink-muted);
  border-top: 1px solid var(--divider);
  padding-top: var(--space-4);
}
/* Phone: the fixed logo column and the tag's minimum width together left
   the item text with barely a word before it ellipsed. */
@media (max-width: 560px) {
  .mo-row { padding: 8px var(--space-3); }
  .mo-line { grid-template-columns: 18px 38px minmax(0, 1fr) auto; gap: var(--space-2); }
  .mo-row.has-caret .mo-line { grid-template-columns: 18px 38px minmax(0, 1fr) auto 10px; }
  .mo-logo { width: 38px; height: 10px; }
  .mo-tag { min-width: 0; padding: 3px 6px; }
  .mo-sub li { margin-left: calc(18px + var(--space-2)); font-size: 10px; }
  .mo-sev { width: 18px; }
  .mo-sev i { width: 3px; }
}

@media (prefers-reduced-motion: reduce) {
  .mo-dot { animation: none; opacity: 1; }
  .mo-dot::after { animation: none; }
  .mo-row { transition: none; }
  .mo-row.is-busy, .mo-row.is-alarm, .mo-row.is-scanning::after,
  .mo-row.is-busy .mo-tag::before, .mo-row.is-busy .mo-prog { animation: none; }
  .mo-sub { transition: none; }
}

.svc-rail {
  display: flex; flex-direction: column; gap: var(--space-3);
  align-self: start;
}
.svc-card {
  display: block;
  background: var(--glass-light);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 8%, transparent);
  border-radius: var(--radius-md);
  padding: var(--space-5);
}
/* Swapped: the stage card is the pale one and the list under it carries the
   sand. The stage already announces itself with the burgundy icon tile, and
   putting the heavier ground under the longer read gives the pair a base to
   sit on rather than a top-heavy header. */
.svc-card:not(.svc-detail) {
  background: color-mix(in srgb, var(--brand-light) 12%, var(--page-base));
  border-color: color-mix(in srgb, var(--brand-dark) 10%, transparent);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* One step down from the stage card, not four. At 46% the list card was
   darker than either the stage above it or the page it sits on, and it read
   as a different material; 30% keeps the pair in the same family. */
.svc-card.svc-detail {
  background: color-mix(in srgb, var(--brand-light) 30%, var(--page-base));
  border-color: color-mix(in srgb, var(--brand-dark) 12%, transparent);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* A 14% step off the card was not enough to read as a chip; at 44% they sit
   on the card the way the audience chips sit on the sector cards. */
.svc-card:not(.svc-detail) .svc-subs li {
  background: color-mix(in srgb, var(--brand-light) 44%, var(--page-base));
  border-color: color-mix(in srgb, var(--brand-dark) 14%, transparent);
}

/* The icon tile leads the card and runs the full height of the two title
   lines beside it, so the eyebrow and the name read as one block against it
   rather than as text with a badge parked in the corner. */
.svc-card:not(.svc-detail) { display: block; }
.svc-card-head {
  display: flex;
  align-items: stretch;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid color-mix(in srgb, var(--brand-dark) 10%, transparent);
}
/* Both halves of the head are pinned to the same 58px so the tile is square
   and its top and bottom edges land on the title block's. Left to size
   themselves the tile came out 58 wide by 49 tall and read as squashed. */
.svc-card-titles {
  min-width: 0;
  min-height: 58px;
  display: flex; flex-direction: column; justify-content: center;
}
/* The eyebrow sits on the name rather than floating above it: at space-2
   plus the label's own leading the pair read as two separate things
   stacked, not as one title. */
.svc-card-titles .label { margin-bottom: 0; line-height: 1.1; }
.svc-card-titles .svc-name { line-height: 1.02; margin-top: 2px; }
.svc-card-titles .svc-name { margin-bottom: 0; }
.svc-glyph {
  flex: none;
  align-self: stretch;
  /* Square, with .svc-card-head setting the same height so the tile and the
     two title lines share their top and bottom edges. Sized off the head
     rather than given its own minimum: with a minimum it stood proud of the
     text and the pair looked misaligned even though they were centred. */
  width: 58px; height: auto; min-height: 58px;
  aspect-ratio: auto;
  border-radius: var(--radius-md);
  /* The one dark note on a pale card. A page-ground tile sitting on a sand
     card was two pale squares inside each other; this gives the panel a
     focal point, with the marks drawn in sand (see MARK in initGlyph). */
  background: color-mix(in srgb, var(--brand-dark) 88%, var(--brand-light));
  border-color: transparent;
  background-image: none;
}
.svc-glyph::before { content: none; }
.svc-glyph::after { display: none; }
/* Order and chaos, inside the icon slot: the left half holds its grid, the
   right half is loose, and the disorder bleeds across the divide. Keeps the
   placeholder shape so a real icon can drop straight in. */
.svc-glyph { position: relative; overflow: hidden; }
.entropy-fx {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
}

.svc-card .label { margin-bottom: var(--space-3); }
.svc-name {
  font-size: var(--step-3); font-weight: 500; letter-spacing: -0.02em;
  color: var(--brand-dark); margin-bottom: var(--space-3);
}
.svc-name .tm { font-size: 0.55em; vertical-align: super; }
.svc-lead {
  font-size: var(--step--1); line-height: var(--lh-body);
  color: var(--ink-muted); margin-bottom: var(--space-4);
}
.svc-subs { gap: 5px; }
/* Burgundy-tinted, so the chips read as part of the service rather than
   as another neutral pill. */
.svc-subs li {
  background: color-mix(in srgb, var(--brand-dark) 7%, transparent);
  border-color: color-mix(in srgb, var(--brand-dark) 16%, transparent);
  color: var(--brand-dark);
}

.svc-items { list-style: none; padding: 0; display: grid; gap: 7px; }
.svc-items li {
  position: relative;
  padding-left: var(--space-5);
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-muted);
}
/* Base marker; each detail card overrides it with the mark that suits what
   the list is doing — found, removed, built. */
.svc-items li::before {
  content: ''; position: absolute; left: 0; top: 0.75em;
  width: 10px; height: 1px; background: var(--accent);
  transform: translateY(-50%);
}
/* What it covers — a check. */
.svc-detail-cover .svc-items li::before {
  /* 0.75em is the middle of a 1.5 line box: at 0.5em every marker sat a
     third of a line high and read as hanging off the top of its item. */
  left: 1px; top: calc(0.75em - 6px);
  width: 5px; height: 9px;
  background: none;
  border-right: 1.5px solid var(--accent);
  border-bottom: 1.5px solid var(--accent);
  transform: rotate(40deg);
}
/* What we remove — a cross. */
.svc-detail-remove .svc-items li::before,
.svc-detail-remove .svc-items li::after {
  content: ''; position: absolute; left: 0; top: calc(0.75em - 0.75px);
  width: 11px; height: 1.5px; background: var(--accent);
  border-radius: 1px;
}
.svc-detail-remove .svc-items li::before { transform: rotate(45deg); }
.svc-detail-remove .svc-items li::after  { transform: rotate(-45deg); }
/* What we build — a plus. */
.svc-detail-build .svc-items li::before,
.svc-detail-build .svc-items li::after {
  content: ''; position: absolute; background: var(--accent); border-radius: 1px;
}
.svc-detail-build .svc-items li::before {
  left: 0; top: calc(0.75em - 0.75px); width: 11px; height: 1.5px;
}
.svc-detail-build .svc-items li::after {
  left: calc(5.5px - 0.75px); top: calc(0.75em - 5.5px);
  width: 1.5px; height: 11px;
}

/* Bottom right, because the panel it overlays moved to the right column.
   .svc-motion reserves the height so the panel's own footer clears it. */
.svc-tabs {
  position: absolute;
  right: var(--space-5); bottom: var(--tabs-bottom, var(--space-5));
  display: flex; align-items: center; gap: 0;
  padding: 4px;
  border-radius: var(--radius-pill);
  /* One step off full burgundy, so the rail is the same material as the icon
     tiles rather than as heavy as the contact section. */
  background: color-mix(in srgb, var(--brand-dark) 86%, var(--brand-light));
  border: 1px solid color-mix(in srgb, var(--brand-dark) 40%, transparent);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--brand-dark) 22%, transparent);
}
.svc-tabs button {
  position: relative;
  display: inline-flex; align-items: center;
  min-height: 34px; padding: 0 var(--space-4);
  border: 0; border-radius: 10px; background: none; cursor: pointer;
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  /* Not --on-dark-muted: on the lightened rail that measured 3.9:1 at 10px. */
  color: color-mix(in srgb, var(--on-dark) 92%, transparent); white-space: nowrap; overflow: hidden;
  flex: none;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.svc-tabs button:hover { color: var(--on-dark); }
.svc-tabs button[aria-selected='true'] { background: var(--page-base); color: var(--brand-dark); }

@media (max-width: 1024px) {
  .hero-grid { grid-template-columns: 1fr; gap: var(--space-6); align-items: start; }
  .svc-pane { grid-template-columns: 1fr; }
  .svc-rail { margin-top: var(--space-3); }
  /* The 16/10 crop was left over from when this box held a still. It now
     holds a list that has to be readable, and a fixed ratio clipped the
     bottom half of it off — the panel sets its own height instead. */
  .svc-media { min-height: 0; aspect-ratio: auto; }
  .svc-motion { min-height: 0; padding: var(--space-5); }
  .svc-pane:not(.is-active) { display: none; }
  .svc-tabs { overflow-x: auto; }
}

/* ---------- Trust strip ---------- */
.trust {
  border-block: 1px solid var(--divider);
  padding-block: var(--space-5);
}
.trust ul {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3) var(--space-5);
}
.trust li { display: flex; align-items: center; gap: var(--space-5); }
.trust li::after { content: ''; width: 3px; height: 3px; border-radius: 50%; background: var(--divider); }
.trust li:last-child::after { display: none; }

/* ---------- Hero: colour panels backdrop ----------
   Same renderer as the problem section, in the light palette. The hero is
   a .wrap, so the canvas is pulled out past the gutters to bleed full width.
   ------------------------------------------------------------------ */
/* Full bleed, with the content re-wrapped inside. The hero used to be a
   .wrap itself, so its overflow:hidden clipped the backdrop to the wrap's
   max-width and left bare page down both sides on a wide monitor. The
   section now runs edge to edge and every child except the canvas becomes
   its own wrap, which puts the backdrop and the scrim across the full
   viewport while the type stays where it was. */
.hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  max-width: none;
  padding-inline: 0;
}
.panels-fx {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 1;
}
.hero > .panels-fx {
  /* No 100vw break-out needed any more: the hero is the full width, so the
     canvas simply fills it. 100vw also counted the scrollbar, which is what
     the clip was there to swallow. */
  inset: 0;
  transform: none;
  width: 100%;
  opacity: 1;
}
/* Scrim keeps every text pair on a predictable ground. */
.hero::after {
  content: '';
  position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(105deg,
      color-mix(in srgb, var(--page-base) 50%, transparent) 0%,
      color-mix(in srgb, var(--page-base) 32%, transparent) 46%,
      color-mix(in srgb, var(--page-base) 46%, transparent) 100%);
}
.hero > *:not(.panels-fx) {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--wrap-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ---------- The Problem ---------- */
/* The ground lightens across the section, driven from scroll position: the
   diagnosis sits in the deepest burgundy and the escalation climbs out of it. */
.problem {
  position: relative;
  isolation: isolate;
  background: color-mix(in srgb, #5A2F37 calc(var(--shift, 0) * 100%), var(--brand-dark));
}
.problem { transition: none; }
.problem > .wrap { position: relative; z-index: 2; }

/* Noise resolving into order — the figure the two lines above describe.
   Chaotic overlapping traces on the left settle into even parallel rules
   on the right. Decorative; the caption carries the meaning for AT. */
.noise-fig {
  margin: var(--space-7) 0 0;
  padding: 0;
}
.noise-fx {
  display: block;
  width: 100%;
  height: clamp(140px, 8rem + 6vw, 240px);
}

/* Asymmetric split: the statement holds the left, the three movements
   stack down the right, and the bands stay visible between them. */
.problem-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-8);
  align-items: start;
}
.problem-lead { position: sticky; top: calc(var(--space-8) + 46px); }
.problem-lead .label { margin-bottom: var(--space-6); }

/* Above the stack breakpoint the figure fills the rest of the left column, so
   it runs down to the bottom of the last movement on the right. The column no
   longer needs to stick, because it is now as tall as the one beside it.
   This has to sit AFTER the base rules above: it was declared before them, so
   `align-items: start` and `position: sticky` won on source order and the
   figure stayed a 233px band instead of filling the column. */
@media (min-width: 1025px) {
  .problem-grid { align-items: stretch; }
  .problem-lead { position: static; display: flex; flex-direction: column; }
  .noise-fig { flex: 1 1 auto; display: flex; min-height: 340px; }
  .noise-fx { height: 100%; }
}

/* ---------- Problem: three beats ----------
   No image here on purpose. The section's job is to be read, so it is
   built as three numbered movements with a brightness gradient that
   leads to the answer in beat three.
   ------------------------------------------------------------------ */
.problem-title { margin-bottom: 0; }
.problem-title { gap: 0; }
.problem-title .line-solid,
.problem-title .line-faded { display: block; }
.problem-title .line-solid { color: var(--brand-light); }
/* 60%: at 32% this measured 1.92:1 on the burgundy panel, under the 3:1
   large-text floor. */
.problem-title .line-faded { color: color-mix(in srgb, var(--brand-light) 60%, transparent); }

.beats {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
.beat {
  border-top: 1px solid var(--divider-on-dark);
  padding-top: var(--space-5);
}
/* A rail that fills a third further with each movement — the sequence
   without numerals, and without leaning on a second accent colour. */
.beat-i {
  display: block;
  width: 76px; height: 2px;
  background: color-mix(in srgb, var(--brand-light) 22%, transparent);
  margin-bottom: var(--space-4);
  overflow: hidden;
}
.beat-i > i {
  display: block;
  position: relative;
  height: 100%;
  width: calc(var(--fill) * 100%);
  overflow: hidden;
  background: color-mix(in srgb, var(--brand-light) 46%, transparent);
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform var(--dur-slow) var(--ease-out);
}
/* A bright block runs along the filled part, end to end, without stopping —
   an old-school indeterminate progress bar. */
.beat-i > i::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  width: 42%;
  background: var(--brand-light);
  animation: beat-load 1.4s linear infinite;
}
@keyframes beat-load {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(260%); }
}
@media (prefers-reduced-motion: reduce) {
  .beat-i > i::after { animation: none; width: 100%; }
}
.beat.is-in .beat-i > i { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  .beat-i > i { transform: scaleX(1); transition: none; }
}
.beat-h {
  font-size: var(--step-1);
  font-weight: 500;
  letter-spacing: -0.015em;
  line-height: 1.2;
  color: var(--brand-light);
  margin-bottom: var(--space-3);
}
.beat-t { line-height: var(--lh-body); }
/* One size, one weight, one colour across all three: the escalation was
   doing the work the rail now does, and it made the section tower. */
.beat-t { font-size: var(--step-0); color: var(--on-dark-muted); }

@media (max-width: 1024px) {
  .problem-grid { grid-template-columns: 1fr; gap: var(--space-7); }
  .problem-lead { position: static; }
}
@media (prefers-reduced-motion: reduce) {
  /* One static frame is still drawn; it simply never advances. */
  .panels-fx { opacity: 0.7; }
}

/* ---------- The bigger problem ----------
   The second half of the Problem section: the same burgundy ground, a rule
   above it, and four numbered escalations rather than a grid of cards.
   ------------------------------------------------------------------ */
/* No rule between the halves: the ground itself lightens as you scroll from
   the diagnosis into the escalation. --shift is driven from JS. */
/* A run of dots between the diagnosis and the escalation, cycling without
   ever completing: the loading state that never resolves. Reads as a bridge
   rather than a rule, which is what a separator was doing wrong. */
#problem > .problem-grid { padding-bottom: var(--space-8); }
.problem-bridge {
  position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-4);
  padding: var(--space-7) 0;
}
.problem-bridge span {
  width: 6px; height: 6px; border-radius: 50%;
  background: color-mix(in srgb, var(--brand-light) 26%, transparent);
  animation: bridge-load 1.9s ease-in-out infinite;
}
.problem-bridge span:nth-child(1) { animation-delay: 0.0s; }
.problem-bridge span:nth-child(2) { animation-delay: 0.1s; }
.problem-bridge span:nth-child(3) { animation-delay: 0.2s; }
.problem-bridge span:nth-child(4) { animation-delay: 0.3s; }
.problem-bridge span:nth-child(5) { animation-delay: 0.4s; }
.problem-bridge span:nth-child(6) { animation-delay: 0.5s; }
.problem-bridge span:nth-child(7) { animation-delay: 0.6s; }
.problem-bridge span:nth-child(8) { animation-delay: 0.7s; }
.problem-bridge span:nth-child(9) { animation-delay: 0.8s; }
@keyframes bridge-load {
  0%, 100% { background: color-mix(in srgb, var(--brand-light) 22%, transparent); transform: scale(1); }
  35%      { background: var(--brand-light); transform: scale(1.5); }
}
@media (prefers-reduced-motion: reduce) {
  .problem-bridge span { animation: none; }
}

.bigger { padding-top: var(--space-8); }
.bigger-grid {
  display: grid;
  /* Same two-track split and the same gap as .bigger-list below, so the
     sphere's column and the list's right column share a centre line. At
     1fr/0.52fr the sphere sat over the gutter between them. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-8);
  align-items: center;
  /* The list below opens a new movement, so it gets the same 1.15 section
     units that separate the two movements inside What We Do. */
  margin-bottom: calc(var(--section-y) * 1.15);
}
/* Centred in its column. Pinned right it sat hard against the section
   gutter on wide screens while the text column kept its own margin, so the
   pair read as two things drifting apart rather than one spread. */
.noise-orb { margin: 0 auto; padding: 0; max-width: 380px; width: 100%; }
.orb-fx { display: block; width: 100%; aspect-ratio: 1; }
@media (max-width: 1024px) {
  .bigger-grid { grid-template-columns: 1fr; gap: var(--space-6); }
  .noise-orb { max-width: 420px; }
}
.bigger-head { max-width: 62ch; }
.bigger-head .label { margin-bottom: var(--space-4); }
.bigger-head .h2 { margin-bottom: var(--space-5); }
.bigger-lede {
  font-size: var(--step-1);
  line-height: var(--lh-loose);
  color: var(--on-dark-muted);
}

.bigger-list {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-8) var(--space-8);
}
.bigger-item {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  gap: var(--space-4);
  padding-top: var(--space-5);
  border-top: 1px solid var(--divider-on-dark);
}
/* Same running loader as the beats above, so the two halves of the section
   read as one continuous escalation. */
/* A quadrant clock per bullet: the first fills one quarter, the second two,
   and so on, so the four read as a meter running toward full. The lit
   quarters flash together; the unlit ones stay as dim ground. */
.bigger-i {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 2px;
  width: 22px; height: 22px;
  /* Centred on the heading's own line box rather than nudged by eye: at
     0.30em it hung below the title it belongs to. */
  margin-top: calc((var(--step-1) * 1.25 - 22px) / 2);
  background: none;
  overflow: visible;
}
.bigger-i > i {
  display: block;
  background: color-mix(in srgb, var(--brand-light) 15%, transparent);
}
/* Grid order is TL, TR, BL, BR; the clock fills TR, BR, BL, TL. */
.bigger-i > i:nth-child(1) { border-radius: 100% 0 0 0; }
.bigger-i > i:nth-child(2) { border-radius: 0 100% 0 0; }
.bigger-i > i:nth-child(3) { border-radius: 0 0 0 100%; }
.bigger-i > i:nth-child(4) { border-radius: 0 0 100% 0; }

.bigger-item.is-in .bigger-clock[data-lit='1'] > i:nth-child(2),
.bigger-item.is-in .bigger-clock[data-lit='2'] > i:nth-child(2),
.bigger-item.is-in .bigger-clock[data-lit='2'] > i:nth-child(4),
.bigger-item.is-in .bigger-clock[data-lit='3'] > i:nth-child(2),
.bigger-item.is-in .bigger-clock[data-lit='3'] > i:nth-child(4),
.bigger-item.is-in .bigger-clock[data-lit='3'] > i:nth-child(3),
.bigger-item.is-in .bigger-clock[data-lit='4'] > i {
  animation: quad-flash 1.9s ease-in-out infinite;
}
@keyframes quad-flash {
  0%, 100% { background: color-mix(in srgb, var(--brand-light) 30%, transparent); }
  45%      { background: var(--brand-light); }
}
@media (prefers-reduced-motion: reduce) {
  .bigger-i > i { animation: none !important; }
  .bigger-item.is-in .bigger-clock[data-lit='1'] > i:nth-child(2),
  .bigger-item.is-in .bigger-clock[data-lit='2'] > i:nth-child(2),
  .bigger-item.is-in .bigger-clock[data-lit='2'] > i:nth-child(4),
  .bigger-item.is-in .bigger-clock[data-lit='3'] > i:nth-child(2),
  .bigger-item.is-in .bigger-clock[data-lit='3'] > i:nth-child(4),
  .bigger-item.is-in .bigger-clock[data-lit='3'] > i:nth-child(3),
  .bigger-item.is-in .bigger-clock[data-lit='4'] > i { background: var(--brand-light); }
}
.bigger-item h3 {
  font-size: var(--step-1);
  font-weight: 500;
  letter-spacing: -0.015em;
  line-height: 1.25;
  color: var(--brand-light);
  margin-bottom: var(--space-3);
}
.bigger-item p {
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--on-dark-muted);
}
@media (max-width: 860px) {
  .bigger-list { grid-template-columns: 1fr; gap: var(--space-5); }
}

/* ---------- How we work: two ways in ----------
   The content is a single binary — whose name goes on the work — so the two
   panels are set as opposites rather than as two equal cards. White-label
   recedes into the page ground; direct comes forward in full burgundy.
   ------------------------------------------------------------------ */
.ways-head { max-width: 62ch; margin-bottom: var(--space-8); }
.ways-head .label { margin-bottom: var(--space-5); }
.ways-head .h2 { margin-bottom: var(--space-4); }
.ways-lede { color: var(--ink-muted); }

.ways {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
  align-items: stretch;
}
.way {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  border-radius: var(--radius-media);
  transition: transform var(--dur-base) var(--ease-out);
}

/* Stamp: the monogram lands on the corner of the card that carries our name,
   the way a maker's mark goes on the finished thing. Struck once on scroll. */
.way-stamp {
  position: absolute;
  top: var(--space-5); right: var(--space-5);
  width: 62px; height: 62px;
  border-radius: var(--radius-round);
  /* Double ring: an outer edge and an inner keyline, the way a struck stamp
     or an applied sticker actually reads. */
  border: 1px solid color-mix(in srgb, var(--brand-light) 44%, transparent);
  box-shadow: inset 0 0 0 4px transparent,
              inset 0 0 0 5px color-mix(in srgb, var(--brand-light) 22%, transparent);
  -webkit-mask: none; mask: none;
  opacity: 0;
  transform: rotate(-24deg) scale(2.1);
  transition: opacity 620ms var(--ease-out), transform 720ms cubic-bezier(.2,1.32,.36,1);
}
.way-stamp::after {
  content: '';
  position: absolute; inset: 15px;
  background-color: var(--brand-light);
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
  opacity: 0.85;
}
.way-stamp { background-color: transparent; }
.way.is-in .way-stamp {
  opacity: 1;
  transform: rotate(-11deg) scale(1);
  transition-delay: 420ms;
}
@media (prefers-reduced-motion: reduce) {
  .way-stamp { opacity: 1; transform: rotate(-11deg); transition: none; }
}
/* Its counterpart on the white-label card: the same die, never struck. A
   dashed ring with the monogram barely there says "no maker's mark goes on
   this one", which is the whole proposition. */
.way-stamp-blank {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--brand-dark) 38%, transparent);
  box-shadow: none;
  transform: rotate(-11deg) scale(0.86);
}
.way-stamp-blank::after {
  background-color: var(--brand-dark);
  opacity: 0.16;
}
/* Two states of the same mark. The direct-engagements one is struck down and
   stays down. The white-label one lifts a single corner as you scroll: this
   mark is meant to come off so a client's own can go on in its place.
   ------------------------------------------------------------------ */
.way-stamp-blank { clip-path: polygon(0 0, 100% 0, 100% 55%, 55% 100%, 0 100%); }

.way-peel {
  position: absolute;
  top: var(--space-5); right: var(--space-5);
  width: 62px; height: 62px;
  transform: rotate(-11deg);
  pointer-events: none;
  perspective: 220px;
}
/* One flap, hinged on the diagonal, flat at rest and folded back on scroll. */
.way-peel::before {
  content: '';
  position: absolute;
  right: 0; bottom: 0;
  width: 30px; height: 30px;
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
  background: linear-gradient(315deg,
    color-mix(in srgb, var(--brand-dark) 30%, var(--chrome-surface)) 0%,
    color-mix(in srgb, var(--brand-dark) 8%, var(--chrome-surface)) 46%,
    var(--page-base) 100%);
  border-radius: 0 0 4px 0;
  transform-origin: 0% 0%;
  transform: rotate3d(1, -1, 0, 0deg);
  box-shadow: 0 0 0 rgba(0, 0, 0, 0);
  transition: transform 1100ms var(--ease-out) 520ms,
              box-shadow 1100ms var(--ease-out) 520ms;
}
.way.is-in .way-peel::before {
  transform: rotate3d(1, -1, 0, 168deg);
  box-shadow: -4px -4px 9px color-mix(in srgb, var(--brand-dark) 26%, transparent);
}
/* The shadow the lifted flap throws back onto the card. */
.way-peel::after {
  content: '';
  position: absolute;
  right: 1px; bottom: 1px;
  width: 30px; height: 30px;
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
  background: linear-gradient(315deg,
    color-mix(in srgb, var(--brand-dark) 18%, transparent) 0%,
    transparent 72%);
  opacity: 0;
  transition: opacity 1100ms var(--ease-out) 520ms;
}
.way.is-in .way-peel::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .way-peel::before { transform: rotate3d(1, -1, 0, 168deg); transition: none; }
  .way-peel::after { opacity: 1; transition: none; }
}
@media (max-width: 480px) {
  .way-peel { width: 48px; height: 48px; }
  .way-peel::before, .way-peel::after { width: 23px; height: 23px; }
}
.way.is-in .way-stamp-blank {
  opacity: 1;
  transform: rotate(-11deg) scale(1);
  transition: opacity 900ms var(--ease-out) 520ms, transform 900ms var(--ease-out) 520ms;
}
@media (prefers-reduced-motion: reduce) {
  .way-stamp-blank { opacity: 1; transform: rotate(-11deg); transition: none; }
}
@media (max-width: 480px) {
  .way-stamp { width: 48px; height: 48px; }
  .way-stamp::after { inset: 11px; }
}
.way-hidden {
  background: var(--chrome-surface);
  color: var(--ink);
  /* Dashed: the arrangement is deliberately not visible from outside. */
  border: 1px dashed color-mix(in srgb, var(--brand-dark) 28%, transparent);
}
.way-named {
  background: var(--brand-dark);
  color: var(--on-dark);
  border: 1px solid var(--brand-dark);
}
/* The engagement's name, set as a mono label — it does the work the "01"
   used to do without pretending the two are a sequence. */
/* Label, then a rule that runs across to the stamp, so the two read as one
   line of masthead rather than two unrelated marks. */
.way-index {
  display: flex; align-items: center; gap: var(--space-4);
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin-bottom: var(--space-6);
  padding-right: 76px;
}
.way-index::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
}
.way-hidden .way-index::after { background: color-mix(in srgb, var(--brand-dark) 22%, transparent); }
.way-named .way-index::after { background: var(--divider-on-dark); }
.way-hidden .way-index { color: var(--ink-muted); }
.way-named .way-index { color: var(--on-dark-muted); }

/* The one line that carries the distinction, set at heading scale. */
.way-stance {
  font-family: var(--font-display);
  font-size: var(--step-4);
  line-height: 1.16;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-5);
}
.way-hidden .way-stance { color: color-mix(in srgb, var(--brand-dark) 65%, transparent); }
.way-named .way-stance { color: var(--brand-light); }

.way-body {
  font-size: var(--step-0);
  line-height: var(--lh-body);
  margin-bottom: var(--space-6);
}
.way-hidden .way-body { color: var(--ink-muted); }
.way-named .way-body { color: var(--on-dark-muted); }

/* Spec rows pin the two panels to the same three questions, so the eye can
   read across and see exactly what changes between them. */
.way-spec { margin-top: auto; }
.way-spec > div {
  display: grid;
  grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
  gap: var(--space-4);
  padding: var(--space-3) 0;
}
.way-hidden .way-spec > div { border-top: 1px solid var(--divider); }
.way-named .way-spec > div { border-top: 1px solid var(--divider-on-dark); }
.way-spec dt {
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  line-height: 1.7;
}
.way-hidden .way-spec dt { color: var(--ink-muted); }
.way-named .way-spec dt { color: var(--on-dark-muted); }
.way-spec dd { font-size: var(--step--1); line-height: 1.7; }
.way-hidden .way-spec dd { color: var(--brand-dark); }
.way-named .way-spec dd { color: var(--brand-light); }

@media (hover: hover) and (pointer: fine) {
  .way:hover { transform: translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) {
  .way, .way:hover { transform: none; transition: none; }
}
@media (max-width: 860px) {
  .ways { grid-template-columns: 1fr; }
}
@media (max-width: 480px) {
  .way-spec > div { grid-template-columns: 1fr; gap: var(--space-1); }
}

/* ---------- Proof ledger ----------
   Figures sized by significance rather than set on a uniform grid, so
   the block reads as a composition instead of a scoreboard. The rule
   between figure and description draws itself on reveal.
   ------------------------------------------------------------------ */
.proof-head { margin-bottom: var(--space-8); }
.proof-line-1 { color: var(--brand-light); }
.proof-line-2 { color: color-mix(in srgb, var(--brand-light) 62%, transparent); }
.proof-head .label { margin-bottom: var(--space-4); }

.ledger { list-style: none; margin: 0; padding: 0; }
.ledger-row {
  display: grid;
  /* The first column names the sector the figure came from, so a reader can
     tell whether the number is one that applies to them. */
  /* Label column pulled in: at 13rem the sector name sat a long way off its
     own figure and the row read as two unrelated halves. */
  grid-template-columns: minmax(0, 14.5rem) minmax(0, auto) minmax(2rem, 1fr) minmax(0, 30ch);
  align-items: baseline;
  gap: var(--space-5);
  padding-block: var(--space-5);
  border-top: 1px solid var(--divider-on-dark);
}
.ledger-row:last-child { border-bottom: 1px solid var(--divider-on-dark); }
/* The sector reads as a stamp on the row rather than a caption beside it:
   each one lands with a slight tilt, a beat after the row before it, so the
   five arrive as a run of impressions rather than a block appearing. */
.ledger-index {
  justify-self: start;
  align-self: center;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 12px 6px 10px;
  border-radius: var(--radius-round);
  background: color-mix(in srgb, var(--brand-light) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--brand-light) 26%, transparent);
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  line-height: 1.4;
  color: var(--on-dark);
  white-space: nowrap;
  transform: rotate(var(--tilt, 0deg));
}
.ledger-ico { flex: none; width: 15px; height: 15px; opacity: .82; }
.ledger-row:nth-child(1) .ledger-index { --tilt: -2.1deg; }
.ledger-row:nth-child(2) .ledger-index { --tilt:  1.6deg; }
.ledger-row:nth-child(3) .ledger-index { --tilt: -1.2deg; }
.ledger-row:nth-child(4) .ledger-index { --tilt:  2.3deg; }
.ledger-row:nth-child(5) .ledger-index { --tilt: -1.7deg; }
@media (prefers-reduced-motion: no-preference) {
  .ledger-index { opacity: 0; }
  .ledger-row.is-in .ledger-index { animation: ledger-stamp 420ms var(--ease-out) both; }
  .ledger-row:nth-child(1).is-in .ledger-index { animation-delay: 0ms; }
  .ledger-row:nth-child(2).is-in .ledger-index { animation-delay: 150ms; }
  .ledger-row:nth-child(3).is-in .ledger-index { animation-delay: 300ms; }
  .ledger-row:nth-child(4).is-in .ledger-index { animation-delay: 450ms; }
  .ledger-row:nth-child(5).is-in .ledger-index { animation-delay: 600ms; }
}
@keyframes ledger-stamp {
  0%   { opacity: 0; transform: rotate(var(--tilt, 0deg)) scale(1.55); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: rotate(var(--tilt, 0deg)) scale(1); }
}
/* A bloom behind the chip at the moment of impact, gone by the time it
   settles. A permanent glow on five chips would be five more things
   competing with the figures. */
.ledger-index { position: relative; z-index: 0; }
.ledger-index::before {
  content: '';
  position: absolute; inset: -14px -20px;
  z-index: -1;
  border-radius: var(--radius-round);
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--brand-light) 34%, transparent), transparent 100%);
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .ledger-row.is-in .ledger-index::before { animation: ledger-bloom 900ms var(--ease-out) both; }
  .ledger-row:nth-child(1).is-in .ledger-index::before { animation-delay: 0ms; }
  .ledger-row:nth-child(2).is-in .ledger-index::before { animation-delay: 150ms; }
  .ledger-row:nth-child(3).is-in .ledger-index::before { animation-delay: 300ms; }
  .ledger-row:nth-child(4).is-in .ledger-index::before { animation-delay: 450ms; }
  .ledger-row:nth-child(5).is-in .ledger-index::before { animation-delay: 600ms; }
}
@keyframes ledger-bloom {
  0%   { opacity: 0; transform: scale(0.7); }
  22%  { opacity: 1; transform: scale(1.06); }
  100% { opacity: 0; transform: scale(1.3); }
}
.ledger-figure {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 0.9;
  letter-spacing: var(--track-tight);
  font-variant-numeric: tabular-nums;
  /* Brought forward from 62%. Held back that far the figures read as a faint
     watermark, and this section is the page's evidence — it has to land as
     hard as the two dark sections around it. */
  color: color-mix(in srgb, var(--brand-light) 88%, transparent);
  white-space: nowrap;
  transition: color 600ms var(--ease-out) 100ms;
}
/* A highlight sweeps across each figure, staggered down the rows so it
   cascades. Gated behind js-fill so that with no JS, or where
   background-clip:text is unsupported, the figures are simply solid sand. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .ledger.js-fill .ledger-figure {
    background-image: linear-gradient(
      100deg,
      color-mix(in srgb, var(--brand-light) 62%, transparent) 0%,
      color-mix(in srgb, var(--brand-light) 62%, transparent) 30%,
      color-mix(in srgb, var(--brand-light) 80%, transparent) 42%,
      #F6EDE3 50%,
      color-mix(in srgb, var(--brand-light) 80%, transparent) 58%,
      color-mix(in srgb, var(--brand-light) 62%, transparent) 70%,
      color-mix(in srgb, var(--brand-light) 62%, transparent) 100%);
    background-size: 300% 100%;
    background-position: 130% 0;
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    animation: figure-shimmer 2.6s ease-in-out infinite;
  }
  .ledger.js-fill .ledger-row:nth-child(1) .ledger-figure { animation-delay: 0s; }
  .ledger.js-fill .ledger-row:nth-child(2) .ledger-figure { animation-delay: 0.12s; }
  .ledger.js-fill .ledger-row:nth-child(3) .ledger-figure { animation-delay: 0.24s; }
  .ledger.js-fill .ledger-row:nth-child(4) .ledger-figure { animation-delay: 0.36s; }
  .ledger.js-fill .ledger-row:nth-child(5) .ledger-figure { animation-delay: 0.48s; }
}
@keyframes figure-shimmer {
  0%   { background-position: 130% 0; }
  46%  { background-position: -30% 0; }
  100% { background-position: -30% 0; }
}
.rank-1 { font-size: clamp(2.75rem, 1.7rem + 4.4vw, 5.75rem); }
.rank-2 { font-size: clamp(2.15rem, 1.45rem + 2.9vw, 3.9rem); }
.rank-3 { font-size: clamp(1.625rem, 1.25rem + 1.6vw, 2.5rem); }

.ledger-rule {
  height: 1px;
  background: var(--divider-on-dark);
  align-self: center;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 700ms var(--ease-out) 120ms;
}
.ledger-row.is-in .ledger-rule { transform: scaleX(1); }

/* Centred, not baseline-aligned: with the row set to baseline the two-line
   description hung its second line below everything else and read as a row
   that had slipped. */
.ledger-desc {
  align-self: center;
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--on-dark-muted);
}


@media (prefers-reduced-motion: reduce) {
  .ledger-rule { transform: scaleX(1); transition: none; }
  .ledger.js-fill .ledger-figure {
    animation: none; background-image: none;
    color: var(--brand-light); -webkit-text-stroke: 0; transition: none;
  }
}

@media (max-width: 900px) {
  .ledger-row {
    grid-template-columns: 1fr;
    gap: var(--space-2);
    align-items: start;
  }
  .ledger-rule { display: none; }
  .ledger-index { grid-column: 1 / -1; margin-bottom: var(--space-2); }
  .ledger-figure { grid-column: 1 / -1; }
  .ledger-desc { grid-column: 1 / -1; margin-top: var(--space-3); }
}

/* ---------- Press reach ----------
   The mastheads are dealt onto the page rather than run past it: each card
   drops in with its own tilt and delay as the section arrives, so the block
   reads as a hand of cards thrown down.
   ------------------------------------------------------------------ */
/* Pulled up under the Problem panel's foot so its curve lands on burgundy
   rather than leaving a hairline of page ground between two dark sections. */
.press {
  background: var(--surface-alt);
  margin-top: calc(-1 * var(--radius-section));
  padding-top: var(--radius-section);
  z-index: 2;
}
#problem { padding-bottom: calc(var(--section-y) * 0.28 + var(--radius-section)); }
/* Tighter into the band, a little more air out of it. */
#press > .section { padding-top: calc(var(--section-y) * 0.55); padding-bottom: calc(var(--section-y) * 1.9); }
/* More room between the claim and the hand of cards below it. */
.press-head { margin-bottom: var(--space-8); max-width: 68ch; }
.press-head .label { margin-bottom: var(--space-4); }

.outlets {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  /* A touch more between the rows than across them, so the two ranks read
     as two rows rather than one dense block. */
  column-gap: var(--space-4);
  row-gap: var(--space-6);
}
/* All twelve begin as one messy pile in the middle of the block, each card
   at its own angle. When the block arrives the pile fades up, then the hand
   is thrown down: every card travels out to its grid slot on its own beat
   and lands at its own tilt. --dx/--dy carry the distance back to the pile,
   --pile the stacked angle, --tilt the landed one. */
.outlet {
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--space-4);
  min-height: 84px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--brand-light) 8%, transparent);
  border: 1px solid var(--divider-on-dark);
  opacity: 0;
  transform: translate(var(--dx, 0px), var(--dy, 0px)) rotate(var(--pile, 0deg)) scale(0.97);
  transition: opacity 260ms var(--ease-out),
              transform 620ms cubic-bezier(.22,1.02,.32,1) var(--d, 0ms),
              background var(--dur-base) var(--ease-out);
  will-change: transform;
}
.outlets.is-piled .outlet { opacity: 1; }
.outlets.is-dealt .outlet {
  transform: translate(0, 0) rotate(var(--tilt, 0deg)) scale(1);
}
.outlets.is-dealt .outlet:hover {
  transform: translate(0, -4px) rotate(0deg) scale(1);
  background: color-mix(in srgb, var(--brand-light) 14%, transparent);
  transition-delay: 0ms;
  z-index: 20;
}
/* Two classes so it outranks the .ph defaults it is sharing markup with. */
.outlet .outlet-logo {
  display: flex; align-items: center; justify-content: center;
  height: 48px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--divider-on-dark);
  background-color: color-mix(in srgb, var(--brand-light) 6%, transparent);
  padding: 0 var(--space-3);
}
/* Still hatched where a logo is not in hand yet. */
.outlet .outlet-logo.ph {
  padding: 0;
  background-image: repeating-linear-gradient(
    45deg, transparent 0 9px,
    color-mix(in srgb, var(--brand-light) 8%, transparent) 9px 10px);
}
/* Masked rather than drawn, so every title lands as one sand silhouette on
   the burgundy instead of twelve different source palettes fighting it. The
   marks that are a solid block with knocked-out letters (BBC, TechCrunch,
   The National) keep their counters, because the knockouts are holes in the
   source alpha. */
.outlet-mark {
  display: block;
  width: 100%;
  height: 26px;
  background-color: var(--brand-light);
  -webkit-mask: var(--logo) center / contain no-repeat;
          mask: var(--logo) center / contain no-repeat;
}

/* Paths live here, not on the element: a relative URL inside a custom
   property resolves against the stylesheet that consumes it, so an
   inline --logo resolved against assets/css/ and loaded nothing. */
.outlet-mark[data-logo='arabian-business'] { --logo: url('../img/press/arabian-business.png'); }
.outlet-mark[data-logo='forbes'] { --logo: url('../img/press/forbes.png'); }
.outlet-mark[data-logo='bbc'] { --logo: url('../img/press/bbc.png'); }
.outlet-mark[data-logo='entrepreneur'] { --logo: url('../img/press/entrepreneur.png'); }
.outlet-mark[data-logo='bloomberg'] { --logo: url('../img/press/bloomberg.png'); }
.outlet-mark[data-logo='financial-times'] { --logo: url('../img/press/financial-times.png'); }
.outlet-mark[data-logo='gulf-news'] { --logo: url('../img/press/gulf-news.png'); }
.outlet-mark[data-logo='techcrunch'] { --logo: url('../img/press/techcrunch.png'); }
.outlet-mark[data-logo='the-national'] { --logo: url('../img/press/the-national.png'); }
.outlet-mark[data-logo='khaleej-times'] { --logo: url('../img/press/khaleej-times.png'); }
.outlet-mark[data-logo='business-insider'] { --logo: url('../img/press/business-insider.png'); }
.outlet-mark[data-logo='fast-company'] { --logo: url('../img/press/fast-company.png'); }
.outlet .outlet-logo::before { content: none; }
.outlet .outlet-logo::after { display: none; }
/* Full strength: at --on-dark-muted the stand-in label measured 3.94:1 on
   the card's translucent ground. */
.outlet .ph-caption { color: var(--brand-light); }
.press-note {
  margin-top: var(--space-8);
  max-width: 74ch;
  font-size: 11px;
  line-height: 1.6;
  color: var(--on-dark-muted);
}
@media (max-width: 1024px) { .outlets { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 720px)  { .outlets { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 480px)  { .outlets { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (prefers-reduced-motion: reduce) {
  .outlets .outlet,
  .outlets.is-dealt .outlet { opacity: 1; transform: rotate(var(--tilt, 0deg)); transition: none; }
}

/* ---------- Tag strip ---------- */
.tags { display: flex; flex-wrap: wrap; gap: var(--space-2); list-style: none; padding: 0; }
.tags li {
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--divider);
  border-radius: 999px;
  color: var(--ink-muted);
  white-space: nowrap;
}

/* ---------- Table (sector matrix) ---------- */
.matrix { width: 100%; border-collapse: collapse; text-align: left; }
.matrix caption { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.matrix th, .matrix td {
  padding: var(--space-5) var(--space-5) var(--space-5) 0;
  border-bottom: 1px solid var(--divider);
  vertical-align: top;
  font-size: var(--step--1);
  line-height: var(--lh-body);
}
.matrix thead th {
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-muted);
  padding-block: var(--space-3);
}
.matrix tbody th {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--brand-dark);
  width: 22%;
}
.table-scroll { overflow-x: auto; }

/* ---------- What we do: the two stances ----------
   Defend and Build are opposed postures, so they are set as a facing pair
   across a single hairline rather than as two stacked editorial rows. The
   moves are the same content the paragraphs carried, cut into scannable
   lines so the capability cards below read as the mechanism beneath them.
   ------------------------------------------------------------------ */
/* No rule under the section heading: the two stances already have their own
   ruled lists, and a third horizontal line above them made the block read as
   a table. The heading's own margin does the separating. */
.stances {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-8);
}
.stance { display: flex; flex-direction: column; }
.stance-name {
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-muted);
  margin-bottom: var(--space-5);
}
.stance-stance {
  font-family: var(--font-display);
  /* Its own scale, not --step-4: sized so the longest state of either line
     stays on one line everywhere the two columns sit side by side. */
  font-size: clamp(1.5rem, 0.15rem + 2.1vw, 2.125rem);
  font-weight: 500;
  line-height: 1.14;
  letter-spacing: -0.02em;
  color: var(--brand-dark);
  margin-bottom: var(--space-5);
}
/* Word roll: the operative noun cycles through the things it stands for, so
   the headline keeps saying something new. The slot animates its own width
   between words, which is why it reads as motion rather than reflow. */
/* A rule under the slot: the word changes above a fixed line, so the swap
   reads as something being filled in rather than the text glitching. */
.roll { box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--brand-dark) 26%, transparent); }
.roll {
  display: inline-block;
  position: relative;
  vertical-align: bottom;
  overflow: hidden;
  height: 1.14em;
  transition: width var(--dur-slow) var(--ease-out);
}
/* Absolute, not a grid child: as a stretched grid item the stack collapsed
   to the slot's one-line height and every word after the first fell outside
   the window. */
.roll-stack {
  position: absolute;
  top: 0; left: 0;
  transition: transform var(--dur-slow) var(--ease-out);
}
.roll-stack > span {
  display: block;
  /* max-content, not the stack's shrink-to-fit width: without it every word
     measured as wide as the longest one and the slot kept a trailing gap. */
  width: max-content;
  height: 1.14em;
  line-height: 1.14;
  white-space: nowrap;
  text-align: left;
}
/* The incoming word arrives as a transparent burgundy and fills to full over
   the next half second. The floor is 58%, not lower: even mid-fill the word
   holds 3.4:1, above the 3:1 large-text threshold. */
.roll-stack > span {
  color: color-mix(in srgb, var(--brand-dark) 58%, transparent);
  transition: color 1100ms var(--ease-out) 520ms;
}
.roll-stack > span[data-live='true'] { color: var(--brand-dark); }
@media (prefers-reduced-motion: reduce) {
  .roll-stack > span { transition: none; color: var(--brand-dark); }
}
@media (prefers-reduced-motion: reduce) {
  .roll, .roll-stack { transition: none; }
}
.stance-moves { list-style: none; margin: 0; padding: 0; }
.stance-moves li {
  position: relative;
  padding: var(--space-3) 0 var(--space-3) var(--space-6);
  border-top: 1px solid var(--divider);
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--ink);
}
/* Marker sits on the text's first-line optical centre, not the row's. */
.stance-moves li::before,
.stance-build .stance-moves li::after {
  content: '';
  position: absolute;
  left: 2px; top: calc(var(--space-3) + 0.62em);
  width: 14px; height: 1px;
  background: var(--brand-dark);
}
/* Build gets the upright stroke too, so its markers read as plus signs
   against Defend's minus signs. */
.stance-build .stance-moves li::after {
  left: 8.5px; top: calc(var(--space-3) + 0.62em - 6.5px);
  width: 1px; height: 14px;
}
/* 1024, not 860: below this a half-width column cannot hold the longest
   state of the rolling headline at a size that still reads as a heading. */
@media (max-width: 1024px) {
  .stances { grid-template-columns: 1fr; gap: var(--space-7); }
}

/* ---------- Capabilities (folded into What We Do) ----------
   A 2x2 of tinted cards with an oversized outline numeral, so the block
   reads as a different kind of object from the editorial rows above it
   rather than more of the same.
   ------------------------------------------------------------------ */
/* 'How we build it' opens a second movement inside the section. */
.caps-label { margin: calc(var(--section-y) * 1.15) 0 var(--space-5); }
/* What each capability actually covers, as chips under the paragraph — the
   same device the method rail uses, so the two sections rhyme. */
.cap-tags { margin-top: var(--space-4); gap: 4px; }
.cap-tags li { padding: 4px 9px; font-size: 9px; letter-spacing: 0.08em; }
/* Kept next to the rule it overrides rather than with the other phone rules:
   at equal specificity the later declaration wins, and that block sits two
   thousand lines above this one. 9px is a desktop size; on a phone these were
   the smallest type on the page and they list what the service includes. */
@media (max-width: 560px) {
  .cap-tags li { font-size: 10.5px; padding: 5px 10px; }
}
.cap-tags li {
  background: var(--page-base);
  border-color: transparent;
  color: var(--ink-muted);
  transition: background var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
}
.cap.is-active .cap-tags li {
  background: color-mix(in srgb, var(--brand-light) 16%, transparent);
  color: var(--brand-light);
}


/* A path drawn between the four cards, filling as you scroll: the highlight
   is following a route rather than jumping between boxes. */
.caps-wrap { position: relative; }
.caps-path {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 0;
  overflow: visible;
}
.caps-path path {
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Scoped through .caps-path so these beat `.caps-path path`, which was
   setting 2 and quietly winning on specificity. The track stays a hairline
   under the charged run: at 1.8 it was nearly the pulse's own weight and the
   pair read as two lines rather than one wire with charge in it. */
.caps-path path.caps-path-track {
  stroke: color-mix(in srgb, var(--brand-dark) 15%, transparent);
  stroke-width: 1;
}
/* A short segment running the route on a loop, under the scroll-driven fill.
   The route already reads as a route; this makes it read as a live one. */
/* Heavier than the 2.5 it started at: non-scaling-stroke means this is screen
   pixels, and at 2.5 the charged segments read as hairline ticks on anything
   but a low-density display. */
.caps-path path.caps-path-pulse {
  stroke: var(--accent);
  stroke-width: 3.2;
  stroke-linecap: round;
  opacity: 0;
  transition: opacity 240ms var(--ease-out);
}
@media (prefers-reduced-motion: reduce) { .caps-path-pulse { display: none; } }
/* The line fills in behind the head rather than hopping as a loose segment. */
.caps-path-live {
  stroke: var(--brand-dark);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 0 var(--len, 1000);
  opacity: 0;
}
.caps > li { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) { .caps-path-live { stroke-dashoffset: 0; } }

/* Offset the right column so the four read as a descending diagonal rather
   than a block of four. The scroll highlight then travels 01 → 04 down that
   diagonal, left to right, like a snake. */
.caps {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6) var(--space-8);
  align-items: start;
}
.caps > li:nth-child(even) { margin-top: clamp(4rem, 2rem + 6vw, 9rem); }
.caps > li { max-width: 94%; }
.caps > li:nth-child(even) { margin-left: auto; }
@media (max-width: 860px) {
  .caps > li:nth-child(even) { margin-top: 0; }
}
.cap {
  position: relative;
  overflow: hidden;
  display: flex; gap: var(--space-5);
  background: var(--chrome-surface);
  border: 1px dashed transparent;
  border-radius: var(--radius-media);
  padding: var(--space-6);
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
/* Touch has no hover, so there the active card follows the scroll; pointer
   devices get the direct hover instead. */
/* The secondary burgundy, solid, with every element inside re-tuned for it. */
.cap.is-active {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
}
.cap.is-active .cap-name { color: var(--brand-light); }
.cap.is-active .cap-text { color: var(--on-dark-muted); }
.cap.is-active .cap-num {
  color: color-mix(in srgb, var(--brand-light) 26%, transparent);
  -webkit-text-stroke-color: transparent;
}
.cap { transition: background var(--dur-slow) var(--ease-out); }

/* No hover state: the scroll highlight already says which card you are on,
   and two competing highlights read as a bug. */
.cap-name, .cap-text { transition: color var(--dur-base) var(--ease-out); }
.cap-num {
  flex: none;
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 1.6rem + 3vw, 4rem);
  font-weight: 500;
  line-height: 0.85;
  letter-spacing: var(--track-tight);
  color: transparent;
  -webkit-text-stroke: 1px color-mix(in srgb, var(--brand-dark) 32%, transparent);
  transition: color var(--dur-slow) var(--ease-out),
              -webkit-text-stroke-color var(--dur-slow) var(--ease-out);
}
.cap.is-active .cap-num {
  color: color-mix(in srgb, var(--brand-light) 22%, transparent);
  -webkit-text-stroke-color: transparent;
}
.cap-body { min-width: 0; }
.cap-name {
  font-size: var(--step-1); font-weight: 500;
  letter-spacing: -0.015em; color: var(--brand-dark);
  margin-bottom: var(--space-3);
}
.cap-text { font-size: var(--step--1); line-height: var(--lh-body); color: var(--ink-muted); }
.cap-link {
  display: inline-flex; align-items: center; gap: var(--space-2);
  margin-top: var(--space-4);
  padding-block: 6px;        /* 23px was a hair under the 24px minimum */
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--accent); text-decoration: none;
}
.cap-link .arrow { transition: transform var(--dur-base) var(--ease-out); }
.cap-link:hover .arrow { transform: translateX(3px); }

@media (max-width: 900px) {
  .caps { grid-template-columns: 1fr; }
  /* The connector used to be dropped once the cards stacked, because the
     two-column zig-zag folded back on itself in one column. It now runs as a
     vertical lane down the outside instead, so the thread survives on a
     phone. Pinned to the same breakpoint the grid stacks at, and after the
     two-column offsets: the auto margin would otherwise keep the cards flush
     right and the script would still read them as two columns. */
  .caps > li,
  .caps > li:nth-child(even) { max-width: 100%; margin-left: var(--space-6); }
}

/* ---------- Cards ---------- */
.card {
  border-top: 1px solid var(--divider);
  padding-top: var(--space-5);
}
.card h3 { margin-bottom: var(--space-4); color: var(--brand-dark); }
.on-dark .card { border-top-color: var(--divider-on-dark); }

/* ---------- Image placeholders ----------
   Every image and video on the page is a labelled box until real assets
   land: a hatched ground, the intended ratio badged in the corner, and the
   brief written into the caption.
   ------------------------------------------------------------------ */
.ph {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  padding: var(--space-5);
  border-radius: var(--radius-media);
  border: 1px solid var(--divider);
  background-color: color-mix(in srgb, var(--brand-dark) 8%, var(--chrome-surface));
  background-image: repeating-linear-gradient(
    45deg,
    transparent 0 9px,
    color-mix(in srgb, var(--brand-dark) 7%, transparent) 9px 10px);
}
/* A slow sweep across the hatch: these are assets still loading, and saying
   so beats a static grey box. */
.ph::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg,
    transparent 0%,
    transparent 22%,
    color-mix(in srgb, var(--brand-dark) 5%, transparent) 38%,
    color-mix(in srgb, #FFFFFF 92%, transparent) 50%,
    color-mix(in srgb, var(--brand-dark) 5%, transparent) 62%,
    transparent 78%,
    transparent 100%);
  background-size: 220% 100%;
  background-position: 130% 0;
  animation: ph-stream 2.1s linear infinite;
}
@keyframes ph-stream {
  0%   { background-position: 130% 0; }
  70%  { background-position: -30% 0; }
  100% { background-position: -30% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .ph::after { animation: none; opacity: 0; }
}
.ph::before {
  content: attr(data-ratio);
  z-index: 2;
  position: absolute;
  top: var(--space-4); right: var(--space-4);
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  color: color-mix(in srgb, var(--ink) 45%, transparent);
}
.ph[data-ratio=""]::before { content: none; }
.ph-caption {
  position: relative;
  z-index: 2;
  font-family: var(--font-label);
  font-size: var(--step-label);
  line-height: 1.7;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  /* 76%: 50% measured 3.16:1 on the hatched ground and 68% still left the
     team portrait briefs at 4.26:1. */
  color: color-mix(in srgb, var(--ink) 76%, transparent);
}
.r-43 { aspect-ratio: 4 / 3; }

/* ---------- Sector rotator ----------
   One pane at a time: a full-bleed image with the sector name over its top-left
   and two glass cards on the right. The tab rail sits along the bottom.
   ------------------------------------------------------------------ */
.sectors { position: relative; }
.sector-stage { position: relative; }
.sector-pane { position: relative; }
.sector-pane:not(.is-active) {
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-slow) var(--ease-out), visibility 0s linear var(--dur-slow);
}
.sector-pane.is-active {
  opacity: 1; visibility: visible;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.sector-media {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-media);
  overflow: hidden;
  padding: 0;
}
.sector-img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  background: var(--page-base);
}
/* A slow push-in that runs exactly as long as the sector holds the stage, so
   the drawing is still moving while its timer bar fills and settles the
   moment it hands over. Driven off --rot-dur, which the rotator publishes. */
.sector-pane.is-active .sector-img {
  animation: sector-zoom var(--rot-dur, 7000ms) linear both;
  will-change: transform;
}
[data-rot='paused'] .sector-pane.is-active .sector-img { animation-play-state: paused; }
@keyframes sector-zoom {
  from { transform: scale(1); }
  to   { transform: scale(1.09); }
}
@media (prefers-reduced-motion: reduce) {
  .sector-pane.is-active .sector-img { animation: none; }
}

.sector-head {
  position: absolute;
  top: var(--space-5); left: var(--space-5);
  z-index: 3;
  max-width: min(480px, 47%);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  /* Sand, not burgundy. It still has to outrank the two near-white rail
     cards beside it, but a solid burgundy block sat on the drawing like a
     patch; a warm card in the same family reads as the same system. Pale,
     with the sand moved onto the two cards beside it — the same swap the
     influence-engine pair got. */
  background: color-mix(in srgb, var(--brand-light) 12%, var(--page-base));
  border: 1px solid color-mix(in srgb, var(--brand-dark) 10%, transparent);
}
/* The title card carries the sector's mark, drawn thin so it reads as a
   drawing rather than a badge, and set on the same baseline as the name. */
/* The mark sits in a burgundy tile that runs the full height of the name and
   its kicker, matching the icon panel on the influence-engine stage card. */
.sector-head { display: flex; align-items: stretch; gap: var(--space-4); }
.sector-head-text { min-width: 0; align-self: center; }
.sector-ico {
  position: relative;
  overflow: hidden;
  flex: none;
  align-self: stretch;
  display: grid; place-items: center;
  width: 54px; min-height: 54px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--brand-dark) 86%, var(--brand-light));
  color: var(--brand-light);
}
/* Quiet tile, no texture behind the mark. The influence-engine panels earn a
   live surface because the whole card is about a running process; here it was
   noise behind a signpost. */
.sector-ico .entropy-fx { display: none; }
.sector-ico-mark {
  position: relative; z-index: 1;
  display: grid; place-items: center;
}
.sector-ico-mark svg { display: block; width: 28px; height: 28px; }
.sector-ico.is-in svg { animation: sector-ico-in 520ms var(--ease-out) both; }
@keyframes sector-ico-in {
  from { opacity: 0; transform: translateY(4px) scale(0.94); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .sector-ico.is-in svg { animation: none; } }
.sector-name {
  font-size: var(--step-3);
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--brand-dark);
  margin-bottom: var(--space-2);
}
.sector-kicker {
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-muted);
}

.sector-rail {
  position: absolute;
  top: var(--space-5); right: var(--space-5);
  z-index: 3;
  width: min(440px, 44%);
  display: flex; flex-direction: column; gap: var(--space-3);
}
/* Fully opaque, not glass: these sit on the artwork, and a translucent card
   let dark passages of the drawing set the contrast (the copy fell to
   3.35:1). Sand rather than near-white now, so the pair carries the weight
   and the title card beside them stays pale. */
.sector-card {
  background: color-mix(in srgb, var(--brand-light) 30%, var(--page-base));
  border: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
  border-radius: var(--radius-md);
  padding: var(--space-5);
}
.sector-card .label { margin-bottom: var(--space-3); }

.sector-work { font-size: var(--step--1); line-height: var(--lh-body); color: var(--ink-muted); }
.sector-card .tags { gap: 4px; }
/* Sand-filled on the light card, so the audience list still reads as its
   own layer rather than another outline chip. */
.sector-card .tags li {
  background: color-mix(in srgb, var(--page-base) 86%, transparent);
  border-color: color-mix(in srgb, var(--brand-dark) 12%, transparent);
  color: var(--ink);
  padding: 4px 9px;
  font-size: 9px;
  letter-spacing: 0.08em;
}
/* Directly after, because at equal specificity source order decides: 9px is a
   desktop size and these name the client types in each sector. */
@media (max-width: 560px) {
  .sector-card .tags li { font-size: 10.5px; padding: 5px 10px; }
}

.sector-tabs {
  position: absolute;
  left: var(--space-5); bottom: var(--space-5);
  display: flex; align-items: center; gap: 0;
  padding: 4px;
  border-radius: var(--radius-pill);
  /* One step off full burgundy, so the rail is the same material as the icon
     tiles rather than as heavy as the contact section. */
  background: color-mix(in srgb, var(--brand-dark) 86%, var(--brand-light));
  border: 1px solid color-mix(in srgb, var(--brand-dark) 40%, transparent);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--brand-dark) 22%, transparent);
  max-width: calc(100% - var(--space-8));
  overflow-x: auto;
  z-index: 4;
  /* A scrolling rail needs a visible edge; a soft fade beats a scrollbar. */
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
          mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
}
.sector-tabs::-webkit-scrollbar { display: none; }
/* Draggable when it overflows. */
.sector-tabs, .svc-tabs { cursor: grab; touch-action: pan-x; }
.sector-tabs.is-dragging, .svc-tabs.is-dragging { cursor: grabbing; user-select: none; }
.svc-tabs { scrollbar-width: none; }
.svc-tabs::-webkit-scrollbar { display: none; }
.sector-tabs button {
  position: relative;
  display: inline-flex; align-items: center;
  min-height: 34px; padding: 0 var(--space-4);
  border: 0; border-radius: 10px; background: none; cursor: pointer;
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  /* Not --on-dark-muted: on the lightened rail that measured 3.9:1 at 10px. */
  color: color-mix(in srgb, var(--on-dark) 92%, transparent); white-space: nowrap; overflow: hidden;
  /* Keep their own width and let the rail scroll: as flex items the seven
     labels were shrinking to fit and truncating to "FIN", "GOV", "TEC". */
  flex: none;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sector-tabs button:hover { color: var(--on-dark); }
.sector-tabs button[aria-selected='true'] { background: var(--page-base); color: var(--brand-dark); }

@media (max-width: 1024px) {
  .sector-head, .sector-rail { position: static; width: auto; max-width: none; }
  .sector-rail { margin-top: var(--space-3); }
  .sector-head { margin-top: var(--space-3); }
  .sector-media { aspect-ratio: 16 / 10; }
  .sector-pane:not(.is-active) { display: none; }
  /* Selector first: on a phone the tabs are the control you reach for. */
  .sector-stage { display: flex; flex-direction: column; }
  .sector-tabs { position: static; order: -1; margin-bottom: var(--space-4); max-width: none; }
}

/* ---------- Case studies ----------
   The mandate names it, the challenge sets it up, and the disclosure carries
   the working: strategy first, then the outcome with its numbers picked out.
   ------------------------------------------------------------------ */
.cases {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-8) var(--space-6);
}
/* No rule above the card. The artwork already gives each case a hard top
   edge, and a line over it only competed with the sector chip. */
.case {
  position: relative;
  display: flex;
  flex-direction: column;
}
.case-media { transition: transform 700ms var(--ease-out); }

.case-media { position: relative; margin-bottom: var(--space-5); }
/* The generated case art. The 4:3 box still comes from .r-43, so the card
   grid does not move when an image is swapped for another. */
/* The same slow push-in the sector stage runs, tied to the card you are
   reading rather than to a timer. */
.case-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-media);
  background: var(--page-base);
  transition: transform 2800ms var(--ease-out);
}
.case:hover .case-img { transform: scale(1.07); }
@media (prefers-reduced-motion: reduce) {
  .case-img, .case:hover .case-img { transition: none; transform: none; }
}
/* Sector chip on the image: you can tell whether a case is yours before you
   have read a word of it. */
/* Sector chip carries the fill, damage chips are the glass annotation. */
.case-sector {
  position: absolute;
  top: var(--space-4); left: var(--space-4);
  z-index: 2;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 6px var(--space-4) 6px 13px;
  border-radius: var(--radius-round);
  /* Matches the disclosure control on the same card, so the two burgundy
     marks on a case read as one family. */
  background: var(--brand-dark);
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--brand-light);
}
/* Phosphor Light, set at the label's own cap height rather than as a badge
   icon sitting over it: the chip should read as one line of type with a mark
   in front of it, so the glyph is sized in em and given real space. */
.case-sector-ico {
  flex: none;
  display: block;
  width: 1.5em; height: 1.5em;
  opacity: .9;
}
.case-title {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.018em;
  color: var(--brand-dark);
  margin-bottom: var(--space-4);
}
/* No "Challenge" heading: it reads as the opening of the story instead. */
.case-lede {
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--ink-muted);
  margin-bottom: var(--space-5);
}

/* What the challenge actually cost, pinned to the image so the damage is the
   first thing on the card rather than a footnote under the copy. */
.case-flags {
  position: absolute;
  left: var(--space-4); right: var(--space-4); bottom: var(--space-4);
  z-index: 3;
  list-style: none; margin: 0; padding: var(--space-3) 0 0;
  /* No rule here: it cut across the drawing. The separation between cases is
     carried by the rule above each card instead. */
  display: flex; flex-wrap: wrap; gap: 4px;
}
/* Clears the placeholder brief that sits at the same bottom edge. */
/* Glass here, burgundy on the sector chip above: the label that names the
   card carries the weight, the damage reads as annotation on the image. */
.case-flags li {
  padding: 5px 10px;
  border-radius: var(--radius-round);
  background: var(--glass-light);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
  font-family: var(--font-label);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--brand-dark);
}

/* Two pills, like the chrome: a square sign and a label beside it. Quiet by
   default — it is an aside, not the call to action. */
/* The control sits on the title line rather than on a row of its own, and
   the panel it opens drops in under the lede where the reader already is. */
.case-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.case-head .case-title { margin-bottom: 0; }
.case-head .case-more { margin-top: 2px; }
.case-head .case-more-sign { width: 30px; height: 30px; }

.case-more {
  display: inline-flex; align-items: stretch; gap: 6px;
  align-self: flex-start;
  margin-top: auto;
  padding: 0;
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--brand-dark);
  text-align: left;
}
.case-more-label {
  display: inline-flex; align-items: center;
  min-height: 36px; padding: 0 var(--space-5);
  border-radius: var(--radius-pill);
  background: var(--chrome-surface);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 20%, transparent);
  overflow: hidden;
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}
/* Character without shouting: the sign turns, the label opens up a little,
   and the pill fills. */
.case-more-label > span {
  display: block;
  transition: letter-spacing var(--dur-base) var(--ease-out);
}

.person-id {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  min-width: 0;
}
/* No label, just the sign, up on the name row where the case cards keep
   theirs. "Read more" set beside a plus was saying the same thing twice, and
   parked under the facts it read as a stray chip at the foot of the card. */
.person-more {
  margin: 0 0 0 auto;
  align-self: center;
  padding: 0;
  background: none;
  border: 0;
}
.person-more .case-more-sign {
  width: 30px; height: 30px;
  background: var(--brand-dark);
  border: 1px solid var(--brand-dark);
  border-radius: var(--radius-pill);
  color: var(--brand-light);
}
.person-more:hover .case-more-sign,
.person-more:focus-visible .case-more-sign {
  background: color-mix(in srgb, var(--brand-dark) 84%, var(--brand-light));
  border-color: color-mix(in srgb, var(--brand-dark) 84%, var(--brand-light));
  color: var(--brand-light);
}
.case-more:hover .case-more-label > span,
.case-more:focus-visible .case-more-label > span { letter-spacing: 0.22em; }
.case-more-sign {
  position: relative;
  width: 36px; height: 36px; flex: none;
  border-radius: var(--radius-pill);
  background: var(--brand-dark);
  border: 1px solid var(--brand-dark);
  color: var(--brand-light);
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}
.case-more-sign::before, .case-more-sign::after {
  content: '';
  position: absolute; top: 50%; left: 50%;
  background: currentColor;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.case-more:hover .case-more-sign::before,
.case-more:hover .case-more-sign::after { transform: rotate(90deg); }
.case-more[aria-expanded='true']:hover .case-more-sign::before { transform: none; }
.case-more-sign::before { width: 11px; height: 1px; margin: -0.5px 0 0 -5.5px; }
.case-more-sign::after  { width: 1px; height: 11px; margin: -5.5px 0 0 -0.5px; }
.case-more[aria-expanded='true'] .case-more-sign::after { transform: rotate(90deg); opacity: 0; }
/* Fills to burgundy on hover: enough character to invite the click, not
   enough to compete with the section's own call to action. */
.case-more:hover, .case-more:focus-visible { color: var(--brand-light); }
.case-more:hover .case-more-sign,
.case-more:hover .case-more-label,
.case-more:focus-visible .case-more-sign,
.case-more:focus-visible .case-more-label {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
}
.case-more[aria-expanded='true'] .case-more-sign { background: var(--brand-dark); border-color: var(--brand-dark); color: var(--brand-light); }

.case-detail {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-slow) var(--ease-out);
}
.case-detail[data-open='true'] { grid-template-rows: 1fr; }
.case-detail-inner {
  overflow: hidden;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  padding-top: var(--space-4);
}
@media (max-width: 1024px) {
  .case-detail-inner { grid-template-columns: 1fr; }
}
.case-card {
  background: var(--chrome-surface);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  /* Either card closes the result: see the handler in site.js. The "-" beside
     the title stays the keyboard-reachable control. */
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
.case-card:hover { background: color-mix(in srgb, var(--brand-dark) 6%, var(--chrome-surface)); }
.case-card-out:hover { background: color-mix(in srgb, var(--brand-light) 12%, var(--brand-dark)); }
.case-card-out { background: var(--brand-dark); }
.case-card-label {
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-muted);
  margin-bottom: var(--space-3);
}
.case-card-out .case-card-label { color: var(--on-dark-muted); }
.case-card p:last-child {
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--ink);
}
/* p.case-outcome, not .case-outcome: the rule above is 0-2-1 and outranked
   the plain class, leaving dark ink on the burgundy card at 1.43:1. */
.case-card-out p.case-outcome { color: var(--on-dark); }

/* The numbers in the outcome light up as the card comes into view, so the
   part worth remembering is the part that moves. */
.case-outcome mark {
  background: none;
  color: var(--on-dark);
  box-shadow: inset 0 -0.08em 0 color-mix(in srgb, var(--brand-light) 30%, transparent);
  transition: color 420ms var(--ease-out), box-shadow 420ms var(--ease-out);
}
.case.is-in .case-outcome mark {
  color: var(--brand-light);
  box-shadow: inset 0 -0.42em 0 color-mix(in srgb, var(--brand-light) 16%, transparent);
}
.case.is-in .case-outcome mark:nth-of-type(2) { transition-delay: 180ms; }
@media (prefers-reduced-motion: reduce) {
  .case-outcome mark { transition: none; }
}

@media (max-width: 860px) {
  .cases { grid-template-columns: 1fr; gap: var(--space-7); }
}
@media (prefers-reduced-motion: reduce) {
  .case-detail, .case-more-sign::before, .case-more-sign::after { transition: none; }
}

/* ---------- Partner cards ----------
   Copy above, image below with the logo chip laid over it. No motion
   pane here: three partners read better side by side than in rotation.
   ------------------------------------------------------------------ */
.pcards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-7);
}
.pcard {
  display: flex; flex-direction: column;
  border-radius: var(--radius-media);
  overflow: hidden;
}

.pcard {
  display: flex; flex-direction: column;
  background: var(--chrome-surface);
  position: relative;
  flex: 1 1 auto;
  border: 0;
  border-radius: var(--radius-media);
  padding: var(--space-5);
}
.pcard { transition: box-shadow var(--dur-base) var(--ease-out); }
.pcard:hover { box-shadow: 0 14px 32px color-mix(in srgb, var(--brand-dark) 12%, transparent); }

.pcard-head {
  flex: 1 1 auto;
  /* The picture comes up to meet the rule so the badge can sit high without
     leaving its frame. Raising the badge instead put its top edge outside
     .pcard-media, which clips, and sliced the wordmark in half. */
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-5);
  border-bottom: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
}
/* Eyebrow set apart, then the name and the paragraph reading as one block
   under it, then the picture with the partner's own mark on it. */
/* Muted below the site's standard eyebrow: elsewhere a label is the only
   small text near it, but here it sits 24px above a large name and at the
   full --ink-muted (8.11:1) the two read as competing lines. 80% of it
   measures 4.84:1, still clear of the 4.5 floor at 10px. */
.pcard-head .label {
  display: flex; align-items: center; gap: var(--space-3);
  margin-bottom: var(--space-5);
  color: color-mix(in srgb, var(--ink-muted) 80%, var(--page-base));
}
/* The footer's status light, borrowed for the eyebrow: same ring, same core,
   same outward ping, drawn in ink rather than on dark. It sits with the mono
   eyebrow rather than the name, which is the same typographic company it
   keeps in the footer. */
.mark-ring {
  position: relative;
  display: inline-block;
  flex: none;
  width: 11px; height: 11px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--ink-muted) 55%, transparent);
}
.mark-ring::after {
  content: '';
  position: absolute; inset: 3px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--ink-muted) 78%, transparent);
}
.mark-ring::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--ink-muted) 55%, transparent);
  animation: live-ping 2s cubic-bezier(.2,.65,.3,1) infinite;
}
/* Offset down the row. In lockstep three of them read as a strobe rather
   than as three separate lights. */
.pcard:nth-child(2) .mark-ring::before { animation-delay: 0.45s; }
.pcard:nth-child(3) .mark-ring::before { animation-delay: 0.9s; }
@media (prefers-reduced-motion: reduce) {
  .mark-ring::before { animation: none; opacity: 0.25; transform: scale(2); }
}
.pcard-name {
  font-size: var(--step-2); font-weight: 500;
  letter-spacing: -0.02em; color: var(--brand-dark);
  margin-bottom: var(--space-3);
}
/* Between the 10px label step it started at and the 13-14px body step, which
   ran a touch large against the card's other copy. */
.pcard-desc { font-size: 0.75rem; line-height: 1.62; color: var(--ink-muted); }
/* The drawings are generated art and one of them — the record sleeve — has
   the disc running off its own left and right edges in the source file. The
   ink is simply not there to recover, so the frame dissolves at those edges
   instead of slicing them, which reads as a bleed rather than a crop. */
/* No radius of its own and pulled out to the card's edges: the picture is
   the foot of the card, not a second card sitting under it. */
.pcard-media {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: 0;
  overflow: hidden;
  padding: 0;
  margin: auto calc(var(--space-5) * -1) calc(var(--space-5) * -1);
}
/* The fade is on the picture, not the frame: the badge sits on top of it and
   must not dissolve with the artwork's edges. */
.pcard-img {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}
/* Taller than its frame by 8%, hung 4% above it, so there is slack above and
   below for the drift to travel into. Height rather than scale: the fade
   above is measured in the picture's own box, and scaling it would carry
   those 7% columns outside the frame and put the hard edge back. */
.pcard-img {
  position: absolute;
  left: 0; right: 0; top: -4%;
  width: 100%; height: 108%;
  object-fit: cover;
  background: var(--page-base);
  /* --pk runs 0 at the bottom of the viewport to 1 at the top, set from the
     scroll handler. The drawing lags the card by a couple of percent over a
     full pass — enough that the ink reads as sitting behind the frame rather
     than printed on it, and small enough that nobody catches it moving. */
  transform: translate3d(0, calc((var(--pk, 0.5) - 0.5) * 4%), 0);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .pcard-img { transform: none; }
}
.pcard-media::before { content: none; }
/* The drawings run to the card's bottom edge and were being sliced by the
   radius. An overlay rather than a mask on the picture itself: the picture
   drifts, and a mask would drift with it, while this is pinned to the frame.
   Under the badge (z 2) and over the artwork. */
.pcard-media::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 26%;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to top, var(--page-base) 0%, transparent 100%);
}
/* A light badge on the light artwork, held off it by a hairline. The marks
   are alpha silhouettes painted burgundy from here, and each carries its own
   height: set to one size, TERM's slab wordmark shouted and ZEE's thin
   constellation vanished. Sized by optical weight, not by box. */

/* The partner's own mark, back on the artwork's top-left corner. */
/* Pulled up and aligned to the copy column: the chip now hangs off the same
   left edge as the eyebrow, the name and the rule above it, and sits close
   enough under that rule to read as belonging to the card rather than
   floating somewhere in the picture. */
.pcard-logo {
  position: absolute;
  /* Held off the frame's top edge rather than flush to it: .pcard-media
     clips, and at top:0 the 3px hover lift took the wordmark's ascenders
     with it. This is the clearance that lift needs. */
  top: var(--space-2); left: var(--space-5);
  z-index: 2;
  display: flex; align-items: center; justify-content: center;
  width: 104px; height: 44px;
  aspect-ratio: auto;
  border-radius: var(--radius-sm);
  background: var(--chrome-surface);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--brand-dark) 10%, transparent);
}
.pcard-logo::before { content: none; }
.pcard-mark {
  display: block;
  width: var(--mw, 90px); height: var(--mh, 24px);
  flex: none;
  background-color: var(--brand-dark);
  -webkit-mask: var(--logo) center / contain no-repeat;
          mask: var(--logo) center / contain no-repeat;
}
.pcard-mark[data-logo='reputation-house'] { --logo: url('../img/partners/logo-reputation-house.png'); --mh: 20px; --mw: 73px; }
.pcard-mark[data-logo='term']             { --logo: url('../img/partners/logo-term.png');             --mh: 16px; --mw: 43px; }
.pcard-mark[data-logo='zee-group']        { --logo: url('../img/partners/logo-zee-group.png');        --mh: 28px; --mw: 44px; }

/* The three arrive one behind the other rather than as a block. Small gaps —
   at anything longer the third card reads as having been forgotten. */
.pcards .pcard.reveal:nth-child(2) { transition-delay: 90ms; }
.pcards .pcard.reveal:nth-child(3) { transition-delay: 180ms; }

@media (hover: hover) and (pointer: fine) {
  .pcard-logo {
    transition: transform var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out);
  }
  .pcard:hover .pcard-logo {
    transform: translateY(-3px);
    box-shadow: 0 12px 26px color-mix(in srgb, var(--brand-dark) 16%, transparent);
  }
}

/* Phone: a swipe rail rather than three tall cards stacked. Three peers read
   as three peers when they sit side by side, and a native scroll-snap row
   costs no script, keeps momentum, and never hides two thirds of the set
   behind a control someone has to find. */
@media (max-width: 900px) {
  .pcards {
    display: flex;
    gap: var(--space-4);
    margin-top: var(--space-5);
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    padding-bottom: var(--space-4);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .pcards::-webkit-scrollbar { display: none; }
  /* Narrow enough that the next card's edge is always in frame: the peek is
     what tells someone there is more to the right before they touch it. */
  .pcard {
    flex: 0 0 min(78vw, 360px);
    scroll-snap-align: start;
  }
}

/* Swipe rail affordance. Hidden until the script confirms the rail actually
   overflows, so it never appears on a layout where all three already show.
   The dots are a read-out first and a control second: keyboard users get the
   cards themselves in the tab order, so the dots stay out of it. */
.pcards-nav { display: none; }
@media (max-width: 900px) {
  .pcards-nav {
    display: flex; justify-content: center; align-items: center;
    gap: 10px; margin-top: var(--space-4);
  }
  .pcards-nav[hidden] { display: none; }
  .pcards-dot {
    -webkit-appearance: none; appearance: none;
    display: grid; place-items: center;
    width: 24px; height: 24px; padding: 0;
    background: none; border: 0; cursor: pointer;
  }
  .pcards-dot > span {
    display: block; width: 7px; height: 7px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--brand-dark) 55%, transparent);
    transition: width var(--dur-base) var(--ease-out),
                background-color var(--dur-base) var(--ease-out);
  }
  .pcards-dot[aria-current='true'] > span {
    width: 20px;
    background: var(--brand-dark);
  }
}


/* "+" that rotates while the label unravels */
.more {
  display: inline-flex; align-items: center; gap: 0;
  min-height: 32px; padding: 0 4px 0 0;
  background: none; border: 0; cursor: pointer;
  color: var(--brand-dark);
  transition: gap var(--dur-base) var(--ease-out);
}
.more-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none;
  border: 1px solid var(--divider);
  border-radius: var(--radius-round);
  transition: transform var(--dur-slow) var(--ease-out),
              background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.more-label {
  display: grid; grid-template-columns: 0fr;
  transition: grid-template-columns var(--dur-slow) var(--ease-out);
}
.more-label > span {
  overflow: hidden; white-space: nowrap;
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
}
.more:hover, .more:focus-visible, .more.is-open { gap: var(--space-3); }
.more:hover .more-icon,
.more:focus-visible .more-icon,
.more.is-open .more-icon {
  transform: rotate(90deg);              /* clockwise */
  background: var(--brand-dark);
  border-color: var(--brand-dark);
  color: var(--brand-light);
}
.more:hover .more-label,
.more:focus-visible .more-label,
.more.is-open .more-label { grid-template-columns: 1fr; }
.more:hover .more-label > span,
.more:focus-visible .more-label > span,
.more.is-open .more-label > span { opacity: 1; transition-delay: 90ms; }
.more[aria-expanded='true'] .more-icon { transform: rotate(135deg); }

/* Tabs, bottom left */
.showcase-tabs {
  position: absolute;
  left: var(--space-5); bottom: var(--space-5);
  display: flex; align-items: center; gap: 2px;
  padding: 4px;
  border-radius: var(--radius-pill);
  background: var(--glass-light);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 8%, transparent);
  max-width: calc(100% - var(--space-8));
  overflow-x: auto;
}
.showcase-tabs button {
  position: relative;
  display: inline-flex; align-items: center;
  min-height: 34px; padding: 0 var(--space-4);
  border: 0; border-radius: 10px;
  background: none; cursor: pointer;
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--ink-muted); white-space: nowrap;
  overflow: hidden;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.showcase-tabs button:hover { color: var(--brand-dark); }
.showcase-tabs button[aria-selected='true'] { background: var(--page-base); color: var(--brand-dark); }
.tab-progress {
  position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: var(--accent-on-dark);
  transform: scaleX(0); transform-origin: left;
}
button[aria-selected='true'] .tab-progress.is-running {
  animation: tabfill var(--tab-dur, 6000ms) linear forwards;
}
@keyframes tabfill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* The rails it lives on are burgundy now, so the glyph is light. */
.showcase-pause {
  min-width: 34px; justify-content: center; padding: 0 var(--space-3) !important;
  color: var(--on-dark) !important;
}
.showcase-pause .ic-play { display: none; }
.showcase-pause[aria-pressed='true'] .ic-pause { display: none; }
.showcase-pause[aria-pressed='true'] .ic-play { display: block; }

@media (prefers-reduced-motion: reduce) {
  .tab-progress { animation: none !important; transform: scaleX(1); }
}

/* ---------- Leadership ---------- */
.people { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--space-8); }
/* Portrait, not the 4:3 the placeholder used: the photographs are shot 9:16,
   and forcing them into a landscape box either cropped the heads off or left
   a letterbox strip. 4:5 is the standard editorial portrait and takes the
   crop out of the height, where there is room to lose it. */
.person-photo {
  position: relative;
  /* Square, not 4:5. The taller box made the photograph the whole card and
     pushed the name and title into the fold below it; and at that height the
     crop had to run to the hands, which cut through them. Square takes a
     head-and-shoulders instead, which is what a leadership portrait is. */
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-media);
  overflow: hidden;
  background: var(--page-base);
}
.person-img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
}
/* Leadership set as two dossiers: portrait, a named header over a rule, then
   the record as numbered rows in the same idiom as the proof ledger, with the
   rest behind a disclosure. */
/* Page ground, not chrome-surface: #team is already chrome-surface, so the
   cards were invisible against it. */
.person {
  display: flex;
  flex-direction: column;
  background: var(--page-base);
  border-radius: var(--radius-media);
  padding: var(--space-5);
}
.person .ph { margin-bottom: var(--space-5); }
/* Name group on the left, the one control on the right, and the row never
   wraps: with the button as a third flex item it dropped to a line of its
   own and left a hole under the role. */
.person-head {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: nowrap; gap: var(--space-4);
  /* The name was sitting flush on the photograph's bottom edge. */
  margin-top: var(--space-5);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid color-mix(in srgb, var(--brand-dark) 20%, transparent);
  margin-bottom: var(--space-4);
}
/* Where each of them is from, on the name line. Real flags with every
   colour remapped onto the brand scale by luminance, so a union jack is
   still a union jack while the page keeps its two colours. */
/* Sized against the name it sits in front of, not against the label type it
   started at: at 0.62em it read as a bullet rather than a flag. */
/* Centred on the name's cap band rather than sitting on its baseline: at
   0.86em the box is taller than the caps, so hung off the baseline it rode
   high against the name. */
.name-flag {
  display: inline-block;
  vertical-align: baseline;
  width: 0.94em; height: 0.94em;
  margin-right: 0.36em;
  translate: 0 0.11em;
}
.name-flag svg {
  display: block; width: 100%; height: 100%;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 18%, transparent);
}

.person h3 { margin-bottom: 0; }
.person .role {
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--accent); display: block;
  /* Its own line before it squeezes against the name — but it must still wrap
     at phone widths, where nowrap pushed the card 14px past the viewport. */
  white-space: nowrap;
}
@media (max-width: 480px) {
  .person .role { white-space: normal; }
}
.person-lede {
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--ink-muted);
}

/* The three headline facts are cards, not list rows: each is a figure with
   a line under it, and side by side they carry the weight the numbered list
   was asking the reader to assemble for themselves. Everything after them
   stays a list, which gives the pair a hierarchy. */
.person-stats {
  list-style: none; margin: var(--space-5) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
}
.pstat {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--brand-light) 34%, var(--page-base));
  border: 1px solid color-mix(in srgb, var(--brand-dark) 10%, transparent);
}
/* The figure is the point of the card, so it is set above the body scale
   rather than at body weight with a caption under it — but capped below
   --step-3, which pushed "3 regions" past a third of a person column. */
/* Third-party marks on a leadership card, painted from alpha silhouettes in
   the site's own ink rather than dropped in at their brand colours: four
   logos in four palettes beside a portrait reads as a sponsor wall. Sized by
   eye, not by box — the round seal carries far more weight per pixel of
   height than the two wide lockups, so it sits shortest of the four. */
.creds {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
}
/* Close enough to the label to read as its content, which also leaves the
   row sitting higher in the card and more clear space beneath it. */
.creds-label { margin-bottom: var(--space-4); }
/* Centred in the card rather than ranged left under the label. A row of
   marks of five different shapes has no common left edge to hang off, so
   ranging them left only made the ragged right edge louder. */
.creds-marks {
  list-style: none;
  margin: 0;
  padding-inline: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-4) var(--space-5);
}
.creds-marks li { display: flex; align-items: center; }
.cred-mark {
  display: block;
  width: var(--cw); height: var(--ch);
  background-color: color-mix(in srgb, var(--ink-muted) 82%, transparent);
  -webkit-mask: var(--cred) center / contain no-repeat;
          mask: var(--cred) center / contain no-repeat;
}
.cred-mark[data-cred='global-news'] { --cred: url('../img/awards/global-news.png'); --cw: 60px; --ch: 33px; }
.cred-mark[data-cred='soroptimist'] { --cred: url('../img/awards/soroptimist.png'); --cw: 106px; --ch: 27px; }
.cred-mark[data-cred='ccib']        { --cred: url('../img/awards/ccib.png');        --cw: 103px; --ch: 29px; }
.cred-mark[data-cred='cidesco']     { --cred: url('../img/awards/cidesco.png');     --cw: 34px; --ch: 34px; }
.cred-mark[data-cred='ceme-london']   { --cred: url('../img/awards/ceme-london.png');   --cw: 103px; --ch: 27px; }
.cred-mark[data-cred='jersey-dress']  { --cred: url('../img/awards/jersey-dress.png');  --cw: 105px; --ch: 26px; }
.cred-mark[data-cred='bear-commerce'] { --cred: url('../img/awards/bear-commerce.png'); --cw: 134px; --ch: 28px; }

.pstat-figure {
  font-family: var(--font-display);
  font-size: clamp(1.125rem, 0.98rem + 0.42vw, 1.4rem);
  font-weight: 500;
  letter-spacing: -0.026em;
  line-height: 1.08;
  color: var(--brand-dark);
  text-wrap: balance;
  /* Two lines' worth whether or not the figure needs them, so the supporting
     lines start on the same baseline across the row. */
  min-height: 2.2em;
  /* Last resort only. The figures are picked so no single word is wider than
     a third of a person column; without this a long one broke mid-word. */
  overflow-wrap: break-word;
  hyphens: none;
}
.pstat-body {
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--ink-muted);
}
/* Three across only while a third of a person column can still hold the
   figure on one or two lines; below that they stack. */
@media (max-width: 1000px) {
  .person-stats { grid-template-columns: 1fr; }
}

.person-facts {
  list-style: none; margin: var(--space-5) 0 0; padding: 0;
  display: grid;
}
.person-facts li {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr);
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-muted);
}
.fact-i {
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  line-height: 1.7;
  /* 72%: at 50% the index measured 2.79:1 on the card ground. */
  color: color-mix(in srgb, var(--brand-dark) 72%, transparent);
}

/* The rest of the record sits behind a disclosure, the same control the case
   studies use, so the two sections behave the same way. */
.person-more { margin-top: var(--space-5); }
/* The first revealed row supplies its own rule; without the reset the button
   sat tight against it and the list restarted with a doubled line. */
.person-facts-more { margin-top: var(--space-5); }
.person-facts-more li:first-child { border-top: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent); }
.person-rest {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-slow) var(--ease-out);
}
.person-rest[data-open='true'] { grid-template-rows: 1fr; }
.person-rest-inner { overflow: hidden; }
@media (prefers-reduced-motion: reduce) { .person-rest { transition: none; } }

/* ---------- Form ---------- */
.form { display: grid; gap: var(--space-5); }
.field { display: grid; gap: var(--space-2); }
.field label {
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--on-dark-muted);
}
.field .req { color: var(--accent-on-dark); }
.field input, .field select, .field textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-alt);
  border: 1px solid var(--divider-on-dark);
  border-radius: var(--radius-sm);
  color: var(--on-dark);
  font-size: var(--step-0);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.field textarea { min-height: 132px; resize: vertical; padding-top: var(--space-3); }
.field input::placeholder, .field textarea::placeholder { color: rgba(185,161,143,.55); }
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--brand-light); }
/* The section form's select is the same custom control; it takes the field box. */
.field .sel-btn {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-alt);
  border: 1px solid var(--divider-on-dark);
  border-radius: var(--radius-sm);
  color: var(--on-dark);
  font-size: var(--step-0);
  min-height: 56px;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.field .sel-btn:hover,
.field .sel.is-open .sel-btn,
.field .sel-btn:focus-visible { border-color: var(--brand-light); }
.field[data-invalid='true'] .sel-btn { border-color: #F0A6A6; }
/* The section form sits on burgundy, so its list keeps the dark palette. */
.form .sel-pop {
  background: var(--surface-alt);
  border-color: var(--divider-on-dark);
  box-shadow: 0 18px 36px rgba(0, 0, 0, 0.28);
}
.form .sel-opt { color: var(--on-dark); }
.form .sel-opt.is-active { background: color-mix(in srgb, var(--brand-light) 14%, transparent); }
.form .sel-opt[aria-selected='true'] { color: var(--brand-light); }
.field-help { font-size: var(--step--1); color: var(--on-dark-muted); }

/* ---------- Anonymous enquiry + dialling code ---------- */
/* The people most likely to need this work are the least likely to want to
   type their name into a form first. The toggle drops the identity row and
   moves the requirement onto "one way to reply" instead of "a corporate
   address", which is the honest minimum. */
.anon {
  display: grid;
  gap: var(--space-2);
  padding-bottom: var(--space-5);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--divider-on-dark);
}
.anon-toggle {
  display: inline-flex; align-items: center; gap: var(--space-3);
  justify-self: start;
  padding: 0;
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--on-dark);
}
.anon-tick {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: none;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in srgb, var(--brand-light) 34%, transparent);
  color: transparent;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.anon-toggle[aria-pressed='true'] .anon-tick {
  background: var(--brand-light);
  border-color: var(--brand-light);
  color: var(--brand-dark);
}
.anon-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.anon-note {
  font-size: var(--step--1);
  line-height: 1.6;
  color: color-mix(in srgb, var(--on-dark-muted) 85%, var(--brand-dark));
}
.field-opt {
  font-family: var(--font-label);
  letter-spacing: var(--track-label);
  color: color-mix(in srgb, var(--on-dark-muted) 78%, var(--brand-dark));
  text-transform: lowercase;
  font-style: normal;
}
/* Dial code and number share one line. The select is sized to its widest
   sensible content rather than to the longest country name, which would
   have taken two thirds of the row for a label nobody reads twice. */
.phone-row { display: grid; grid-template-columns: 132px minmax(0, 1fr); gap: var(--space-2); }
/* Chrome now honours white-space on a select, and at this width the long
   country names wrapped the closed control to four lines. Short labels plus
   an explicit nowrap keeps it one line high next to the number. */
/* Every select on the page is swapped for the custom button + listbox pair,
   so the rules that matter here are the ones on .sel-btn, not on the native
   control underneath it. A step under the fields beside it: the dial code is
   a setting, not an answer, and at body size "UAE +971" wrapped inside a
   column narrow enough to leave the number room. */
.phone-row .sel-btn {
  padding-inline: var(--space-3);
  font-size: var(--step--1);
  min-height: 56px;
}
.phone-row .sel-value {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.phone-row input[type='tel'] { min-height: 56px; }
/* The open list is not bound by the closed control's width, so let it take
   the room the country names need rather than wrapping "Bahrain +973". */
.phone-row .sel-pop { min-width: 218px; }
/* Revealed only when the list has no code for them. */
.dial-other {
  margin-top: var(--space-2);
  min-height: 44px;
}
.anon-mini { padding-bottom: var(--space-4); margin-bottom: var(--space-4); }
/* Full --on-dark-muted here, not the section form's 85% of it: the drawer's
   ground is solid burgundy rather than the card's lifted surface, and the
   same mix measured 4.17:1 at label size against it. */
.anon-mini .anon-note {
  font-size: var(--step-label);
  line-height: 1.6;
  color: var(--on-dark-muted);
}
.mini-field .phone-row { gap: 6px; grid-template-columns: 124px minmax(0, 1fr); }
.mini-field .phone-row .sel-btn { min-height: 42px; padding-inline: 10px; font-size: var(--step--1); }
.mini-field .phone-row input[type='tel'] { min-height: 42px; }

.field-error {
  font-size: var(--step--1);
  color: #F0A6A6;
  display: flex; align-items: center; gap: var(--space-2);
}
.field[data-invalid='true'] input,
.field[data-invalid='true'] select,
.field[data-invalid='true'] textarea { border-color: #F0A6A6; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5); }
.error-summary {
  border: 1px solid #F0A6A6;
  border-radius: var(--radius-sm);
  padding: var(--space-5);
  background: var(--surface-alt);
}
.error-summary h3 { font-size: var(--step-1); color: #F0A6A6; margin-bottom: var(--space-3); }
.error-summary ul { margin: 0; padding-left: var(--space-5); font-size: var(--step--1); }
.error-summary a { color: var(--on-dark); }
[hidden] { display: none !important; }

.assurance {
  border-left: 2px solid var(--accent-on-dark);
  padding-left: var(--space-5);
  font-size: var(--step--1);
  color: var(--on-dark-muted);
  max-width: 52ch;
}

/* Partners + leadership bands */
#partners {
  background: var(--chrome-surface);
  border-radius: var(--radius-section) var(--radius-section) 0 0;
  margin-top: calc(-1 * var(--radius-section));
  padding-top: calc(var(--section-y) * 0.55 + var(--radius-section));
  position: relative;
  z-index: 2;
}
#partners .pcard { background: var(--page-base); }
/* Leadership shares the Fractional Placements surface: a second rounded lip
   between two boxes of the same colour is invisible and only costs space. */
#team {
  background: var(--chrome-surface);
  /* Leadership is the one subject change on the page that gets neither a
     colour change nor a full section unit of air, because it shares this
     surface with Fractional Placements. A hairline marks it instead — drawn
     inside the content column, not run to the window edges. */
  padding-top: calc(var(--section-y) * 0.6);
  position: relative;
  z-index: 2;
}
#team::before {
  content: '';
  position: absolute;
  top: 0; left: var(--gutter); right: var(--gutter);
  height: 1px;
  background: color-mix(in srgb, var(--brand-dark) 11%, transparent);
}

/* ---------- Get in touch ---------- */
.gi-head { margin-bottom: var(--space-7); }
.gi-title { margin-bottom: var(--space-2); }
/* Same size as the heading, dimmed — a continuation of the sentence
   rather than a subheading. */
.gi-lede {
  font-family: var(--font-display);
  font-size: var(--step-5);
  line-height: var(--lh-snug);
  letter-spacing: var(--track-tight);
  font-weight: 500;
  /* 62%: at 30% this measured 1.84:1 on burgundy, under the 3:1 large-text
     floor, and it is the second half of the sentence — not decoration. */
  color: color-mix(in srgb, var(--brand-light) 62%, transparent);
  max-width: 30ch;
}
/* Only the first line holds itself on one line. The lede is now a child of
   this heading and was inheriting the nowrap, overflowing by 253px at 1024. */
@media (min-width: 900px) {
  .gi-title > span:first-child { white-space: nowrap; }
}
.gi-lede { white-space: normal; }

/* ---------- Form card ---------- */
.form-card {
  background: var(--surface-alt);
  border-radius: var(--radius-media);
  padding: clamp(1.25rem, 0.6rem + 2.4vw, 2.5rem);
  max-width: 58rem;
}
.form { gap: var(--space-4); }
.form-row { gap: var(--space-4); }
.field input, .field select, .field textarea {
  background: color-mix(in srgb, var(--brand-light) 7%, transparent);
  border-radius: var(--radius-md);
  min-height: 56px;
}
.field textarea { min-height: 150px; }
/* A step down, in line with the hero's pair and the drawer's button. Full
   width across a card this wide, a five-word label at --step-1 stopped
   reading as a control and started reading as a banner. Still a step above
   the drawer's, which is the smaller surface. */
.btn-submit {
  width: 100%;
  justify-content: center;
  min-height: 56px;
  font-size: var(--step-0);
  border-radius: var(--radius-md);
  margin-top: var(--space-2);
}
/* Not-yet-available, not unreadable. Two problems here: the muted fill
   measured 3.86:1 at 10px, and `.on-dark .btn-primary:hover` outranked the
   disabled rule, so hovering painted the full sand fill under that same
   muted label — 1.2:1. Both states are pinned now, and the dashed edge
   carries the "blocked" signal instead of a dim label. */
.on-dark .btn-submit[aria-disabled='true'],
.on-dark .btn-submit[aria-disabled='true']:hover {
  background: color-mix(in srgb, var(--brand-light) 10%, transparent);
  border: 1px dashed color-mix(in srgb, var(--brand-light) 45%, transparent);
  /* 88% measured 4.44:1 at 10px; the dashed edge and tooltip carry the
     disabled signal instead of a dimmer label. */
  color: var(--brand-light);
  cursor: not-allowed;
}

/* Hover/focus hint explaining why the button will not send yet. */
.submit-wrap { position: relative; margin-top: var(--space-2); }
.submit-wrap .btn-submit { margin-top: 0; }
.submit-tip {
  position: absolute;
  left: 50%; bottom: calc(100% + 10px);
  transform: translate(-50%, 4px);
  z-index: 3;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--brand-light);
  color: var(--ink);
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              visibility var(--dur-fast);
}
.submit-tip::after {
  content: '';
  position: absolute; top: 100%; left: 50%;
  width: 8px; height: 8px;
  margin: -4px 0 0 -4px;
  background: var(--brand-light);
  transform: rotate(45deg);
}
.submit-wrap[data-blocked='true']:hover .submit-tip,
.submit-wrap[data-blocked='true']:focus-within .submit-tip {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}
@media (prefers-reduced-motion: reduce) {
  .submit-tip { transition: none; }
}

/* ---------- Custom select ----------
   Replaces the native control so the open list carries the form's own
   colours. The real <select> stays in the DOM, hidden, holding the value.
   ------------------------------------------------------------------ */
.sel { position: relative; }
.sel-native {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}
.sel-btn {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
/* 0.62 measured 4.18:1 on the light field ground. */
.sel-value.is-placeholder { opacity: 0.74; }
/* A short code in a disc rather than an emoji flag. Emoji flags do not
   render at all on Windows, which is most of the desktop audience, and the
   ones that do render carry their own colour into a palette that has three
   of them. The code is legible everywhere and reads as part of the set. */
.sel-value, .sel-opt { display: flex; align-items: center; }
.sel-chip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 21px; height: 21px; flex: none;
  margin-right: 9px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--brand-light) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--brand-light) 26%, transparent);
  font-family: var(--font-label);
  font-size: 8.5px;
  letter-spacing: 0.04em;
  line-height: 1;
  color: var(--brand-light);
}

.sel-opt[aria-selected='true']::after { margin-left: auto; float: none; }
.sel-chev {
  flex: none;
  width: 7px; height: 7px;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--dur-base) var(--ease-out);
}
.sel.is-open .sel-chev { transform: rotate(225deg) translate(-2px, -2px); }

/* The pop-up is the box now, because a searchable select needs a text input
   above the list and a <ul role="listbox"> cannot hold one. */
.sel-pop {
  position: absolute;
  top: calc(100% + 6px); left: 0; right: 0;
  z-index: 6;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
  border: 1px solid var(--divider-on-dark);
  box-shadow: 0 18px 36px rgba(0, 0, 0, 0.28);
  overflow: hidden;
}
.sel-list {
  position: static;
  list-style: none; margin: 0; padding: 5px;
  max-height: 240px;
  overflow-y: auto;
}
.sel-search {
  display: block;
  width: 100%;
  min-height: 40px;
  padding: 8px var(--space-3);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--divider-on-dark);
  border-radius: 0;
  color: var(--on-dark);
  font: inherit;
  font-size: var(--step--1);
}
.sel-search::placeholder { color: rgba(185,161,143,.55); }
.sel-search:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sel-opt {
  padding: 9px var(--space-4);
  border-radius: var(--radius-sm);
  font-size: var(--step--1);
  line-height: 1.4;
  color: var(--on-dark);
  cursor: pointer;
}
.sel-opt.is-active { background: color-mix(in srgb, var(--brand-light) 14%, transparent); }
.sel-opt[aria-selected='true'] { color: var(--brand-light); }
.sel-opt[aria-selected='true']::after {
  content: '';
  float: right;
  width: 5px; height: 9px;
  margin-top: 3px;
  border-right: 1.4px solid currentColor;
  border-bottom: 1.4px solid currentColor;
  transform: rotate(45deg);
}

/* ---------- Disclosure gate ---------- */
.gate { margin-top: var(--space-3); }
.gate-bar { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.gate-toggle, .gate-accept {
  display: inline-flex; align-items: center; gap: var(--space-3);
  min-height: 52px; padding: 0 var(--space-5);
  border-radius: var(--radius-md);
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  cursor: pointer;
  transition: background var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.gate-toggle {
  flex: 1 1 auto;
  justify-content: space-between;
  background: color-mix(in srgb, var(--brand-light) 7%, transparent);
  border: 1px solid var(--divider-on-dark);
  color: var(--on-dark);
}
.gate-toggle:hover { border-color: var(--brand-light); }
.gate-chev { display: inline-flex; transition: transform var(--dur-slow) var(--ease-out); }
.gate-toggle[aria-expanded='true'] .gate-chev { transform: rotate(135deg); }

.gate-accept {
  flex: none;
  background: transparent;
  border: 1px solid var(--divider-on-dark);
  color: var(--on-dark);
}
.gate-accept:hover { border-color: var(--brand-light); }
.gate-tick {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: var(--radius-round);
  border: 1px solid currentColor;
  color: inherit;
}
.gate-tick svg { opacity: 0; transition: opacity var(--dur-fast) var(--ease-out); }
.gate-accept[aria-pressed='true'] {
  background: var(--brand-light); border-color: var(--brand-light); color: var(--ink);
}
.gate-accept[aria-pressed='true'] .gate-tick { background: var(--ink); color: var(--brand-light); border-color: var(--ink); }
.gate-accept[aria-pressed='true'] .gate-tick svg { opacity: 1; }

/* Quieter than the labels around it: this is a nudge, not an error, and at
   the full --on-dark-muted (6.16:1) it read as loud as the field names. 85%
   of it measures 4.86:1, still clear of the 4.5 floor at 13.7px. */
.gate-hint {
  font-size: var(--step--1);
  color: color-mix(in srgb, var(--on-dark-muted) 85%, var(--brand-dark));
  margin-top: var(--space-3);
}
.gate[data-accepted='true'] .gate-hint { color: var(--accent-on-dark); }

.gate-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-slow) var(--ease-out); }
.gate-panel[data-open='true'] { grid-template-rows: 1fr; }
.gate-panel-inner { overflow: hidden; }
.gate-block { padding-top: var(--space-5); }
.gate-block h3 {
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  font-weight: 500; color: var(--accent-on-dark); margin-bottom: var(--space-3);
}
.gate-block p { font-size: var(--step--1); line-height: var(--lh-body); color: var(--on-dark-muted); max-width: 68ch; }

/* ---------- Footer ---------- */
/* Get in touch and the footer are the same dark family, so the usual
   section-end padding between them just read as a dead band. */
#contact > .section { padding-bottom: calc(var(--section-y) * 0.55 + var(--radius-section)); }
.site-footer {
  padding-top: var(--space-4);
  background: var(--surface-alt);
  border-radius: var(--radius-section) var(--radius-section) 0 0;
  margin-top: calc(-1 * var(--radius-section));
  color: var(--on-dark);
  position: relative;
  z-index: 2;
}
.site-footer h1, .site-footer h2, .site-footer h3, .site-footer h4 { color: var(--brand-light); }


.wordmark {
  display: inline-flex; align-items: center; min-height: 44px;
  font-family: var(--font-display); font-size: var(--step-2);
  font-weight: 400; letter-spacing: -0.015em; line-height: 1; text-decoration: none;
}

.footer { padding-block: var(--space-5); }

/* The mark is the right-hand column of the same row as the link columns. */
.footer-mark { border-top: 1px solid var(--divider-on-dark); padding-top: var(--space-5); }
.footer-mark-lockup {
  display: flex; align-items: center; gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.brand-card-mark {
  display: block;
  width: 34px; height: 30px;
  background-color: var(--brand-light);
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
}
.brand-card-word {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 400;
  letter-spacing: -0.025em;
  line-height: 1;
  color: var(--brand-light);
  /* "mosaek" is all x-height bar the k, so its ink sits low in the em box and
     the two boxes centring exactly still looked a pixel or two adrift. */
  translate: 0 -0.06em;
}
/* A single thick rule down the left rather than a box around it: the panel
   made two short lines look like a form field. The rule spans both lines and
   reads as a pull quote. */
.brand-card-line {
  padding: var(--space-2) 0 var(--space-2) var(--space-5);
  border-left: 3px solid color-mix(in srgb, var(--brand-light) 55%, transparent);
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  line-height: 1.7;
  color: var(--on-dark-muted);
  /* One line while there is room for it, two when there is not. */
  text-wrap: balance;
}
/* Sits above the promise, so the lockup is closed off and the promise opens
   the block under it. */
.footer-mark-rule {
  height: 1px;
  margin: var(--space-5) 0;
  background: var(--divider-on-dark);
}

.footer-top {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 3fr);
  gap: var(--space-6);
  align-items: start;
}
.footer-cols {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}
/* Every column starts its body at the same line: the heading is given a fixed
   block so a one-line and a four-line column still align. */
.footer-col { border-top: 1px solid var(--divider-on-dark); padding-top: var(--space-5); }
.footer-col h4 { min-height: 1.4em; }
.footer-col > :nth-child(2) { margin-top: 0; }
/* The three columns start their first line on the same rule, which the boxes
   do not do by themselves: a Direct Lines row carries a 30px icon and centres
   its text against it, and a Contact line carries padding for its tap target.
   Both are pulled back by the difference, measured rather than guessed. */
.footer-col .social { display: flex; }
.footer-col > .social:nth-child(2) { margin-top: -10.2px; }
.footer-col > p:nth-child(2) { margin-top: -3.5px; }


.footer h4 {
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: color-mix(in srgb, var(--on-dark-muted) 82%, transparent);
  font-weight: 500; margin-bottom: var(--space-4);
}
/* Brought forward from the reference-matter tint it was set in: this is the
   registered entity, and it should read as a statement of record rather than
   as small print. */
.footer address {
  font-style: normal;
  font-size: var(--step--1);
  line-height: var(--lh-loose);
  color: var(--on-dark-muted);
}
.footer address b { font-weight: 500; }
.footer-col p { font-size: var(--step--1); }
.footer-col .link-plain { color: color-mix(in srgb, var(--on-dark-muted) 86%, transparent); }
.footer-col .link-plain:hover { color: var(--brand-light); opacity: 1; }
.footer-col .social { color: color-mix(in srgb, var(--on-dark-muted) 86%, transparent); }
.footer-col .social:hover { color: var(--brand-light); }

.social {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-size: var(--step--1);
  text-decoration: none;
  color: var(--on-dark);
  padding-block: 6px;
}
.social-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: none;
  border-radius: var(--radius-round);
  background: color-mix(in srgb, var(--brand-light) 16%, transparent);
  color: var(--brand-light);
  transition: background var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
}
.social:hover .social-icon { background: var(--brand-light); color: var(--brand-dark); }
.social .arrow { transition: transform var(--dur-base) var(--ease-out); }
.social:hover .arrow { transform: translateX(3px); }

.link-plain {
  display: inline-block;
  padding-block: 4px;        /* clears the 24px target minimum */
  color: inherit;
  text-decoration: none;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.link-plain:hover { opacity: 0.7; }

/* Plain bar: a hairline above it does the separating, no panel. */
.footer-bottom {
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid var(--divider-on-dark);
  align-items: flex-start;
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-4);
  font-family: var(--font-label); font-size: var(--step-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--on-dark-muted);
}
/* Copyright left, the standing confidentiality note right — two lines, with
   its own mono heading so the promise reads before the small print. */
/* The mark is set at reading size rather than at the mono label's, where it
   came out as a speck beside the line it belongs to. */
/* Offset measured off the real glyphs rather than judged by eye: rendered at
   scale, JetBrains Mono's (c) sits with its ink centre 0.1475em above the
   digits' centre, which is 0.123 of this element's own em once it is set at
   1.2. Shifting it by exactly that puts the two on one centre line. */
.footer-copy-mark { font-size: 1.2em; line-height: 1; vertical-align: -0.123em; }
.footer-copy {
  font-family: var(--font-label);
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: color-mix(in srgb, var(--on-dark-muted) 86%, transparent);
}
/* Two balanced blocks: the legal pair on the left, the standing NDA note on
   the right. Both are capped so neither leaves an orphan on line two. */
.footer-legal { display: grid; gap: var(--space-3); max-width: 46ch; }
.footer-legal .footer-copy { margin-bottom: 0; }
.footer-note {
  max-width: 46ch;
  font-family: var(--font-body);
  font-size: 11px;
  line-height: 1.6;
  letter-spacing: 0;
  text-transform: none;
  color: color-mix(in srgb, var(--on-dark-muted) 86%, transparent);
}
.footer-note-nda { margin-left: auto; text-align: right; }
/* A ring with a small core rather than a filled disc: it reads as a status
   light instead of a bullet. */
.footer-note b .live-dot {
  width: 11px; height: 11px;
  margin-right: 0;
  background: none;
  border: 1px solid color-mix(in srgb, var(--on-dark-muted) 70%, transparent);
}
.footer-note b .live-dot::after {
  content: '';
  position: absolute; inset: 3px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--on-dark-muted) 86%, transparent);
}
.footer-note b .live-dot::before {
  background: none;
  border: 1px solid color-mix(in srgb, var(--on-dark-muted) 70%, transparent);
}
.footer-note b {
  display: flex; align-items: center; gap: var(--space-3);
  justify-content: flex-end;
  font-family: var(--font-label);
  font-weight: 500;
  font-size: var(--step-label);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  /* Matched to the copyright opposite so the bar reads as one line of type. */
  color: color-mix(in srgb, var(--on-dark-muted) 86%, transparent);
  margin-bottom: var(--space-3);
}
@media (min-width: 721px) {
  .footer-copy { white-space: nowrap; }
}

/* ---------- Scroll reveal (Standard tier) ---------- */
.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}
.reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 1024px) {
  .editorial-row > .row-label { grid-column: 1 / -1; }
  .editorial-row > .row-body  { grid-column: 1 / -1; }
  .stat { grid-column: span 6; }
  .partners { grid-template-columns: 1fr; }
}

@media (max-width: 900px) {
  /* The arrow only means something alongside hover, so drop it on touch. */
  .contact-arrow { display: none; }
  /* Pills collapse into a glass sheet that drops from the chrome. */
  /* Two columns rather than a single stack: six destinations in one column
     ran most of the screen's height and pushed the one thing anyone came for
     off the bottom. Paired up they fit in three rows, and Contact takes a
     full-width row of its own underneath. */
  .pills.dock {
    position: fixed;
    top: calc(48px + var(--space-4) + var(--space-3));
    right: var(--gutter);
    left: var(--gutter);
    height: auto;
    max-height: calc(100dvh - 48px - var(--space-4) - var(--space-3) - var(--gutter));
    overflow-y: auto;
    overscroll-behavior: contain;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /* Explicit rows filled column-first, so the numbering reads down the left
       column and then down the right. Row-major would have put 02 across the
       gap from 01, which is not how anyone reads an index. Four rows, not
       three: at three the seventh item opened a third column and clipped
       every label in the sheet to two characters. The last row belongs to
       Contact, so five rows here leaves four for the index. */
    grid-template-rows: repeat(5, auto);
    grid-auto-flow: column;
    counter-reset: dock;
    align-items: stretch;
    gap: 4px 0;
    padding: var(--space-3);
    border-radius: var(--radius-media);
    transform: translateY(-12px);
    opacity: 0;
    visibility: hidden;
    transition: transform var(--dur-base) var(--ease-out),
                opacity var(--dur-base) var(--ease-out),
                visibility 0s linear var(--dur-base);
  }
  .pills[data-open='true'] {
    transform: translateY(0);
    opacity: 1;
    visibility: visible;
    transition-delay: 0s;
  }
  /* In the drop-down sheet the dock stops being a dock: there is no pointer
     to magnify toward and no room to make people hover for a name, so every
     item is an icon with its label already out. */
  .dock-item { height: 46px; font-size: var(--step--1); }
  .dock-item { padding: 0 var(--space-3); gap: var(--space-3); }
  .dock-ico { width: 18px; height: 18px; transform: none; }
  .dock-name { max-width: none; opacity: 1; margin-left: 0; }
  /* Numbered from a counter rather than in the markup: the order is a
     property of this layout, and on the desktop rail there is nothing to
     number. Tabular figures so 01-06 hold one column width. */
  .dock-item::before {
    counter-increment: dock;
    content: counter(dock, decimal-leading-zero);
    font-family: var(--font-label);
    font-size: var(--step-label);
    letter-spacing: 0.06em;
    font-variant-numeric: tabular-nums;
    /* 85%, not the 60% these started at: over the sheet's own glass that
       measured 2.92:1 and the numbers were barely there even on the light
       ground. Four grounds to clear at 10px — the light sheet, its hover
       tint, the inverted sheet, and the solid sand of the current item —
       and each one is pinned below. */
    color: color-mix(in srgb, var(--ink-muted) 85%, transparent);
    flex: none;
  }
  .dock-item:hover::before,
  .dock-item:focus-visible::before { color: var(--ink-muted); }
  /* The numbers follow the sheet, not the page. Over a burgundy section the
     bar inverts and ink-on-ink took 02 through 06 down to nothing. Sand on
     the hover tint rather than the muted tone, which tops out at 4.26:1
     there however far it is pushed. */
  .chrome[data-right-dark='true'] .dock-item::before {
    color: color-mix(in srgb, var(--on-dark-muted) 90%, transparent);
  }
  .chrome[data-right-dark='true'] .dock-item:hover::before,
  .chrome[data-right-dark='true'] .dock-item:focus-visible::before {
    color: color-mix(in srgb, var(--brand-light) 90%, transparent);
  }
  /* The current item wears a solid sand fill in both modes, so its number
     goes back to ink either way. */
  .dock-item[aria-current='true']::before,
  .chrome[data-right-dark='true'] .dock-item[aria-current='true']::before {
    color: var(--ink-muted);
  }
  /* A hairline down the middle so the two runs read as two columns of an
     index rather than six loose rows. Drawn on the right-hand items, which
     column-first flow puts at children 4 to 6. */
  .pills.dock .dock-item:nth-child(n+4) {
    border-left: 1px solid color-mix(in srgb, var(--brand-dark) 9%, transparent);
  }
  .chrome[data-right-dark='true'] .pills.dock .dock-item:nth-child(n+4) {
    border-left-color: color-mix(in srgb, var(--brand-light) 18%, transparent);
  }
  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 48px; height: 48px;
    background: var(--glass-light);
    backdrop-filter: var(--glass-blur);
    -webkit-backdrop-filter: var(--glass-blur);
    border: 1px solid color-mix(in srgb, var(--brand-dark) 8%, transparent);
    border-radius: var(--radius-pill);
    cursor: pointer;
    color: var(--brand-dark);
  }
  .dock-sep { display: none; }
  /* Contact spans the pair and sits under them, sized up so it reads as the
     action rather than a seventh destination. */
  /* Column, not the desktop row: the drawer is in normal flow here, so left
     as a row it sat beside the trigger and squeezed it to its text width. */
  .contact-pill {
    grid-column: 1 / -1;
    grid-row: 5;
    margin-top: var(--space-3);
    flex-direction: column;
    align-items: stretch;
  }
  /* Scoped through .pills.dock to outrank `.dock.is-open .contact-trigger`,
     which is still setting the ravelled desktop width at the top of the page. */
  .pills.dock .contact-trigger {
    width: 100%;
    height: 48px;
    padding: 0 var(--space-4);
    justify-content: flex-start;
    gap: var(--space-3);
    border-radius: var(--radius-md);
    font-size: var(--step--1);
  }
  .pills.dock .contact-name { max-width: none; opacity: 1; margin-left: 0; }
  /* The drawer opens inside the sheet on touch too, rather than sending the
     reader down to the section form: the sheet already scrolls, so the mini
     form can live in it. Static, not absolutely positioned — hung off the
     trigger it would have escaped the sheet's own scroll. */
  .contact-drawer {
    position: static;
    width: auto;
    max-height: none;
    margin-top: 4px;
  }
  .contact-drawer-inner { max-height: none; }
  .contact-drawer .mini-meta a { gap: var(--space-2); }
  .contact-chev { display: none; }
}

@media (max-width: 900px) {
  .showcase-card { width: min(300px, 48%); padding: var(--space-4); }
  .showcase-logo { width: 44px; height: 44px; }
}

@media (max-width: 900px) {
  .footer-top { grid-template-columns: 1fr; gap: var(--space-5); }
  .footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  .footer-cols { grid-template-columns: 1fr; gap: var(--space-5); }
  .footer-bottom { flex-direction: column; gap: var(--space-4); }
  .footer-note-nda { text-align: left; margin-left: 0; }
  .footer-note b { justify-content: flex-start; }
  .footer-legal, .footer-note { max-width: none; }
  .gate-bar { flex-direction: column; }
  .gate-accept { justify-content: center; }
  /* Portrait frame: media stacks, card drops to the bottom edge,
     tabs move to the top so the two never collide. */
  .showcase-frame { aspect-ratio: 3 / 4; }
  .showcase-pane { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; }
  /* Tabs sit at the top on mobile, so captions clear below them. */
  .showcase-pane .ph { padding-top: 74px; }
  .showcase-pane .ph::before { top: 56px; }
  .showcase-card {
    top: auto; bottom: var(--space-4);
    left: var(--space-4); right: var(--space-4);
    width: auto;
  }
  .showcase-tabs {
    top: var(--space-4); bottom: auto;
    left: var(--space-4); right: var(--space-4);
    max-width: none;
  }
  .showcase-tabs button { padding: 0 var(--space-3); }
  .stat { grid-column: span 12; }
  .people { grid-template-columns: 1fr; gap: var(--space-7); }
  .form-row { grid-template-columns: 1fr; }
  /* Label left, arrow pinned right. Width is left to the flex line above so
     the pair sits side by side wherever the row can hold both at full label
     width and goes full-bleed only when it cannot; a hard 100% here was
     forcing the stack at every width under 720. */
  .hero-actions-row .btn { justify-content: space-between; text-align: left; }
}

/* ============================================================
   WHAT IS AT RISK — the exposure rail
   ============================================================ */
/* Six plates that run sideways while the page holds still. The order is the
   argument (a deal, then capital, then the record, then the people around it)
   so a rail reads better than a grid, where the eye picks its own entry point
   and the escalation is lost.

   The heading travels inside the pinned viewport rather than sitting above
   it. Left outside, the first screen was the heading and then forty per cent
   of nothing before the plates arrived.

   Pinned with position:fixed rather than position:sticky: body carries
   overflow-x:hidden, which quietly disqualifies every sticky descendant.
   Below 900px the rail collapses to a stack and none of it applies. */
/* Full bleed, not a centred slab. Once the viewport pins it is fixed to the
   window, so a slab with its own margins would have let the plates run out
   past their own background. Edge to edge with the section corner instead:
   the ground change is what separates it from the hero. */
/* Butted straight against the hero, the same way the dark sections are: the
   corner curve sits on the join and the ground change does the separating. A
   margin here left a strip of page between the two, and the hero's own edge
   then read as a hard rule across the screen. */
.exposure {
  background: color-mix(in srgb, var(--brand-light) 17%, var(--page-base));
  border-radius: var(--radius-section) var(--radius-section) 0 0;
  margin-top: 0;
  overflow: hidden;
}
.exposure-head { margin-inline: auto; width: 100%; max-width: var(--wrap-max); }
.hrail-track, .hrail-meter { margin-inline: auto; max-width: var(--wrap-max); }
.exposure-head { padding: var(--section-y) var(--gutter) 0; }
.exposure-head .h2, .exposure-lede { max-width: 74ch; }
.exposure-lede { margin-bottom: 0; }
.exposure-head .label { margin-bottom: var(--space-6); }
.exposure-head .h2 { margin-bottom: var(--space-4); color: var(--brand-dark); }
.exposure-lede {
  font-size: var(--step-0);
  line-height: var(--lh-loose);
  color: var(--ink-muted);
  max-width: 64ch;
}

.plates-rail { list-style: none; margin: 0; padding-inline: 0; }
.plate { display: grid; align-content: start; gap: var(--space-4); }
/* Painted from the mask rather than dropped in as an image, so the plate can
   invert with the card it sits on instead of staying burgundy on burgundy. */
.plate-art {
  display: block;
  width: 100%;
  aspect-ratio: 720 / 430;
  background-color: currentColor;
  /* Sized in per cent rather than by `contain`, because the drift animation
     interpolates the mask box and a keyword has nothing to interpolate to. */
  -webkit-mask: var(--art) 50% 50% / 100% 100% no-repeat;
          mask: var(--art) 50% 50% / 100% 100% no-repeat;
  transition: transform 620ms var(--ease-out);
}
.plate-art[data-art='01-opportunities'] { --art: url('../img/exposure/01-opportunities.svg'); }
.plate-art[data-art='02-capital']       { --art: url('../img/exposure/02-capital.svg'); }
.plate-art[data-art='03-record']        { --art: url('../img/exposure/03-record.svg'); }
.plate-art[data-art='04-philanthropy']  { --art: url('../img/exposure/04-philanthropy.svg'); }
.plate-art[data-art='05-stature']       { --art: url('../img/exposure/05-stature.svg'); }
.plate-art[data-art='06-privacy']       { --art: url('../img/exposure/06-privacy.svg'); }

.plate-copy { display: grid; gap: var(--space-3); }
/* The index set large and hollow in the corner of the plate, so the sequence
   is legible from across the room and the category can go back to being a
   small chip instead of competing with the cost line. */
.plate { position: relative; }
.plate-num {
  position: absolute;
  top: calc(var(--space-4) * -1); left: calc(var(--space-3) * -1);
  z-index: 2;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.5rem, 3.6vw, 3.75rem);
  line-height: 1;
  letter-spacing: -0.05em;
  /* Filled, not outlined. A hairline outline at this size is a shape you
     decode rather than a number you read. */
  color: color-mix(in srgb, var(--brand-dark) 22%, transparent);
  transition: color 520ms var(--ease-out);
  pointer-events: none;
}
.plate-cat {
  justify-self: start;
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--brand-dark) 74%, transparent);
  padding: 4px var(--space-3) 3px;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 20%, transparent);
  border-radius: var(--radius-round);
}
/* The cost leads, not the category. Naming where it lands is a taxonomy;
   naming what it takes is the point of the section. */
.plate-cost {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--step-3);
  letter-spacing: -0.026em;
  line-height: 1.12;
  color: var(--brand-dark);
}
.plate-body {
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--ink-muted);
}

@media (min-width: 901px) {
  .hrail { position: relative; }
  .hrail-vp {
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100vh;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    overflow: hidden;
  }
  .hrail.is-pinned .hrail-vp { position: fixed; top: 0; left: 0; }
  .hrail.is-past  .hrail-vp { top: auto; bottom: 0; }
  /* Clear of the bar. The viewport is pinned to the top of the window while
     the rail runs, so the head started underneath the chrome and the label
     was reading through it. */
  .exposure .hrail-vp .exposure-head { padding-top: clamp(5rem, 10vh, 6.5rem); padding-bottom: 0; }
  /* Full size, like every other section head. It was shrunk to buy height
     inside the pinned viewport; the plates turn out to have the room. */
  .exposure .hrail-vp .exposure-lede { max-width: 56ch; }
  .hrail-scroller { min-height: 0; }
  /* The plates start close under the standfirst rather than floating in the
     middle of the remaining height. */
  .hrail-scroller { display: flex; align-items: center; overflow: hidden; padding-top: clamp(var(--space-4), 2vh, var(--space-6)); }
  .hrail-track {
    display: flex;
    align-items: center;
    gap: clamp(var(--space-6), 3vw, var(--space-8));
    padding-inline: var(--gutter);
    will-change: transform;
  }
  /* Staggered, so the row reads as a set of plates laid out rather than a
     filmstrip. The drop is per-plate and comes from the markup. */
  .plates-rail.hrail-track .plate {
    flex: 0 0 min(50vw, 540px);
    transform: translateY(var(--drop, 0));
    padding: var(--space-5);
    border-radius: var(--radius-media);
    background: transparent;
    color: var(--brand-dark);
    transition: background 520ms var(--ease-out),
                color 520ms var(--ease-out),
                transform 620ms var(--ease-out);
  }
  /* The plate nearest the middle of the screen takes the burgundy, tilts a
     degree and pushes its artwork in — one card carrying the attention rather
     than six competing for it. */
  .plates-rail .plate.is-active {
    background: var(--brand-dark);
    color: var(--brand-light);
    transform: translateY(calc(var(--drop, 0px) - 10px)) rotate(-0.7deg);
  }
  /* A live plate. The artwork is a field of lines, so it undulates rather
     than simply scaling: the same drawing, drifting. */
  .plates-rail .plate.is-active .plate-art {
    animation: plate-punch 460ms var(--ease-out) forwards,
               plate-wave 6.5s ease-in-out 460ms infinite,
               plate-drift 11s ease-in-out infinite;
  }
  /* The push-in lands in under half a second; the undulation takes over from
     where it stops. Scaling slowly from rest read as the picture drifting
     rather than the card coming forward. */
  @keyframes plate-punch {
    from { transform: scale(1) rotate(0deg); }
    to   { transform: scale(1.12) rotate(0.7deg); }
  }
  @keyframes plate-wave {
    0%, 100% { transform: scale(1.12) rotate(0.7deg) skewY(0deg); }
    50%      { transform: scale(1.18) rotate(-0.6deg) skewY(1.1deg); }
  }
  /* The drawing itself, not the frame around it. Resizing and shifting the
     mask box slides the line field against its own sampling grid, so the
     lines ripple rather than the picture simply growing. */
  @keyframes plate-drift {
    0%, 100% { -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
               -webkit-mask-position: 50% 50%; mask-position: 50% 50%; }
    35%      { -webkit-mask-size: 112% 104%; mask-size: 112% 104%;
               -webkit-mask-position: 58% 44%; mask-position: 58% 44%; }
    70%      { -webkit-mask-size: 105% 112%; mask-size: 105% 112%;
               -webkit-mask-position: 42% 57%; mask-position: 42% 57%; }
  }
  .plates-rail .plate.is-active .plate-cat {
    color: var(--brand-light);
    border-color: color-mix(in srgb, var(--brand-light) 34%, transparent);
  }
  .plates-rail .plate.is-active .plate-num {
    color: color-mix(in srgb, var(--brand-light) 42%, transparent);
  }
  .plates-rail .plate.is-active .plate-cost { color: var(--brand-light); }
  .plates-rail .plate.is-active .plate-body { color: var(--on-dark); }

  .hrail-meter {
    position: relative;
    height: 1px;
    width: calc(100% - var(--gutter) * 2);
    margin: 0 auto clamp(var(--space-5), 4vh, var(--space-7));
    background: color-mix(in srgb, var(--brand-dark) 12%, transparent);
  }
  .hrail-meter > i {
    position: absolute; inset: 0 auto 0 0;
    width: calc(100% * var(--hp, 0));
    background: var(--brand-dark);
  }
}

@media (prefers-reduced-motion: reduce) {
  .plate, .plate-art { transition: none; }
  .plates-rail .plate.is-active .plate-art { animation: none; transform: scale(1.1); }
}

@media (max-width: 900px) {
  .exposure-head { padding-inline: var(--gutter); }
  .hrail-meter { display: none; }
  .plates-rail {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-8);
    padding: var(--space-7) var(--gutter) var(--section-y);
  }
  .plate { transform: none !important; }
}

/* ============================================================
   WHERE WE SIT — three lanes, not a table
   ============================================================ */
/* A comparison table would have put three firms in a scoring grid, which is
   both a fight we do not want with the advisors who refer us and the least
   readable object on a phone. Three lanes say the same thing by shape: each
   one covers a stretch of the same problem, and only one of them runs the
   whole length. */
.lanes-head { max-width: 64ch; margin-bottom: var(--space-8); }
.lanes-head .label { margin-bottom: var(--space-4); }
.lanes-head .h2 { margin-bottom: var(--space-5); color: var(--brand-dark); }
.lanes-lede {
  font-size: var(--step-1);
  line-height: var(--lh-loose);
  color: var(--ink-muted);
  max-width: 60ch;
}

.lanes {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
}
.lane {
  position: relative;
  display: grid;
  align-content: start;
  gap: var(--space-5);
  padding: var(--space-6) var(--space-6) var(--space-6) calc(var(--space-6) * 2);
  border-radius: var(--radius-md);
  border: 1px solid var(--divider);
  background: color-mix(in srgb, var(--brand-light) 10%, var(--page-base));
}
.lane-us {
  border-color: color-mix(in srgb, var(--brand-dark) 34%, transparent);
  background: color-mix(in srgb, var(--brand-light) 24%, var(--page-base));
}
.lane-head { display: grid; gap: var(--space-2); }
.lane-name {
  font-size: var(--step-3);
  font-weight: 500;
  letter-spacing: -0.024em;
  line-height: 1.05;
  color: var(--brand-dark);
}
.lane-verb {
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-muted);
}
.lane-verb em { font-style: normal; color: var(--brand-dark); }

/* The comparison is a race, not a table. A track runs down each lane and
   fills as the section passes the viewport; PR's head stalls where the desk
   stops, law's where the jurisdiction does, and only one of them reaches the
   verdict at the bottom. The reach figures are a claim about span, not a
   score. */
.lane-track {
  position: absolute;
  left: var(--space-6);
  top: calc(var(--space-6) + 4.6rem);
  bottom: var(--space-6);
  width: 2px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--brand-dark) 11%, transparent);
  overflow: visible;
}
.lane-fill {
  position: absolute;
  inset: 0 0 auto;
  width: 100%;
  border-radius: inherit;
  background: color-mix(in srgb, var(--brand-dark) 40%, transparent);
  height: min(calc(var(--p, 0) * 118%), var(--reach, 100%));
}
.lane-dot {
  position: absolute;
  left: 50%;
  top: min(calc(var(--p, 0) * 118%), var(--reach, 100%));
  width: 9px; height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: var(--brand-dark);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand-dark) 12%, transparent);
}
.lane-us .lane-fill { background: var(--brand-dark); }
.lane-us .lane-dot { box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-dark) 18%, transparent); }
/* Only the lane that ran the full length gets its head to breathe; the two
   that stopped read as stopped. */
.lane-us.is-full .lane-dot { animation: lane-pulse 2.4s ease-in-out infinite; }
@keyframes lane-pulse {
  0%, 100% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-dark) 18%, transparent); }
  50%      { box-shadow: 0 0 0 10px color-mix(in srgb, var(--brand-dark) 5%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .lane-fill, .lane-dot { transition: none; }
  .lane-us.is-full .lane-dot { animation: none; }
}

.lane-spec { display: grid; gap: var(--space-4); margin: 0; }
.lane-spec > div {
  display: grid; gap: 4px;
  padding-top: var(--space-4);
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
}
.lane-spec dt {
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  /* 55% measured 3.11:1 against the lighter of the three lane grounds. */
  color: color-mix(in srgb, var(--brand-dark) 80%, transparent);
}
.lane-spec dd {
  margin: 0;
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--ink-muted);
}
@media (max-width: 900px) {
  .lanes { grid-template-columns: 1fr; gap: var(--space-4); }
  .lane { padding: var(--space-5) var(--space-5) var(--space-5) calc(var(--space-5) * 2); }
  .lane-track { left: var(--space-5); top: calc(var(--space-5) + 4.4rem); bottom: var(--space-5); }
}

/* ============================================================
   WHO OPENS THE DOOR — the advisor plates
   ============================================================ */
.adv-head {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--space-7) var(--space-8);
  align-items: end;
  margin-bottom: var(--space-8);
}
.adv-head .label { margin-bottom: var(--space-4); }
.adv-lede {
  font-size: var(--step-0);
  line-height: var(--lh-loose);
  color: var(--on-dark-muted);
}

/* Nameplates, not cards: a thin rule that extends on approach, the way a
   plaque catches the light as you walk past it. */
.plates {
  list-style: none; margin: 0; padding-inline: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5);
}
.plate {
  display: grid;
  align-content: start;
  gap: var(--space-3);
  padding-top: var(--space-4);
  position: relative;
}
.plate::before {
  content: '';
  position: absolute; inset: 0 0 auto;
  height: 1px;
  background: var(--divider-on-dark);
}
/* .plate-rule and .plate-name belonged to a nameplate component that is no
   longer in the markup, and the .plate-body colour with them: sand, meant
   for a card that used to be burgundy by default. Left unscoped it was
   painting the exposure rail's body copy sand on a sand band, 1.86:1, on
   every plate that was not the lit one. */

.adv-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5) var(--space-7);
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: 1px solid var(--divider-on-dark);
}
.adv-terms {
  list-style: none; margin: 0; padding-inline: 0;
  display: flex; flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
}
.adv-terms li {
  display: grid;
  grid-template-columns: 13px minmax(0, max-content);
  column-gap: 9px;
  grid-template-rows: 20px 20px;
  row-gap: 4px;
  align-items: center;
}
.adv-terms li::before {
  content: '';
  grid-column: 1; grid-row: 1;
  width: 11px; height: 6px;
  margin-top: 5px;
  border-left: 1.5px solid color-mix(in srgb, var(--brand-light) 62%, transparent);
  border-bottom: 1.5px solid color-mix(in srgb, var(--brand-light) 62%, transparent);
  transform: rotate(-45deg);
}
.adv-terms b {
  grid-column: 2; grid-row: 1;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--step--1);
  line-height: 1.2;
  letter-spacing: -0.006em;
  color: var(--brand-light);
}
.adv-terms span {
  grid-column: 2; grid-row: 2;
  font-family: var(--font-label);
  font-size: 0.75rem;
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: var(--on-dark-muted);
}
.adv-cta {
  flex: none;
  min-height: 62px;
  min-width: 320px;
  justify-content: space-between;
  padding-inline: var(--space-5);
}
.adv-cta .btn-text { display: grid; gap: 2px; text-align: left; }
.adv-cta .arrow { margin-left: var(--space-5); }
.adv-cta .btn-sub {
  font-family: var(--font-label);
  font-size: 0.625rem;
  letter-spacing: 0.02em;
  line-height: 1.45;
}

@media (max-width: 1100px) { .plates { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 860px) {
  .adv-head { grid-template-columns: 1fr; align-items: start; gap: var(--space-5); }
}
@media (max-width: 560px) {
  .plates { grid-template-columns: 1fr; gap: var(--space-4); }
  .adv-foot { flex-direction: column; align-items: stretch; }
  .adv-cta { width: 100%; justify-content: space-between; }
}

/* ---------- The Shift: three figures ----------
   Every number here is structural rather than surveyed: how many results a
   buyer used to weigh, how much an engine reports back, how many positions
   an answer has. Nothing that needs a footnote, and nothing that stops being
   true when someone reruns the study. */
.shift-figs {
  list-style: none; margin: 0 0 calc(var(--section-y) * 0.9); padding-inline: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6) var(--space-8);
  padding-top: var(--space-6);
  border-top: 1px solid var(--divider-on-dark);
}
.shift-fig { display: grid; align-content: start; gap: var(--space-3); justify-items: start; }
/* A chip rather than a floating caption. The figure on its own made you read
   the sentence underneath to find out what it was counting; the chip answers
   that before your eye gets to the number. */
.shift-chip {
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--brand-light);
  padding: 5px var(--space-3) 4px;
  border: 1px solid color-mix(in srgb, var(--brand-light) 30%, transparent);
  border-radius: var(--radius-round);
  background: color-mix(in srgb, var(--brand-light) 9%, transparent);
}
/* The one-line reading of the figure, so the block can be taken in at a
   glance and the sentence below is there only if you want the why. */
.shift-lead {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--step-1);
  letter-spacing: -0.014em;
  line-height: 1.25;
  color: var(--brand-light);
}
.shift-num {
  display: flex; align-items: baseline; gap: 0.18em;
  position: relative;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.75rem, 1.6rem + 4.6vw, 5rem);
  line-height: 0.92;
  letter-spacing: -0.045em;
  color: var(--brand-light);
}
.shift-num em {
  font-style: normal;
  font-size: 0.42em;
  letter-spacing: 0;
  transform: translateY(-0.34em);
  color: color-mix(in srgb, var(--brand-light) 55%, transparent);
}
/* The rule is drawn rather than set as a strikethrough: text-decoration would
   have arrived with the glyph, and the point of this one is that it lands. */
.shift-num-struck > span { position: relative; }
.shift-num-struck > i {
  position: absolute;
  left: -0.12em; top: 52%;
  width: 0.86em; height: 2px;
  transform-origin: left center;
  transform: scaleX(0) rotate(-8deg);
  background: var(--accent-on-dark);
  transition: transform 620ms var(--ease-out) 320ms;
}
.shift-figs.is-in .shift-num-struck > i { transform: scaleX(1) rotate(-8deg); }
@media (prefers-reduced-motion: reduce) {
  .shift-num-struck > i { transition: none; transform: scaleX(1) rotate(-8deg); }
}
.shift-cap {
  margin-top: calc(var(--space-3) * -1 + 2px);
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--on-dark-muted);
  max-width: 34ch;
}
@media (max-width: 860px) {
  .shift-figs { grid-template-columns: 1fr; gap: var(--space-5); }
  .shift-cap { max-width: none; }
}

/* ---------- Contact head: the in/visible line ----------
   The joke in the phrase is that both readings are true, so the page shows
   both: "in" and its slash fade almost out and come back, and the line reads
   alternately as the invisible seeds and the visible ones. It runs slowly on
   purpose — anything faster and it is a blinking word beside a form. */
.gi-head { position: relative; isolation: isolate; }
.gi-label { margin-bottom: var(--space-4); }
/* A shop sign with a failing tube: it stutters, gives up, sits dark for a
   beat and comes back. Nothing eases — that is the whole difference between a
   sign going out and a word fading. */
.io { animation: io-flicker 9s steps(1, end) infinite 1.2s; }
/* Landing on the section runs a quicker pass first, so the joke lands while
   you are still reading the line rather than eight seconds later. */
.gi-head.is-in .io { animation-duration: 5.4s; animation-delay: 0.35s; }
@keyframes io-flicker {
  0%,  36%  { opacity: 1; }
  37%       { opacity: 0.14; }
  38%       { opacity: 1; }
  39%       { opacity: 0.14; }
  41%       { opacity: 0.85; }
  42%, 74%  { opacity: 0.12; }
  75%       { opacity: 0.55; }
  76%       { opacity: 0.12; }
  78%       { opacity: 1; }
  79%       { opacity: 0.3; }
  81%, 100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .io { animation: none; } }

.gi-note {
  margin-top: var(--space-5);
  max-width: 54ch;
  font-size: var(--step-0);
  line-height: var(--lh-loose);
  color: var(--on-dark-muted);
}

/* Seeds, going up rather than falling: the section is the one place on the
   page where something is being started rather than defended. */
.seedfield {
  position: absolute;
  inset: -8% -2% -14%;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.seedfield > i {
  position: absolute;
  left: var(--x);
  bottom: -8px;
  width: var(--s); height: var(--s);
  border-radius: 50%;
  background: var(--brand-light);
  opacity: 0;
  animation: seed-rise var(--d) linear var(--t) infinite;
}
/* Travel is a per-seed pixel distance, not a percentage: a percentage on a
   3px dot is 3px of movement. */
@keyframes seed-rise {
  0%   { transform: translate(0, 0); opacity: 0; }
  12%  { opacity: 0.42; }
  76%  { opacity: 0.26; }
  100% { transform: translate(var(--dx), calc(var(--r) * -1)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .seedfield { display: none; }
}

/* ---------- Leadership: three roles ----------
   Three burgundy cards sat as a wall between the heading and the two
   portraits, and the section read as two unrelated blocks. It is a spine now:
   the roles hang off one line that carries on down past the last of them and
   into the people below, so "three roles" and "two principals" are visibly
   the same thought. */
.pedigree-wrap { position: relative; }
.pedigree-label { margin-bottom: var(--space-6); }
.pedigree {
  position: relative;
  list-style: none;
  /* No bottom margin: the handoff picks the line straight up from where the
     spine stops, and a gap between the two read as the line breaking. */
  margin: 0;
  padding: 0 0 0 var(--space-7);
}
/* The spine itself, drawn down as the block arrives and running on past the
   last row into the gap above the portraits. */
.pedigree::before {
  content: '';
  position: absolute;
  left: 0; top: 6px; bottom: 0;
  width: 1px;
  background: color-mix(in srgb, var(--brand-dark) 22%, transparent);
  transform-origin: top;
  transform: scaleY(0);
  transition: transform 1.4s var(--ease-out);
}
.pedigree.is-in::before { transform: scaleY(1); }

.pedigree-row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 13.5rem) minmax(0, 1fr);
  align-items: baseline;
  gap: var(--space-3) var(--space-6);
  padding-block: var(--space-6);
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
}
.pedigree-row:first-child { border-top: 0; padding-top: 0; }
.pedigree-node {
  position: absolute;
  left: calc(var(--space-7) * -1 - 4.5px);
  top: calc(var(--space-6) + 0.55em);
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--page-base);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--brand-dark) 30%, transparent);
  transition: background 420ms var(--ease-out), box-shadow 420ms var(--ease-out);
}
.pedigree-row:first-child .pedigree-node { top: 0.55em; }
/* The node fill belonged to a class the observer puts on the list, not the
   row, so it never matched. The spine sequence below owns it now, with the
   list-level class left as the no-JS fallback. */
.pedigree.is-in .pedigree-node,
.pedigree-row.is-lit .pedigree-node {
  background: var(--brand-dark);
  box-shadow: inset 0 0 0 1.5px var(--brand-dark);
}
.pedigree-index {
  grid-column: 1; grid-row: 1;
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  color: color-mix(in srgb, var(--brand-dark) 42%, transparent);
}
.pedigree-name {
  grid-column: 1; grid-row: 2;
  font-size: clamp(1.5rem, 1rem + 1.5vw, 2.375rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--brand-dark);
}
.pedigree-body {
  grid-column: 2; grid-row: 1 / span 2;
  align-self: center;
  max-width: 62ch;
  font-size: var(--step-0);
  line-height: var(--lh-loose);
  color: var(--ink-muted);
}
@media (max-width: 820px) {
  .pedigree { padding-left: var(--space-6); }
  .pedigree-row { grid-template-columns: 1fr; gap: var(--space-3); }
  .pedigree-body { grid-column: 1; grid-row: 3; align-self: start; }
  .pedigree-node { left: calc(var(--space-6) * -1 - 4.5px); }
  }
/* ---------- Leadership: one role at a time, then the people ----------
   Three roles and two portraits arriving together read as a wall of text.
   Scroll now draws the spine, and each role takes full ink as the head
   passes its own node, so the section is read advisors -> protectors ->
   engineers. The portraits are the pay-off: they only rise once the spine
   has run out past the last role. Every one of these rules is gated on
   [data-seq], which only JS sets, so the block renders complete and legible
   with scripting off or motion reduced.
------------------------------------------------------------------- */
.pedigree-wrap[data-seq] .pedigree::before {
  transform: scaleY(var(--p, 0));
  transition: none;
}
/* The head of the charge, riding the spine. */
.pedigree-wrap[data-seq] .pedigree::after {
  content: '';
  position: absolute;
  left: -3.5px;
  top: calc(6px + (100% - 6px) * var(--p, 0));
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--brand-dark);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-dark) 11%, transparent);
  opacity: 0;
  transition: opacity 300ms var(--ease-out);
  pointer-events: none;
}
.pedigree-wrap[data-seq].is-running .pedigree::after { opacity: 1; }

.pedigree-wrap[data-seq] .pedigree-row {
  transition: opacity 560ms var(--ease-out);
}
.pedigree-wrap[data-seq] .pedigree-row:not(.is-lit) { opacity: 0.28; }
.pedigree-wrap[data-seq] .pedigree-name {
  position: relative;
  transition: transform 640ms var(--ease-out);
}
.pedigree-wrap[data-seq] .pedigree-row:not(.is-lit) .pedigree-name {
  transform: translateY(7px);
}
/* A rule drawn under the role as it lights, the same gesture the shift
   figures use, so the three roles read as being ticked off in order. */
.pedigree-wrap[data-seq] .pedigree-name::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -0.22em;
  height: 1px;
  background: color-mix(in srgb, var(--brand-dark) 34%, transparent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 720ms var(--ease-out) 120ms;
}
.pedigree-wrap[data-seq] .pedigree-row.is-lit .pedigree-name::after { transform: scaleX(1); }
.pedigree-wrap[data-seq] .pedigree-body {
  transition: opacity 560ms var(--ease-out) 90ms;
}

/* The hinge is set up further down the sheet, where the whole reveal lives. */

@media (prefers-reduced-motion: reduce) {
  .pedigree::before { transform: scaleY(1); transition: none; }
}

/* ---------- Contact head: the in/visible line ----------
   The joke in the phrase is that both readings are true, so the page shows
   both: "in" and its slash fade almost out and come back, and the line reads
   alternately as the invisible seeds and the visible ones. It runs slowly on
   purpose — anything faster and it is a blinking word beside a form. */
.gi-head { position: relative; isolation: isolate; }
.gi-label { margin-bottom: var(--space-4); }
/* A shop sign with a failing tube: it stutters, gives up, sits dark for a
   beat and comes back. Nothing eases — that is the whole difference between a
   sign going out and a word fading. */
.io { animation: io-flicker 9s steps(1, end) infinite 1.2s; }
/* Landing on the section runs a quicker pass first, so the joke lands while
   you are still reading the line rather than eight seconds later. */
.gi-head.is-in .io { animation-duration: 5.4s; animation-delay: 0.35s; }
@keyframes io-flicker {
  0%,  36%  { opacity: 1; }
  37%       { opacity: 0.14; }
  38%       { opacity: 1; }
  39%       { opacity: 0.14; }
  41%       { opacity: 0.85; }
  42%, 74%  { opacity: 0.12; }
  75%       { opacity: 0.55; }
  76%       { opacity: 0.12; }
  78%       { opacity: 1; }
  79%       { opacity: 0.3; }
  81%, 100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .io { animation: none; } }

.gi-note {
  margin-top: var(--space-5);
  max-width: 54ch;
  font-size: var(--step-0);
  line-height: var(--lh-loose);
  color: var(--on-dark-muted);
}

/* Seeds, going up rather than falling: the section is the one place on the
   page where something is being started rather than defended. */
.seedfield {
  position: absolute;
  inset: -8% -2% -14%;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.seedfield > i {
  position: absolute;
  left: var(--x);
  bottom: -8px;
  width: var(--s); height: var(--s);
  border-radius: 50%;
  background: var(--brand-light);
  opacity: 0;
  animation: seed-rise var(--d) linear var(--t) infinite;
}
/* Travel is a per-seed pixel distance, not a percentage: a percentage on a
   3px dot is 3px of movement. */
@keyframes seed-rise {
  0%   { transform: translate(0, 0); opacity: 0; }
  12%  { opacity: 0.42; }
  76%  { opacity: 0.26; }
  100% { transform: translate(var(--dx), calc(var(--r) * -1)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .seedfield { display: none; }
}

/* ---------- Leadership: three roles ----------
   The two profiles say who; this says what the seat has been. Burgundy cards
   on the page ground rather than another light panel, because it is the one
   block in this section that is about the record instead of the people. */

/* ============================================================
   CALLBACK — the floating line
   ============================================================ */
/* The alternative to a form for people who will not fill one in. It asks for
   one field and gives a reason not to need the others. The button borrows the
   hero eyebrow's expanding rings so the thing hovering over every section
   still reads as part of the same object. */
.callback {
  position: fixed;
  right: var(--gutter);
  bottom: var(--gutter);
  z-index: 90;
  display: grid;
  justify-items: end;
  gap: var(--space-3);
}
.callback-fab {
  position: relative;
  display: grid;
  place-items: center;
  width: 58px; height: 58px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--brand-dark);
  color: var(--brand-light);
  cursor: pointer;
  box-shadow: 0 10px 30px rgba(36, 16, 22, 0.22);
  transition: background var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.callback-fab:hover { background: var(--surface-alt); transform: translateY(-2px); }
.callback-glyph { display: block; width: 23px; height: 23px; transition: opacity var(--dur-fast) var(--ease-out); }
.callback-glyph svg { width: 100%; height: 100%; }
/* Rings on the button, not on the icon: they read as a line ringing rather
   than as a decorative halo. */
.callback-rings { position: absolute; inset: 0; pointer-events: none; }
.callback-rings > i {
  position: absolute; inset: 0;
  border: 1px solid var(--brand-dark);
  border-radius: 50%;
  opacity: 0;
  animation: cb-ring 3.6s cubic-bezier(.22,.7,.35,1) infinite;
}
.callback-rings > i:nth-child(2) { animation-delay: 1.2s; }
.callback-rings > i:nth-child(3) { animation-delay: 2.4s; }
@keyframes cb-ring {
  0%   { transform: scale(1);   opacity: 0; }
  14%  { opacity: 0.5; }
  100% { transform: scale(1.9); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .callback-rings { display: none; } }

/* The close mark is drawn from the button rather than swapped in as a second
   icon, so the state change is one object turning rather than two crossfading. */
.callback-x { position: absolute; width: 16px; height: 16px; opacity: 0; }
.callback-x::before, .callback-x::after {
  content: '';
  position: absolute; left: 0; top: 50%;
  width: 100%; height: 1.5px;
  background: currentColor;
  border-radius: 2px;
}
.callback-x::before { transform: rotate(45deg); }
.callback-x::after  { transform: rotate(-45deg); }
.callback.is-open .callback-glyph { opacity: 0; }
.callback.is-open .callback-x { opacity: 1; }
.callback.is-open .callback-rings { opacity: 0; }

.callback-panel {
  width: min(340px, calc(100vw - var(--gutter) * 2));
  padding: var(--space-5);
  border-radius: var(--radius-media);
  background: var(--surface-alt);
  border: 1px solid var(--divider-on-dark);
  box-shadow: 0 26px 60px rgba(0, 0, 0, .34);
  transform-origin: bottom right;
  animation: cb-in 260ms var(--ease-out);
}
@keyframes cb-in {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .callback-panel { animation: none; } }

.callback-title {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 500;
  letter-spacing: -0.018em;
  color: var(--brand-light);
  margin-bottom: var(--space-2);
}
.callback-lede {
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--on-dark-muted);
  margin-bottom: var(--space-4);
}
.callback-row { margin-bottom: var(--space-3); }
.callback-row .sel-btn, .callback-row input[type='tel'] { min-height: 48px; }
.callback-submit {
  width: 100%;
  justify-content: space-between;
  min-height: 50px;
  font-size: var(--step-0);
  margin-bottom: var(--space-3);
}
.callback-err { margin-bottom: var(--space-3); }
.callback-status {
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--brand-light);
}
.callback-status:empty { display: none; }
.callback-direct {
  font-size: var(--step--1);
  color: var(--on-dark-muted);
  padding-top: var(--space-3);
  border-top: 1px solid var(--divider-on-dark);
}
.callback-direct a { color: var(--brand-light); }
.callback-note {
  margin-top: var(--space-3);
  font-size: 0.6875rem;
  line-height: 1.5;
  color: var(--on-dark-muted);
}

/* The footer trigger is a button, so it needs the anchor's own styling back. */
/* No `font: inherit` here — the global reset already gives buttons the page
   font, and repeating it in a later rule outranked .social's own size, which
   is why this one row sat larger than the two under it. */
.social-btn {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: left;
}

@media (max-width: 560px) {
  .callback-fab { width: 52px; height: 52px; }
  .callback-panel { padding: var(--space-4); }
}

/* Over a burgundy section the burgundy button disappeared. It flips to the
   sand on the same sampling the chrome uses, so the pair never disagree about
   what they are sitting on. */
.callback[data-over-dark='true'] .callback-fab {
  background: var(--brand-light);
  color: var(--brand-dark);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.callback[data-over-dark='true'] .callback-fab:hover { background: #DCC9B8; }
.callback[data-over-dark='true'] .callback-rings > i { border-color: var(--brand-light); }
.callback-fab { transition: background var(--dur-base) var(--ease-out),
                            color var(--dur-base) var(--ease-out),
                            transform var(--dur-base) var(--ease-out); }

/* The panel sits on the same burgundy the section form does, so its controls
   take the same treatment rather than falling back to the browser's. */
.callback-form input[type='tel'] {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-alt);
  border: 1px solid var(--divider-on-dark);
  border-radius: var(--radius-sm);
  color: var(--on-dark);
  font-size: var(--step-0);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.callback-form input[type='tel']::placeholder { color: rgba(185, 161, 143, .55); }
.callback-form input[type='tel']:hover,
.callback-form input[type='tel']:focus-visible { border-color: var(--brand-light); }
.callback-form .sel-btn {
  width: 100%;
  padding: var(--space-3) var(--space-3);
  background: var(--surface-alt);
  border: 1px solid var(--divider-on-dark);
  border-radius: var(--radius-sm);
  color: var(--on-dark);
  font-size: var(--step--1);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.callback-form .sel-btn:hover,
.callback-form .sel.is-open .sel-btn,
.callback-form .sel-btn:focus-visible { border-color: var(--brand-light); }
.callback-form .sel-pop {
  background: var(--brand-dark);
  border-color: var(--divider-on-dark);
}
.callback-form .sel-opt { color: var(--on-dark); }
.callback-form .sel-opt.is-active,
.callback-form .sel-opt[aria-selected='true'] {
  background: color-mix(in srgb, var(--brand-light) 13%, transparent);
}
.callback-form .sel-search { color: var(--on-dark); }
.callback-form .sel-search::placeholder { color: rgba(185, 161, 143, .55); }
.callback-row { grid-template-columns: 118px minmax(0, 1fr); }

/* The disc that carries the arrow, not just the arrow: the button is wide and
   the glyph on its own was too small a target for the eye to track on hover. */
.callback-submit .arrow {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--brand-dark) 12%, transparent);
  transition: background var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.callback-submit:hover { background: #DCC9B8; }
.callback-submit:hover .arrow,
.callback-submit:focus-visible .arrow {
  background: color-mix(in srgb, var(--brand-dark) 22%, transparent);
  transform: translateX(3px);
}
.callback-direct {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--space-2) var(--space-3);
}
.callback-direct a { font-size: var(--step-0); }

/* ============================================================
   SEEDING, IN PRACTICE — two threads
   ============================================================ */
/* The method section says what seeding is. This shows it, in the shape it
   actually happens in: a hostile post, two replies that answer it with things
   somebody can check, and what the thread looked like a week later. */
.seed-head {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--space-7) var(--space-8);
  align-items: end;
  margin-bottom: var(--space-8);
}
.seed-head .label { margin-bottom: var(--space-4); }
.seed-lede {
  font-size: var(--step-0);
  line-height: var(--lh-loose);
  color: var(--on-dark-muted);
}

.threads {
  list-style: none; margin: 0; padding-inline: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
}
.thread {
  display: grid;
  align-content: start;
  gap: var(--space-4);
  padding: var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid var(--divider-on-dark);
  background: color-mix(in srgb, var(--surface-alt) 60%, transparent);
}
.thread-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.thread-src, .thread-kind {
  padding: 4px var(--space-3) 3px;
  border-radius: var(--radius-round);
  border: 1px solid color-mix(in srgb, var(--brand-light) 26%, transparent);
  color: var(--brand-light);
}
.thread-kind {
  border-color: color-mix(in srgb, var(--accent-on-dark) 44%, transparent);
  color: var(--accent-on-dark);
}
.thread-clock { margin-left: auto; color: var(--on-dark-muted); }

/* The hostile post keeps a vote gutter, because that is the part of the
   surface that decides what anyone reads next. */
.thread-post { display: grid; grid-template-columns: 46px minmax(0, 1fr); gap: var(--space-4); }
.thread-vote {
  display: grid; justify-items: center; align-content: start; gap: 4px;
  padding-top: 2px;
  font-family: var(--font-label);
  font-size: 0.6875rem;
  color: var(--accent-on-dark);
}
.tv-up, .tv-down {
  width: 0; height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
}
.tv-up   { border-bottom: 9px solid currentColor; }
.tv-down { border-top: 9px solid currentColor; }
.thread-vote-down { color: var(--on-dark-muted); }
.thread-claim {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--step-1);
  line-height: 1.3;
  letter-spacing: -0.014em;
  color: var(--brand-light);
  margin-bottom: var(--space-2);
}
.thread-meta {
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--on-dark-muted);
}

/* Replies are indented off a rail, the way the surface itself nests them, and
   set in the mono face so they read as somebody else's words rather than as
   more of our copy. */
.replies {
  list-style: none; margin: 0; padding-inline: 0;
  display: grid; gap: var(--space-4);
  padding-left: var(--space-5);
  border-left: 1px solid color-mix(in srgb, var(--brand-light) 20%, transparent);
}
.reply { display: grid; gap: var(--space-2); }
.reply-who {
  display: flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--on-dark-muted);
}
.reply-who > i {
  display: grid; place-items: center;
  width: 22px; height: 22px; flex: none;
  border-radius: 50%;
  font-style: normal;
  font-size: 8.5px;
  letter-spacing: 0.04em;
  color: var(--brand-light);
  background: color-mix(in srgb, var(--brand-light) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--brand-light) 26%, transparent);
}
.reply-body {
  font-family: var(--font-label);
  font-size: 0.75rem;
  line-height: 1.65;
  color: var(--on-dark);
}
/* The replies arrive rather than being there: the whole point is that the
   thread changed after they landed. */
.reply { opacity: 0; transform: translateY(8px); transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out); }
.thread.is-in .reply { opacity: 1; transform: none; }
.thread.is-in .reply:nth-child(1) { transition-delay: 260ms; }
.thread.is-in .reply:nth-child(2) { transition-delay: 620ms; }
@media (prefers-reduced-motion: reduce) {
  .reply { opacity: 1; transform: none; transition: none; }
}

.thread-out {
  list-style: none; margin: 0; padding-inline: 0;
  display: grid; gap: var(--space-2);
  padding-top: var(--space-4);
  border-top: 1px solid var(--divider-on-dark);
}
.thread-out li {
  display: grid;
  grid-template-columns: 13px minmax(0, 1fr);
  column-gap: 9px;
  align-items: start;
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--brand-light);
}
.thread-out li::before {
  content: '';
  width: 11px; height: 6px;
  margin-top: 5px;
  border-left: 1.5px solid color-mix(in srgb, var(--brand-light) 62%, transparent);
  border-bottom: 1.5px solid color-mix(in srgb, var(--brand-light) 62%, transparent);
  transform: rotate(-45deg);
}

.seed-note {
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid var(--divider-on-dark);
  max-width: 78ch;
  font-size: var(--step--1);
  line-height: var(--lh-loose);
  color: var(--on-dark-muted);
}
.seed-note b {
  display: block;
  margin-bottom: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 500;
  letter-spacing: -0.018em;
  line-height: 1.2;
  color: var(--brand-light);
}

@media (max-width: 900px) {
  .seed-head { grid-template-columns: 1fr; align-items: start; gap: var(--space-5); }
  .threads { grid-template-columns: 1fr; gap: var(--space-4); }
}

/* Where we sit gets the same slab as the exposure grid, so the four light
   sections either side of it do not read as one continuous page. */
/* Same box as .wrap, so the tint's edges line up with every other section's
   content column instead of sitting 30px inside it. */
.lanes-sec {
  background: color-mix(in srgb, var(--brand-light) 15%, var(--page-base));
  border-radius: var(--radius-section);
  max-width: var(--wrap-max);
  margin: var(--space-8) auto var(--space-9);
}
.lanes-sec .lane { background: var(--page-base); }
.lanes-sec .lane-us { background: color-mix(in srgb, var(--brand-light) 20%, var(--page-base)); }


/* ---------- Where we sit: guiding the read ----------
   The head travelling down the track passes each row in turn and switches its
   node from hollow to solid, so the eye is walked down the lane rather than
   left to pick a starting point. Nothing is dimmed to do it: unread rows stay
   at full contrast, and only the node and the label's weight change. */
.lane-spec > div, .lane-when { position: relative; }
.lane-spec > div::before, .lane-when::before {
  content: '';
  position: absolute;
  left: calc((var(--space-6) + 3px) * -1);
  top: var(--space-4);
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--page-base);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--brand-dark) 22%, transparent);
  transition: box-shadow 320ms var(--ease-out), background 320ms var(--ease-out);
}
.lane-spec > div.is-passed::before, .lane-when.is-passed::before {
  background: var(--brand-dark);
  box-shadow: inset 0 0 0 1.5px var(--brand-dark);
}
.lane-spec > div.is-passed dt, .lane-when.is-passed b { color: var(--brand-dark); }
@media (max-width: 900px) {
  .lane-spec > div::before, .lane-when::before { left: calc((var(--space-5) + 3px) * -1); }
}


.lane-when {
  padding-top: var(--space-4);
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
  font-size: var(--step--1);
  line-height: var(--lh-body);
  color: var(--ink-muted);
}
.lane-when b {
  display: block;
  margin-bottom: 4px;
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 400;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--brand-dark) 80%, transparent);
}
.lane-us .lane-when b { color: var(--brand-dark); }
/* The verdict is the last thing on the card, so it carries the weight. */

/* ---------- The ethics stamp ----------
   The claim on this section is the one a reader is most entitled to doubt, so
   it is set as a seal rather than a sentence: guilloche teeth, two weights of
   ring and type running the circle, the way a bank stamps something it is
   prepared to stand behind. */
.seed-foot {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr);
  gap: var(--space-6);
  align-items: center;
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid var(--divider-on-dark);
}
.seed-note { margin-top: 0; padding-top: 0; border-top: 0; }
.ethic-stamp {
  position: relative;
  display: block;
  width: 168px; height: 168px;
  color: var(--brand-light);
}
.ethic-stamp svg { width: 100%; height: 100%; display: block; }
.es-ring { fill: none; stroke: currentColor; stroke-width: 1.6; opacity: 0.72; }
.es-ring-thin { stroke-width: 0.7; opacity: 0.5; }
.es-teeth line { stroke: currentColor; stroke-width: 0.7; opacity: 0.34; }
.es-type {
  fill: currentColor;
  font-family: var(--font-label);
  font-size: 11.5px;
  letter-spacing: 2.4px;
  opacity: 0.92;
}
.es-type-sm { font-size: 9.5px; letter-spacing: 2px; opacity: 0.7; }
.ethic-mark {
  position: absolute;
  top: 50%; left: 50%;
  width: 46px; height: 39px;
  transform: translate(-50%, -50%);
  background-color: currentColor;
  opacity: 0.92;
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
}
@media (max-width: 720px) {
  .seed-foot { grid-template-columns: 1fr; justify-items: start; gap: var(--space-5); }
}

/* ---------- Fixes pass ---------- */

/* The callback's submit borrowed the sliding two-arrow mechanism, which needs
   an overflow box the size of one glyph; wrapping it in a disc showed both
   arrows at once. This one shows the literal glyph and moves the disc. */
.callback-submit .arrow::before,
.callback-submit .arrow::after { display: none; }
.callback-submit .arrow {
  font-size: var(--step-0);
  line-height: 1;
  overflow: visible;
}
/* The panel sits on burgundy but outside .on-dark, so the focus ring was
   still coming through in the light-ground bronze. */
.callback :focus-visible { outline-color: var(--brand-light); }
.callback-form input[type='tel']:focus-visible { outline-offset: 1px; }

/* Three glyphs at three different sizes inside three identical discs read as
   three different weights. One size, centred. */
.social-icon svg { width: 15px; height: 15px; }

/* The lane's verdict sits on the floor of the card, so the rule above it
   lands at the same height in all three however long the copy above runs. */
.lane {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  /* Its own stacking context, so the engraved ground can sit behind the copy
     without falling out of the card. */
  isolation: isolate;
  overflow: hidden;
}
.lane-spec { flex: none; }


/* Seeding sits between two burgundy sections. A deeper ground steps it back
   from Proof below it, so the two do not read as one long block. */
.seeding { background: var(--surface-alt); }
.thread { background: color-mix(in srgb, var(--brand-dark) 62%, transparent); }

/* The platform is the fastest thing to recognise in this section, so it is
   shown rather than described. */
.thread-src > i { background-color: currentColor; }
/* Heights are matched by eye, not by number: reddit is a lowercase wordmark
   with a descender and Google is a cap-and-lowercase one, so equal boxes
   would have drawn one noticeably larger than the other. */
.thread-src[data-plat='reddit'] > i {
  width: 43px; height: 13px;
  -webkit-mask: url('../img/orbit/reddit.png') center / contain no-repeat;
          mask: url('../img/orbit/reddit.png') center / contain no-repeat;
}
.thread-src[data-plat='google'] > i {
  width: 38px; height: 12.5px;
  -webkit-mask: url('../img/orbit/google.png') center / contain no-repeat;
          mask: url('../img/orbit/google.png') center / contain no-repeat;
}
.thread-sector {
  padding: 4px var(--space-3) 3px;
  border-radius: var(--radius-round);
  border: 1px dashed color-mix(in srgb, var(--brand-light) 26%, transparent);
  color: var(--on-dark-muted);
}

/* Hairlines between the three conditions: without them the six lines read as
   one paragraph broken into columns. */
/* Every column carries the same inset, and the first pulls itself back out
   to the container edge. Putting the padding only on the divided columns made
   two of the three narrower than the first, which is why the last gloss was
   the one that wrapped. */

/* The mark alone. Setting the name beside its own logo said it twice. The
   chip is sized around the mark rather than around a line of text, so the
   glyph sits on the chip's own optical centre. */
.thread-src {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px var(--space-4) 5px;
  line-height: 1;
}
.thread-src > i { display: block; margin: 0; vertical-align: unset; }

/* ---------- Full-bleed curves ----------
   The light section that curves back over a dark one is itself a .wrap, so on
   anything wider than 1440 its background stopped at the wrap's edge and left
   two strips of burgundy showing past the curve. The colour is painted by a
   viewport-wide layer behind the content instead of by the box itself. */
.on-dark + section:not(.on-dark),
#partners { background: none; }
.on-dark + section:not(.on-dark)::before,
#partners::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  border-radius: var(--radius-section) var(--radius-section) 0 0;
  z-index: -1;
  pointer-events: none;
}
.on-dark + section:not(.on-dark)::before { background: var(--page-base); }
#partners::before { background: var(--chrome-surface); }
/* The exposure band is not a wrap, so it bleeds on its own; it only needs the
   same guard against a sub-pixel gap at the edges. */
.exposure { width: 100%; }

/* ---------- Method blurb ---------- */
/* Three coined words with a trademark on them need one sentence, or the
   reader has to infer all three from the cards below. */
.svc-blurb {
  margin-top: var(--space-4);
  max-width: 74ch;
  font-size: var(--step-1);
  line-height: var(--lh-loose);
  color: var(--ink-muted);
}
.svc-intro { margin-bottom: var(--space-7); }

/* ---------- Seeding lede ----------
   It was a paragraph floating beside a heading with nothing holding it. The
   same rule the hero standfirst uses, so the two read as the same kind of
   object. */
.seed-lede {
  border-left: 4px solid var(--brand-light);
  padding-left: var(--space-5);
}
.seed-lede b, .seed-lede strong { color: var(--brand-light); font-weight: 500; }

/* Seeding hands straight over to Proof, and both are dark. Without pulling
   the second up under the first, the page ground showed through either side
   of the incoming curve as two white notches. Same move the press band makes
   under the problem panel. */
#seeding > .section { padding-bottom: calc(var(--section-y) * 0.9 + var(--radius-section)); }
#proof {
  margin-top: calc(-1 * var(--radius-section));
  padding-top: var(--radius-section);
  position: relative;
  z-index: 2;
}

/* The verdicts are a matched set, so they are cut to hold one line each at
   the card's own width rather than being allowed to wrap unevenly. */


/* Optical parity, not box parity: the Phosphor phone fills far more of its
   viewBox than the LinkedIn mark does, so equal boxes drew it heavier. */
.social-btn .social-icon svg { width: 13px; height: 13px; }
.social-icon svg { width: 15px; height: 15px; }

/* The seeds were almost subliminal. Enough to notice now, still not enough to
   compete with the heading in front of them. */
@keyframes seed-rise {
  0%   { transform: translate(0, 0); opacity: 0; }
  10%  { opacity: 0.62; }
  74%  { opacity: 0.42; }
  100% { transform: translate(var(--dx), calc(var(--r) * -1)); opacity: 0; }
}
.seedfield > i { box-shadow: 0 0 6px color-mix(in srgb, var(--brand-light) 40%, transparent); }

/* Each condition is one line of term and one line of gloss. At the previous
   size two of the three terms wrapped, so the block ran to five lines in three
   different shapes. */

/* ============================================================
   AGENCIES AND ADVISORS — the hub
   ============================================================ */
/* The section had been a grid of eight nameplates, and scrolling past it
   nobody could tell it was about referrals. It is a hub now: the firms sit on
   a ring around the house mark, and taking one writes what we carry for that
   firm into the middle. The detail lands in the centre rather than beside
   each node, which is the only place on a radial layout with room for a
   sentence. */
.hub {
  position: relative;
  width: 100%;
  max-width: 780px;
  margin: var(--space-7) auto 0;
  aspect-ratio: 1;
}
.hub-web { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.hub-orbit {
  fill: none;
  stroke: var(--divider-on-dark);
  stroke-width: 0.22;
  stroke-dasharray: 1.2 1.6;
}
.hub-spoke {
  stroke: color-mix(in srgb, var(--brand-light) 22%, transparent);
  stroke-width: 0.28;
  transition: stroke 260ms var(--ease-out), stroke-width 260ms var(--ease-out);
}
.hub-spoke.is-lit { stroke: var(--brand-light); stroke-width: 0.6; }

.hub-core {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 47%; aspect-ratio: 1;
  display: grid;
  place-items: center;
  padding: 9%;
  border-radius: 50%;
  border: 1px solid var(--divider-on-dark);
  background: color-mix(in srgb, var(--brand-dark) 70%, transparent);
  isolation: isolate;
  overflow: hidden;
}
/* The same echo the Mosaek lane carries, so the two places the house mark
   appears large are recognisably one motif. */
.hub-core-mark { position: absolute; inset: 0; z-index: -1; color: var(--brand-light); }
.hub-core-mark > i {
  position: absolute; top: 50%; left: 50%;
  width: 34%; aspect-ratio: 32 / 27;
  transform: translate(-50%, -50%) scale(var(--k));
  background-color: currentColor;
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
  animation: hub-pulse 9s ease-in-out infinite;
}
.hub-core-mark > i:nth-child(1) { --k: 1;    opacity: .3;  animation-delay: 0s; }
.hub-core-mark > i:nth-child(2) { --k: 1.5;  opacity: .22; animation-delay: -1.5s; }
.hub-core-mark > i:nth-child(3) { --k: 2;    opacity: .16; animation-delay: -3s; }
.hub-core-mark > i:nth-child(4) { --k: 2.5;  opacity: .11; animation-delay: -4.5s; }
.hub-core-mark > i:nth-child(5) { --k: 3;    opacity: .07; animation-delay: -6s; }
.hub-core-mark > i:nth-child(6) { --k: 3.5;  opacity: .04; animation-delay: -7.5s; }
@keyframes hub-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(var(--k)); }
  50%      { transform: translate(-50%, -50%) scale(calc(var(--k) * 1.1)); }
}
@media (prefers-reduced-motion: reduce) { .hub-core-mark > i { animation: none; } }


.hub-ring { list-style: none; margin: 0; padding: 0; }
/* Each node is the whole square, rotated: a percentage translate resolves
   against the element's own box, so a zero-sized node translated by 46% moved
   nothing at all and all eight stacked on the centre. Rotating a full-size
   layer and pinning the pill to its top edge puts the pill on the ring with a
   percentage that has something to measure. */
.hub-node {
  position: absolute;
  inset: 0;
  transform: rotate(var(--a));
  pointer-events: none;
}
.hub-pin {
  position: absolute;
  top: 1.5%; left: 50%;
  transform: translateX(-50%) rotate(calc(var(--a) * -1));
  pointer-events: auto;
  display: grid;
  justify-items: center;
}
.hub-dot {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  white-space: nowrap;
  padding: 8px var(--space-3) 7px;
  border-radius: var(--radius-round);
  border: 1px solid var(--divider-on-dark);
  background: var(--brand-dark);
  color: var(--on-dark);
  font-family: var(--font-label);
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 260ms var(--ease-out), color 260ms var(--ease-out),
              border-color 260ms var(--ease-out), transform 260ms var(--ease-out);
}
/* On a phone the ring becomes a stack and each dot is a full-width tap
   target, so its label stops being a tick on a diagram and starts being the
   thing you read before you press it. */
@media (max-width: 759px) {
  .hub-dot { font-size: 10.5px; }
}
.hub-dot:hover, .hub-dot:focus-visible, .hub-dot.is-on {
  background: var(--brand-light);
  color: var(--brand-dark);
  border-color: var(--brand-light);
  transform: scale(1.04);
}
/* The line comes out of the chip it belongs to, as a card. Nodes on the lower
   half open upward so the card never falls off the bottom of the ring. */
.hub-line {
  position: absolute;
  left: 50%;
  width: 250px;
  margin: 0;
  padding: var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--divider-on-dark);
  background: var(--surface-alt);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .34);
  font-family: var(--font-body);
  font-size: var(--step--1);
  line-height: var(--lh-body);
  letter-spacing: var(--track-normal);
  text-transform: none;
  color: var(--on-dark);
  opacity: 0;
  visibility: hidden;
  z-index: 4;
  transition: opacity 220ms var(--ease-out), transform 260ms var(--ease-out),
              visibility 0s linear 260ms;
}
.hub-node[data-half='down'] .hub-line {
  top: calc(100% + 10px);
  transform: translate(-50%, -6px);
}
.hub-node[data-half='up'] .hub-line {
  bottom: calc(100% + 10px);
  transform: translate(-50%, 6px);
}
.hub-node.is-open .hub-line {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition-delay: 0s;
}
.hub-node.is-open { z-index: 5; }

.hub-plus {
  position: relative;
  width: 9px; height: 9px; flex: none;
}
.hub-plus::before, .hub-plus::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  background: currentColor;
  transform: translate(-50%, -50%);
}
.hub-plus::before { width: 9px; height: 1.4px; }
.hub-plus::after  { width: 1.4px; height: 9px; transition: transform 260ms var(--ease-out); }
.hub-dot.is-on .hub-plus::after { transform: translate(-50%, -50%) rotate(90deg); }

@media (max-width: 759px) {
  .hub { aspect-ratio: auto; max-width: none; margin-top: var(--space-6); }
  .hub-web, .hub-core { display: none; }
  .hub-ring { display: grid; gap: var(--space-4); }
  .hub-node { position: static; transform: none; pointer-events: auto; }
  .hub-pin { position: static; transform: none; justify-items: stretch; }
  .hub-dot { width: 100%; justify-content: space-between; }
  /* Through the attribute, because the open/closed cards above are keyed on
     .hub-node[data-half] .hub-line and a bare .hub-line loses to them: the
     card stayed translated -50% of its own width and walked off the left of
     the screen while the chips looked perfectly correct. */
  .hub-node[data-half] .hub-line,
  .hub-line {
    position: static;
    width: auto;
    opacity: 1;
    visibility: visible;
    transform: none;
    inset: auto;
    margin-top: var(--space-2);
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0 0 0 var(--space-4);
    color: var(--on-dark-muted);
  }
}

/* The ring pushed out and the core pulled in, so a pill never sits on the
   core's edge; and a scrim under the readout, because the echo behind it is
   busiest exactly where the sentence goes. */
.hub-core { width: 42%; }
.hub-core::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    color-mix(in srgb, var(--brand-dark) 92%, transparent) 0%,
    color-mix(in srgb, var(--brand-dark) 72%, transparent) 58%,
    transparent 100%);
  z-index: -1;
}

/* Once the rail has run its length the meter has nothing left to report, so
   it goes rather than sitting as a rule above the next section. */
.hrail-meter { transition: opacity 320ms var(--ease-out); }
.hrail.is-past .hrail-meter { opacity: 0; }

/* ---------- The standfirst highlighter ----------
   Five phrases, marked one after another the way somebody reads with a pen in
   their hand rather than all lighting at once. The stroke is drawn as a
   background that grows from the left, so it looks laid down instead of
   switched on. */
.hero-intro-body .hl {
  background: linear-gradient(color-mix(in srgb, var(--brand-light) 62%, transparent) 0 0) no-repeat;
  background-size: 0% 62%;
  background-position: 0 78%;
  color: inherit;
  padding-inline: 1px;
  border-radius: 1px;
}
.hero-intro-copy.is-in .hl {
  animation: hl-draw 620ms var(--ease-out) forwards;
  animation-delay: var(--d, 0ms);
}
@keyframes hl-draw { to { background-size: 100% 62%; } }
@media (prefers-reduced-motion: reduce) {
  .hero-intro-body .hl { background-size: 100% 62%; animation: none; }
}

/* ---------- The orbit's pulse ----------
   One slow wave leaving the globe and passing out through all three rings,
   every eleven seconds. A constant burst would be wallpaper inside four; this
   one is only there when you happen to be looking. */
.orbit-burst {
  position: absolute;
  bottom: var(--orbit-reveal); left: 50%;
  width: var(--ring-3); height: var(--ring-3);
  transform: translate(-50%, 50%);
  pointer-events: none;
  z-index: 0;
}
.orbit-burst > i {
  position: absolute; inset: 0;
  border-radius: 50%;
  border: 1px solid var(--brand-dark);
  opacity: 0;
  animation: orbit-burst 11s cubic-bezier(.16,.72,.3,1) infinite;
}
.orbit-burst > i:nth-child(2) { animation-delay: 0.9s; }
.orbit-burst > i:nth-child(3) { animation-delay: 1.8s; }
@keyframes orbit-burst {
  0%   { transform: scale(0.16); opacity: 0; }
  6%   { opacity: 0.26; }
  46%  { opacity: 0.05; }
  55%, 100% { transform: scale(1.04); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .orbit-burst { display: none; } }

/* ---------- Landing ----------
   The headline held a whole screen on its own and the rings only arrived on
   the second. Less air above, and a wider ring set that runs off both edges,
   so the first thing you see is a system rather than a sentence. */
@media (min-width: 901px) {
  /* Only the air above the headline. The ring set is left alone: --mark-u is
     calibrated as ring-3 / 688, so widening the rings without recalibrating
     every chip pulls the globe and the marks apart. */
  .hero { padding-block: clamp(7rem, 5rem + 4.6vw, 9.5rem) clamp(4.5rem, 3.4rem + 3.4vw, 7rem); }
}

/* A caret sitting at the end of the line while it is being written. */
.reply-body { min-height: 1.65em; }
.reply-body.is-typing::after {
  content: '';
  display: inline-block;
  width: 0.5em; height: 0.95em;
  margin-left: 2px;
  vertical-align: -0.14em;
  background: currentColor;
  animation: reply-caret 900ms steps(1) infinite;
}
@keyframes reply-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* Profile marks rather than initials in a disc. Geometric and faceless on
   purpose: these stand in for real accounts that are not ours to show, so the
   mark says "a person" without inventing a likeness. */
.reply-av {
  width: 30px; height: 30px; flex: none;
  border-radius: 50%;
  display: block;
}
.reply-who { gap: var(--space-3); }

/* ---------- Hub, second pass ----------
   The readout was inside the core, where a pill on the ring could land on top
   of it and the echo behind it made the sentence hard to hold. It sits under
   the ring now, on its own line, where there is room for it. The core keeps
   the mark, and the mark turns: taking a firm advances it, so the middle
   visibly answers the node you touched. */
.hub { max-width: 680px; }
.hub-core { width: 30%; }
.hub-core::before { display: none; }
.hub-core-mark {
  transition: transform 900ms cubic-bezier(.22,.7,.25,1);
  transform: rotate(var(--spin, 0deg));
}
.hub-core-mark > i { opacity: calc(var(--o, 1) * 1); }


/* A drawn edge on every plate. Without one they were paragraphs sharing a
   ground, and only the lit plate looked like a card at all. */
@media (min-width: 901px) {
  .plates-rail.hrail-track .plate {
    border: 1px solid color-mix(in srgb, var(--brand-dark) 15%, transparent);
    transition: background 520ms var(--ease-out), color 520ms var(--ease-out),
                border-color 520ms var(--ease-out), transform 620ms var(--ease-out);
  }
  .plates-rail .plate.is-active { border-color: var(--brand-dark); }
}

/* Room under the last plate before the next section's curve arrives. */
@media (min-width: 901px) {
  .exposure { padding-bottom: clamp(var(--space-6), 5vh, var(--space-8)); }
}

/* ---------- Capability cards: the outcome first ---------- */
/* Seven cards is a lot to read in order. The chip says what you get before
   the name says what it is called, so a fast scroll still lands. */
.cap-chip {
  display: inline-block;
  margin-bottom: var(--space-3);
  padding: 4px var(--space-3) 3px;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 22%, transparent);
  border-radius: var(--radius-round);
  background: color-mix(in srgb, var(--brand-dark) 5%, transparent);
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--brand-dark) 82%, transparent);
}


/* ---------- Introduce a client, on one line ---------- */
.adv-cta .btn-text {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: nowrap;
}
.adv-cta .btn-sub { white-space: nowrap; }
.adv-cta .btn-sub::before {
  content: '·';
  margin-right: var(--space-3);
  opacity: 0.55;
}
.adv-cta { min-width: 0; }
@media (max-width: 760px) {
  .adv-cta .btn-text { flex-direction: column; gap: 2px; }
  .adv-cta .btn-sub { white-space: normal; }
  .adv-cta .btn-sub::before { content: none; }
}

/* ---------- Where we sit: ours is the answer ----------
   The three were the same card in three tints, so the one that resolves the
   argument had to be found rather than seen. It takes the burgundy, the way
   the lit plate does in What it reaches, and the mark behind it is the full
   lockup rather than the monogram. */
.lanes-sec .lane-us {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
  color: var(--on-dark);
}
.lane-us .lane-name { color: var(--brand-light); }
.lane-us .lane-verb { color: var(--on-dark-muted); }
.lane-us .lane-verb em { color: var(--brand-light); }
.lane-us .lane-spec > div { border-top-color: var(--divider-on-dark); }
.lane-us .lane-spec dt { color: color-mix(in srgb, var(--brand-light) 72%, transparent); }
.lane-us .lane-spec dd { color: var(--on-dark); }
.lane-us .lane-when {
  border-top-color: var(--divider-on-dark);
  color: var(--on-dark);
}
.lane-us .lane-when b { color: var(--brand-light); }
.lane-us .lane-track { background: color-mix(in srgb, var(--brand-light) 18%, transparent); }
.lane-us .lane-fill { background: var(--brand-light); }
.lane-us .lane-dot { background: var(--brand-light); box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-light) 18%, transparent); }
.lane-us .lane-spec > div::before, .lane-us .lane-when::before {
  background: var(--brand-dark);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--brand-light) 34%, transparent);
}
.lane-us .lane-spec > div.is-passed::before, .lane-us .lane-when.is-passed::before {
  background: var(--brand-light);
  box-shadow: inset 0 0 0 1.5px var(--brand-light);
}
.lane-us .lane-spec > div.is-passed dt, .lane-us .lane-when.is-passed b { color: var(--brand-light); }
@keyframes lane-pulse-light {
  0%, 100% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-light) 18%, transparent); }
  50%      { box-shadow: 0 0 0 10px color-mix(in srgb, var(--brand-light) 6%, transparent); }
}
.lane-us.is-full .lane-dot { animation-name: lane-pulse-light; }

/* ---------- Dock: the current stop, and the list of them ---------- */

.dock-subs {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: var(--z-nav);
  display: grid;
  min-width: 13rem;
  padding: 5px;
  border-radius: var(--radius-md);
  background: var(--chrome-surface);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
  box-shadow: 0 16px 34px rgba(36, 16, 22, .16);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 200ms var(--ease-out), transform 220ms var(--ease-out),
              visibility 0s linear 220ms;
}
.dock-subs.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}
.dock-sub {
  padding: 7px var(--space-3);
  border-radius: 7px;
  font-family: var(--font-label);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.dock-sub:hover { background: color-mix(in srgb, var(--brand-dark) 8%, transparent); color: var(--brand-dark); }
/* The dock is the positioning context for the panels, and only on the rail:
   below 900 the panels are hidden and the dock becomes a fixed sheet. Left
   unscoped this rule sat after that media query at equal specificity and won
   it, so the sheet stayed in flow at 288px, stretched the fixed bar it lives
   in, and the centred logo chip landed on the headline. */
@media (min-width: 901px) {
  .pills.dock { position: relative; }
}
@media (max-width: 900px) {
  .dock-subs, .dock-trail { display: none; }
}

/* The active card takes the burgundy, so its chip has to flip with it. */
.cap.is-active .cap-chip {
  color: var(--brand-light);
  border-color: color-mix(in srgb, var(--brand-light) 32%, transparent);
  background: color-mix(in srgb, var(--brand-light) 10%, transparent);
}

/* ============================================================
   HERO ASSURANCES — one strip, five marks
   ============================================================ */
/* The badge, the guarantee and the three outcomes used to sit either side of
   a vertical rule in two separate blocks. They are the same kind of promise,
   so they now run as one row under a single horizontal rule, each struck from
   the same die. */
.hero-assurances {
  grid-column: 1 / -1;
  grid-row: 3;
  list-style: none;
  margin: var(--space-7) 0 0;
  padding: var(--space-6) 0 0;
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
  display: grid;
  /* Four equal columns. The Google badge used to take a fifth, wider one,
     which pushed the four promises apart and left them unevenly spaced; it
     now sits with the calls to action, where it reads as a credential rather
     than as a fifth promise. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  align-items: center;
  gap: var(--space-4);
}
.assure {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-3);
  min-width: 0;
  padding-left: var(--space-5);
  border-left: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
}
.assure:first-child { padding-left: 0; border-left: 0; }

/* The badge, under the calls to action it backs up. */
.hero-cred {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
}
.hero-cred-badge { display: block; width: auto; max-width: 100%; height: 38px; }

.assure-seal { display: block; width: 38px; height: 38px; color: var(--brand-dark); }
.assure-seal svg { display: block; width: 100%; height: 100%; }
/* One chassis, four glyphs: the ring and its tooth band stay at the same
   weight on every mark so the row reads as a set. */
.gs-ring   { opacity: 0.46; }
.gs-thin   { opacity: 0.3; }
.gs-teeth  { opacity: 0.26; }
.gs-wreath { opacity: 0.58; }
.gs-tick   { opacity: 1; }

.assure-text { display: grid; gap: 2px; min-width: 0; }
.assure-text b {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.8125rem;
  letter-spacing: -0.012em;
  line-height: 1.25;
  color: var(--brand-dark);
}
.assure-text i {
  font-style: normal;
  font-family: var(--font-display);
  font-size: 0.75rem;
  line-height: 1.25;
  color: var(--ink-muted);
}

@media (max-width: 1180px) {
  .hero-assurances { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5) var(--space-6); }
  .assure:nth-child(odd) { padding-left: 0; border-left: 0; }
}
@media (max-width: 720px) {
  .hero-assurances { grid-template-columns: 1fr; gap: var(--space-5); }
  .assure { padding-left: 0; border-left: 0; }
}

/* ---------- Fix pass: callback, footer lines, hub width ---------- */

/* The hover flipped the fill to sand while the label stayed sand. Deepening
   it to --surface-alt then made it the panel's own colour, so the button
   disappeared instead. It lifts away from the panel now: 18% sand over the
   panel ground, which keeps the sand label at 5.23:1 and puts 1.46:1 between
   the button and the card it sits on. */
.callback-submit:hover,
.callback-submit:focus-visible {
  background: color-mix(in srgb, var(--brand-light) 18%, var(--surface-alt));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand-light) 34%, transparent);
}
.callback-submit:hover .arrow,
.callback-submit:focus-visible .arrow {
  background: color-mix(in srgb, var(--brand-light) 20%, transparent);
  transform: translateX(3px);
}
.callback-submit .arrow { background: color-mix(in srgb, var(--brand-light) 12%, transparent); }

/* One glyph size across the three direct lines, and one rhythm between them:
   the rows were spaced by their own padding, which the taller LinkedIn mark
   quietly changed. */
.social-icon svg,
.social-btn .social-icon svg { width: 15px; height: 15px; }
.footer-col .social { padding-block: 0; min-height: 42px; }
.footer-col { display: grid; align-content: start; }
.footer-col .social + .social { margin-top: var(--space-3); }

/* The ring's own max-width, not the section's: below 1080 this is a plain
   list and was still being squeezed into a 680px column. */
.hub { max-width: none; }
@media (min-width: 1081px) { .hub { max-width: 680px; } }

/* ---------- Dock: the stop you are on, as its own tab ----------
   It hangs off the underside of the bar and slides across to sit under
   whichever item owns it, so the bar keeps its own widths and the stop is
   read as a second line rather than as a competing label. */
.dock-trail {
  position: absolute;
  top: 100%;
  left: 0;
  display: block;
  max-width: 14rem;
  padding: 4px var(--space-3) 5px;
  border-radius: 0 0 9px 9px;
  /* Inverted from the active pill above it. Same fill read as the pill
     growing a second line rather than as a separate readout. */
  background: var(--brand-dark);
  color: var(--brand-light);
  font-family: var(--font-label);
  font-size: 8.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition: left 380ms var(--ease-out), opacity 220ms var(--ease-out),
              transform 220ms var(--ease-out);
}
.dock-trail.is-on { opacity: 1; transform: none; }
.dock-trail-text {
  display: block;
  font-style: normal;
  transform-origin: 50% 0;
  transition: transform 170ms var(--ease-out), opacity 170ms var(--ease-out);
}
/* Half a turn out, text swapped at the bottom of it, half a turn back. */
.dock-trail.is-turning .dock-trail-text { transform: rotateX(-88deg); opacity: 0.15; }

/* The panel's left edge is set from the item it belongs to; without that it
   opened at the left end of the bar whichever item you were pointing at. */
.dock-subs { left: 0; }

/* ---------- Lanes: the engraved grounds ----------
   Painted from a mask rather than drawn inline, so the field takes the card's
   own ink and inverts with it. Faded in from the left on each, because the
   copy runs down that side and a full-strength field under it was the thing
   that read as "weird shapes". */

/* Ours signs itself. The other two are set in type because they are
   categories; this one is a name. */
.lane-name-us { display: block; }

/* The closing note was floating under three cards with nothing holding it. */

/* ---------- The Shift: the line, not a ring round the number ----------
   Circling the figures put the mark on the thing that was already the biggest
   object on screen. The sentence underneath is what has to be read, so that
   is what gets struck through with a highlighter, one after another, and the
   figure lifts a little as its turn comes. */
.lead-hl {
  background: linear-gradient(color-mix(in srgb, var(--accent-on-dark) 48%, transparent) 0 0) no-repeat;
  background-size: 0% 64%;
  background-position: 0 84%;
  padding-inline: 2px;
  border-radius: 1px;
}
.shift-figs.is-in .lead-hl { animation: lead-draw 560ms var(--ease-out) forwards; }
.shift-figs.is-in .shift-fig:nth-child(1) .lead-hl { animation-delay: 260ms; }
.shift-figs.is-in .shift-fig:nth-child(2) .lead-hl { animation-delay: 1100ms; }
.shift-figs.is-in .shift-fig:nth-child(3) .lead-hl { animation-delay: 1940ms; }
@keyframes lead-draw { to { background-size: 100% 64%; } }

.shift-num { transform-origin: left center; transition: transform 520ms var(--ease-out); }
.shift-figs.is-in .shift-fig:nth-child(1) .shift-num { transition-delay: 160ms; }
.shift-figs.is-in .shift-fig:nth-child(2) .shift-num { transition-delay: 1000ms; }
.shift-figs.is-in .shift-fig:nth-child(3) .shift-num { transition-delay: 1840ms; }
.shift-figs.is-in .shift-num { transform: scale(1.06); }
@media (prefers-reduced-motion: reduce) {
  .lead-hl { background-size: 100% 64%; animation: none !important; }
  .shift-num { transform: none !important; transition: none; }
}

/* ---------- Leadership: the hand-off ----------
   The spine used to run past the last role and simply stop inside the
   portraits. It now turns out of the column and ends on the edge of the
   people block, so the line arrives somewhere instead of trailing off. */
/* ---------- The handoff: down, then the whole way across ----------
   The old stub turned right and stopped a few centimetres later, so the
   spine appeared to hand over to nothing. The charge now runs down off the
   last role and then travels the full width of the block to finish past the
   outer edge of the second portrait, with a clear gap before the cards
   start, so the line delivers the people rather than touching them.
------------------------------------------------------------------- */
.pedigree-handoff {
  position: relative;
  display: block;
  height: var(--space-7);
  margin-bottom: var(--space-5);
}
.pedigree-handoff i {
  position: absolute;
  display: block;
  background: color-mix(in srgb, var(--brand-dark) 22%, transparent);
}
/* One movement, driven by the same scroll the spine above it is driven by,
   rather than two timed animations fired by a class. --h runs 0 to 1 across
   the whole L: the first 28% is the drop, the rest is the run. The head is a
   single dot whose top and left are read off the same number, so it turns
   the corner instead of handing over to a second dot. */
.ph-down {
  left: 0; top: 0;
  width: 1px; height: 100%;
  transform: scaleY(clamp(0, calc(var(--h, 0) / 0.28), 1));
  transform-origin: top;
}
.ph-across {
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  transform: scaleX(clamp(0, calc((var(--h, 0) - 0.28) / 0.72), 1));
  transform-origin: left;
}
.ph-head {
  position: absolute;
  width: 10px; height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: var(--brand-dark);
  top: calc(100% * clamp(0, calc(var(--h, 0) / 0.28), 1));
  left: calc(100% * clamp(0, calc((var(--h, 0) - 0.28) / 0.72), 1));
  opacity: clamp(0, calc(var(--h, 0) * 24), 1);
}
/* The turn gets a node of its own once the head is past it. */
.pedigree-handoff::after {
  content: '';
  position: absolute;
  left: -4.5px; bottom: -4.5px;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--brand-dark);
  opacity: clamp(0, calc((var(--h, 0) - 0.28) * 20), 1);
}

@media (prefers-reduced-motion: reduce) {
  .ph-down { transform: scaleY(1); }
  .ph-across { transform: scaleX(1); }
  .ph-head { display: none; }
}

/* ---------- Seeding: the claim, and the phrases that answer it ---------- */
/* The kind chip is the one word that says what went wrong, so it is filled
   rather than outlined and it is the only bronze on the card. */
.thread-kind {
  background: var(--accent-on-dark);
  border-color: var(--accent-on-dark);
  color: var(--surface-alt);
  font-weight: 500;
}

/* The checkable part of each reply, marked a beat after the reply is written.
   It is the whole argument of the section: the correction is made with things
   somebody can go and look up. */
.rhl {
  background: linear-gradient(color-mix(in srgb, var(--brand-light) 30%, transparent) 0 0) no-repeat;
  background-size: 0% 72%;
  background-position: 0 82%;
  color: var(--brand-light);
  padding-inline: 2px;
  border-radius: 1px;
  transition: background-size 480ms var(--ease-out);
}
.reply-body.is-marked .rhl { background-size: 100% 72%; }
.reply-body.is-marked .rhl:nth-of-type(2) { transition-delay: 260ms; }
@media (prefers-reduced-motion: reduce) { .rhl { background-size: 100% 72%; transition: none; } }

/* ---------- For Firms: foot, to the hero's pattern ---------- */
.adv-terms li {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-3);
  grid-template-rows: auto auto;
  row-gap: 2px;
}
.adv-terms li::before { content: none; }
/* Scoped through the list, because the older `.adv-terms span` rule is a more
   specific selector than a bare class and was placing this in the text column
   on top of the words. */
.adv-terms li .adv-tick {
  grid-column: 1; grid-row: 1 / span 2;
  width: 26px; height: 26px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--brand-light) 26%, transparent);
  background: color-mix(in srgb, var(--brand-light) 8%, transparent);
  position: relative;
}
.adv-terms li .adv-tick::after {
  content: '';
  position: absolute;
  left: 7px; top: 9px;
  width: 10px; height: 5px;
  border-left: 1.6px solid var(--brand-light);
  border-bottom: 1.6px solid var(--brand-light);
  transform: rotate(-45deg);
}
.adv-terms b {
  grid-column: 2; grid-row: 1;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.8125rem;
  letter-spacing: -0.012em;
  line-height: 1.25;
  color: var(--brand-light);
}
.adv-terms i {
  grid-column: 2; grid-row: 2;
  font-style: normal;
  font-family: var(--font-display);
  font-size: 0.75rem;
  line-height: 1.25;
  color: var(--on-dark-muted);
}

/* The same shape as the hero's calls to action: mark, two lines, disc. */
.adv-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 62px;
  min-width: 340px;
  justify-content: flex-start;
  padding: var(--space-3) var(--space-4) var(--space-3) var(--space-5);
}
.adv-cta .btn-ico { width: 20px; height: 20px; flex: none; opacity: 0.82; }
.adv-cta .btn-ico svg { width: 100%; height: 100%; }
.adv-cta .btn-text { display: grid; gap: 2px; text-align: left; flex-direction: row; }
.adv-cta .btn-label { font-size: var(--step-0); }
.adv-cta .btn-sub {
  font-family: var(--font-label);
  font-size: 0.625rem;
  letter-spacing: 0.02em;
  line-height: 1.45;
  white-space: nowrap;
}
.adv-cta .btn-sub::before { content: none; }
.adv-cta .arrow {
  margin-left: auto;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--brand-dark) 14%, transparent);
  font-size: var(--step-0);
  overflow: visible;
  transition: background var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.adv-cta .arrow::before, .adv-cta .arrow::after { display: none; }
.adv-cta:hover .arrow { background: color-mix(in srgb, var(--brand-dark) 22%, transparent); transform: translateX(3px); }
@media (max-width: 560px) {
  .adv-cta { min-width: 0; width: 100%; }
  .adv-cta .btn-sub { white-space: normal; }
}

/* ============================================================
   SECTION SEAMS
   ============================================================ */
/* A section with a curved top shows whatever is behind it in the two corner
   gaps. Where the section above was not the page ground — the tinted exposure
   band, the chrome-surface team band — those gaps were showing page base, so
   the join read as two white notches. Every curve now lands ON the section
   above it rather than on the page, which needs two things: the margin that
   pulls it up, and a stacking order where later always paints over earlier. */
#top, .hero { position: relative; z-index: 0; }
#exposure     { position: relative; z-index: 1; }
#problem      { z-index: 2; }
#what-we-do   { z-index: 3; }
#services     { position: relative; z-index: 3; }
#seeding      { z-index: 4; }
#proof        { z-index: 5; }
#press        { z-index: 6; }
#sectors      { z-index: 7; }
#work, #where-we-sit, #how-we-work { position: relative; z-index: 7; }
#advisors     { z-index: 8; }
#partners     { z-index: 9; }
#team         { z-index: 10; }
#contact      { z-index: 11; }
.site-footer  { z-index: 12; }

/* The three joins that were landing on the page ground instead of on the
   section above. Each is pulled up by the corner radius and given the same
   amount back as padding, so nothing moves except the colour in the gaps. */
#exposure {
  margin-top: calc(-1 * var(--radius-section));
  padding-top: var(--radius-section);
}
#problem {
  margin-top: calc(-1 * var(--radius-section));
  padding-top: var(--radius-section);
}
#contact {
  margin-top: calc(-1 * var(--radius-section));
  padding-top: var(--radius-section);
}
/* Room back for what the overlap eats. */
.hero { padding-bottom: calc(clamp(4.5rem, 3.4rem + 3.4vw, 7rem) + var(--radius-section)); }
#team { padding-bottom: calc(var(--section-y) + var(--radius-section)); }

/* ---------- Lanes: a mark on the two that are set in type ---------- */
/* Ours signs itself with the lockup, so the other two get a mark of their own
   rather than a bare word: the broadcast cone and the portico, the same two
   shapes their grounds are drawn from. */
.lane-name { display: flex; align-items: center; gap: var(--space-3); }
.lane-ico { display: block; width: 26px; height: 26px; flex: none; opacity: 0.72; }
.lane-ico svg { display: block; width: 100%; height: 100%; }
.lane:hover .lane-ico { opacity: 1; }

/* The ground drifts on its own and comes forward on approach, so the card
   answers the pointer the way the plates on the exposure rail do. */
@media (prefers-reduced-motion: reduce) {
}

/* ---------- The stamp turns ----------
   Two tooth bands running against each other, like the blades of a shutter.
   Slow enough that it reads as a mechanism at rest rather than as a loader. */
/* The shutter. Two tooth bands turning against each other, the wording
   rocking with the outer one, and the mark turning against all of it. The
   first version was too slow and too faint to register as movement at all. */
.es-teeth-out { animation: es-spin 34s linear infinite; transform-origin: 100px 100px; }
.es-teeth-in  { animation: es-spin 23s linear infinite reverse; transform-origin: 100px 100px; }
.es-teeth line { opacity: 0.5; stroke-width: 0.9; }
.es-teeth-in line { opacity: 0.62; }
/* The type rocks rather than orbits: a full turn would carry
   ETHICAL BY DEFAULT round to the bottom upside down. */
.es-type-ring {
  transform-origin: 100px 100px;
  animation: es-rock 34s ease-in-out infinite;
}
@keyframes es-rock {
  0%, 100% { transform: rotate(-5deg); }
  50%      { transform: rotate(5deg); }
}
@keyframes es-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .es-teeth-out, .es-teeth-in, .es-type-ring, .ethic-mark { animation: none; }
}

/* ---------- Seeding: the kind, as a corner tab ----------
   A solid bronze pill in the chip row was shouting over the platform mark
   next to it. It is a tab on the corner of the card now: still the first
   thing you read, at a quarter of the volume. */
.thread { position: relative; }
.thread-kind {
  position: absolute;
  top: 0; right: var(--space-5);
  transform: translateY(-50%);
  background: color-mix(in srgb, var(--accent-on-dark) 20%, var(--surface-alt));
  border: 1px solid color-mix(in srgb, var(--accent-on-dark) 52%, transparent);
  color: var(--accent-on-dark);
  font-weight: 400;
  padding: 5px var(--space-3) 4px;
  z-index: 2;
}

/* Each card is dim until the wire reaches it, then its index and chip take
   full ink: the charge arrives, and the card is on. */
.cap-num { transition: color 420ms var(--ease-out), opacity 420ms var(--ease-out); opacity: 0.4; }
.cap.is-wired .cap-num { opacity: 1; }
.cap-chip { transition: border-color 420ms var(--ease-out), color 420ms var(--ease-out), background 420ms var(--ease-out); }
/* The active card is burgundy and the off-state colour below is burgundy
   ink, so the two together left the chip invisible until the wire arrived,
   which read as the chip turning up late. The highlight always wins. */
.cap:not(.is-wired):not(.is-active) .cap-chip {
  /* 72%, not lower: the chip is the card's one-line claim, so it has to stay
     readable before the wire gets there. 4.74:1 on the card. The frame and
     fill carry the rest of the off state. */
  color: color-mix(in srgb, var(--brand-dark) 72%, transparent);
  border-color: color-mix(in srgb, var(--brand-dark) 10%, transparent);
  background: transparent;
}
@media (prefers-reduced-motion: reduce) {
  .cap-num { opacity: 1; }
  .cap:not(.is-wired):not(.is-active) .cap-chip { color: inherit; }
}

/* ---------- The exposure band: one head, and a field behind the plates ----
   The label, the heading and the standfirst were three stacked blocks with
   the rail underneath, which left the top of a full-height section reading as
   loose parts. They are one band now: heading on the left, standfirst beside
   it, a plate counter on the end, and a hairline closing the band off from
   the rail. It also costs less height, so the plates get more of the screen.
------------------------------------------------------------------- */
.exposure-count {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  color: color-mix(in srgb, var(--brand-dark) 68%, transparent);
  white-space: nowrap;
}
.exposure-count b { font-weight: 500; color: var(--brand-dark); }
.exposure-count i {
  display: block;
  width: var(--space-5);
  height: 1px;
  background: color-mix(in srgb, var(--brand-dark) 24%, transparent);
}

@media (min-width: 901px) {
  .exposure .hrail-vp .exposure-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr) auto;
    column-gap: clamp(var(--space-6), 3.4vw, var(--space-8));
    align-items: end;
    padding-bottom: clamp(var(--space-4), 2vh, var(--space-6));
  }
  .exposure .hrail-vp .exposure-head::after {
    content: '';
    grid-column: 1 / -1;
    grid-row: 3;
    height: 1px;
    margin-top: clamp(var(--space-6), 4.4vh, var(--space-8));
    background: color-mix(in srgb, var(--brand-dark) 20%, transparent);
  }
  .exposure .hrail-vp .exposure-head .label { grid-column: 1; grid-row: 1; margin-bottom: var(--space-3); }
  .exposure .hrail-vp .exposure-head .h2 { grid-column: 1; grid-row: 2; margin-bottom: 0; }
  .exposure .hrail-vp .exposure-lede {
    grid-column: 2; grid-row: 1 / span 2;
    align-self: end;
    max-width: 52ch;
    margin-bottom: 0;
  }
  .exposure .hrail-vp .exposure-count { grid-column: 3; grid-row: 2; align-self: end; }

  /* The field: a warm pool the plates travel out of and a cooler one they
     travel into, drifting against the rail's own progress.

     It belongs to the section, not to the pinned viewport. While the rail is
     pinned that viewport is position: fixed, which `overflow: hidden` on an
     ancestor does not clip, so a pool painted inside it covered the whole
     screen and washed over the hero above it. Painted on the section it is
     clipped by the section's own box and stays under the curve.

     The hairline horizon that used to run under the plates is gone with it:
     it escaped the same way, and the rail reads cleaner without a line for
     the cards to sit on. */
  .hrail-vp { z-index: 0; }
  .exposure::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
      radial-gradient(38rem 30rem at 14% 18%,
        color-mix(in srgb, var(--brand-light) 26%, transparent), transparent 68%),
      radial-gradient(34rem 28rem at 86% 84%,
        color-mix(in srgb, var(--brand-dark) 5%, transparent), transparent 70%);
    transform: translate3d(calc(var(--hp, 0) * -6%), 0, 0);
  }
  .exposure .hrail { z-index: 1; }
}
@media (max-width: 900px) {
  .exposure-count { margin-top: var(--space-5); }
}
@media (prefers-reduced-motion: reduce) {
  .exposure::after { transform: none; }
}

/* ---------- Seeding: the platform, as the opposite corner tab ----------
   The mark and the kind sat next to each other at the top of the card, which
   put the two loudest things in the same place and left the whole bar
   weighted right. The platform now straddles the top-left corner the way the
   kind straddles the top-right: light chip, mark in burgundy, so it reads as
   the source stamped on the card rather than another label in the row. The
   sector and the clock keep the row to themselves underneath.
------------------------------------------------------------------- */
.thread-src {
  position: absolute;
  top: 0; left: var(--space-5);
  transform: translateY(-50%);
  background: var(--brand-light);
  border-color: var(--brand-light);
  color: var(--brand-dark);
  z-index: 2;
}
.thread-bar { min-height: 1.9em; }

@media (max-width: 680px) {
  /* Two tabs on one short edge collide, so at this width they come back into
     the flow and the row carries all four again. */
  .thread-src, .thread-kind {
    position: static;
    transform: none;
    left: auto; right: auto;
  }
}

/* The mark turns against the bands. It is centred by a translate, so the
   spin has to carry that translate with it or the glyph walks off centre. */
.ethic-mark { animation: es-mark-spin 52s linear infinite; }
@keyframes es-mark-spin {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(-360deg); }
}

/* ---------- Where we sit: clearer marks, and the verb as a chip ----------
   The broadcast cone and the portico were both read as generic buildings at
   26px. A megaphone and a pair of scales say which discipline is which
   before the name does, and the verb line that sat loose under the name is
   now the same chip the rest of the page uses for a one-line claim.
------------------------------------------------------------------- */
.lane-ico { width: 30px; height: 30px; opacity: 0.8; }
.lane-head { justify-items: start; gap: var(--space-5); }
.lane-verb {
  display: inline-block;
  margin: 0;
  padding: 4px var(--space-3) 3px;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 20%, transparent);
  border-radius: var(--radius-round);
  background: color-mix(in srgb, var(--brand-dark) 5%, transparent);
  transition: border-color 420ms var(--ease-out), background 420ms var(--ease-out);
}
.lane:hover .lane-verb {
  border-color: color-mix(in srgb, var(--brand-dark) 34%, transparent);
  background: color-mix(in srgb, var(--brand-dark) 8%, transparent);
}
.lanes-sec .lane-us .lane-verb {
  border-color: color-mix(in srgb, var(--brand-light) 30%, transparent);
  background: color-mix(in srgb, var(--brand-light) 10%, transparent);
}
.lanes-sec .lane-us:hover .lane-verb {
  border-color: color-mix(in srgb, var(--brand-light) 46%, transparent);
  background: color-mix(in srgb, var(--brand-light) 15%, transparent);
}

/* The two routes paragraph gets the same rule as the seeding lede: both are
   a standfirst sitting beside a heading with nothing holding them. */
.adv-lede {
  border-left: 4px solid var(--brand-light);
  padding-left: var(--space-5);
}
.adv-lede b, .adv-lede strong { color: var(--brand-light); font-weight: 500; }

/* ---------- Hero proof row: the badge, then the promises on a loop -------
   The badge under the buttons and the four promises in a row underneath were
   two separate claims in two separate places. They are one line now: the
   Google credential, a divider, and the four promises cycling through one
   slot. Showing one at a time means each gets read instead of scanned, and
   it keeps the whole thing to a single row at the foot of the hero.
------------------------------------------------------------------- */
.hero-proof {
  grid-column: 1 / -1;
  grid-row: 3;
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-top: var(--space-7);
  padding-top: var(--space-7);
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
}
/* The badge keeps no rule of its own now that the row carries one. */
.hero-proof .hero-cred {
  flex: none;
  margin: 0;
  padding: 0;
  border-top: 0;
}
.hero-proof-sep {
  flex: none;
  width: 1px;
  align-self: stretch;
  min-height: 42px;
  background: color-mix(in srgb, var(--brand-dark) 16%, transparent);
}
/* The four promises run as one continuous band across the rest of the row,
   so all four are on screen and the line fills out to the end of the rule
   rather than stopping a third of the way along. JS clones the set until it
   covers the window plus one whole set and measures the period, so the loop
   has no seam at any width. */
.hero-marquee {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  /* Soft ends, so a promise fades at the edge instead of being cut. */
  -webkit-mask: linear-gradient(90deg, transparent, #000 1.25rem, #000 calc(100% - 3rem), transparent);
          mask: linear-gradient(90deg, transparent, #000 1.25rem, #000 calc(100% - 3rem), transparent);
}
.hero-proof .hero-assurances {
  display: flex;
  width: max-content;
  align-items: center;
  gap: var(--space-7);
  grid-column: auto;
  grid-row: auto;
  margin: 0;
  padding: 0;
  border-top: 0;
  will-change: transform;
}
.hero-proof .hero-assurances[data-marquee] {
  animation: hero-marquee var(--marquee-dur, 38s) linear infinite;
}
.hero-proof .hero-marquee:hover .hero-assurances[data-marquee],
.hero-proof .hero-marquee:focus-within .hero-assurances[data-marquee] {
  animation-play-state: paused;
}
@keyframes hero-marquee { to { transform: translateX(calc(-1 * var(--marquee-step, 50%))); } }
.hero-proof .assure {
  flex: none;
  padding-left: 0;
  border-left: 0;
}

@media (max-width: 860px) {
  .hero-proof { flex-wrap: wrap; gap: var(--space-4) var(--space-5); }
  .hero-proof-sep { display: none; }
  .hero-proof .hero-assurances { flex-basis: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  /* Nothing travels: the band wraps and sits still. */
  .hero-proof .hero-marquee { -webkit-mask: none; mask: none; }
  .hero-proof .hero-assurances {
    width: auto;
    flex-wrap: wrap;
    gap: var(--space-4) var(--space-6);
    animation: none;
  }
}

/* ---------- Dock trail over a dark section ----------
   The trail is burgundy, so over a burgundy section it was the same colour
   as the page behind it and the stop became unreadable. Over dark it flips
   to sand like the pill above it, but a step deeper, so the two still read
   as a pill and a readout hanging off it rather than one tall label.
------------------------------------------------------------------- */
.chrome[data-right-dark='true'] .dock-trail {
  /* 82% sand: 4.82:1 for the type on it, and 1.32:1 against the solid sand
     pill above, which is the visible step without breaking the pairing. */
  background: color-mix(in srgb, var(--brand-light) 82%, var(--brand-dark));
  color: var(--brand-dark);
}

/* One promise beside the badge can carry more weight than four crammed into
   a row could, so it takes a size up. */
.hero-proof .assure-text b { font-size: 0.9375rem; }
.hero-proof .assure-text i { font-size: 0.8125rem; }
.hero-proof .assure-seal { width: 42px; height: 42px; }
.hero-proof .assure { grid-template-columns: 42px minmax(0, 1fr); column-gap: var(--space-4); }

/* justify-items: start on the lane head makes every child shrink to its
   content, and the lockup is sized as a percentage of its parent, so it
   collapsed to nothing. It carries its own width now. */
/* The word alone, set from the real wordmark rather than typed out, and a
   size up on the other two names so the house mark carries the card. */
.lanes-sec .lane-name-us { width: auto; color: var(--brand-light); }
.lane-word {
  display: block;
  /* Sized off the heading's own line box rather than a fixed width, so the
     wordmark stands exactly as tall as the two names beside it and the three
     chips underneath stay on one line. Width follows from the ratio. */
  height: 1.16em;
  aspect-ratio: 1802 / 428;
  max-width: 100%;
  background-color: var(--brand-light);
  -webkit-mask: url('../img/mosaek-word.png') left center / contain no-repeat;
          mask: url('../img/mosaek-word.png') left center / contain no-repeat;
}

/* ---------- Seeding: air around the cards ----------
   The corner tabs straddle the top edge, so the first row inside the card
   was sitting right underneath them. The card takes more room at the top,
   and the whole band gets a clear gap from the heading above and from the
   note below, rather than the three blocks running together.
------------------------------------------------------------------- */
.threads .thread { padding-top: var(--space-6); }
.seed-head { margin-bottom: calc(var(--space-8) + var(--space-5)); }
.seed-foot { margin-top: calc(var(--space-7) + var(--space-5)); }

/* The mark turns clockwise against the bands, and fast enough to read as
   part of the same mechanism rather than as drift. */
.ethic-mark { animation-name: es-mark-turn; animation-duration: 14s; }
@keyframes es-mark-turn {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}

/* ---------- Where we sit: the mark the head comes out of ----------
   The engraved field behind each card was one thing too many next to a
   travelling head, a filling track and four rows lighting in sequence, so it
   is gone. In its place the discipline's mark moves out of the title and
   into the left rail as a medallion sitting on top of the track: the line
   drops out of it and the head is released from it as you scroll, which is
   what the three lanes are actually describing.
------------------------------------------------------------------- */
.lane-ico {
  position: absolute;
  left: var(--space-6);
  top: var(--space-6);
  transform: translateX(-50%);
  width: 38px; height: 38px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 20%, transparent);
  background: var(--page-base);
  color: var(--brand-dark);
  opacity: 1;
  z-index: 2;
  transition: background 420ms var(--ease-out), border-color 420ms var(--ease-out),
              color 420ms var(--ease-out), box-shadow 520ms var(--ease-out);
}
.lane-ico svg { width: 19px; height: 19px; }
/* Lit the moment its own head leaves. */
.lane.is-live .lane-ico {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
  color: var(--brand-light);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-dark) 10%, transparent);
}
.lanes-sec .lane-us .lane-ico {
  border-color: color-mix(in srgb, var(--brand-light) 30%, transparent);
  background: var(--brand-dark);
  color: var(--brand-light);
}
.lanes-sec .lane-us.is-live .lane-ico {
  background: var(--brand-light);
  border-color: var(--brand-light);
  color: var(--brand-dark);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-light) 14%, transparent);
}
/* The track starts under the medallion rather than under the header, so the
   head has somewhere to be born from. */
.lane-track { top: calc(var(--space-6) + 38px + 12px); }
@media (max-width: 900px) {
  /* The medallion is centred on the track, so the track has to sit at least
     half its width in from the card edge or the card's own clip takes a bite
     out of it. */
  .lane-ico { left: var(--space-5); top: var(--space-5); }
  .lanes .lane-track { left: var(--space-5); top: calc(var(--space-5) + 38px + 12px); }
  .lane-spec > div::before, .lane-when::before { left: calc((var(--space-5) + 3px) * -1); }
}

/* The closing note is the one line that is not a comparison, so it gets the
   same kind of mark the lanes carry: two overlapping rings, alongside rather
   than against. */

/* The third lane's mark is the monogram, painted from the same file the
   chrome uses, so all three cards hang their track off a medallion. */
.lane-ico-us::before {
  content: '';
  display: block;
  width: 21px; height: 18px;
  background-color: currentColor;
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
}

/* ---------- The hub between 760 and 1080 ----------
   Below the desktop width this used to drop to a plain list, which threw
   away the one idea the section has: eight kinds of firm arranged around one
   house mark. It keeps the ring instead. The orbit is scaled in and the
   chips are pulled off the edge towards it, which is what buys the room the
   long labels need at the three and nine o'clock positions; everything else
   is the same layout. Under 760 the list is still the honest answer.
------------------------------------------------------------------- */
@media (min-width: 760px) and (max-width: 1080px) {
  .hub { aspect-ratio: 1; max-width: 680px; margin-inline: auto; }
  .hub-web {
    transform: scale(0.78);
    transform-origin: 50% 50%;
  }
  /* 15% in from the edge puts the chip on the scaled orbit, and leaves the
     widest label room to sit either side of its own node. */
  .hub-pin { top: 15%; }
  .hub-dot {
    gap: var(--space-2);
    padding: 6px var(--space-3) 5px;
    font-size: 0.625rem;
    letter-spacing: 0.1em;
  }
  .hub-core { width: 26%; }
  .hub-line { width: 220px; padding: var(--space-3); }
}

/* ---------- The contact drawer, framed like the callback panel ----------
   Both are a burgundy card floating over the page, so they should be edged
   the same way: a sand hairline and the same drop, which is what stops the
   drawer melting into whatever dark section is behind it.
------------------------------------------------------------------- */
/* Only once it is open. The closed drawer is a 0fr grid row, so a border on
   it had nothing to wrap and rendered as a 2px burgundy line hanging under
   the dock across the width of the panel. */
.contact-pill.is-open .contact-drawer {
  border: 1px solid var(--divider-on-dark);
  box-shadow: 0 26px 60px rgba(0, 0, 0, .34);
}

/* ---------- Where we sit: the three marks ----------
   One red, not two: a second burgundy would be a fourth colour on a page
   that has three. The medallions carry the difference instead, each sitting
   on its own weight of sand and ending on the solid burgundy of the house
   mark, so the row steps rather than repeating.
------------------------------------------------------------------- */
/* One ground for the two light cards. The marks are what separate them. */

/* ---------- Spacing pass ----------
   The seeding meta row sits closer to the corner tabs with matching air
   under it; the stamp and its note drop away from the rule above them; the
   plates on the exposure rail ride higher off the progress bar; and the
   contact section closes the gap to the curve it sits under.
------------------------------------------------------------------- */
.thread-bar { margin-bottom: var(--space-2); }
.seed-foot { padding-top: var(--space-7); }
@media (min-width: 901px) {
  /* Matched to the scroller's own padding-top. The plates are centred in
     that box, so the two paddings are exactly what the gap above and the gap
     below differ by: unequal padding was putting the cards 45px off centre
     between the band rule and the progress bar. */
  .exposure .hrail-scroller { padding-bottom: clamp(var(--space-4), 2vh, var(--space-6)); }
}

/* The contact section already carries the curve's own radius as padding, so
   a full section unit on top of that left a dead band between the curve and
   the first line. */
#contact > .section { padding-top: calc(var(--section-y) * 0.5); }

/* ---------- Where we sit: marks, alignment and the finish ----------
   The medallion is centred on the title rather than on the card's padding
   box, so the mark and the name share a centre line. Each discipline's
   medallion sits on its own weight of sand, which is what separates the two
   light cards now that they share a ground. And the lane that runs the whole
   length declares itself when it gets there.
------------------------------------------------------------------- */
.lane-ico { top: calc(var(--space-6) - 4px); }

/* Resting grounds, light to dark across the row. */
/* :not(.is-live), or these would win over the lit state that comes earlier
   in the sheet and the medallion would never fill. */
.lanes .lane-col:nth-child(1) .lane:not(.is-live) .lane-ico { background: color-mix(in srgb, var(--brand-light) 24%, var(--page-base)); }
.lanes .lane-col:nth-child(2) .lane:not(.is-live) .lane-ico { background: color-mix(in srgb, var(--brand-light) 58%, var(--page-base)); }

/* The lane that reaches the end takes a sand edge, so the row resolves on an
   answer rather than just stopping. */
.lanes-sec .lane-us {
  transition: box-shadow 620ms var(--ease-out), border-color 620ms var(--ease-out);
}
.lanes-sec .lane-us.is-full {
  border-color: color-mix(in srgb, var(--brand-light) 78%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand-light) 42%, transparent),
              0 18px 48px color-mix(in srgb, var(--brand-dark) 26%, transparent);
}
@media (prefers-reduced-motion: reduce) {
  .lanes-sec .lane-us { transition: none; }
}

/* ---------- Seeding: the platform mark as a sticker ----------
   Bigger, and set at a slight angle half off the corner, so it reads as
   something stuck onto the card rather than another label the card was laid
   out with. The kind tab opposite it leans the other way.
------------------------------------------------------------------- */
.thread-src {
  top: 0; left: var(--space-6);
  transform: translateY(-52%) rotate(-2.5deg);
  padding: 9px var(--space-5) 8px;
  border-radius: var(--radius-round);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--brand-dark) 42%, transparent);
  transition: transform 420ms var(--ease-out);
}
.thread-src[data-plat='reddit'] > i { width: 54px; height: 16px; }
.thread-src[data-plat='google'] > i { width: 48px; height: 16px; }
.thread-kind {
  transform: translateY(-52%) rotate(1.8deg);
  padding: 7px var(--space-4) 6px;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--brand-dark) 34%, transparent);
  transition: transform 420ms var(--ease-out);
}
.thread:hover .thread-src { transform: translateY(-52%) rotate(-1deg); }
.thread:hover .thread-kind { transform: translateY(-52%) rotate(0.6deg); }
/* The card needs a little more head room now the stickers are taller. */
.threads .thread { padding-top: var(--space-7); }

@media (max-width: 680px) {
  .thread-src, .thread-kind { transform: none; box-shadow: none; }
  .thread:hover .thread-src, .thread:hover .thread-kind { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .thread-src, .thread-kind { transition: none; }
}

/* ---------- The hero / exposure seam ----------
   The band was 17% sand over the page ground, which is close enough to the
   ground that the curve was doing all the work and the join read as a soft
   smudge. Deepened, with a hairline on the curve itself, so the section has
   a visible edge and a visible floor change without a hard colour break.
------------------------------------------------------------------- */
.exposure {
  background: color-mix(in srgb, var(--brand-light) 30%, var(--page-base));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--brand-dark) 11%, transparent);
}

/* The ring sits equally between the heading it answers and the rule under
   it. Measured to the rule rather than to the row of terms above it: the
   gaps come out at 74px either side, with the ring's own top margin pulled
   in one step to get there. */

/* ---------- Defend and Build: the signs draw themselves ----------
   Two columns of flat marks read as a bulleted list. Drawn in as the block
   arrives, staggered down each column, they read as two opposite operations
   being written out: Defend takes things away a line at a time, Build adds
   its upright a beat after the horizontal lands.
------------------------------------------------------------------- */
.stance-moves li::before {
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 460ms var(--ease-out), height 220ms var(--ease-out);
}
.stance-build .stance-moves li::after {
  transform: scaleY(0);
  transform-origin: center;
  transition: transform 460ms var(--ease-out), width 220ms var(--ease-out);
}
.stance.is-in .stance-moves li::before,
.stance-build.is-in .stance-moves li::after { transform: none; }

.stance-moves li:nth-child(1)::before { transition-delay: 120ms; }
.stance-moves li:nth-child(2)::before { transition-delay: 220ms; }
.stance-moves li:nth-child(3)::before { transition-delay: 320ms; }
.stance-moves li:nth-child(4)::before { transition-delay: 420ms; }
.stance-moves li:nth-child(5)::before { transition-delay: 520ms; }
/* The upright always lands after its own horizontal. */
.stance-build .stance-moves li:nth-child(1)::after { transition-delay: 300ms; }
.stance-build .stance-moves li:nth-child(2)::after { transition-delay: 400ms; }
.stance-build .stance-moves li:nth-child(3)::after { transition-delay: 500ms; }
.stance-build .stance-moves li:nth-child(4)::after { transition-delay: 600ms; }
.stance-build .stance-moves li:nth-child(5)::after { transition-delay: 700ms; }

/* Under the pointer the sign takes weight and the line steps aside for it. */
.stance-moves li {
  transition: border-color 320ms var(--ease-out), padding-left 320ms var(--ease-out);
}
.stance-moves li:hover {
  border-top-color: color-mix(in srgb, var(--brand-dark) 34%, transparent);
  padding-left: calc(var(--space-6) + 6px);
}
.stance-moves li:hover::before { height: 1.6px; }
.stance-build .stance-moves li:hover::after { width: 1.6px; }

@media (prefers-reduced-motion: reduce) {
  .stance-moves li::before,
  .stance-build .stance-moves li::after { transform: none; transition: none; }
  .stance-moves li { transition: none; }
  .stance-moves li:hover { padding-left: var(--space-6); }
}

/* ---------- Seeding: pace, stickers and the alarm ----------
   The highlights come up faster behind the typing, the platform and the kind
   sit further onto their corners so the card reads as something stamped, and
   the kind breathes: it is the alarm on the card, and a still one read as
   another label.
------------------------------------------------------------------- */
.rhl { transition: background-size 340ms var(--ease-out); }
.reply-body.is-marked .rhl:nth-of-type(2) { transition-delay: 170ms; }

.thread-src { left: var(--space-4); transform: translateY(-58%) rotate(-3.5deg); }
.thread-kind { right: var(--space-4); transform: translateY(-58%) rotate(2.5deg); }
.thread:hover .thread-src { transform: translateY(-58%) rotate(-1.5deg); }
.thread:hover .thread-kind { transform: translateY(-58%) rotate(1deg); }

/* The alarm, and loud enough to read as one: a bronze ring that throws out
   from the tab and a glow under it that comes up with it. The first version
   topped out at a 7px ring at 28% and was invisible against a card this
   dark. */
.thread-kind { animation: kind-breathe 2.8s ease-in-out infinite; }
@keyframes kind-breathe {
  0%, 100% {
    border-color: color-mix(in srgb, var(--accent-on-dark) 52%, transparent);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--brand-dark) 34%, transparent),
                0 0 0 0 color-mix(in srgb, var(--accent-on-dark) 55%, transparent),
                0 0 0 0 color-mix(in srgb, var(--accent-on-dark) 0%, transparent);
  }
  45% {
    border-color: var(--accent-on-dark);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--brand-dark) 34%, transparent),
                0 0 0 6px color-mix(in srgb, var(--accent-on-dark) 0%, transparent),
                0 0 22px 3px color-mix(in srgb, var(--accent-on-dark) 40%, transparent);
  }
  100% {
    box-shadow: 0 4px 14px color-mix(in srgb, var(--brand-dark) 34%, transparent),
                0 0 0 14px color-mix(in srgb, var(--accent-on-dark) 0%, transparent),
                0 0 0 0 color-mix(in srgb, var(--accent-on-dark) 0%, transparent);
  }
}

/* ---------- The stickers land ----------
   They arrive as the card does: thrown in oversized and off-angle, then
   stamped down onto the corner with a short overshoot. It is the one piece
   of the section that is supposed to feel physical.
------------------------------------------------------------------- */
.thread.is-in .thread-src { animation: slap-l 540ms cubic-bezier(.25, .9, .3, 1.25) 120ms both; }
.thread.is-in .thread-kind {
  animation: slap-r 540ms cubic-bezier(.25, .9, .3, 1.25) 240ms both,
             kind-breathe 2.8s ease-in-out 780ms infinite;
}
.thread.reveal:not(.is-in) .thread-src,
.thread.reveal:not(.is-in) .thread-kind { opacity: 0; }
@keyframes slap-l {
  0%   { opacity: 0; transform: translateY(-58%) rotate(-19deg) scale(1.75); }
  60%  { opacity: 1; transform: translateY(-58%) rotate(-1deg) scale(0.95); }
  100% { opacity: 1; transform: translateY(-58%) rotate(-3.5deg) scale(1); }
}
@keyframes slap-r {
  0%   { opacity: 0; transform: translateY(-58%) rotate(16deg) scale(1.75); }
  60%  { opacity: 1; transform: translateY(-58%) rotate(0.5deg) scale(0.95); }
  100% { opacity: 1; transform: translateY(-58%) rotate(2.5deg) scale(1); }
}
/* A touch more air before the cards start. */
.seed-head { margin-bottom: calc(var(--space-8) + var(--space-6)); }

@media (max-width: 680px) {
  .thread-src, .thread-kind { transform: none; animation: none; }
  .thread:hover .thread-src, .thread:hover .thread-kind { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .thread-kind, .thread.is-in .thread-kind, .thread.is-in .thread-src { animation: none; }
  .thread.reveal:not(.is-in) .thread-src,
  .thread.reveal:not(.is-in) .thread-kind { opacity: 1; }
}

/* ---------- Return on Influence: the same entry as Where the work lands --- */
#proof > .section { padding-top: calc(var(--section-y) * 0.55); }

/* ---------- Where we sit: head, marks, line and verdict ----------
   The heading and the standfirst sat stacked in one narrow column, which put
   a lot of air between the label and the comparison and left the right half
   of the band empty. They run side by side now, the way For Firms does.
------------------------------------------------------------------- */
.lanes-head {
  max-width: none;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--space-7) var(--space-8);
  align-items: end;
}
.lanes-head .label { margin-bottom: var(--space-6); }
.lanes-head .h2 { margin-bottom: 0; }
.lanes-lede { margin-bottom: 0; }
@media (max-width: 900px) {
  .lanes-head { grid-template-columns: 1fr; gap: var(--space-5); align-items: start; }
}

/* The wordmark a size down, with the mark it is always set with, both on the
   medallion's own centre line. */
.lanes-sec .lane-name-us {
  display: flex;
  align-items: center;
  gap: 0.28em;
}
.lane-word { height: 0.96em; }
.lane-tm {
  font-family: var(--font-display);
  font-size: 0.46em;
  line-height: 1;
  align-self: flex-start;
  margin-top: 0.22em;
  color: color-mix(in srgb, var(--brand-light) 70%, transparent);
}

/* Each medallion keeps its own ground whether the lane is running or not:
   filling both light ones burgundy on the way past made the two marks
   identical exactly when anyone was looking at them. */
.lanes .lane-col:nth-child(1) .lane .lane-ico,
.lanes .lane-col:nth-child(1) .lane:not(.is-live) .lane-ico {
  background: color-mix(in srgb, var(--brand-light) 26%, var(--page-base));
  color: var(--brand-dark);
}
.lanes .lane-col:nth-child(2) .lane .lane-ico,
.lanes .lane-col:nth-child(2) .lane:not(.is-live) .lane-ico {
  background: color-mix(in srgb, var(--brand-light) 62%, var(--page-base));
  color: var(--brand-dark);
}
.lanes .lane-col:nth-child(1) .lane.is-live .lane-ico,
.lanes .lane-col:nth-child(2) .lane.is-live .lane-ico {
  border-color: var(--brand-dark);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-dark) 10%, transparent);
}

/* The track streams while it fills, so the line reads as a live channel
   rather than a bar that has been set to a value. */
.lane-fill { overflow: hidden; }
.lane-fill::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 38px;
  background: linear-gradient(180deg, transparent, var(--brand-dark), transparent);
  animation: lane-stream 2.4s linear infinite;
}
.lane-us .lane-fill::after {
  background: linear-gradient(180deg, transparent, var(--page-base), transparent);
}
.lanes .lane-col:nth-child(2) .lane .lane-fill::after { animation-delay: -0.8s; }
.lanes .lane-col:nth-child(3) .lane .lane-fill::after { animation-delay: -1.6s; }
@keyframes lane-stream {
  from { top: -38px; }
  to   { top: 100%; }
}

/* The verdict is the card's conclusion, so it is set as one: its own ground
   and a stub on the edge, rather than one more paragraph under one more rule. */

@media (prefers-reduced-motion: reduce) {
  .lane-fill::after { animation: none; display: none; }
}

/* ---------- Leadership: the cards hinge down off the line ----------
   The cover that opened like a book was a separate object arriving on top of
   the section; it covered the people rather than introducing them. The rail
   that has just run the width of the block is the hinge instead: each card
   is folded up flat against it and swings down into place as the head
   finishes, so the line delivers the two people rather than a lid coming
   off a box. The second one follows the first by a beat, the way the head
   crossed the rail.

   Gated on [data-seq], which only JS sets, so with scripting off or motion
   reduced the portraits are simply on the page.
------------------------------------------------------------------- */
.people { position: relative; }
.people[data-seq] { perspective: 1500px; perspective-origin: 50% 0; }
.people[data-seq] .person {
  transform-origin: top center;
  transform-style: preserve-3d;
  transition: transform 920ms cubic-bezier(.3, .72, .26, 1),
              opacity 520ms var(--ease-out);
}
.people[data-seq]:not(.is-handed) .person {
  transform: rotateX(-84deg);
  opacity: 0;
}
.people[data-seq].is-handed .person { transform: none; opacity: 1; }
.people[data-seq].is-handed .person + .person { transition-delay: 170ms, 170ms; }

@media (prefers-reduced-motion: reduce) {
  .people[data-seq] { perspective: none; }
  .people[data-seq] .person,
  .people[data-seq]:not(.is-handed) .person { transform: none; opacity: 1; transition: none; }
}

/* ---------- Leadership: the surface runs to the window ----------
   #team is a .wrap, so its own background stopped at 1440 and the page
   ground showed down both sides on anything wider. Painted by a viewport-
   wide layer behind the content instead, the way the other shared surfaces
   on the page are.
------------------------------------------------------------------- */
#team { background: none; }
#team::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  background: var(--chrome-surface);
  z-index: -1;
  pointer-events: none;
}

/* ---------- The head on the line is the mark ----------
   A dot travelling a line is a progress indicator. The monogram travelling
   it is the house walking the reader down its own spine and across to the
   people, which is what the sequence is actually saying.
------------------------------------------------------------------- */
.pedigree-wrap[data-seq] .pedigree::after {
  left: -16.5px;
  width: 34px; height: 29px;
  filter: drop-shadow(0 0 0.35px currentColor) drop-shadow(0 0 0.35px currentColor);
  color: var(--brand-dark);
  border-radius: 0;
  background-color: var(--brand-dark);
  box-shadow: none;
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
}
.ph-head {
  width: 34px; height: 29px;
  margin: -14.5px 0 0 -17px;
  filter: drop-shadow(0 0 0.35px currentColor) drop-shadow(0 0 0.35px currentColor);
  color: var(--brand-dark);
  border-radius: 0;
  background-color: var(--brand-dark);
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
}

/* ---------- The Influence Engine: tabs where tabs go ----------
   The three stages sat as a floating pill in the bottom-right corner of the
   panel, which read as a caption on the illustration rather than as a
   control, so nobody knew the stage could be switched. They are a tab strip
   on top of the stage now: a rail the active tab sits on and breaks, which
   is the one arrangement everyone already knows how to use.
------------------------------------------------------------------- */
.svc-tabs {
  position: static;
  right: auto; bottom: auto;
  display: flex;
  align-items: flex-end;
  gap: 2px;
  margin: 0 0 var(--space-6);
  padding: 0;
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--brand-dark) 16%, transparent);
  border-radius: 0;
  background: none;
  box-shadow: none;
}
.svc-tabs button {
  min-height: 42px;
  padding: 0 var(--space-5);
  margin-bottom: -1px;
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: 10px 10px 0 0;
  background: none;
  color: color-mix(in srgb, var(--brand-dark) 62%, transparent);
}
.svc-tabs button:hover {
  color: var(--brand-dark);
  background: color-mix(in srgb, var(--brand-dark) 4%, transparent);
}
.svc-tabs button[aria-selected='true'] {
  /* Full ink, not a tint. On a light rail a tinted tab and a muted tab are
     the same object at two strengths; filled, there is no reading it wrong. */
  background: var(--brand-dark);
  color: var(--brand-light);
  border-color: var(--brand-dark);
  /* Its own floor, drawn over the rail so the tab and the stage read as
     joined and the rail passes behind the others. */
  box-shadow: 0 1px 0 var(--brand-dark);
}
.svc-tabs button[aria-selected='true'] .svc-tab-ico { opacity: 0.9; }
/* The fill runs along the top of the active tab, where a tab's indicator
   belongs, rather than under it where the rail already is. */
.svc-tabs .tab-progress { top: 0; bottom: auto; }
/* The pause control is on a light rail now, so its glyph is ink. */
.svc-tabs .showcase-pause {
  margin-left: auto;
  color: color-mix(in srgb, var(--brand-dark) 62%, transparent) !important;
}
.svc-tabs .showcase-pause:hover { color: var(--brand-dark) !important; }

@media (max-width: 1024px) {
  .svc-tabs { overflow-x: auto; margin-top: 0; }
}

/* The standfirst takes the same rule the For Firms one does, so the two
   sections open the same way. */
.lanes-lede {
  border-left: 4px solid var(--brand-dark);
  padding-left: var(--space-5);
}
.lanes-lede b, .lanes-lede strong { color: var(--brand-dark); font-weight: 500; }

/* ---------- Where we sit: the marks, and the house card ----------
   The two outside marks sit on the card's own ground with nothing behind
   them — a filled disc on a flat card read as a button. Only the house mark
   is filled, which is the point the row is making.
------------------------------------------------------------------- */
.lanes .lane-col:nth-child(1) .lane .lane-ico,
.lanes .lane-col:nth-child(1) .lane:not(.is-live) .lane-ico,
.lanes .lane-col:nth-child(2) .lane .lane-ico,
.lanes .lane-col:nth-child(2) .lane:not(.is-live) .lane-ico {
  background: transparent;
  color: var(--brand-dark);
}
.lanes .lane-col:nth-child(1) .lane.is-live .lane-ico,
.lanes .lane-col:nth-child(2) .lane.is-live .lane-ico {
  /* The ring takes weight on its own. A halo outside an unfilled circle read
     as a second, softer circle rather than as the mark lighting up. */
  border-color: var(--brand-dark);
  box-shadow: none;
}
/* Another step in from the edge, card padding moving with it so the row
   nodes stay on the line. */
.lane-ico { left: calc(var(--space-6) + 8px); }
.lanes-sec .lane-track { left: calc(var(--space-6) + 8px); }
.lanes-sec .lane { padding-left: calc(var(--space-6) * 2 + 8px); }

/* The house card is not a flat panel: the red moves across it, slowly, in
   its own shades, so the one card that reaches the end is also the one that
   looks alive. */
.lanes-sec .lane-us {
  background-color: var(--brand-dark);
  background-image:
    radial-gradient(62% 58% at 28% 22%, color-mix(in srgb, var(--brand-light) 17%, transparent), transparent 70%),
    radial-gradient(54% 56% at 78% 72%, color-mix(in srgb, var(--brand-light) 11%, transparent), transparent 72%),
    radial-gradient(70% 60% at 50% 110%, color-mix(in srgb, var(--brand-light) 7%, transparent), transparent 70%);
  background-size: 190% 190%, 170% 170%, 200% 200%;
  background-repeat: no-repeat;
  animation: lane-us-shift 26s ease-in-out infinite alternate;
}
@keyframes lane-us-shift {
  from { background-position: 4% 0%, 100% 100%, 50% 100%; }
  to   { background-position: 72% 46%, 18% 6%, 20% 70%; }
}
@media (prefers-reduced-motion: reduce) {
  .lanes-sec .lane-us { animation: none; }
}

@media (max-width: 900px) {
  .lanes-sec .lane { padding-left: calc(var(--space-5) * 2 + 8px); }
  .lane-ico { left: calc(var(--space-5) + 8px); }
  .lanes-sec .lane-track { left: calc(var(--space-5) + 8px); }
}

/* ---------- For Firms: the terms qualify the offer, not the footer --------
   The three commitments were sitting at the bottom next to the call to
   action, which is where they were read last. They belong directly under the
   two routes they are the terms of, so the ring arrives with the deal
   already explained; a rule closes the band off from it.
------------------------------------------------------------------- */
/* The three commitments are the terms of both routes, not a footnote to the
   standfirst, so they run the full width on their own row in three even
   columns — one line, evenly spaced, directly under the offer they qualify
   and above the rule that closes the band off from the ring. */
/* The three commitments are back in the foot beside the call to action,
   where they read as the small print on the button rather than a band of
   their own. Air between the label and the heading it introduces. */
.adv-head .label { margin-bottom: var(--space-6); }
.adv-foot { justify-content: space-between; }
.adv-head-rule {
  display: block;
  height: 1px;
  margin-top: var(--space-7);
  background: var(--divider-on-dark);
}
/* The mark earns its space on a button this wide. */
.adv-cta .btn-ico { width: 30px; height: 30px; }
.adv-cta .btn-ico svg { width: 100%; height: 100%; }


/* ---------- The Influence Engine tabs: mark, shape and clock ----------
   Three words on a bare rail still read as a caption. Each stage takes its
   own mark, every tab keeps a shape whether it is selected or not so the
   set reads as a control, and the dwell runs as a bar across the top of the
   live one so it is obvious the stage is about to change.
------------------------------------------------------------------- */
.svc-tabs button {
  gap: var(--space-3);
  /* Off tabs are muted, not absent. */
  background: color-mix(in srgb, var(--brand-dark) 5%, transparent);
  border-color: color-mix(in srgb, var(--brand-dark) 10%, transparent);
  border-bottom: 0;
}
.svc-tabs button:hover {
  background: color-mix(in srgb, var(--brand-dark) 9%, transparent);
  border-color: color-mix(in srgb, var(--brand-dark) 18%, transparent);
}
.svc-tab-ico {
  display: inline-flex;
  width: 17px; height: 17px;
  flex: none;
  opacity: 0.6;
  transition: opacity var(--dur-base) var(--ease-out);
}
.svc-tab-ico svg { width: 100%; height: 100%; }
.svc-tabs button[aria-selected='true'] .svc-tab-ico { opacity: 1; }
.svc-tab-name { display: inline-block; }

/* The clock. Bronze, 3px, along the top edge of the live tab, under the
   tab's own rounded corners. */
.svc-tabs .tab-progress {
  top: 0; bottom: auto;
  height: 3px;
  border-radius: 999px;
  /* The light bronze, not the dark one: the live tab is burgundy now, and
     #7E5D23 on it was a dark bar on a dark ground. */
  background: var(--accent-on-dark);
}
.svc-tabs button { overflow: hidden; }

/* ---------- Where we sit: the verdict is the end of the track ----------
   Set in a tinted box with a stub on its edge, the closing line read as a
   chip that had been dropped on the bottom of the card — the one element
   not attached to anything. It is the last stop on the line instead: the
   same node every other row has, one size up and filled when the lane
   finishes, with the track running down into it. Nothing floats.
------------------------------------------------------------------- */
/* No node here: the track's own head parks at the bottom of the lane, and a
   second circle beside it read as two markers arguing. The verdict's rule
   simply starts at the track instead, so the line turns into the statement. */
/* The track runs all the way down to that last node. */
.lanes-sec .lane-track { bottom: var(--space-5); }

/* The two outside marks are cut out of the card rather than set on it: a
   perforated ring reads as an absence, which is the right weight next to a
   house mark that is solid. */
.lanes .lane-col:nth-child(1) .lane .lane-ico,
.lanes .lane-col:nth-child(2) .lane .lane-ico {
  border-style: dashed;
  border-width: 1.5px;
  border-color: color-mix(in srgb, var(--brand-dark) 34%, transparent);
}
.lanes .lane-col:nth-child(1) .lane.is-live .lane-ico,
.lanes .lane-col:nth-child(2) .lane.is-live .lane-ico {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--brand-dark) 62%, transparent);
}
.lanes .lane-ico svg { stroke-width: 0; }

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

/* ---------- For Firms: the ring, built like the seal ----------
   One dashed circle and eight spokes was a diagram. A tick band outside it
   and a hairline inside give the same construction the ethics stamp has, so
   the ring reads as an instrument rather than an illustration — and the core
   it all points at is edged strongly enough to be the centre of it.
------------------------------------------------------------------- */
.hub-ticks {
  fill: none;
  stroke: color-mix(in srgb, var(--brand-light) 26%, transparent);
  stroke-width: 1.5;
  stroke-dasharray: 0.22 1.68;
}
.hub-ring-mid {
  fill: none;
  stroke: color-mix(in srgb, var(--brand-light) 13%, transparent);
  stroke-width: 0.2;
}
.hub-core {
  border-width: 1.5px;
  border-color: color-mix(in srgb, var(--brand-light) 40%, transparent);
}
/* A second edge outside the first, so the core has a rim rather than a line. */
.hub-core::after {
  content: '';
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--brand-light) 15%, transparent);
  pointer-events: none;
}
@media (min-width: 1081px) {
  .hub { max-width: 820px; }
}

/* The three direct lines sit closer together, still evenly: they are one
   block of three ways to reach the same people, not three separate rows. */
.footer-col .social { padding-block: 2px; }

/* ---------- Where we sit: the house card declares itself ----------
   The drifting shades were faint enough to read as a printing fault. The
   base stays the brand red — it is most of the card at any moment — but the
   pools that move across it are strong enough to see, and the card carries a
   sand edge from the start so it is obviously the one being recommended
   rather than only becoming so once the lane finishes.
------------------------------------------------------------------- */
.lanes-sec .lane-us {
  background-image:
    radial-gradient(62% 58% at 28% 22%, color-mix(in srgb, var(--brand-light) 26%, transparent), transparent 70%),
    radial-gradient(54% 56% at 78% 72%, color-mix(in srgb, var(--brand-light) 17%, transparent), transparent 72%),
    radial-gradient(70% 60% at 50% 110%, color-mix(in srgb, var(--brand-light) 11%, transparent), transparent 70%);
  border-color: color-mix(in srgb, var(--brand-light) 46%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand-light) 18%, transparent);
}
.lanes-sec .lane-us.is-full {
  border-color: color-mix(in srgb, var(--brand-light) 86%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand-light) 48%, transparent),
              0 18px 48px color-mix(in srgb, var(--brand-dark) 30%, transparent);
}

/* ---------- For Firms: the ring turns ----------
   The same idea the seal uses: a tick band travelling one way and the core's
   rim the other, so the construction is working rather than drawn.
------------------------------------------------------------------- */
.hub-ticks {
  transform-origin: 50px 50px;
  animation: hub-turn 72s linear infinite;
}
.hub-core::after {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--brand-light) 26%, transparent);
  animation: hub-turn 46s linear infinite reverse;
}
@keyframes hub-turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .hub-ticks, .hub-core::after { animation: none; }
}

/* The ring sits close to the rule above it and the rule below, and evenly
   between the two: the chain is head, rule, ring, rule, so three separate
   margins were adding up to over a hundred pixels on the top side alone. */
.adv-head-rule { margin-top: var(--space-5); }
.hub { margin-top: var(--space-5); }
.adv-foot { margin-top: var(--space-6); }

/* ---------- Where we sit: the verdict is its own card ----------
   Inside the card, the verdict's rule crossed the track and the two lines
   fought. It is a separate plate underneath each one now, so the three read
   across as a row of answers. Set in the page's own face rather than the
   label one, on a single line, and turned face up by the lane above it: each
   plate flips as its own track reaches the end, which means the row resolves
   left to right as you scroll, and the house answer turns last.
------------------------------------------------------------------- */
.lanes { align-items: stretch; }
.lane-col {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: var(--space-3);
  min-width: 0;
  perspective: 900px;
}
.lane-close {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-3);
  margin: 0;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  border: 1.5px dashed color-mix(in srgb, var(--brand-dark) 22%, transparent);
  background: color-mix(in srgb, var(--brand-dark) 4%, var(--page-base));
  font-family: var(--font-display);
  font-size: var(--step-0);
  letter-spacing: -0.012em;
  line-height: 1.3;
  text-wrap: nowrap;
  transform-origin: top center;
  transition: transform 720ms cubic-bezier(.3, .72, .26, 1),
              opacity 420ms var(--ease-out),
              border-color var(--dur-slow) var(--ease-out),
              background var(--dur-slow) var(--ease-out),
              box-shadow var(--dur-slow) var(--ease-out);
}
/* The discipline's own mark, so the plate is read with the card it answers
   for rather than as a loose caption. */
.close-ico {
  display: block;
  width: 22px; height: 22px;
  color: color-mix(in srgb, var(--brand-dark) 62%, transparent);
}
.close-ico svg { display: block; width: 100%; height: 100%; }
.close-ico-us {
  width: 23px; height: 20px;
  background-color: var(--brand-dark);
  -webkit-mask: url('../img/mosaek-mark.png') center / contain no-repeat;
          mask: url('../img/mosaek-mark.png') center / contain no-repeat;
}
.lane-close b {
  font-weight: 500;
  color: var(--brand-dark);
}
.lane-close em {
  font-style: normal;
  /* 78%: the second half is the other half of the sentence, at body size, so
     it has to clear AA rather than sit back as a caption. */
  color: color-mix(in srgb, var(--brand-dark) 78%, transparent);
}

/* ---------- The verdict runs as a band ----------
   One statement sitting still in a wide plate left most of the plate empty.
   It repeats instead, travelling slowly across its own rail, so the claim is
   on the card the whole time rather than once. The mark stays put at the
   left: it is what the plate is, not part of what it says.
------------------------------------------------------------------- */
.close-rail {
  min-width: 0;
  overflow: hidden;
  -webkit-mask: linear-gradient(90deg, transparent, #000 1.5rem, #000 calc(100% - 2.5rem), transparent);
          mask: linear-gradient(90deg, transparent, #000 1.5rem, #000 calc(100% - 2.5rem), transparent);
}
.close-track {
  display: flex;
  width: max-content;
  align-items: center;
  gap: var(--space-7);
  will-change: transform;
}
.close-track[data-marquee] {
  animation: close-run var(--marquee-dur, 30s) linear infinite;
}
@keyframes close-run { to { transform: translateX(calc(-1 * var(--marquee-step, 50%))); } }
.close-text { flex: none; }

/* Two of the three are ruled out, and say so with the same mark; only the
   house answer is signed. A slow glow keeps the crosses from reading as
   close buttons. */
.close-ico-x {
  color: var(--accent);
  animation: x-glow 3.4s ease-in-out infinite;
}
@keyframes x-glow {
  0%, 100% { filter: drop-shadow(0 0 0 color-mix(in srgb, var(--accent) 0%, transparent)); opacity: 0.78; }
  50%      { filter: drop-shadow(0 0 5px color-mix(in srgb, var(--accent) 60%, transparent)); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .close-rail { -webkit-mask: none; mask: none; }
  .close-track { animation: none; }
  .close-ico-x { animation: none; opacity: 1; }
}

/* Face down until the row reaches the reading line, then they turn over left
   to right. Gated on the attribute the script sets, so with scripting off
   every plate is simply up. */
.lanes-sec[data-flip] .lane-close {
  transform: rotateX(-88deg);
  opacity: 0;
}
.lanes-sec[data-flip] .lane-close.is-up {
  transform: none;
  opacity: 1;
}
.lanes-sec[data-flip] .lane-col:nth-child(2) .lane-close.is-up { transition-delay: 140ms; }
/* The house answer turns last, and takes a beat longer doing it. */
.lanes-sec[data-flip] .lane-col:nth-child(3) .lane-close.is-up {
  transition-delay: 320ms;
  transition-duration: 860ms;
}

/* The house answer is the one being recommended, so it is not a dashed
   outline: solid sand, and it lifts as it lands. */
.lane-col:last-child .lane-close {
  background: var(--brand-light);
  border: 1.5px solid var(--brand-light);
}
.lane-col:last-child .lane-close b { color: var(--brand-dark); }
.lane-col:last-child .close-ico { color: var(--brand-dark); }
/* 90% on the sand plate: 82% measured 4.44, which is under AA. */
.lane-col:last-child .lane-close em { color: color-mix(in srgb, var(--brand-dark) 90%, transparent); }
.lanes-sec[data-flip] .lane-col:last-child .lane-close.is-up {
  box-shadow: 0 14px 38px color-mix(in srgb, var(--brand-dark) 24%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .lane-col { perspective: none; }
  .lanes-sec[data-flip] .lane-close { transform: none; opacity: 1; transition: none; }
}

/* The track now ends at the foot of the card itself. */
.lanes-sec .lane-track { bottom: var(--space-6); }

/* ---------- The exposure numerals ---------- */
.plate-num { font-size: clamp(3.25rem, 4.6vw, 5rem); }

/* ---------- For Firms: a bigger core, turning both ways ----------
   The outer tick band goes one way, the core's rim the other, and a third
   ring inside the core turns against that again, so the middle of the
   diagram is the busiest part rather than the stillest.
------------------------------------------------------------------- */
@media (min-width: 1081px) {
  .hub-core { width: 35%; }
}
.hub-core::before {
  content: '';
  display: block;
  position: absolute;
  inset: 7%;
  border-radius: 50%;
  border: 1px dashed color-mix(in srgb, var(--brand-light) 22%, transparent);
  animation: hub-turn 34s linear infinite;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .hub-core::before { animation: none; }
}

/* The three direct lines as one block: a flex row each, no inline leading
   between them, so the pitch is the icon plus the gap and nothing else. */
.footer-col .social {
  display: flex;
  padding-block: 0;
  margin-block: 0;
}
.footer-col h4 + .social { margin-top: var(--space-4); }
.footer-col .social + .social { margin-top: 4px; }
/* The row is 42px tall on its own, so the icon comes down a notch too: the
   three lines are one block, and 70px of pitch read as three separate ones. */
.footer-col .social-icon { width: 28px; height: 28px; }

/* ---------- Where we sit: the marks, corrected ----------
   The two outside marks are cut out of the card; the house mark is filled
   sand with its own lift, so the three step from absence to presence.
------------------------------------------------------------------- */
.lanes-sec .lane-us .lane-ico {
  border-style: solid;
  border-width: 1px;
  border-color: color-mix(in srgb, var(--brand-light) 42%, transparent);
  background: color-mix(in srgb, var(--brand-light) 88%, var(--brand-dark));
  color: var(--brand-dark);
}
.lanes-sec .lane-us.is-live .lane-ico {
  background: var(--brand-light);
  border-color: var(--brand-light);
  color: var(--brand-dark);
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--brand-light) 14%, transparent);
}

/* The house card: the brand red carries it, with the shades moving across
   quietly enough that it still reads as one colour rather than three. */
.lanes-sec .lane-us {
  background-image:
    radial-gradient(62% 58% at 28% 22%, color-mix(in srgb, var(--brand-light) 15%, transparent), transparent 70%),
    radial-gradient(54% 56% at 78% 72%, color-mix(in srgb, var(--brand-light) 10%, transparent), transparent 72%),
    radial-gradient(70% 60% at 50% 110%, color-mix(in srgb, var(--brand-light) 6%, transparent), transparent 70%);
}

/* ---------- For Firms: the turning ring sits outside the core ----------
   Inside the core's edge it was lost against the house pattern. It is a
   heavier dashed ring just outside the frame line instead, which is where a
   bezel belongs.
------------------------------------------------------------------- */
.hub-core::before { display: none; }
.hub-core::after {
  inset: -11px;
  border: 2px dashed color-mix(in srgb, var(--brand-light) 30%, transparent);
  animation: hub-turn 40s linear infinite reverse;
}

/* ---------- The footer columns start on the same line ----------
   Direct Lines began 44px below Contact, because the first row carried a
   heading margin the left column's first line does not.
------------------------------------------------------------------- */
.footer-col h4 + .social { margin-top: var(--space-2); }

/* ============================================================
   QUESTIONS — the answers, for people and for machines
   ============================================================ */
/* The last thing before contact, because by here the reader has objections.
   Built on <details>, so it is keyboard-native, works with the script off,
   and every answer is in the markup rather than fetched on open — which is
   the whole point: a crawler and a model have to be able to read it without
   clicking anything.
------------------------------------------------------------------- */
#faq {
  background: none;
  position: relative;
  z-index: 10;
  padding-top: calc(var(--section-y) * 0.6);
  padding-bottom: calc(var(--section-y) + var(--radius-section));
}
#faq::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  background: var(--chrome-surface);
  z-index: -1;
  pointer-events: none;
}
/* A hairline marks the subject change; Leadership shares this surface. */
#faq::before {
  content: '';
  position: absolute;
  top: 0; left: var(--gutter); right: var(--gutter);
  height: 1px;
  background: color-mix(in srgb, var(--brand-dark) 11%, transparent);
}
/* Leadership no longer carries the curve's compensation: this does. */
#team { padding-bottom: var(--section-y); }

.faq-head {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--space-7) var(--space-8);
  align-items: end;
  margin-bottom: var(--space-8);
}
.faq-head .label { margin-bottom: var(--space-6); }
.faq-head .h2 { margin-bottom: 0; color: var(--brand-dark); }
.faq-lede {
  margin-bottom: 0;
  border-left: 4px solid var(--brand-dark);
  padding-left: var(--space-5);
  font-size: var(--step-0);
  line-height: var(--lh-loose);
  color: var(--ink-muted);
}
.faq-lede b { color: var(--brand-dark); font-weight: 500; }

/* ---------- The window ----------
   Forty questions stacked would run several screens. They sit inside a frame
   built to look like the thing the reader has just been asking: a list of
   subjects down one side, the exchange on the other, a bar across the top
   with the mark on it. The conceit is doing an argument's work. The section
   is about what machines say about you, so it is shaped like one.

   Every panel stays in the markup and is hidden with CSS only, never fetched
   on click, so a crawler and a model read all forty answers whichever
   subject happens to be open.
------------------------------------------------------------------- */
.faq-window {
  border: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
  border-radius: 16px;
  background: var(--page-base);
  overflow: hidden;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--brand-dark) 5%, transparent),
              0 18px 48px -32px color-mix(in srgb, var(--brand-dark) 42%, transparent);
}

/* The bar. Not a parody of an operating system: the mark, the name, and a
   count, which is the only honest status a page like this can show. */
.faq-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  background: var(--brand-dark);
  color: var(--brand-light);
}
.faq-bar-mark {
  position: relative;
  width: 22px; height: 22px;
  flex: none;
  border-radius: 50%;
  background: var(--accent-on-dark);
}
.faq-bar-mark::before {
  content: '';
  position: absolute;
  inset: 0;
  -webkit-mask: url('../img/mosaek-mark.png') center / 58% 58% no-repeat;
          mask: url('../img/mosaek-mark.png') center / 58% 58% no-repeat;
  background-color: var(--brand-dark);
}
.faq-bar-name {
  font-family: var(--font-label);
  font-size: var(--step-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
}
.faq-bar-state {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
  font-family: var(--font-label);
  font-size: 0.625rem;
  letter-spacing: 0.06em;
  color: color-mix(in srgb, var(--brand-light) 78%, transparent);
}
.faq-bar-state i {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent-on-dark);
  animation: faq-live 2.4s var(--ease-out) infinite;
}
@keyframes faq-live {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .faq-bar-state i { animation: none; opacity: 1; }
}

.faq-body {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  align-items: stretch;
}

/* ---------- The subjects, down the side ---------- */
.faq-rail {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: var(--space-4) 0;
  border-right: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
  background: color-mix(in srgb, var(--brand-dark) 4%, transparent);
}
.faq-rail button {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 44px;
  padding: 0 var(--space-5) 0 calc(var(--space-5) - 3px);
  border: 0;
  border-left: 3px solid transparent;
  background: none;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-label);
  font-size: var(--step-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
  color: color-mix(in srgb, var(--brand-dark) 86%, transparent);
  transition: background var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.faq-rail button:hover {
  background: color-mix(in srgb, var(--brand-dark) 6%, transparent);
}
.faq-rail button[aria-selected='true'] {
  background: var(--page-base);
  border-left-color: var(--brand-dark);
  color: var(--brand-dark);
  font-weight: 500;
}
.faq-rail button:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }
.faq-tab-ico { display: block; width: 17px; height: 17px; flex: none; opacity: 0.6; }
.faq-tab-ico svg { display: block; width: 100%; height: 100%; }
.faq-rail button[aria-selected='true'] .faq-tab-ico { opacity: 0.95; }
.faq-tab-n {
  margin-left: auto;
  font-family: var(--font-label);
  font-size: 0.625rem;
  color: color-mix(in srgb, var(--brand-dark) 74%, transparent);
}

/* Hidden with CSS, not removed: the answers have to be in the document
   whichever tab is open, because that is what gets read and cited. */
.faq-panes {
  position: relative;
  min-width: 0;
  padding: var(--space-5) var(--space-6);
}
.faq-panel {
  position: absolute;
  inset: 0 0 auto;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease-out), visibility 0s linear var(--dur-slow);
}
.faq-panel.is-active {
  position: relative;
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity var(--dur-slow) var(--ease-out);
}
/* The rows arrive in sequence when a tab is taken. */
.faq-panel.is-active .faq-item {
  animation: faq-row 460ms var(--ease-out) both;
}
.faq-panel.is-active .faq-item:nth-child(1) { animation-delay: 40ms; }
.faq-panel.is-active .faq-item:nth-child(2) { animation-delay: 100ms; }
.faq-panel.is-active .faq-item:nth-child(3) { animation-delay: 160ms; }
.faq-panel.is-active .faq-item:nth-child(4) { animation-delay: 220ms; }
.faq-panel.is-active .faq-item:nth-child(5) { animation-delay: 280ms; }
@keyframes faq-row {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .faq-panel.is-active .faq-item { animation: none; }
}

/* The spine. Every stop on this page hangs off a line, and so does this. */
.faq-list {
  position: relative;
  display: grid;
  padding-left: var(--space-6);
}
.faq-group { display: block; }
.faq-list::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 1px;
  background: color-mix(in srgb, var(--brand-dark) 12%, transparent);
}

.faq-item { border-top: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent); }
.faq-item:first-child { border-top: 0; }
.faq-q {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20px;
  align-items: center;
  column-gap: var(--space-5);
  padding: var(--space-5) 0;
  cursor: pointer;
  list-style: none;
  font-size: var(--step-1);
  font-weight: 500;
  letter-spacing: -0.014em;
  line-height: 1.3;
  color: var(--brand-dark);
  transition: color var(--dur-base) var(--ease-out);
}
.faq-q::-webkit-details-marker { display: none; }
.faq-q:hover { color: color-mix(in srgb, var(--brand-dark) 76%, transparent); }
.faq-q:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px; }

/* The node on the spine, filled once the answer is open. */
.faq-node {
  position: absolute;
  left: calc((var(--space-6) + 4px) * -1);
  top: calc(var(--space-5) + 0.62em);
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--chrome-surface);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--brand-dark) 26%, transparent);
  transition: background 320ms var(--ease-out), box-shadow 320ms var(--ease-out);
}
.faq-item[open] .faq-node {
  background: var(--brand-dark);
  box-shadow: inset 0 0 0 1.5px var(--brand-dark),
              0 0 0 4px color-mix(in srgb, var(--brand-dark) 10%, transparent);
}

/* A plus that becomes a minus, the same mark the ring uses. */
.faq-sign {
  position: relative;
  display: block;
  width: 20px; height: 20px;
  justify-self: end;
}
.faq-sign::before, .faq-sign::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  background: currentColor;
  transform: translate(-50%, -50%);
  border-radius: 1px;
}
.faq-sign::before { width: 13px; height: 1.5px; }
.faq-sign::after {
  width: 1.5px; height: 13px;
  transition: transform 300ms var(--ease-out), opacity 300ms var(--ease-out);
}
.faq-item[open] .faq-sign::after { transform: translate(-50%, -50%) rotate(90deg); opacity: 0; }

/* The panel's height is driven by the Web Animations API, so it needs a
   base height of 0 while shut and nothing fixed while open. */
.faq-a { overflow: hidden; }
.faq-item:not([open]) .faq-a { height: 0; }

/* ---------- The answer arrives as a reply ----------
   Half the point of the section is that these questions are being put to a
   model rather than to a person, so the answer is dressed as one: the mark
   in a circle, three dots for a beat, then the paragraph. It fades in rather
   than typing out, because nobody waits to read a line they came for.

   The paragraph is in the markup either way and is only ever moved by
   opacity and height, so what a crawler or a model reads is unaffected by
   any of this. The dots are decoration and are hidden from the
   accessibility tree.
------------------------------------------------------------------- */
.faq-reply {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr);
  align-items: start;
  gap: var(--space-4);
  padding: 0 var(--space-7) var(--space-6) 0;
}
.faq-avatar {
  position: relative;
  width: 34px; height: 34px;
  border-radius: 50%;
  /* The one speaking gets the accent as its ground and the brand colour as
     its mark, which is the inverse of everything around it. A sand disc was
     the other candidate and all but disappeared into the page. */
  background: var(--accent-on-dark);
  box-shadow: 0 1px 3px color-mix(in srgb, var(--brand-dark) 24%, transparent);
}
.faq-avatar::before {
  content: '';
  position: absolute;
  inset: 0;
  -webkit-mask: url('../img/mosaek-mark.png') center / 56% 56% no-repeat;
          mask: url('../img/mosaek-mark.png') center / 56% 56% no-repeat;
  background-color: var(--brand-dark);
}
.faq-bubble {
  position: relative;
  max-width: 70ch;
  padding: var(--space-5);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 10%, transparent);
  border-radius: 3px 14px 14px 14px;
  background: var(--page-base);
}
.faq-bubble p {
  margin: 0;
  font-size: var(--step-0);
  line-height: var(--lh-loose);
  color: var(--ink-muted);
  transition: opacity 420ms var(--ease-out);
}
/* While the beat runs, the paragraph gives up its space to the dots so the
   bubble opens at the height of a reply being written, not a finished one. */
.faq-reply.is-thinking .faq-bubble p {
  opacity: 0;
  height: 0;
  overflow: hidden;
}

.faq-dots { display: none; }
.faq-reply.is-thinking .faq-dots {
  display: flex;
  align-items: center;
  gap: 5px;
  height: calc(var(--step-0) * var(--lh-loose));
}
.faq-dots i {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--brand-dark) 48%, transparent);
  animation: faq-dot 1.1s var(--ease-out) infinite;
}
.faq-dots i:nth-child(2) { animation-delay: 160ms; }
.faq-dots i:nth-child(3) { animation-delay: 320ms; }
@keyframes faq-dot {
  0%, 60%, 100% { opacity: 0.3; transform: none; }
  30%           { opacity: 1;   transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .faq-dots i { animation: none; }
}

/* Below this the window loses its side column: the subjects lie down into a
   strip across the top and scroll sideways. Same markup, same tablist, and
   the names stay, because an icon alone cannot tell Removals from Search. */
@media (max-width: 880px) {
  .faq-head { grid-template-columns: 1fr; align-items: start; gap: var(--space-5); }
  .faq-body { grid-template-columns: minmax(0, 1fr); }
  .faq-rail {
    flex-direction: row;
    gap: 0;
    padding: 0;
    border-right: 0;
    border-bottom: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-mask: linear-gradient(90deg, #000 calc(100% - 40px), transparent);
            mask: linear-gradient(90deg, #000 calc(100% - 40px), transparent);
  }
  .faq-rail:not([data-overflow]) { -webkit-mask: none; mask: none; }
  .faq-rail::-webkit-scrollbar { display: none; }
  .faq-rail button {
    width: auto;
    flex: none;
    min-height: 46px;
    padding: 0 var(--space-4);
    gap: var(--space-2);
    border-left: 0;
    border-bottom: 3px solid transparent;
  }
  .faq-rail button[aria-selected='true'] {
    border-left-color: transparent;
    border-bottom-color: var(--brand-dark);
  }
  .faq-tab-n { display: none; }
  .faq-panes { padding: var(--space-4) var(--space-5); }
  .faq-list { padding-left: var(--space-5); }
  .faq-node { left: calc((var(--space-5) + 4px) * -1); }
  .faq-q { font-size: var(--step-0); }
  .faq-reply { padding-right: 0; }
}
@media (max-width: 560px) {
  /* The circle is the whole joke, so it stays; the gutter it sits in does not. */
  .faq-reply { grid-template-columns: 28px minmax(0, 1fr); gap: var(--space-3); }
  .faq-avatar { width: 28px; height: 28px; }
  .faq-bubble { padding: var(--space-4); }
  .faq-window { border-radius: 12px; }
  .faq-panes { padding: var(--space-3) var(--space-4); }
  .faq-list { padding-left: var(--space-4); }
  .faq-node { left: calc((var(--space-4) + 4px) * -1); }
}

/* The swipe counters belong to the phone rails and to nothing else. Hidden
   by default so they never appear beside a desktop grid; the phone block
   below turns them back on. */
.mrail-meter { display: none; }

/* The scrolled-state section readout beside the menu button. Phone only. */
.nav-now { display: none; }
/* The deck's counter belongs to the phone deck and nothing else. */
.deck-meter { display: none; }
/* The service marks are a phone affordance; the desktop cards do without. */
.cap-ico { display: none; }
/* The fold control belongs to the phone's shortened answer groups. The
   script writes one into every long panel at any width, so it is hidden here
   and the phone block turns it back on. */
.faq-more { display: none; }

/* ============================================================
   PHONE — everything below 760px
   ============================================================ */
/* One block, at the end of the sheet, for every phone-only override.

   Not because the rules belong together thematically, but because they kept
   losing. A media query adds no specificity, so a phone rule written beside
   the component it adjusts is beaten by any later rule of equal weight
   elsewhere in nine thousand lines. Written last, it cannot be. Nothing in
   here applies above 760px, so the desktop layout is untouched by all of it.
   ------------------------------------------------------------------ */
@media (max-width: 759px) {

  /* ---------- Hero: the headline takes the screen ----------
     clamp(2rem, 0.2rem + 4.3vw, 4.5rem) resolves to its 32px floor on a
     phone, because 4.3vw of 412px is nothing. The floor is the problem, not
     the formula: this is the first thing anyone sees and it was set at list
     size. It runs on vw here with a floor high enough to matter. */
  .hero h1 {
    font-size: clamp(2.5rem, 10.6vw, 3.5rem);
    line-height: 1.04;
    letter-spacing: -0.028em;
    margin-bottom: var(--space-6);
  }
  /* The second line is pushed right on desktop to step down and across. At
     phone width that push costs it a line break, so it sits back. */
  .hero h1 .head-answer { padding-left: 0; text-align: left; }

  /* The three calls become one. The other two are destinations further down
     a page the reader is already scrolling; on a phone they competed with
     the only one that matters. Both stay in the markup and in the dock. */
  .hero-actions-row .btn-ghost,
  .hero-actions-row .btn-third { display: none; }

  /* ---------- Hero: the promise band sits under the buttons ----------
     .hero-proof carries grid-row: 3 from its own rule further up, which put
     it in the same row as the three calls and straight on top of them. */
  .hero-foot > .hero-proof {
    grid-row: 4;
    margin-top: var(--space-7);
    padding-top: var(--space-6);
  }
  .hero-foot > .hero-actions-row { grid-row: 3; }
  .hero-foot > .hero-intro { grid-row: 2; }

  /* ---------- The bar says where you are ----------
     The stop-name used to hang under the dock as a tab, which on a phone sat
     under a closed menu and read as a stray chip. It moves into the bar
     instead: once you are past the hero, the mark and the name of the
     section you are in sit to the left of the menu button.
  ------------------------------------------------------------------- */
  .dock-subs, .dock-trail { display: none !important; }

  /* Inside the button, not beside it: two pills in a 412px bar read as two
     controls and only one of them did anything. The readout is the button's
     own label now, and the whole pill opens the menu. */
  .nav-toggle { width: auto; min-width: 48px; padding: 0; }
  .chrome.is-scrolled .nav-toggle { padding: 0 var(--space-4); }
  .nav-toggle > svg { order: -1; flex: none; }
  .nav-now {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    max-width: 0;
    margin-left: 0;
    overflow: hidden;
    white-space: nowrap;
    opacity: 0;
    font-family: var(--font-label);
    font-size: var(--step-label);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    transition: max-width 420ms var(--ease-out), opacity 300ms var(--ease-out),
                margin 420ms var(--ease-out);
  }
  .chrome.is-scrolled .nav-now {
    max-width: 11rem;
    margin-left: var(--space-3);
    opacity: 1;
  }
  /* Open, the label would be naming a section the sheet is already covering. */
  .nav-toggle[aria-expanded='true'] .nav-now { max-width: 0; opacity: 0; margin-left: 0; }
  .nav-now-ico { display: block; width: 16px; height: 16px; flex: none; opacity: 0.7; }
  .nav-now-ico svg { display: block; width: 100%; height: 100%; }
  .nav-now-name { font-weight: 500; }
  .chrome[data-right-dark='true'] .nav-now { color: var(--brand-light); }

  /* The sheet holds eight stops and a call. It does not need its own
     scrollbar on top of the page's. */
  .pills.dock {
    max-height: none;
    overflow-y: visible;
  }

  /* ---------- The swipe rail ----------
     The desktop answer to a long set of cards is a pinned viewport that
     drives them sideways on scroll. That does not survive a touch screen, so
     the phone gets the native equivalent: a snapping horizontal scroller,
     bled to the screen edges so a card's shoulder shows and the reader knows
     there is more. One pattern, used by every set that runs long.
  ------------------------------------------------------------------- */
  [data-mrail] {
    display: flex;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: var(--space-4);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Out through the section gutter and back in as padding, so the first
       card starts on the text's left edge and the last can still reach it. */
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
    padding-block: var(--space-2) var(--space-5);
  }
  [data-mrail]::-webkit-scrollbar { display: none; }
  [data-mrail] > * {
    flex: 0 0 var(--mrail-w, 84%);
    min-width: 0;
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }

  /* The counter under a rail: where you are in the set, and how many. */
  .mrail-meter {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-3);
    font-family: var(--font-label);
    font-size: var(--step-label);
    letter-spacing: var(--track-label);
    color: color-mix(in srgb, var(--brand-dark) 74%, transparent);
  }
  .on-dark .mrail-meter,
  [data-mrail-dark] + .mrail-meter {
    color: color-mix(in srgb, var(--brand-light) 80%, transparent);
  }
  .mrail-meter-bar {
    position: relative;
    flex: 1 1 auto;
    height: 2px;
    border-radius: 2px;
    background: color-mix(in srgb, currentColor 22%, transparent);
  }
  .mrail-meter-bar > i {
    position: absolute;
    top: 0; bottom: 0; left: 0;
    width: var(--mrail-p, 25%);
    border-radius: 2px;
    background: currentColor;
    transition: width 220ms var(--ease-out), transform 220ms var(--ease-out);
    transform: translateX(var(--mrail-x, 0));
  }

  /* ---------- What it reaches: the pinned rail, at phone scale ----------
     The same mechanic the desktop uses: the section is made as tall as the
     track is wide, it holds while the plates run past, then the page carries
     on. The script drives both; only the sizes differ here.
  ------------------------------------------------------------------- */
  .hrail { position: relative; }
  .hrail-vp {
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100vh;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    align-content: start;
  }
  .hrail.is-pinned .hrail-vp { position: fixed; top: 0; left: 0; }
  .hrail.is-past  .hrail-vp { top: auto; bottom: 0; }
  /* Clears the bar while the section is pinned to the top of the screen. */
  .exposure .hrail-vp .exposure-head {
    padding-top: calc(48px + var(--space-6) + var(--space-5));
    padding-bottom: 0;
  }
  .hrail-scroller {
    display: flex;
    align-items: center;
    overflow: hidden;
    min-height: 0;
    /* Even above and below, so the plate sits in the middle of what is left
       of the screen rather than crowding the counter under it. */
    padding-block: var(--space-6);
  }
  .hrail-track {
    display: flex;
    align-items: center;
    gap: var(--space-5);
    padding-inline: var(--gutter);
    will-change: transform;
  }
  /* 78vw leaves the next plate's shoulder showing without the live one
     reaching either edge: the first starts on the gutter and the last can
     still come to rest on it. */
  .plates-rail.hrail-track .plate {
    flex: 0 0 78vw;
    transform: none;
  }
  .hrail-meter { display: block; margin-top: var(--space-5); }

  .plates-rail .plate {
    padding: var(--space-5);
    border-radius: var(--radius-media);
    border: 1px solid color-mix(in srgb, var(--brand-dark) 10%, transparent);
    background: transparent;
    color: var(--brand-dark);
    transition: background 420ms var(--ease-out),
                color 420ms var(--ease-out),
                border-color 420ms var(--ease-out);
  }
  .plates-rail .plate.is-active {
    background: var(--brand-dark);
    border-color: var(--brand-dark);
    color: var(--brand-light);
  }
  .plates-rail .plate.is-active .plate-cat {
    color: var(--brand-light);
    border-color: color-mix(in srgb, var(--brand-light) 34%, transparent);
  }
  .plates-rail .plate.is-active .plate-num { color: color-mix(in srgb, var(--brand-light) 42%, transparent); }
  .plates-rail .plate.is-active .plate-cost { color: var(--brand-light); }
  .plates-rail .plate.is-active .plate-body { color: var(--on-dark); }
  /* The drawing was sitting a long way back in its own frame. It is set
     larger and cropped, so the plate leads with the picture. */
  .plates-rail .plate-art {
    width: 100%;
    height: 148px;
    -webkit-mask-size: 128% 128%;
            mask-size: 128% 128%;
    -webkit-mask-position: center;
            mask-position: center;
  }

  /* ---------- The shift: the orb sits centred in its own air ----------
     The gap above came from the grid's row-gap (32px) and the gap below from
     the grid's margin-bottom (55px), so the globe sat high between the lede
     and the rule under it. One value drives both now. */
  .bigger-grid {
    --orb-air: 2.5rem;
    row-gap: var(--orb-air);
    margin-bottom: var(--orb-air);
  }

  /* ---------- Where we sit: the mark centres on the title ----------
     The mark is absolute against the lane and the title is in flow, so the
     two were never tied together and the title rode 12px high of the mark's
     centre. The offset is held in one variable and the timeline that hangs
     below the mark is derived from it, so they cannot drift apart. */
  .lane { --lane-ico-top: calc(var(--space-6) - 12px); }
  .lane .lane-ico { top: var(--lane-ico-top); }
  .lane .lane-track { top: calc(var(--lane-ico-top) + 38px + 12px); }

  /* ---------- The other four sets that run long ----------
     Each was a column of full-width cards the reader had to scroll past to
     reach the next section. They swipe instead, on the same rail.
  ------------------------------------------------------------------- */

  /* The three figures of the shift: short cards, so more than one shows. */
  .shift-figs {
    --mrail-w: 72%;
    border-top: 0;
    padding-top: 0;
  }
  .shift-figs .shift-fig {
    padding: var(--space-5);
    border: 1px solid color-mix(in srgb, var(--brand-light) 20%, transparent);
    border-radius: var(--radius-media);
  }

  /* What the machine does to you: four, and each one is a paragraph. */
  .bigger-list { --mrail-w: 86%; }
  .bigger-list .bigger-item {
    padding: var(--space-5);
    border: 1px solid color-mix(in srgb, var(--brand-light) 18%, transparent);
    border-top: 1px solid color-mix(in srgb, var(--brand-light) 18%, transparent);
    border-radius: var(--radius-media);
  }

  /* ---------- What we actually run: the deck ----------
     Seven cards, one on top of the other, and the page's own scroll deals
     them: the front card lifts away and the next comes forward. Each sits
     at a slightly different angle so the pile reads as a hand that has been
     spread rather than a stack of paper.

     Same pinning mechanic as the rail above it, for the same reason: the
     body carries overflow-x:hidden, which disqualifies position:sticky on
     every descendant.
  ------------------------------------------------------------------- */
  .deck { position: relative; }
  .deck-vp {
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100vh;
    display: grid;
    align-content: center;
    padding-inline: var(--gutter);
    padding-top: 48px;
  }
  .deck.is-pinned .deck-vp { position: fixed; top: 0; left: 0; }
  .deck.is-past  .deck-vp { top: auto; bottom: 0; }

  .caps {
    position: relative;
    display: block;
    height: var(--deck-h, 60vh);
    margin: 0;
    padding: 0;
    overflow: visible;
    perspective: 1200px;
  }
  /* The connector drawn behind the column has no column to run down. */
  .caps-path, .caps-path-track, .caps-path-live, .caps-path-pulse { display: none; }

  .caps .cap {
    position: absolute;
    inset: 0;
    display: block;
    padding: var(--space-5);
    border-radius: var(--radius-media);
    border: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
    background: var(--page-base);
    box-shadow: 0 18px 40px -28px color-mix(in srgb, var(--brand-dark) 60%, transparent);
    transform-origin: 50% 100%;
    transform: translate3d(0, var(--dy, 0px), 0) scale(var(--sc, 1)) rotate(var(--rot, 0deg));
    opacity: var(--op, 1);
    z-index: var(--z, 1);
    transition: none;
    overflow: hidden;
  }
  .caps .cap::before, .caps .cap::after { content: none; }
  /* The card in hand takes the burgundy the desktop gives the live one. */
  .caps .cap.is-front {
    background: var(--brand-dark);
    border-color: var(--brand-dark);
    color: var(--brand-light);
  }
  .caps .cap.is-front .cap-name { color: var(--brand-light); }
  .caps .cap.is-front .cap-text { color: var(--on-dark); }
  .caps .cap.is-front .cap-num { color: color-mix(in srgb, var(--brand-light) 30%, transparent); }
  .caps .cap.is-front .cap-tags li {
    background: color-mix(in srgb, var(--brand-light) 14%, transparent);
    color: var(--brand-light);
  }

  /* ---------- The card's own order ----------
     The kicker was a chip across the top and never fitted on one line. It
     goes to the foot of the card, under a rule, where there is width for it
     and where it reads as the result rather than the label. */
  .caps .cap-body { display: flex; flex-direction: column; height: 100%; }
  .caps .cap-chip {
    order: 9;
    margin: auto 0 0;
    padding: var(--space-3) 0 0;
    border-top: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
    font-size: 0.6875rem;
    letter-spacing: 0.05em;
    color: var(--accent);
    background: none;
    border-radius: 0;
    max-width: none;
    text-align: left;
  }
  .caps .cap.is-front .cap-chip {
    border-top-color: color-mix(in srgb, var(--brand-light) 26%, transparent);
    color: var(--accent-on-dark);
  }
  .caps .cap-name { order: 1; }
  .caps .cap-text { order: 2; }
  .caps .cap-tags { order: 3; margin-bottom: var(--space-4); }

  /* The deck's own counter, under the pile. */
  .deck-meter {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-5);
    font-family: var(--font-label);
    font-size: var(--step-label);
    letter-spacing: var(--track-label);
    color: color-mix(in srgb, var(--brand-dark) 74%, transparent);
  }
  .deck-meter-bar {
    position: relative; flex: 1 1 auto; height: 2px; border-radius: 2px;
    background: color-mix(in srgb, var(--brand-dark) 20%, transparent);
  }
  .deck-meter-bar > i {
    position: absolute; inset: 0 auto 0 0;
    width: var(--deck-p, 14%);
    border-radius: 2px; background: var(--brand-dark);
  }

  /* Case studies. */
  .cases { --mrail-w: 88%; }
  .cases .case { border-radius: var(--radius-media); overflow: hidden; }

  /* ---------- Cards give their width back to the words ----------
     A desktop card interior keeps a numeral gutter, an icon column and 24px
     of padding on every side. On a 412px screen that left a 325px card with
     a 215px text column: a third of the card spent on furniture, and prose
     breaking at 22 characters. The decorative numeral becomes a watermark in
     the corner, the padding comes down, and the measure goes up by about a
     third on every card in the page.
  ------------------------------------------------------------------- */
  .cap, .plate, .bigger-item, .shift-fig, .case-head, .thread {
    padding: var(--space-4);
  }
  .cap {
    position: relative;
    display: block;
  }
  .cap .cap-num {
    position: absolute;
    top: var(--space-4);
    right: var(--space-4);
    margin: 0;
    opacity: 0.3;
    pointer-events: none;
  }
  .cap .cap-body { width: 100%; }
  /* The chip is the only line that reaches the numeral's corner. */
  .cap .cap-chip { padding-right: 3rem; }

  /* The clock mark rides with the heading rather than holding its own
     column down the whole card. */
  .bigger-item {
    display: grid;
    grid-template-columns: 20px minmax(0, 1fr);
    column-gap: var(--space-3);
  }

  /* ---------- A section's name sits near its top ----------
     --section-y is 48px here and several heads add their own padding on top
     of the section's, so the label floated a long way below the seam it
     belongs to. The heads give their padding back; the section keeps its. */
  .exposure-head { padding-top: var(--space-6); }
  .section > .label:first-child,
  .section > .faq-head,
  .proof-head, .svc-intro, .seed-head, .bigger-head { padding-top: 0; }
  .label { margin-bottom: var(--space-4); }

  /* ---------- The Influence Engine reads in the order it works ----------
     Tabs, then what the movement is, then it running, then what it covers.
     The name card and the detail card are both inside .svc-rail, so the rail
     steps out of the box model and lets its two children be ordered against
     the animation that sits between them. */
  .svc-pane {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }
  .svc-pane .svc-rail { display: contents; }
  .svc-pane .svc-card:not(.svc-detail) { order: 1; }
  .svc-pane .svc-media { order: 2; }
  .svc-pane .svc-card.svc-detail { order: 3; }

  /* ---------- The service's own mark, beside its name ---------- */
  .caps .cap-name {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
  }
  .cap-ico { display: block; width: 19px; height: 19px; flex: none; opacity: 0.72; align-self: center; }
  .cap-ico svg { display: block; width: 100%; height: 100%; }
  .caps .cap.is-front .cap-ico { opacity: 0.95; }

  /* ---------- The thread's source sits on the card's edge ----------
     Where it came from straddles the left border and what it is sits hard
     right, so the two read as a stamp on the card rather than a row of
     chips inside it. */
  .thread { overflow: visible; }
  .thread .thread-bar {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    row-gap: var(--space-3);
  }
  .thread .thread-src {
    grid-column: 1; grid-row: 1;
    margin-left: calc(-1 * (var(--space-4) + 14px));
    box-shadow: 0 2px 12px color-mix(in srgb, var(--brand-dark) 34%, transparent);
  }
  /* "Coordinated flood" is twice the length of "Viral claim" and was
     breaking across two lines inside its own pill. The kind takes whatever
     width it needs on its own row-end and stops wrapping; below 360px it
     drops to its own line rather than squeezing the source off the edge. */
  .thread .thread-kind {
    grid-column: 3; grid-row: 1;
    justify-self: end;
    white-space: nowrap;
    font-size: 0.625rem;
    padding: 4px var(--space-3) 3px;
  }
  .thread .thread-sector { grid-column: 1 / 3; grid-row: 2; justify-self: start; }
  .thread .thread-clock { grid-column: 3; grid-row: 2; margin-left: 0; justify-self: end; white-space: nowrap; }

  /* The sub-line under a call sat on two lines inside a button that had the
     width for one. */
  .btn-sub, .adv-cta .btn-sub { white-space: nowrap; font-size: 0.625rem; letter-spacing: 0.02em; }

  /* ---------- Seams between sections ----------
     On a wide screen a change of subject is carried by the air around it. At
     phone width the air shrinks faster than the type does, so the sections
     began running into one another. Each of these gets the air back and a
     rule to land on. */
  #seeding, #work, #how-we-work {
    margin-top: var(--space-7);
    /* !important only because two of these three carry an inline
       padding-top:0 that the desktop layout needs and nothing in a
       stylesheet can outrank. Scoped to phones, so desktop keeps its zero. */
    padding-top: var(--section-y) !important;
    border-top: 1px solid color-mix(in srgb, var(--brand-dark) 12%, transparent);
  }
  .on-dark#seeding, .on-dark#how-we-work {
    border-top-color: var(--divider-on-dark);
  }

  /* ---------- Swaying the narrative: the head needs room ----------
     Label, headline and standfirst were sitting on top of one another. */
  .seed-head .label { margin-bottom: var(--space-5); }
  .seed-head .h2 { margin-bottom: var(--space-5); }
  .seed-lede { margin-top: var(--space-5); }

  /* ---------- Ethical by default: the seal sits beside its text ----------
     The two-column grid collapses to one below 900, which stacked a 168px
     seal above a paragraph and cost most of a screen. The seal comes down to
     a size that fits in a row with the copy. */
  .seed-foot {
    grid-template-columns: 88px minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
    justify-items: stretch;
  }
  .seed-foot .ethic-stamp { width: 88px; height: 88px; }
  .seed-foot .ethic-stamp svg { width: 100%; height: 100%; }

  /* ---------- Advisors: the mark survives the ring ----------
     The radial web is a desktop layout: eight firms around a centre, joined
     by spokes. On a phone it becomes a list, and hiding the web took the
     centre with it, so the section lost the one image it had.

     The core comes back as a medallion above the list. Same mark, same
     breathing echo, placed as a header rather than a hub, so the section
     still opens on the house mark the desktop opens on.
  ------------------------------------------------------------------- */
  .hub-core {
    display: grid;
    position: relative;
    top: auto; left: auto;
    transform: none;
    width: 124px;
    margin: 0 auto var(--space-6);
    padding: 0;
    border-color: color-mix(in srgb, var(--brand-light) 24%, transparent);
    background: color-mix(in srgb, var(--brand-dark) 55%, transparent);
  }
  /* Two rings outside the disc, the same device the lane's mark carries. */
  .hub-core::before, .hub-core::after {
    content: '';
    position: absolute;
    left: 50%; top: 50%;
    border-radius: 50%;
    border: 1px solid color-mix(in srgb, var(--brand-light) 16%, transparent);
    transform: translate(-50%, -50%);
    pointer-events: none;
  }
  .hub-core::before { width: 150px; height: 150px; }
  .hub-core::after {
    width: 178px; height: 178px;
    border-style: dashed;
    border-color: color-mix(in srgb, var(--brand-light) 11%, transparent);
    animation: hub-turn 44s linear infinite;
  }
  @keyframes hub-turn { to { transform: translate(-50%, -50%) rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) {
    .hub-core::after { animation: none; }
  }
  /* The ring needs room for the rings drawn outside the disc. */
  .hub { padding-top: var(--space-6); }

  /* ---------- Answers: the long groups fold ----------
     Removals runs to thirteen questions and Influence to twelve. Unfolded,
     one subject was most of a phone screen's worth of scrolling before the
     next section. Six show, the rest are behind a control.

     Every answer stays in the document exactly as it was: this hides rows
     with CSS, it does not remove them, so a crawler and a model still read
     all eighty-one whichever subject is open. */
  .faq-list .faq-item:nth-child(n + 7) { display: none; }
  .faq-panel.is-expanded .faq-list .faq-item:nth-child(n + 7) { display: block; }
  .faq-more {
    display: none;
    width: 100%;
    margin-top: var(--space-4);
    padding: var(--space-4);
    border: 1px solid color-mix(in srgb, var(--brand-dark) 16%, transparent);
    border-radius: var(--radius-md);
    background: none;
    cursor: pointer;
    font-family: var(--font-label);
    font-size: var(--step-label);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--brand-dark);
  }
  .faq-panel[data-long] .faq-more { display: block; }

  /* ---------- Leadership: the column stops outgrowing its container ----------
     .people drops to a single `1fr` below 720, and a bare 1fr is
     minmax(auto, 1fr): the floor is the min-content of a .person, which at
     24px of padding a side came to 360px inside a 334px container. The same
     trap the hero foot had. */
  .people { grid-template-columns: minmax(0, 1fr); }
  .people > * { min-width: 0; }

  /* ---------- Leadership: the stat cards run as a band ----------
     Three cards stacked took a third of a screen each to say four words.
     They run sideways instead, on the same rail the rest of the page uses. */
  .person-stats {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--space-3);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
    padding-block: var(--space-2) var(--space-4);
  }
  .person-stats::-webkit-scrollbar { display: none; }
  .person-stats .pstat {
    flex: 0 0 64%;
    min-width: 0;
    scroll-snap-align: start;
    display: grid;
    align-content: start;
    gap: var(--space-2);
    padding: var(--space-4);
  }
  .person-stats .pstat-figure { font-size: var(--step-2); line-height: 1.1; }
  .person-stats .pstat-body { font-size: 0.75rem; line-height: 1.35; }

  /* ---------- Leadership: the roles label gets its own air ----------
     It sat straight under the headline and read as a third line of it. */
  .pedigree-label, .team .label + .pedigree, .pedigree-head {
    margin-top: var(--space-7);
  }
  #team .label { margin-top: var(--space-6); }

  /* ---------- Advisors: the three terms hold one row ----------
     Wrapping put two on one line and one orphaned beneath. They are three
     short promises and they read as a set. */
  /* Three columns put each term in 115px and broke both its lines. They run
     as three rows instead, each one line, which is shorter than the wrapped
     grid was and reads in one pass. */
  .adv-terms {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-3);
  }
  /* The grid put the promise on one row and its note on the next, so every
     term took two lines. The tick comes out of the flow and the two halves
     run as one sentence. */
  .adv-terms li {
    display: block;
    position: relative;
    padding-left: 22px;
    font-size: 0.75rem;
    line-height: 1.45;
  }
  .adv-terms li .adv-tick { position: absolute; left: 0; top: 0.3em; }
  .adv-terms li::before { content: none; }
  .adv-terms b { display: inline; font-size: inherit; }
  .adv-terms b::after { content: ' \00b7 '; opacity: 0.55; }
  .adv-terms li > i { display: inline; font-style: normal; font-size: inherit; }

  /* ---------- Who we work with: the order a phone reads in ----------
     Scroller, then the sector's own card, then the picture, then the two
     detail cards side by side. The picture stops being the first thing and
     the name of the sector stops being the last. */
  .sector-pane {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
  }
  .sector-pane .sector-media { order: 2; }
  /* Side by side was the wrong call: at two columns the work's paragraph
     broke at 22 characters and the client chips wrapped one per line, which
     is taller than stacking them was. Full width each, and the chips flow
     two and three to a row, which is where the height actually comes back. */
  .sector-pane .sector-rail {
    order: 3;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-3);
  }
  .sector-pane .sector-card { padding: var(--space-4); }
  .sector-pane .sector-card .tags { gap: 6px; }
}
