/* ============================================================
   DIRECTION B — CORRIDOR · component styles
   One emerald on a white sheet. Paths, nodes, travel.
   ============================================================ */

/* ---------------- primitives ---------------- */

/* The section label has been four things. A green dot and loose mono
   caps, which marked the line but could not hold the hero's three
   service names apart. Then a pill, which could — and which then had to
   be the only pill on the page, because a pill is a strong shape and the
   eye counts them. Then mono caps with a short hairline stub running off
   the end of the words: quiet, unmistakably not clickable, and — on a
   1600px measure — a small object floating in the corner of a very wide
   empty band.

   It is a rail now. The hairline is not a stub that stops after three
   centimetres; it crosses the whole measure and closes on the section's
   own number, so the label reads as the head of a numbered clause rather
   than as a caption nobody placed. A 2px accent bar stands in the margin
   at the left of it — the mark a reviewer puts beside a clause, not a
   bullet — and it is the one part of the device that carries colour at
   full strength. The rail is drawn the same way the corridor spans are,
   it cannot be mistaken for a thing you can click, and it leaves the
   pill to mean one thing: a named party. */
.eyebrow {
  font-family: var(--mono);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
  display: flex; align-items: center; gap: 14px;
  /* the rail spends the column it is given; the words inside it do not */
  width: 100%; max-width: 100%;
  align-self: start;
}
/* the margin mark */
.eyebrow::before {
  content: ""; flex: none;
  width: 2px; align-self: stretch; min-height: 1.15em;
  background: var(--accent);
}
/* the rail. It is painted as a background rather than as a border so it
   can be drawn: where the head reveals, the line runs out from the words
   to the number instead of appearing at full length. */
.eyebrow::after {
  content: ""; flex: 1 1 auto; min-width: 20px; height: 1px;
  background-image: linear-gradient(90deg, var(--accent-dim), var(--rule) 42%);
  background-repeat: no-repeat;
  background-position: left center;
  background-size: 100% 1px;
}

/* the multi-part label: the container is only a row, each part is a pill,
   and three pills in a row are their own mark — the rail would be a
   fourth object on the first screen. */
.eyebrow--parts {
  display: inline-flex; flex-wrap: wrap; gap: 8px;
  width: fit-content;
}
.eyebrow--parts::before, .eyebrow--parts::after { content: none; }
.eyebrow--parts > span {
  padding: 6px 13px;
  border: 1px solid var(--accent-dim);
  border-radius: 999px;
  background: var(--accent-wash);
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.02;
  color: var(--ink);
  text-wrap: balance;
}
h1 { font-size: var(--fs-display); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); line-height: 1.16; letter-spacing: -0.018em; }

.lede { font-size: var(--fs-lede); line-height: 1.6; color: var(--ink-2); max-width: 60ch; text-wrap: pretty; }
p { text-wrap: pretty; }
.muted { color: var(--ink-2); }
.mono { font-family: var(--mono); font-size: var(--fs-mono); letter-spacing: .01em; }

/* ---------------- the section head ----------------

   The label, the headline, and the line under it that says who the
   section is for or what it covers. Stacked, all three sat in the left
   third of a 1600px page with half a screen of nothing beside them: the
   headline had air, but the composition had a hole in it. Above 1100 the
   head splits — the rail crosses the full measure, the headline takes the
   left column, and whatever follows it (the lede, the legal note) takes
   the right, set at the headline's own top. Nothing is centred and
   nothing is stretched; the block simply spends the width it was given,
   and the second column gives the eye somewhere to land before the
   content starts.

   The sections are numbered because the page is one document, not ten
   posters. The number is the quietest thing in the head and the only
   thing in it that says the reader is at 03 of 10 rather than adrift. */
main { counter-reset: sec; }

.sec-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(18px, 2.2vw, 30px);
  margin-bottom: clamp(52px, 6vw, 96px);
  counter-increment: sec;
  /* where the head is a cell in a taller grid — About, Legal — the rows
     would otherwise take the spare height and push the rail a hand's
     width off the headline it belongs to */
  align-content: start;
}
.sec-head h2 { max-width: 18ch; }
.sec-head--wide h2 { max-width: 24ch; }

/* A head that ends in the audience band needs less air under it than one
   that ends in a sentence. The band closes on a rule, so the section gap
   below it lands under a line that has already ended the head — two
   endings, one after the other, and the cards drift away from the
   headline they belong to. */
.sec-head:has(> .aud) { margin-bottom: clamp(28px, 3vw, 48px); }

/* The number closes the rail. The pseudo keeps the flex:1 the primitive
   gives it, so the hairline is painted as its background and stops four
   characters short of the right edge, where the digits begin. */
.sec-head > .eyebrow::after, .wmap__case-t > .eyebrow::after {
  content: counter(sec, decimal-leading-zero);
  height: auto; align-self: center;
  color: var(--ink-3);
  letter-spacing: .12em;
  font-variant-numeric: tabular-nums;
  text-align: right;
  background-size: calc(100% - 4ch) 1px;
}

@media (min-width: 1100px) {
  .sec-head {
    grid-template-columns: minmax(0, 1.04fr) minmax(0, .96fr);
    column-gap: clamp(40px, 5vw, 96px);
    align-items: start;
  }
  .sec-head > .eyebrow { grid-column: 1 / -1; }
  .sec-head > h2 { grid-column: 1; }
  /* anything that is not the label and not the audience band is the
     section's own sentence, and it sits beside the headline */
  .sec-head > p:not(.eyebrow, .aud) { grid-column: 2; margin-top: .34em; max-width: 54ch; }
  .sec-head > .aud { grid-column: 1 / -1; }

  /* Two heads are already inside a column - About and Legal hand their
     right-hand side to the body text beside them — so they keep the
     stack. Splitting a .85fr column again gives a headline four lines
     deep and a note two words wide. */
  :is(.who__in, .legal__in) > .sec-head { grid-template-columns: minmax(0, 1fr); }
  :is(.who__in, .legal__in) > .sec-head > :is(h2, p) { grid-column: 1; max-width: none; }
  :is(.who__in, .legal__in) > .sec-head > h2 { max-width: 18ch; }
  :is(.who__in, .legal__in) > .sec-head > p:not(.eyebrow) { margin-top: 0; max-width: 38ch; }
}

/* The hero and the credentials strip pad nothing against their
   neighbours, so the side that touches them takes the whole gap. */
.hero + .section { padding-top: var(--section-gap); }
.creds { margin-block: var(--section-y); }

.chip {
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-2);
  border: 1px solid var(--rule); background: var(--surface);
  padding: 5px 10px; border-radius: var(--radius);
  display: inline-block; white-space: nowrap;
}
.chip--accent { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-wash); }

/* ---------------- buttons ---------------- */

/* One button, one arrow. The island's CTA carried its arrow in a round
   token inset in the pill — a small toggle riding at the end of the
   button — and every other button carried a bare → glyph in the text
   flow. Two different answers to the same gesture on the same page.
   The token is the one that survives: it reads as a control rather than
   as punctuation, and it is the shape the header already taught. So
   .btn now builds exactly what .cta-head builds, at button scale, and
   both primary and secondary carry it. The right padding drops to 5px
   because the token, not the label, is what sits against that edge. */
.btn {
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--display); font-weight: 700; font-size: .9375rem;
  letter-spacing: -0.005em;
  padding: 5px 5px 5px 24px; border-radius: 999px;
  background: var(--accent); color: var(--on-accent); border: 1px solid var(--accent);
  min-height: 48px; white-space: nowrap;
  transition: background .18s ease, box-shadow .18s ease, transform .18s ease;
}
.btn:hover { background: var(--accent-hi); border-color: var(--accent-hi); box-shadow: 0 0 32px rgba(53,217,160,.28); }

.btn__arw {
  display: grid; place-items: center; flex: none;
  width: 36px; aspect-ratio: 1;
  border-radius: 999px; background: var(--on-accent-wash);
  transition: background .18s ease, transform .22s cubic-bezier(.22,.61,.36,1);
}
.btn__arw svg { width: 15px; height: 15px; display: block; }
.btn:hover .btn__arw { background: var(--on-accent-wash-hi); transform: translateX(2px); }

/* a button with no arrow keeps the old symmetric padding */
.btn:not(:has(.btn__arw)) { padding: 14px 24px; }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn--ghost:hover { background: var(--accent-wash); border-color: var(--accent-dim); color: var(--accent); box-shadow: none; }
/* on the outlined button the token cannot be ink-on-accent: it is a
   tint of the ink it sits beside, so it works on paper and on ground */
.btn--ghost .btn__arw { background: color-mix(in srgb, var(--ink) 10%, transparent); }
.btn--ghost:hover .btn__arw { background: color-mix(in srgb, var(--accent) 18%, transparent); }

.btn--sm { padding: 5px 5px 5px 18px; min-height: 40px; font-size: .875rem; gap: 10px; }
.btn--sm .btn__arw { width: 30px; }
.btn--sm .btn__arw svg { width: 13px; height: 13px; }
.btn--sm:not(:has(.btn__arw)) { padding: 9px 17px; }

.txtlink {
  font-family: var(--mono); font-size: var(--fs-mono);
  font-weight: 500; font-variation-settings: 'wght' 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--accent);
  display: inline-flex; align-items: center; gap: 9px;
  position: relative; padding-bottom: 17px;
  /* as a grid or flex item it would stretch to the whole column, and its
     hover area with it — a link is as wide as its words */
  width: fit-content;
}
/* Under the pointer — the link's own, or the card's it sits in (head.js
   sets .is-hot) — the words wake up with the bars: a light passes over
   them once, left to right, the way a scanner reads a barcode, and they
   gain weight. The mono is one width at every weight, so nothing moves;
   the file is variable, so 640 is drawn, not synthesised. Bars only: the
   zigzag leaves the words alone. The arrow leans the way it points in
   both. */
.txtlink { transition: font-variation-settings .35s cubic-bezier(.4,0,.2,1); }
:root[data-uline="bars"] .txtlink.is-hot {
  font-variation-settings: 'wght' 640;
  color: transparent;
  background: linear-gradient(100deg, var(--accent) 42%, color-mix(in srgb, var(--accent-hi), #fff 45%) 50%, var(--accent) 58%) 100% 0 / 300% 100% no-repeat;
  -webkit-background-clip: text; background-clip: text;
  animation: txtlink-scan .75s cubic-bezier(.4,0,.2,1) forwards;
}
@keyframes txtlink-scan { to { background-position: 0 0; } }
/* an offset, not a transform: text clipped out of the parent's background
   is drawn where it is laid out, and a transform happens after that. The
   background has to reach where the arrow goes, so the box runs 6px past
   the words, and hands them back as margin. */
.txtlink { padding-right: 6px; margin-right: -6px; }
.txtlink > span:not(.txtlink__line) { position: relative; left: 0; transition: left .3s cubic-bezier(.22,.61,.36,1); }
.txtlink.is-hot > span:not(.txtlink__line) { left: 4px; }
/* The line under a link. head.js lays one strip under each line of the
   link — measured per line, so a link that wraps gets one under every
   line and none past its words — and the review's LINK LINE (data-uline)
   says which of two it is, the zigzag unless bars are set:

   bars  the mark: bars on its pitch (19 wide, 14 apart), hanging from a
         straight top edge so it still reads as an underline. At rest they
         are an ordinary rule: 1px, each bar widened over its gap, at a
         third of the ink — a link, visibly, before anyone points at it.
         Under the pointer (--amp easing 0 → 1) the rule comes up to full
         ink, breaks into its bars, and a wave runs through them toward
         the arrow.
   zig   a zigzag with square corners, the same 90° the plates and rack
         units are drawn with. Hover only: drawn in from the left like a
         pen stroke, then it crawls. */
@property --amp { syntax: '<number>'; inherits: true; initial-value: 0; }
@property --w { syntax: '<number>'; inherits: false; initial-value: 0; }

.txtlink__line { position: absolute; pointer-events: none; }
/* words that take the zigzag without being a .txtlink — the hero's
   sectors — whatever LINK LINE is set */
.uline-zig { position: relative; }

.txtlink__line--bars {
  height: 12px;
  display: flex; gap: 2px; align-items: flex-start;
  --amp: 0; transition: --amp .6s cubic-bezier(.4,0,.2,1);
  opacity: calc(.35 + .65 * var(--amp));
}
.txtlink__line--bars.is-on { --amp: 1; }
.txtlink__line--bars i {
  width: 3px; height: 100%; background: var(--accent);
  transform-origin: top left;
  /* across: at rest 3px + the 2px gap after it, so the bars close into
     one rule (the last has no gap to close); under the pointer, 3px.
     down: 1px of 12 at rest; under the pointer the trough lifts to 3.6
     and the crest reaches the full 12 */
  --gap: 2 / 3;
  transform: scale(calc(1 + var(--gap) * (1 - var(--amp))),
                   calc(1 / 12 + var(--amp) * (.3 - 1 / 12 + .7 * var(--w))));
}
.txtlink__line--bars i:last-child { --gap: 0; }
.txtlink__line--bars.is-flow i { animation: txtlink-wave .8s ease-in-out var(--d) infinite alternate; }
@keyframes txtlink-wave { to { --w: 1; } }

.txtlink__line--zig {
  height: 5px; background: var(--accent);
  --zigzag: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='5'%3E%3Cpath d='M-3 4 0 1 3 4 6 1 9 4' fill='none' stroke='%23000' stroke-width='1.3'/%3E%3C/svg%3E");
  -webkit-mask: var(--zigzag) 0 0 / 6px 5px repeat-x;
          mask: var(--zigzag) 0 0 / 6px 5px repeat-x;
  clip-path: inset(0 100% 0 0);
  transition: clip-path .42s cubic-bezier(.22,.61,.36,1) calc(var(--row) * .3s);
}
.txtlink__line--zig.is-on { clip-path: inset(0); animation: txtlink-zig .7s linear infinite; }
@keyframes txtlink-zig { to { -webkit-mask-position: 6px 0; mask-position: 6px 0; } }

@media (prefers-reduced-motion: reduce) {
  .txtlink__line--bars, .txtlink__line--zig { transition: none; }
  .txtlink__line--bars.is-flow i, .txtlink__line--zig.is-on,
  :root[data-uline="bars"] .txtlink.is-hot { animation: none; }
}

/* ---------------- header: the glass island ----------------

   Not a bar any more. A floating capsule that the page runs
   underneath, built the way Liquid Glass is built: a translucent
   body that blurs and saturates what is behind it, a brighter
   refracted ring around the rim, a conic edge light, and a
   specular highlight that follows the pointer. It gets denser
   once you scroll, because there is more behind it to hide.
   ----------------------------------------------------------- */

:root {
  --head-h:    64px;   /* the island's row, and the clearance the page leaves for it */
  --head-top:  14px;   /* how far it floats off the top */
  --head-side: clamp(14px, 3vw, 28px);
  --head-maxw: 1728px;   /* content is 1600, so the island overhangs it in both states */
}
@media (max-width: 900px) { :root { --head-h: 58px; --head-top: 10px; } }

.site-head {
  position: fixed; top: var(--head-top); left: 0; right: 0; z-index: 60;
  padding-inline: var(--head-side);
  pointer-events: none;                 /* only the island itself is clickable */
  background: none; border: 0;
}

.site-head__in {
  pointer-events: auto;
  position: relative; isolation: isolate; z-index: 2;
  max-width: var(--head-maxw);
  padding-inline: 22px 8px;
  display: flex; align-items: center;
  gap: clamp(16px, 2.2vw, 30px);
  height: var(--head-h);
  border-radius: 999px;
  background: var(--head-film);
  backdrop-filter: blur(20px) saturate(190%);
  -webkit-backdrop-filter: blur(20px) saturate(190%);
  box-shadow: var(--head-inset), var(--head-drop);
  transition: background .3s ease, box-shadow .3s ease, backdrop-filter .3s ease,
              max-width .4s cubic-bezier(.22,.61,.36,1);
}
.site-head__in > * { position: relative; z-index: 2; }

/* the rim: a second, stronger blur masked to a ring, so the edge
   reads as thicker glass bending what passes behind it */
.head-lens {
  position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
  pointer-events: none;
  backdrop-filter: blur(9px) brightness(1.07) saturate(1.2);
  -webkit-backdrop-filter: blur(9px) brightness(1.07) saturate(1.2);
  /* faded towards the middle, so the glass thickens at the rim
     instead of drawing a second border inside the first */
  -webkit-mask: radial-gradient(78% 128% at 50% 50%, transparent 34%, #000 94%);
          mask: radial-gradient(78% 128% at 50% 50%, transparent 34%, #000 94%);
}

/* the edge light */
.site-head__in::before {
  content: ""; position: absolute; inset: 0; z-index: 3;
  border-radius: inherit; padding: 1px; pointer-events: none;
  background: conic-gradient(from 200deg at 50% 50%,
    var(--head-rim-a),
    color-mix(in srgb, var(--accent) 30%, transparent) 16%,
    var(--head-rim-b) 36%,
    var(--head-rim-c) 60%,
    color-mix(in srgb, var(--accent) 18%, transparent) 78%,
    var(--head-rim-a));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
}

/* the specular, tracking the pointer */
.site-head__in::after {
  content: ""; position: absolute; inset: 0; z-index: 4;
  border-radius: inherit; pointer-events: none;
  background: radial-gradient(170px 76px at var(--mx, 50%) var(--my, 0%),
    var(--head-spec), transparent 70%);
  opacity: var(--spec, 0);
  transition: opacity .35s ease;
}

/* denser, and drawn in a little, once there is something behind it */
/* below ~1500px the open state is already clamped by the viewport, so the
   contracted width is taken off whatever is actually available — the
   island still visibly draws in, and never gets narrower than the text. */
.site-head.is-stuck .site-head__in {
  max-width: min(calc(var(--head-maxw) - 80px), calc(100% - 40px));
}
.site-head.is-stuck .site-head__in,
.site-head.is-menu-open .site-head__in {
  background: var(--head-film-stuck);
  backdrop-filter: blur(28px) saturate(200%);
  -webkit-backdrop-filter: blur(28px) saturate(200%);
  box-shadow: var(--head-inset-stuck), var(--head-drop-stuck);
}

/* the page runs under the island, so the first block keeps its own
   air plus the height the island floats at */
/* shared/pages.css loads after this file and sets padding-block on
   .page-hero__in, so the clearance is written at higher specificity.
   The hero's own top padding is set where the hero is defined. */
.page-hero > .page-hero__in {
  padding-top: calc(clamp(44px, 5vw, 84px) + var(--head-top) + var(--head-h));
}

.brand { --mark-h: calc(15px * var(--mark-scale, 1)); --brand-gap: 12px; color: var(--ink); flex: none; }
/* The lockup is one drawing in one colour, the way the lab holds it — so
   the worldwide line takes the mark's colour, not the type colour around
   it. It is artwork here, not a word set in the site's face. */
.brand .cfl-mark, .brand .cfl-ww { color: var(--accent); }
.brand-word { font-family: var(--display); font-weight: 700; font-size: .9375rem; letter-spacing: .17em; text-transform: uppercase; }

.site-head { --head-mark-h: 30px; }
.site-head .brand { --mark-h: calc(var(--head-mark-h) * var(--mark-scale, 1)); --brand-gap: 15px; }
.site-head .brand-word { font-size: 1.0625rem; letter-spacing: .115em; }

/* The descriptor, under the wordmark rather than beside it.
   "CFL Worldwide" does not say what the company does, and the word that
   does — cross-border — is the one the client picked precisely because
   it settles the question before anyone has read a heading. It sits in
   the header so it is on every page and above every fold.

   Under the wordmark, not next to it: the island is 64px of a fixed row
   that already carries five nav items and a CTA, and there is no
   horizontal room for another 240px of type. Stacked, it costs nothing —
   the two lines together are 34px inside a 64px row, so the header height
   does not move with them. */
/* Both lines set their own leading. They would otherwise inherit the
   body's 1.72, which is right for a paragraph and wrong for a two-line
   lockup: at 17px and 13px it adds 21px of half-leading between them,
   which reads as two unrelated pieces of type rather than one block —
   and drops the descriptor below the mark it is supposed to sit with. */
.site-head .brand-text { align-content: center; gap: 4px; }
.site-head .brand-word { line-height: 1; }
/* One voice with the nav: the display face in sentence case, weight and
   ink doing the ranking — the nav 500 in --ink-2, the descriptor 400 in
   --ink-3, set off the mark by a hairline. It was mono caps on .11em,
   which with the nav at 17/500 and the button at 17/700 put three
   voices in one row, and the tracked mono repeated the mark's verticals. */
.site-head .brand-desc {
  font-family: var(--display);
  font-size: 1.0625rem; font-weight: 400;
  line-height: 1.25;
  letter-spacing: 0; text-transform: none;
  color: var(--ink-3);
  padding-block: 3px;
  padding-left: 15px;
  border-left: 1px solid var(--rule);
}
/* just above the burger (1121-1140) 17px pushes the button off the island;
   the phone sizes further down still win */
@media (max-width: 1140px) { .site-head .brand-desc { font-size: 1rem; } }
/* His tagline of 2026-10-06 is twice the length of the descriptor it
   replaced. Where the island has room it is one line; below that it
   breaks into two balanced lines — "Cross-Border Technology" over
   "Logistics & Trade Compliance" — which take the width the old one-line
   descriptor took and still sit inside the island's row. */
@media (max-width: 1499px) {
  .site-head .brand-desc { max-width: calc(13.4em + 16px); text-wrap: balance; }
}
/* ---- the lockup ----

   The logo the lab builds: the mark, and the client's own "worldwide"
   artwork under it at exactly the mark's width. shared/logos.js measures
   the mark and publishes the word's height and the gap as --ww-h and
   --ww-gap, both out of the lab's own numbers, and sets data-lockup on
   the marks that take one — Stack, Parallelogram and Toggle spell CFL
   themselves and do not, which is the lab's list.

   The descriptor moves to the right of that block rather than under the
   word. Three stacked lines do not fit a 64px island; two do, and the two
   that have to stay together are the ones that make the logo. The block
   ends up the same width either way, because the descriptor is the
   longest line in both arrangements.

   .brand-text is dissolved with `display: contents` so the word and the
   descriptor become cells of the brand's own grid — the markup is one
   lockup in both arrangements, only the placement changes. */
/* reset.css gives .brand-word a display, which outranks the UA's rule for
   [hidden] — so the marks that take no worldwide line need this to put it
   away. Without JavaScript the element keeps the name as text. */
.brand-word[hidden] { display: none; }
.brand-word .cfl-ww { display: block; height: var(--ww-h, 9px); width: auto; }

html[data-lockup="1"] .site-head .brand {
  display: grid;
  grid-template-columns: auto auto;
  align-items: center;
  /* .brand carries one `gap` for the row it normally is; as a grid that
     would open 15px between the mark and the word under it, where the
     lockup wants the lab's own 0.52 of the word's height. The rows set
     their own leading instead, because only one of the two gaps belongs
     to the lockup — the descriptor's is the header's, not the logo's. */
  column-gap: var(--brand-gap);
  row-gap: 0;
}
html[data-lockup="1"] .site-head .brand-text { display: contents; }
html[data-lockup="1"] .site-head .brand-mark { grid-area: 1 / 1; justify-self: center; }
html[data-lockup="1"] .site-head .brand-word {
  grid-area: 2 / 1;
  justify-self: center;
  margin-top: var(--ww-gap, 4px);
  line-height: 0;
}
html[data-lockup="1"] .site-head .brand-desc { grid-area: 1 / 2 / span 2 / 2; }

/* The descriptor stays beside the lockup at every width. It used to go
   under it below 1024, three lines with the mark stepped down to 19px —
   room the nav needed then. A sentence in the display face does not fit
   under the lockup in a 58px island, and does not need to: by then the
   nav has gone behind the burger, so there is room beside the lockup and
   the mark keeps its size. */

/* The footer brand has no descriptor, so the lockup is the whole block. */
html[data-lockup="1"] .foot-brand .brand {
  flex-direction: column; align-items: flex-start; gap: var(--ww-gap, 4px);
}

/* Below the nav breakpoint the CTA goes behind the burger, which gives
   the brand block the room the descriptor needs — so it stays, one step
   smaller and tighter. It is the line that says what the company is, and
   a phone is exactly where somebody arrives not knowing. */
@media (max-width: 900px) {
  .site-head { --head-mark-h: 25px; }
  .site-head .brand { --brand-gap: 12px; }
  .site-head .brand-word { font-size: .9375rem; letter-spacing: .13em; }
  .site-head .brand-text { gap: 2px; }
  .site-head .brand-desc {
    font-size: .8125rem; padding-left: 12px;
    text-wrap: balance;
  }
}
/* the narrowest phones: the same two lines a step smaller, or a third
   line grows the descriptor past the island's row */
@media (max-width: 380px) {
  .site-head .brand-desc { font-size: .75rem; }
}
/* the one width where it genuinely does not fit next to the burger */
@media (max-width: 359px) {
  .site-head .brand-desc { display: none; }
}

/* The nav is drawn in the button's own language: a recessed pill, and a
   raised knob under the pointer and on the section you are in — the
   toggle, said twice. The pill is the CTA's height, 48px, and the knob
   inside it is 40, within two pixels of the CTA's own arrow disc, so the
   two pills on the right read as one family rather than as a menu beside
   a button. */
.site-nav {
  margin-left: auto; display: flex; align-items: center;
  gap: 2px;
  /* The icon's empty slot sits before each word, so the pill showed 24px
     more air before Solutions than after About. Its far end takes the
     same slot back: icon 16 + gap (8, then 7). */
  padding: 4px 28px 4px 4px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 4.5%, transparent);
  box-shadow: inset 0 1px 2px color-mix(in srgb, var(--ink) 9%, transparent),
              inset 0 0 0 1px color-mix(in srgb, var(--ink) 3%, transparent);
}
/* Solutions, Industries and Knowledge open a list and have no page, so
   they are buttons; they wear the links' pill exactly */
.site-nav > button { appearance: none; border: 0; background: none; margin: 0; cursor: pointer; line-height: inherit; }
.site-nav > :is(a, button) {
  display: inline-flex; align-items: center; gap: 8px;
  height: 40px; padding: 0 16px; border-radius: 999px;
  font-family: var(--display); font-weight: 500; font-size: 1rem;
  color: var(--ink-2);
  transition: color .16s ease, background .22s ease, box-shadow .22s ease;
}
.site-nav > :is(a, button):is(:hover, :focus-visible, [aria-current]) {
  color: var(--ink);
  background: var(--surface);
  box-shadow: 0 1px 1px color-mix(in srgb, var(--ink) 7%, transparent),
              0 2px 8px -2px color-mix(in srgb, var(--ink) 16%, transparent),
              inset 0 1px 0 #fff;
}
/* ---- nav icons ----

   One per destination, each doing the thing the page is about when you
   reach for it: the four services fan out, the sector lifts out of the
   dial, the corridor draws itself end to end, the years redraw, the
   signal goes out. Pure CSS on the SVG parts — no JS, no libraries.

   Nothing at rest. Five drawings beside five words made the nav read as
   ten things — five different visual languages, and About's three bars
   a second copy of the mark a few hundred pixels from the real one. So
   the slot is kept empty, and on hover the icon comes in beside its
   word — in the accent, playing its move — and nothing else in the row
   shifts.
   -------------------------------------------------------------- */

.navi { display: none; flex: none; }
.site-nav .navi {
  display: inline-flex;
  color: var(--accent);
  opacity: 0; transform: translateX(6px) scale(.8);
  transition: opacity .18s ease, transform .32s cubic-bezier(.22, .61, .36, 1);
}
.navi svg { width: 16px; height: 16px; display: block; overflow: visible; }

/* An active item keeps its icon out, in the accent and in its move, as if
   the pointer were still on it: the page you are on (aria-current, which
   the section's own pages carry too) and the item whose list is open
   (aria-expanded). */
.site-nav > :is(a, button):is(:hover, :focus-visible, [aria-current], [aria-expanded="true"]) .navi {
  opacity: 1; transform: none;
}

.navi [class*="ni-"] {
  transform-box: fill-box; transform-origin: 50% 50%;
  transition: transform .34s cubic-bezier(.34, 1.3, .5, 1),
              opacity .24s ease, fill .22s ease;
}

/* Solutions — three services, one chain of responsibility */
.ni-q4 { fill: transparent; }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-q1 { transform: translate(-1.2px, -1.2px); }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-q2 { transform: translate(1.2px, -1.2px); }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-q3 { transform: translate(-1.2px, 1.2px); }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-q4 { transform: translate(2.1px, 2.1px); fill: currentColor; }

/* Industries — the wedge lifts out of the dial */
.ni-wedge { fill: transparent; }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-wedge { transform: translate(1.6px, -1.1px); fill: currentColor; }

/* Cases — the corridor draws itself, then the far end arrives */
.ni-arc { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset .52s cubic-bezier(.45, 0, .18, 1); }
.ni-b { opacity: .42; }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-arc { stroke-dashoffset: 0; }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-b { transform: scale(1.4); opacity: 1; transition-delay: .3s; }

/* About — sixteen years, redrawn */
.navi .ni-y { transform-origin: 50% 100%; }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-y1 { transform: scaleY(1.45); }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-y2 { transform: scaleY(.6); }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-y3 { transform: scaleY(1.8); }

/* Contact — the signal goes out */
.ni-w1, .ni-w2 { opacity: .4; }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-dot { transform: scale(1.25); }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-w1 { opacity: 1; transform: translateX(.9px); }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-w2 { opacity: 1; transform: translateX(1.8px); transition-delay: .09s; }

@media (prefers-reduced-motion: reduce) {
  .navi [class*="ni-"], .ni-arc { transition: none; }
}

/* when the island runs out of room, the items close up first */
@media (max-width: 1300px) {
  .site-nav { padding-right: 27px; }
  .site-nav > :is(a, button) { padding: 0 12px; gap: 7px; }
}
/* Below this the icons go and the items close up again: the row buys its
   width back from the padding before it buys it from the type. */
@media (max-width: 1120px) {
  .site-nav { padding-right: 4px; }
  .site-nav .navi { display: none; }
  .site-nav > :is(a, button) { padding: 0 10px; }
}

/* ---- The lists under the nav items ----

   Solutions, Industries, Cases and Knowledge each open a sheet of what is
   under them, every entry with the line that says what it is about — for
   the industries his subtitle for each, so the list reads as ten answers
   rather than ten names. A sheet hangs under the island on the island's
   own left edge rule: its first column starts under its word, and it
   never runs past the island's right end. shared/head.js opens one at a
   time (hover, focus, a first tap) and sets --dd-x; --dd-w is the
   sheet's own width.

   Solid rather than glass: it lives inside the island, and a
   backdrop-filter inside another one only ever sees the island, not the
   page — so the blur that makes .head-menu glass would do nothing here,
   and even 3% of see-through lets a 90px headline ghost through the list. */
.nav-chev { width: 10px; height: 10px; flex: none; margin-left: -3px; opacity: .55; transition: transform .24s cubic-bezier(.22,.61,.36,1), opacity .16s ease; }
.nav-dd-top[aria-expanded="true"] { color: var(--ink); background: color-mix(in srgb, var(--ink) 5.5%, transparent); }
.nav-dd-top[aria-expanded="true"] .nav-chev { transform: rotate(180deg); opacity: 1; }

.nav-dd {
  position: absolute; z-index: 5;
  top: calc(50% + var(--head-h) / 2 + 10px);
  left: var(--dd-x, 0px);
  width: min(var(--dd-w, 740px), calc(100vw - 2 * var(--head-side) - 8px));
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity .16s ease, transform .24s cubic-bezier(.22,.61,.36,1), visibility 0s linear .24s;
}
/* the gap between the island and the sheet still belongs to the menu,
   so the pointer can cross it without the list closing under it */
.nav-dd::before { content: ""; position: absolute; left: 0; right: 0; top: -16px; height: 16px; }
.nav-dd.is-open {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .16s ease, transform .24s cubic-bezier(.22,.61,.36,1), visibility 0s;
}
.nav-dd__in {
  max-height: calc(100vh - var(--head-top) - var(--head-h) - 28px);
  overflow-y: auto; overscroll-behavior: contain;
  padding: 18px 14px 12px;
  border-radius: 22px; border: 1px solid var(--rule);
  background: var(--surface);
  box-shadow: 0 30px 70px -22px var(--shadow), 0 2px 6px -2px color-mix(in srgb, var(--ink) 8%, transparent), inset 0 1px 0 var(--panel-sheen);
}
.nav-dd__lbl {
  display: flex; gap: 8px; margin: 0 12px 8px;
  font-family: var(--mono); font-size: .625rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3);
}
.nav-dd__lbl span { color: var(--accent); }
.nav-dd__list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 6px; }
.nav-dd__list a {
  display: grid; grid-template-columns: 38px minmax(0, 1fr); column-gap: 12px; row-gap: 3px; align-content: start;
  height: 100%; padding: 10px 12px 11px; border-radius: 12px;
  transition: background .16s ease;
}
/* the home page's industry icon, on the home card's tile, a size down;
   the item's hover wakes it the way the card's does */
.nav-dd__ic {
  grid-row: span 2; display: grid; place-items: center;
  width: 38px; height: 38px; border-radius: var(--radius);
  background: var(--accent-wash); color: var(--accent);
  transition: background .2s ease, color .2s ease;
}
.nav-dd__ic svg { width: 24px; height: 24px; stroke-width: 1.05; overflow: visible; }
.nav-dd__list a:is(:hover, :focus-visible, [aria-current]) .nav-dd__ic { background: var(--accent); color: var(--on-accent); }
.nav-dd__t {
  font-family: var(--display); font-weight: 500; font-size: .9688rem; line-height: 1.3;
  letter-spacing: -.005em; color: var(--ink);
  transition: color .16s ease;
}
.nav-dd__s { font-size: .8125rem; line-height: 1.4; color: var(--ink-3); }
.nav-dd__list a:is(:hover, :focus-visible) { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.nav-dd__list a:is(:hover, :focus-visible) .nav-dd__t { color: var(--accent); }
.nav-dd__list a[aria-current] { background: var(--accent-wash); }
.nav-dd__list a[aria-current] .nav-dd__t { color: var(--accent); }
.nav-dd__all {
  display: flex; justify-content: space-between; align-items: center;
  margin: 8px 12px 0; padding: 12px 0 4px; border-top: 1px solid var(--rule);
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent);
}
.nav-dd__all i { font-style: normal; transition: transform .18s ease; }
.nav-dd__all:is(:hover, :focus-visible) i { transform: translateX(3px); }

/* Solutions and Knowledge: three entries, one column. Industries: his
   five (2026-10-06), one column too — each carries his whole sentence,
   which two columns would stack six lines deep */
.nav-dd--sol { --dd-w: 470px; }
.nav-dd--know { --dd-w: 540px; }
.nav-dd--ind { --dd-w: 620px; }
.nav-dd__list--one { grid-template-columns: minmax(0, 1fr); }
/* an article's service and reading time over its title */
.nav-dd__k {
  font-family: var(--mono); font-size: .625rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent);
}
.nav-dd__list a:has(.nav-dd__k) .nav-dd__ic { grid-row: span 3; }

/* The services' icons are the hero's, drawn on 24 rather than 20, so they
   take a heavier line to sit at the industries' weight. Hovered: the
   importer's stamp comes down, compliance ticks, the freight moves. */
.nav-dd__ic--svc svg { stroke-width: 1.25; }
.nav-dd__ic [class*="si-"] {
  transform-box: fill-box; transform-origin: 50% 50%;
  transition: transform .4s cubic-bezier(.34, 1.3, .5, 1), fill-opacity .25s ease, opacity .25s ease;
}
.nav-dd__list a:is(:hover, :focus-visible) .si-stamp { fill-opacity: .9; animation: si-stamp .45s cubic-bezier(.3, 0, .2, 1); }
@keyframes si-stamp { 0% { transform: translateY(-3px) scale(1.12); } 55% { transform: translateY(.5px) scale(.96); } 100% { transform: none; } }
.nav-dd__ic .si-check { stroke-dasharray: 1; }
.nav-dd__list a:is(:hover, :focus-visible) .si-check { animation: ii-draw .5s cubic-bezier(.45, 0, .18, 1); }
.nav-dd__list a:is(:hover, :focus-visible) .si-wake { animation: si-wake .8s ease-in-out infinite; }
@keyframes si-wake { 50% { transform: translateX(-1.6px); opacity: .35; } }

/* Cases: the one case study beside the corridors */
.nav-dd--cases { --dd-w: 700px; }
.nav-dd__cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr); gap: 6px; }
.nav-dd__case {
  display: grid; gap: 10px; align-content: start;
  padding: 12px 14px 14px; border-radius: 14px;
  background: var(--accent-wash);
  transition: background .16s ease;
}
/* our crate, as on the map's case card — its own wood, no grade */
.nav-dd__case-img {
  justify-self: center; width: 92px; margin: -2px 0 2px;
  filter: drop-shadow(0 8px 8px var(--shadow));
  transition: transform .4s cubic-bezier(.34, 1.3, .5, 1);
}
.nav-dd__case-img > img { width: 100%; height: auto; }
.nav-dd__case-f { display: grid; gap: 4px; font-size: .8125rem; line-height: 1.35; color: var(--ink-2); }
.nav-dd__case-f b { font-family: var(--display); font-weight: 600; color: var(--accent); margin-right: 2px; }
.nav-dd__case-go {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 4px 12px;
  margin-top: 4px; padding-top: 10px; border-top: 1px solid var(--rule);
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .12em; text-transform: uppercase; color: var(--accent);
}
.nav-dd__case-go i { font-style: normal; display: inline-block; transition: transform .18s ease; }
.nav-dd__case-d { letter-spacing: .06em; color: var(--ink-3); }
.nav-dd__case:is(:hover, :focus-visible) { background: color-mix(in srgb, var(--accent) 11%, transparent); }
.nav-dd__case:is(:hover, :focus-visible) .nav-dd__t { color: var(--accent); }
.nav-dd__case:is(:hover, :focus-visible) .nav-dd__case-img { transform: translateY(-3px); }
.nav-dd__case:is(:hover, :focus-visible) .nav-dd__case-go i { transform: translateX(3px); }
.nav-dd__case[aria-current] .nav-dd__t { color: var(--accent); }

.nav-dd__corr { display: grid; gap: 1px; }
.nav-dd__corr a {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px; row-gap: 1px;
  padding: 7px 12px 8px; border-radius: 10px;
  transition: background .16s ease;
}
.nav-dd__route {
  font-family: var(--display); font-weight: 500; font-size: .9375rem; line-height: 1.3; color: var(--ink);
  transition: color .16s ease;
}
.nav-dd__route i { font-style: normal; color: var(--ink-3); }
.nav-dd__days {
  align-self: center; grid-row: span 2;
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent); white-space: nowrap;
}
.nav-dd__corr .nav-dd__s { font-size: .75rem; }
.nav-dd__corr a:is(:hover, :focus-visible) { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.nav-dd__corr a:is(:hover, :focus-visible) .nav-dd__route { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .nav-dd, .nav-chev, .nav-dd__case-img { transition: none; }
  .nav-dd__ic [class*="si-"] { animation: none !important; transform: none !important; }
}

/* ---- the one button on the island ---- */

/* 16px label, 48px pill, 36px disc — a step over the page's own buttons
   (.btn, 15px) to keep step with the nav beside it. At 17px it
   out-shouted the hero's primary button, which is the one the first
   screen is built to lead to. */
.cta-head {
  display: inline-flex; align-items: center; flex: none; gap: 12px;
  height: min(48px, calc(var(--head-h) - 16px));
  padding: 0 5px 0 22px; border-radius: 999px;
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  /* a step lighter than 700. Familjen is one variable file, but fonts.css
     declares it as discrete 500/700 faces, so font-weight: 600 would
     resolve to 700; the axis is set directly. */
  font-variation-settings: 'wght' 600;
  letter-spacing: -0.005em; white-space: nowrap;
  background: var(--accent); color: var(--on-accent); border: 0;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .38);
  transition: background .18s ease, box-shadow .18s ease;
}
.cta-head__arw {
  display: grid; place-items: center; flex: none;
  width: min(36px, calc(var(--head-h) - 26px)); aspect-ratio: 1;
  border-radius: 999px; background: var(--on-accent-wash);
  transition: background .18s ease, transform .22s cubic-bezier(.22,.61,.36,1);
}
.cta-head__arw svg { width: 15px; height: 15px; display: block; }
.cta-head:hover {
  background: var(--accent-hi);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .45), 0 4px 18px -6px color-mix(in srgb, var(--accent) 65%, transparent);
}
.cta-head:hover .cta-head__arw { background: var(--on-accent-wash-hi); transform: translateX(2px); }

/* ---- the region switch ----

   Review, 2026-10-06; shared/head.js builds it, off, and the switcher's r
   shows or hides it. The nav's recessed pill a size down, holding one
   knob — the globe and the region's code — set just left of the nav so
   it reads as a small aside to it rather than a sixth nav item. The list
   it opens is the nav's sheet, smaller, hung from the pill's left edge. */
.rgn { flex: none; }
.rgn[hidden] { display: none; }
.has-rgn > .rgn { margin-left: auto; }
.has-rgn > .site-nav { margin-left: 0; }
.rgn-top {
  appearance: none; border: 0; margin: 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  height: 36px; padding: 0 11px 0 10px; border-radius: 999px;
  font-family: var(--display); font-weight: 500; font-size: .8125rem; line-height: 1;
  color: var(--ink-2);
  background: color-mix(in srgb, var(--ink) 4.5%, transparent);
  box-shadow: inset 0 1px 2px color-mix(in srgb, var(--ink) 9%, transparent),
              inset 0 0 0 1px color-mix(in srgb, var(--ink) 3%, transparent);
  transition: color .16s ease, background .22s ease, box-shadow .22s ease;
}
.rgn-top:is(:hover, :focus-visible, [aria-expanded="true"]) {
  color: var(--ink);
  background: var(--surface);
  box-shadow: 0 1px 1px color-mix(in srgb, var(--ink) 7%, transparent),
              0 2px 8px -2px color-mix(in srgb, var(--ink) 16%, transparent),
              inset 0 1px 0 #fff;
}
.rgn-top__ic { display: inline-flex; color: var(--accent); }
.rgn-top__ic svg { width: 14px; height: 14px; display: block; }
.rgn-top__code { letter-spacing: .02em; font-variant-numeric: tabular-nums; }
.rgn-top[aria-expanded="true"] .nav-chev { transform: rotate(180deg); opacity: 1; }

.rgn-dd {
  position: absolute; z-index: 5; left: 0;
  top: calc(50% + var(--head-h) / 2 + 10px);
  width: min(260px, calc(100vw - 2 * var(--head-side) - 8px));
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity .16s ease, transform .24s cubic-bezier(.22,.61,.36,1), visibility 0s linear .24s;
}
.rgn-dd::before { content: ""; position: absolute; left: 0; right: 0; top: -16px; height: 16px; }
.rgn-dd.is-open {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .16s ease, transform .24s cubic-bezier(.22,.61,.36,1), visibility 0s;
}
.rgn-dd__in {
  padding: 12px 6px 6px;
  border-radius: 18px; border: 1px solid var(--rule);
  background: var(--surface);
  box-shadow: 0 30px 70px -22px var(--shadow), 0 2px 6px -2px color-mix(in srgb, var(--ink) 8%, transparent), inset 0 1px 0 var(--panel-sheen);
}
.rgn-dd__list { display: grid; gap: 1px; }
.rgn-dd .nav-dd__lbl { margin: 0 10px 6px; }
.rgn-dd__list button {
  appearance: none; border: 0; margin: 0; background: none; cursor: pointer; text-align: left;
  display: grid; grid-template-columns: 46px minmax(0, 1fr) 14px; align-items: center; column-gap: 10px;
  width: 100%; padding: 6px 10px; border-radius: 10px;
  color: var(--ink);
  transition: background .16s ease, color .16s ease;
}
.rgn-dd__code {
  padding: 3px 0; border-radius: 5px; text-align: center;
  font-family: var(--mono); font-size: .625rem; letter-spacing: .08em;
  background: var(--accent-wash); color: var(--accent);
  transition: background .2s ease, color .2s ease;
}
.rgn-dd__name { font-family: var(--display); font-weight: 500; font-size: .8438rem; line-height: 1.3; }
.rgn-dd__tick { width: 14px; height: 14px; color: var(--accent); opacity: 0; }
.rgn-dd__list button:is(:hover, :focus-visible) { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.rgn-dd__list button:is(:hover, :focus-visible) .rgn-dd__name { color: var(--accent); }
.rgn-dd__list button[aria-pressed="true"] { background: var(--accent-wash); }
.rgn-dd__list button[aria-pressed="true"] .rgn-dd__code { background: var(--accent); color: var(--on-accent); }
.rgn-dd__list button[aria-pressed="true"] .rgn-dd__tick { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .rgn-dd, .rgn-top { transition: none; }
}

/* ---- burger, and the panel it opens ---- */

.head-burger {
  display: none; flex: none;
  width: 40px; height: 40px; border-radius: 999px;
  flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  border: 0; cursor: pointer; color: var(--ink);
  background: rgba(255, 255, 255, .07);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15);
  transition: background .2s ease;
}
.head-burger:hover { background: rgba(255, 255, 255, .12); }
/* The bars are the mark's bars: `bar` in shared/logos.js is each mark's
   own rule weight as a fraction of its ink height, measured off the
   rendered mark, and shared/logos.js writes it to --mark-bar when the
   mark mounts. The set now runs from Parallelogram's hairline comb to
   Mango's fattest stem, so the range is clamped at both ends: under
   1.5px a bar stops being a bar, and over 8px three of them plus their
   gaps no longer fit the 40px button. Square ends: the one mark with a
   round one is Toggle's pill, which is a container rather than a rule. */
.head-burger span {
  display: block; width: 23px; background: currentColor; border-radius: 0;
  height: clamp(1.5px, calc(var(--head-mark-h) * var(--mark-scale, 1) * var(--mark-bar, .132)), 8px);
  transition: transform .26s cubic-bezier(.22,.61,.36,1);
}
.is-menu-open .head-burger span:first-child {
  transform: translateY(calc(50% + 2.5px)) rotate(45deg);
}
.is-menu-open .head-burger span:last-child {
  transform: translateY(calc(-50% - 2.5px)) rotate(-45deg);
}

/* ---------------- the panel ------------------------------------

   Composition from gather.ai's full-screen menu — MAIN as large
   links, the deeper level in columns under monospaced labels, one
   promoted card — but held in a glass sheet hung under the header
   instead of covering the screen. The page stays visible through it,
   which is the point of the glass everywhere else on this header.

   Painted from the tokens, like everything else here.
   ---------------------------------------------------------------- */

/* the scrim: dims the page and takes the click that closes the panel */
.head-menu {
  /* z-index is against the header's own children — the island is 2, so the
     mark and the close cross stay visible over the open panel. */
  position: fixed; inset: 0; z-index: 1;
  /* the header itself is pointer-events:none so the page stays clickable
     around the island — the panel has to take its events back, otherwise
     the scrim never sees the click that is supposed to close it. */
  pointer-events: auto;
  display: none;
  padding: calc(var(--head-top) + var(--head-h) + 12px) var(--head-side) 16px;
  background: color-mix(in srgb, var(--ground) 64%, transparent);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  opacity: 0; visibility: hidden;
  transition: opacity .22s ease, visibility .22s;
}
.is-menu-open .head-menu { opacity: 1; visibility: visible; }

/* the sheet itself */
.head-menu__in {
  max-width: var(--maxw); margin-inline: auto;
  max-height: 100%; overflow-y: auto; overscroll-behavior: contain;
  padding: clamp(22px, 3vw, 40px);
  border-radius: 26px; border: 1px solid var(--rule);
  background: color-mix(in srgb, var(--surface) 78%, transparent);
  backdrop-filter: blur(44px) saturate(185%);
  -webkit-backdrop-filter: blur(44px) saturate(185%);
  box-shadow: 0 30px 70px -22px var(--shadow), inset 0 1px 0 var(--panel-sheen);
  transform: translateY(-12px) scale(.985); transform-origin: 50% 0;
  transition: transform .38s cubic-bezier(.22,.61,.36,1);
}
.is-menu-open .head-menu__in { transform: none; }

.hm-lbl {
  font-family: var(--mono); font-size: .625rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 16px;
}
.hm-lbl--gap { margin-top: 26px; }

/* one dense row: the big links, two columns of small ones, the card */
.hm-grid {
  display: grid; gap: clamp(20px, 2.6vw, 44px);
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1.15fr) minmax(0, 1.1fr);
  align-items: start;
}

.hm-big {
  display: grid; grid-auto-flow: column;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(3, auto);
  column-gap: clamp(16px, 2vw, 32px); row-gap: 2px;
}
.hm-big a {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.35rem, .95rem + 1.3vw, 2rem);
  letter-spacing: -0.035em; line-height: 1.2;
  color: var(--ink); padding-block: 3px;
  transition: color .16s ease;
  width: max-content; max-width: 100%;
}
.hm-big a:hover { color: var(--accent); }

.hm-col { display: grid; justify-items: start; gap: 10px; align-content: start; }
.hm-col a {
  color: var(--ink-2); font-size: var(--fs-sm); line-height: 1.35;
  transition: color .16s ease;
}
.hm-col a:hover { color: var(--accent); }
.hm-col a i { font-style: normal; color: var(--accent); padding-inline: 3px; }
/* the clearance time, set as data rather than as part of the sentence */
.hm-col a em {
  font-style: normal; font-family: var(--mono); font-size: .6875rem;
  color: var(--ink-3); margin-left: 7px;
}
.hm-more {
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent) !important; margin-top: 4px;
}

/* the promoted card: our corridor check, where theirs is an ROI
   calculator. Same job — the thing a visitor can start now. */
.hm-card {
  display: grid; gap: 9px; align-content: start;
  padding: 18px; border: 1px solid var(--rule); border-radius: var(--radius);
  background: color-mix(in srgb, var(--ground) 55%, transparent);
  transition: border-color .18s ease, background .18s ease;
}
.hm-card:hover {
  border-color: var(--accent-dim);
  background: color-mix(in srgb, var(--ground) 75%, transparent);
}
.hm-card .hm-lbl { margin-bottom: 0; }
.hm-card__h { font-size: .8125rem; color: var(--ink-2); line-height: 1.45; }
.hm-card__v {
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  color: var(--ink); letter-spacing: -.02em; margin-top: 2px;
}
.hm-card__v b { color: var(--accent); font-size: 1.5em; margin-right: 4px; }
.hm-card__go {
  font-family: var(--mono); font-size: .625rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--accent); margin-top: 6px;
}
.hm-card__go i { font-style: normal; transition: transform .18s ease; display: inline-block; }
.hm-card:hover .hm-card__go i { transform: translateX(3px); }

/* the strip along the bottom: the things a visitor copies rather than clicks */
.hm-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
  margin-top: clamp(20px, 2.6vw, 34px); padding-top: 16px;
  border-top: 1px solid var(--rule);
}
.hm-mail { color: var(--accent); font-size: var(--fs-mono); }
.hm-addr { font-size: var(--fs-mono); color: var(--ink-3); }
.hm-foot__sep { width: 1px; height: 12px; background: var(--rule); }

/* everything rises a little, in order, once the panel is open */
.head-menu :is(.hm-main, .hm-col, .hm-card, .hm-foot) {
  opacity: 0; transform: translateY(8px);
  transition: opacity .3s ease, transform .4s cubic-bezier(.22,.61,.36,1);
}
.is-menu-open .head-menu :is(.hm-main, .hm-col, .hm-card, .hm-foot) {
  opacity: 1; transform: none;
}
.is-menu-open .head-menu .hm-main { transition-delay: .06s; }
.is-menu-open .head-menu .hm-col:nth-of-type(2) { transition-delay: .11s; }
.is-menu-open .head-menu .hm-col:nth-of-type(3) { transition-delay: .15s; }
.is-menu-open .head-menu .hm-card { transition-delay: .19s; }
.is-menu-open .head-menu .hm-foot { transition-delay: .23s; }

@media (max-width: 1100px) {
  .hm-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hm-main { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
  .head-menu { padding-inline: 12px; }
  .head-menu__in { padding: 20px 18px; border-radius: 20px; }
  .hm-grid { grid-template-columns: minmax(0, 1fr); }
  .hm-big { grid-auto-flow: row; grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  .hm-foot__sep { display: none; }
  .hm-foot { gap: 6px 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .head-menu :is(.hm-main, .hm-col, .hm-card, .hm-foot) {
    opacity: 1; transform: none; transition: none;
  }
  .head-menu__in { transform: none; transition: none; }
}

.head-menu { display: block; }

/* The nav goes behind the burger at the width where it stops fitting, and
   that width moved out with the type: five items at 17px, a brand block
   with the descriptor beside it and the button together need about 1080px
   before they start pushing at each other. Below that the header is the
   mark, the button and the burger, which is what it already did lower
   down — only sooner. */
@media (max-width: 1080px) {
  .site-nav { display: none; }
  .head-burger { display: flex; }
  .site-head .cta-head { margin-left: auto; }
}
@media (max-width: 900px) {
  .site-head__in { padding-inline: 18px 8px; }
  .site-head .cta-head { padding: 0 4px 0 16px; gap: 9px; }
}
@media (max-width: 620px) {
  /* the mark and the way in; the button lives in the panel */
  .site-head__in > .cta-head { display: none; }
  .head-burger { margin-left: auto; }
}
/* The region pill is small enough that the nav keeps its own breakpoint:
   at 1081px there are still 120px between the mark and the pill. The pill
   carries the push to the right, so once the nav has gone the button and
   the burger lose theirs. Under 700px the mark, the button and the burger
   fill the island, and under 620 the mark and the burger do: the pill
   goes, and the panel's row of codes takes over. */
@media (max-width: 1080px) {
  .has-rgn > :is(.cta-head, .head-burger) { margin-left: 0; }
}
@media (max-width: 700px) {
  .site-head__in > .rgn { display: none; }
  .has-rgn > .cta-head { margin-left: auto; }
}
@media (max-width: 620px) {
  .has-rgn > .head-burger { margin-left: auto; }
}

.hm-rgn { display: none; }
@media (max-width: 700px) {
  .hm-rgn:not([hidden]) {
    display: block;
    margin-top: 24px; padding-top: 18px; border-top: 1px solid var(--rule);
  }
  .hm-rgn .hm-lbl { margin-bottom: 12px; }
  .hm-rgn__codes { display: flex; flex-wrap: wrap; gap: 6px; }
  .hm-rgn__codes button {
    appearance: none; border: 0; margin: 0; cursor: pointer;
    height: 34px; padding: 0 12px; border-radius: 999px;
    font-family: var(--mono); font-size: .6875rem; letter-spacing: .1em;
    color: var(--ink-2);
    background: color-mix(in srgb, var(--ink) 4.5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 6%, transparent);
    transition: background .16s ease, color .16s ease;
  }
  .hm-rgn__codes button[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); box-shadow: none; }
  .hm-rgn + .hm-foot { margin-top: 18px; }
}

/* Safari and anything without backdrop-filter gets a solid island
   rather than a transparent one it cannot blur behind. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-head__in, .head-menu { background: color-mix(in srgb, var(--surface) 92%, #fff); }
  .head-lens { display: none; }
}

/* ---------------- hero ---------------- */

/* The map used to live here. It moved to #reach, and the hero is
   typographic: the claim, the qualifier, and the four numbers. */
.hero { position: relative; border-bottom: 1px solid var(--rule); }
.hero__in {
  position: relative;
  /* the island floats over this block, so the top pad carries its clearance */
  padding-block: calc(clamp(72px, 10vw, 156px) + var(--head-top) + 16px) clamp(48px, 6vw, 84px);
}
.hero__copy { display: grid; gap: 30px; max-width: 54rem; }
.hero h1 {
  /* The client draft headline is one 100-character sentence rather than
     the six-word claim this hero was cut for. At 17ch it set seven lines
     and ate the screen, so the measure widens and the size steps down
     until it sets four. Same for the two full-bleed variants below.

     It stepped down once more when the page went to 1600. A headline
     sized for a 1280 column is not the same object on a screen half
     again as wide: it stopped being a claim and started being a wall,
     and the first screen had no air left in it. The ceiling comes off
     roughly an eighth, the lede with it, and what the type gives up the
     section gets back as ground. */
  max-width: 26ch;
  font-size: clamp(1.9rem, .92rem + 2.65vw, 3.2rem);
}
.hero .lede {
  max-width: 52ch;
  font-size: clamp(1rem, .95rem + .28vw, 1.14rem);
}
.hero__cta { display: flex; flex-wrap: wrap; gap: 14px; }

/* His subheadline as the sentence it is — "helps [who] move … through
   [how]" — with its two lists set side by side: who on the left, the
   services on the right, each under the words that lead into it. The two
   columns are one object, so they follow one rule: both heads are two
   lines on one floor, and every item on either side is the same row — the
   same icon size, the same type, the same height — so row three on the left
   is level with row three on the right. Every span is display-level, so
   the whitespace between them never shows.

   Level is not alike, though: set the same, the services read as more
   names on the sectors' list. So only the sectors are ruled, and each
   service is a framed cell in the weight of the name that opens the
   sentence — what CFL does, against who it is for. */
.lede--parts {
  display: grid; max-width: 44rem;
  /* 2026-09-30: his new sentence leads with the services — "CFL Worldwide
     coordinates [how] for companies deploying … — including [who]" — so
     the services take the left column and the sectors the right, and
     both keep to one line a name */
  grid-template-columns: max-content max-content;
  grid-template-areas: "hhow hwho" "how who";
  column-gap: clamp(22px, 2.6vw, 44px); row-gap: 12px;
}
.lede--parts b { font-weight: 600; color: var(--ink); }
.lp__head { display: block; align-self: end; font-size: 1rem; line-height: 1.45; }
/* side by side, each head is two lines, broken where its phrase does —
   "CFL Worldwide / coordinates" against "for companies deploying
   high-value / technology internationally — including". A third line on
   one side dropped the other head a line below the headline, and the
   gap read as a hole. */
.lp__head--how b, .lp__nl { display: block; }
.lp__head--who { grid-area: hwho; }
.lp__head--how { grid-area: hhow; }
.lp__who, .lp__how { display: grid; align-self: start; }
.lp__who { grid-area: who; }
.lp__how { grid-area: how; }
.lp__i {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 0; border-top: 1px solid var(--rule);
  font-size: .92em; line-height: 1.3; color: var(--ink); white-space: nowrap;
}
/* The rules separate the sectors; nothing needs separating from the head
   above the first, so it has none. The air over it is what the first
   service cell has over its text — a 1px frame and 9px of padding — so
   the first sector and the first service sit on one line. */
.lp__who .lp__i:first-child { border-top: 0; padding-top: 10px; }
.lp__ic { flex: none; width: 20px; height: 20px; color: var(--accent); }
/* each sector is a link to its card under Industries We Support; it reads
   as the list it is until the pointer finds it, and then the text links'
   zigzag draws in under its words (head.js, the .txtlink block below) */
/* Four services against five sectors: left as rows, the services column
   stopped a row short of the sectors and the pair read lopsided. So the
   services column stretches to the sectors' height and its four cells
   spread over it — both columns start on one line and end on one line.
   The cells keep their own height, so the icon sits the same distance
   from every edge of its frame; one container, no tile inside it, so
   there is no second radius to disagree with the first. */
.lp__how { align-self: stretch; align-content: space-between; gap: 8px; }
.lp__how .lp__i {
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface); padding: 9px 18px 9px 14px; font-weight: 600;
}

/* one column on the phone, where two would crush the names; stacked, the
   heads have the width and run on as the sentence does */
@media (max-width: 560px) {
  .lede--parts { grid-template-columns: minmax(0, 1fr); grid-template-areas: "hhow" "how" "hwho" "who"; }
  .lp__head--who { margin-top: 10px; }
  .lp__head--how b, .lp__nl { display: inline; }
  .lp__i { white-space: normal; }
  .lp__how { gap: 6px; }
}

/* ---------------- reach: the full-bleed scroll-driven world map ------

   A dot matrix, not a coastline. The land is a regular lon/lat grid
   generated at build time (shared/build-worldgrid.mjs), drawn as one
   path per grid column so the reveal can sweep a column at a time.

   The map runs edge to edge and the copy sits inside it, in the empty
   Pacific under North America — there is no section header or footer
   taking height away from it.

   Scroll writes four numbers on .wmap and CSS resolves every element
   from them; the handler never touches an individual dot or arc:
     --wave  0 .. cols+span   the land sweeping in, left to right
     --p1    0 .. 1           the 47 market marks arriving
     --out   0 .. 1           the reach copy standing down
     --b     0 .. 1           the corridor table on the floor (in, then out)
     --dim1  0 .. 1           the markets no corridor lands in falling back
     --fade  0 .. 1           six of the seven corridors leaving
     --z     ~.8 .. 3.2       the camera: framing the seven, then pushing in
     --zi                     its reciprocal, which is what the sizes spend
     --dim   0 .. 1           every other market falling back
   and each corridor carries its own --d, 0 .. 1, as it draws.
   ------------------------------------------------------------------- */

/* shared/reset.css sets overflow-x:hidden on html and body, which makes
   both of them scroll containers and silently kills every position:sticky
   on the page — the header and the six-stage diagram included. `clip`
   contains overflow the same way without creating a scroll container. */
html, body { overflow-x: clip; }

/* Every size on the map is written here and multiplied by --zm where it is
   used, so the camera moves the geography and leaves the symbols alone —
   a map zooming, not a photograph being enlarged. The breakpoints below
   change these numbers rather than the rules that spend them.

   --zm is how much of the camera a symbol keeps. At --zi it keeps none and
   holds the size it was drawn. That is right where the map renders about
   1:1 and a mark is already four pixels across; it is wrong on a phone,
   where the map is drawn at a quarter scale and the numbers below are
   inflated to buy the pixels back — dividing the camera out again spends
   2.2 units of corridor down to half a pixel of line. So the narrower the
   screen, the more of the push-in its symbols are allowed to take. */
.wmap {
  --wave: 0; --p1: 0; --out: 0; --b: 0; --dim1: 0; --fade: 0; --dim: 0; --z: 1; --zi: 1;
  --zm: var(--zi);
  /* --g: how much bigger the corridor's own symbols stand once the camera
     is in (GROW in graphics.js). Only the corridor spends it. */
  --g: 1;
  --w-dot: 3px; --w-arc: 2.2px; --w-ring: 1.2px; --fs-lbl: 13px;
  --r-mark: 4.2px; --r-hq: 5.2px; --r-ring: 13px; --r-halo: 11px; --r-end: 4px;
  border-bottom: 1px solid var(--rule);
}
/* Three frames on one stage: the markets, the seven corridors with their
   table, and the case. Each holds about a screen of scroll. */
.wmap__track { position: relative; height: 560vh; }
/* where index.html#coverage lands: the share of the track at which the
   corridor table has the floor (CORR_AT in graphics.js) */
.wmap__anchor { position: absolute; left: 0; top: calc((100% - 100vh) * .55); width: 1px; height: 1px; }
.wmap__stage {
  /* svh: on a phone 100vh is the screen with the browser's bars put
     away, so with them out the foot of the stage sat under the toolbar */
  position: sticky; top: 0; height: 100vh; height: 100svh; min-height: 620px;
  display: flex; align-items: center; overflow: hidden;
}
.wmap__glow {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 100vw; height: 100vw; max-width: 1900px; max-height: 1000px;
  background: radial-gradient(circle, var(--glow), transparent 62%);
  opacity: clamp(0, calc(var(--wave) / 40), 1);
  pointer-events: none;
}

/* the map takes the whole stage; the copy is positioned inside it */
.wmap__figure { position: relative; width: 100%; }
.wmap__figure > svg { width: 100%; height: auto; display: block; overflow: visible; }

.wmap__panel {
  position: absolute; left: var(--gutter); bottom: 1%;
  width: min(30rem, 32vw); z-index: 3;
}
.wmap__panel h2 { font-size: clamp(1.4rem, .9rem + 1.4vw, 2.1rem); max-width: 18ch; margin-top: 4px; }
/* Once the camera starts moving, the reach copy has done its job and the
   case takes the floor. It goes on the same scroll ramp as everything
   else rather than on a transition, so it leaves with the move instead of
   catching up with it afterwards. Opacity rather than display, so the h2
   that names this section stays in the accessibility tree. */
.wmap__panel { opacity: calc(1 - var(--out)); transform: translateY(calc(var(--out) * 10px)); }
/* It is copy, not controls, and it stands over the lower-left quarter of
   the map — which is Mexico and Chile. Once the marks are live the map
   has the better claim on the pointer, so the copy stops taking it. */
.wmap.is-live .wmap__panel,
.wmap[data-case="on"] .wmap__panel { pointer-events: none; }
.wmap__sub { color: var(--ink-2); font-size: var(--fs-body); margin-top: 12px; max-width: 39ch; text-wrap: pretty; }

/* land — one path per grid column, swept in by --wave */
.wm__col {
  /* the one thing on the map that does not spend --zm: the land is a
     texture rather than a symbol, and a dot that held its size while the
     grid spread out to 24 units left a loose field with no coastline in
     it. It magnifies, and the marks drawn on top of it do not. */
  fill: none; stroke: var(--accent-dim); stroke-linecap: round;
  stroke-width: var(--w-dot);
  opacity: calc(clamp(0, calc((var(--wave) - var(--i)) / 9), 1) * (1 - var(--dim) * .22));
}
/* A brighter band rides the leading edge and fades out behind it, so
   the land reads as being drawn rather than as being switched on. */
.wm__col--edge {
  stroke: var(--accent);
  opacity: calc(
    clamp(0, calc((var(--wave) - var(--i)) / 2), 1) -
    clamp(0, calc((var(--wave) - var(--i) - 3) / 7), 1)
  );
}

/* market marks — the pin colour, never the brand green. Thirty of them
   at four units instead of forty-seven at two and a half, each sitting
   in a hole punched out of the land so it never merges with the grid. */
/* --dim used to take 45% off every mark, which read as the whole map
   going grey. It takes almost all of it now, and the two ends of the
   corridor — and whatever the pointer is on — are exempt, so what is
   left lit at the end of the section is exactly what it is about. */
.wm__c {
  --dim-k: .88; --dim1-k: .62;
  opacity: calc(clamp(0, calc((var(--p1) * 58 - var(--i)) / 5), 1) *
                (1 - var(--dim1) * var(--dim1-k)) *
                (1 - var(--dim) * var(--dim-k)));
}
/* the eleven markets a corridor lands in hold through the corridor frame */
.wm__c--end { --dim1-k: 0; }
.wm__c--corr, .wm__c.is-hot { --dim-k: 0; }
.wm__c--corr .wm__c-base, .wm__c--corr .wm__c-lit { r: calc(var(--r-mark) * var(--zm) * var(--g)); }
.wm__c--corr .wm__c-lit { stroke-width: calc(1.4px * var(--zm) * var(--g)); }
.wm__c-base { fill: var(--pin-dim); opacity: .55; r: calc(var(--r-mark) * var(--zm)); }
.wm__c-lit {
  fill: var(--pin); stroke: var(--ground); stroke-width: calc(1.4px * var(--zm));
  r: calc(var(--r-mark) * var(--zm));
}
.wm__c--hq .wm__c-lit { r: calc(var(--r-hq) * var(--zm)); }
.wm__c-ring {
  fill: none; stroke: var(--pin); stroke-width: calc(var(--w-ring) * var(--zm));
  opacity: .6; r: calc(var(--r-ring) * var(--zm));
}

/* an invisible target, because a four-unit dot is not a pointer target */
.wm__hit { fill: transparent; pointer-events: none; outline: none; r: calc(17px * var(--zm)); }
.wm__land, .wm__edge, .wm__arcs, .wm__lbls { pointer-events: none; }
.wmap.is-live .wmap__figure > svg { cursor: crosshair; }
.wm__c.is-hot .wm__c-lit { fill: var(--pin-hi); opacity: 1; }
.wm__c.is-hot .wm__c-halo { opacity: .95; }
.wm__c-halo {
  fill: none; stroke: var(--pin-hi); stroke-width: calc(1.4px * var(--zm));
  r: calc(var(--r-halo) * var(--zm)); opacity: 0;
  transition: opacity .18s ease;
}

/* the corridors — seven, drawn one after another once the markets are in,
   each on its own --d (graphics.js staggers them off one ramp). Then six
   leave on --fade and the first, Tokyo to Hong Kong, is the one the camera
   closes on. A row of the table, or a market, lights its own corridors and
   quiets the rest (.has-hot / .is-hot). */
.wm__corr { transition: opacity .22s ease; }
.wm__corr--alt { opacity: calc(1 - var(--fade)); }
.wm__arcs.has-hot .wm__corr:not(.is-hot) { opacity: calc(.22 * (1 - var(--fade))); }
.wm__arc {
  fill: none; stroke: var(--accent); stroke-linecap: round;
  stroke-width: calc(var(--w-arc) * var(--zm) * var(--g));
  stroke-dasharray: var(--len);
  stroke-dashoffset: calc(var(--len) * (1 - var(--d)));
  transition: stroke-width .22s ease;
}
.wm__corr--alt .wm__arc { stroke-width: calc(var(--w-arc) * var(--zm)); }
.wm__corr.is-hot .wm__arc { stroke-width: calc(var(--w-arc) * 1.7 * var(--zm) * var(--g)); }
.wm__arc-end {
  fill: var(--accent); r: calc(var(--r-end) * var(--zm) * var(--g));
  opacity: clamp(0, calc((var(--d) - var(--at)) * 16), 1);
}
.wm__corr--alt .wm__arc-end { r: calc(var(--r-end) * var(--zm)); }
/* the lit head that rides the end of the line, and goes out when the
   line has arrived and there is nothing left for it to lead */
.wm__arc-head {
  fill: var(--accent); r: calc(3.4px * var(--zm) * var(--g));
  opacity: calc(clamp(0, calc(var(--d) * 16), 1) *
                clamp(0, calc((1 - var(--d)) * 10), 1));
}
/* the hairline that ties the caption to the arc it is about. It is drawn
   outside the camera, so its sizes are plain units rather than --zm ones,
   and it arrives with the caption: an elbow pointing at empty air while
   the caption is still on its way reads as a mistake. */
.wm__lead {
  fill: none; stroke: var(--accent); stroke-width: 1px;
  stroke-dasharray: 3px 4px; pointer-events: none;
  opacity: 0; transition: opacity .5s cubic-bezier(.2, .7, .3, 1);
}
.wmap[data-case="on"] .wm__lead { opacity: .55; }
.wm__lbl {
  --dim-k: 1;
  font-family: var(--mono); font-size: calc(var(--fs-lbl) * var(--zm));
  letter-spacing: .1em; fill: var(--ink-2);
  /* A name set straight on the dot grid reads as a name with dirt in it:
     the grid runs through the counters and between the letters. This is
     the cartographer's answer rather than a plate behind the word — the
     glyph is stroked in the ground colour first and filled second, so it
     carves its own clearance out of the dots and nothing rectangular
     appears on the map. */
  paint-order: stroke;
  stroke: var(--ground); stroke-width: calc(4px * var(--zm));
  stroke-linejoin: round;
  /* Each arrives as its corridor starts to draw (--u, the arcs' ramp) —
     since 2026-10-06 the markets' frame carries no names. They used to
     arrive with the markets: (var(--p1) * 9 - var(--i) * .5 - 3.5) / 2.
     Then they go the way the marks go: by the time the camera has landed,
     the only two names left are the two the case is about. */
  opacity: calc(clamp(0, calc((var(--u, 0) - var(--i) * .1) * 8), 1) *
                (1 - var(--dim) * var(--dim-k)));
  pointer-events: none;
}
.wm__lbl--corr {
  --dim-k: 0;
  font-size: calc(var(--fs-lbl) * var(--zm) * var(--g));
  stroke-width: calc(4px * var(--zm) * var(--g));
}

/* readout — the one figure the reach copy stands on */
.wmap__readout { border-top: 1px solid var(--rule); margin-top: 22px; padding-top: 16px; }
.wmap__k {
  font-family: var(--mono); font-size: .75rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3);
}
.wmap__v { font-size: var(--fs-body); color: var(--ink-2); margin-top: 6px; }
.wmap__v b { color: var(--accent); font-family: var(--display); font-weight: 700; font-size: 1.15em; }

/* ---- the corridor table, on the map ----
   The second frame. It takes the floor from the reach copy while the seven
   arcs draw, as a card on the left gutter — over the Americas, which the
   camera has moved out of its way (graphics.js frames the corridors in
   what the card leaves of the stage). A card rather than copy on the map:
   seven rows of figures read as set on paper, not printed over dots.
   Centred on the stage below the floating header, and it gives up air
   before it gives up a row. */
.wmap__corr {
  position: absolute; z-index: 4; left: var(--gutter); top: 50%;
  width: min(28rem, 34vw); max-height: calc(100vh - 120px);
  transform: translateY(calc(-50% + 40px + (1 - var(--b)) * 12px));
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--radius);
  padding: clamp(20px, 3.2vh, 32px) clamp(22px, 2.2vw, 32px) clamp(14px, 2.2vh, 24px);
  box-shadow: 0 18px 44px var(--shadow);
  opacity: var(--b); pointer-events: none;
}
.wmap[data-phase="1"] .wmap__corr { pointer-events: auto; }
.wmap__corr h2 { font-size: clamp(1.3rem, .9rem + 1vw, 1.8rem); max-width: 16ch; margin-top: 4px; }
.wmap__corr .wmap__sub { font-size: var(--fs-sm); max-width: 40ch; margin-top: 10px; }
.wmap__corr .cov__list { margin-top: clamp(14px, 2.4vh, 24px); }
.wmap__corr .cov__row { row-gap: 3px; line-height: 1.35; padding-block: clamp(6px, 1.05vh, 11px); }
.wmap__corr .cov__row--head { line-height: 1.5; padding-bottom: 7px; }
@media (max-height: 740px) and (min-width: 1101px) {
  .wmap__corr .wmap__sub { display: none; }
}

/* ---- the market card ---- */

.wmap__pop {
  position: absolute; z-index: 6; width: 21rem; max-width: calc(100vw - 2 * var(--gutter));
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 16px 18px 14px;
  box-shadow: 0 18px 44px var(--shadow);
  pointer-events: none;
  /* it comes out of the mark rather than out of nowhere: the edge nearest
     the dot holds still, and the card unfolds away from it. place() sets
     which edge that is. */
  opacity: 0; transform: translate(var(--pop-dx, 0px), 5px) scale(.965);
  transform-origin: var(--pop-ox, 0%) 50%;
  transition: opacity .17s cubic-bezier(.4, 0, 1, 1), transform .2s cubic-bezier(.4, 0, 1, 1);
}
/* The card takes the pointer as soon as it is open. It used to wait
   for a click, which meant the files inside it could not be reached by
   hovering at all — you aimed at the card and it closed on the way. */
.wmap__pop.is-open {
  opacity: 1; transform: none; pointer-events: auto;
  transition: opacity .2s cubic-bezier(.2, .7, .3, 1),
              transform .42s cubic-bezier(.16, 1, .3, 1);
}
/* Running the pointer along the map moves one card from market to market
   instead of closing and opening thirty of them. Only a market change
   glides; a card re-pinned because the camera moved it snaps, or it would
   spend the whole push-in chasing its own dot. */
.wmap__pop.is-glide.is-open {
  transition: opacity .2s cubic-bezier(.2, .7, .3, 1),
              transform .42s cubic-bezier(.16, 1, .3, 1),
              left .34s cubic-bezier(.22, .68, .3, 1),
              top .34s cubic-bezier(.22, .68, .3, 1);
}
/* The contents arrive in the order they are read. fill() writes a fresh
   .pop__in for every market, so this replays on each one without anything
   having to reset it. */
@keyframes wmPopIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.wmap__pop.is-open .pop__in > * { animation: wmPopIn .36s cubic-bezier(.16, 1, .3, 1) both; }
.wmap__pop.is-open .pop__in > :nth-child(2) { animation-delay: .035s; }
.wmap__pop.is-open .pop__in > :nth-child(3) { animation-delay: .07s; }
.wmap__pop.is-open .pop__in > :nth-child(4) { animation-delay: .1s; }
.wmap__pop.is-open .pop__in > :nth-child(5) { animation-delay: .13s; }
.wmap__pop.is-open .pop__in > :nth-child(n+6) { animation-delay: .16s; }
.wmap__pop.is-pinned { border-color: var(--pin-dim); }
/* The bridge: an invisible pad around the card that covers the gap back
   to the mark. Without it the pointer falls through the hole between
   dot and card, the map reports "no market here", and the card shuts. */
.wmap__pop::before {
  content: ''; position: absolute; z-index: -1;
  top: -14px; bottom: -14px; left: -30px; right: -30px;
}
.pop__top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.pop__n { font-family: var(--display); font-weight: 700; font-size: 1.15rem; letter-spacing: -.02em; line-height: 1.15; }
.pop__role {
  font-family: var(--mono); font-size: .625rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent); border: 1px solid var(--accent-dim); border-radius: 2px;
  padding: 3px 6px; white-space: nowrap; flex: none;
}
.pop__hub { font-family: var(--mono); font-size: .6875rem; color: var(--ink-3); margin-top: 6px; letter-spacing: .04em; }
.pop__stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 14px; }
.pop__s { border-top: 1px solid var(--rule); padding-top: 8px; }
.pop__sv { font-family: var(--display); font-weight: 700; font-size: 1.05rem; color: var(--accent); line-height: 1.1; font-variant-numeric: tabular-nums; }
.pop__sl { font-family: var(--mono); font-size: .5625rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); margin-top: 5px; }
.pop__note { font-size: .8125rem; line-height: 1.5; color: var(--ink-2); margin-top: 13px; }
.pop__corr { margin-top: 12px; display: grid; gap: 5px; }
.pop__corr-r { font-family: var(--mono); font-size: .6875rem; color: var(--ink-2); display: flex; justify-content: space-between; gap: 10px; }
.pop__corr-r i { color: var(--accent); font-style: normal; }
/* ---- the files on the card ----
   A document is a shape before it is a label: a sheet with its corner
   turned. Drawn with clip-path so the cut is in the silhouette rather
   than painted on top of a rectangle that is still a rectangle. */
.pop__fk {
  font-family: var(--mono); font-size: .5625rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-3);
  margin-top: 15px; padding-top: 12px; border-top: 1px solid var(--rule);
}
.pop__files { margin-top: 9px; display: grid; gap: 7px; }
.filecard {
  --cut: 15px;
  position: relative; display: grid; gap: 2px 11px;
  grid-template-columns: auto minmax(0, 1fr) auto; align-items: center;
  width: 100%; padding: 10px 12px 10px 11px; text-align: left; cursor: pointer;
  border: 0; background: var(--surface-2); color: var(--ink); font: inherit;
  box-shadow: inset 3px 0 0 var(--pin);
  clip-path: polygon(0 0, calc(100% - var(--cut)) 0, 100% var(--cut), 100% 100%, 0 100%);
  transition: background .16s ease;
}
.filecard:hover, .filecard:focus-visible { background: var(--pin-wash); }
/* the turned corner, drawn inside the cut so it reads as a flap */
.filecard__fold {
  position: absolute; top: 0; right: 0; width: var(--cut); height: var(--cut);
  background: var(--pin-dim); opacity: .5;
  clip-path: polygon(0 0, 100% 100%, 0 100%);
}
.filecard__fmt {
  grid-row: span 2; align-self: center;
  font-family: var(--mono); font-size: .5625rem; letter-spacing: .08em;
  color: var(--pin); border: 1px solid var(--pin-dim); border-radius: 2px;
  padding: 4px 5px; white-space: nowrap;
}
.filecard__t { font-size: .8125rem; line-height: 1.35; }
/* --ink-3 clears AA on --surface but not on --surface-2, which is what
   the card is painted in, so the note line runs one step brighter. */
.filecard__m {
  grid-column: 2; font-family: var(--mono); font-size: .5625rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2);
}
.filecard__go {
  grid-row: span 2; align-self: center; color: var(--pin);
  font-size: .875rem; padding-right: 4px;
}

/* ---- the case, beside its own arc ----
   The reach panel says how many markets there are; this says what one
   of them cost and how long it took. It arrives in the lower right,
   across the diagonal from the corridor the camera has flown to the upper
   left, and a leader ties it to the arc (placeCase in graphics.js).

   A solid card rather than a scrim. The scrim let the caption float on
   the map, and what showed through it was the dot grid — the three
   figures are the point of the block and they read as set on paper,
   not as printed over a texture. */
.wmap__case {
  position: absolute; z-index: 5; width: min(30rem, 38vw);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 26px 28px 24px;
  box-shadow: 0 18px 44px var(--shadow);
  opacity: 0; transform: translateY(14px); pointer-events: none;
  transition: opacity .5s cubic-bezier(.2, .7, .3, 1),
              transform .62s cubic-bezier(.16, 1, .3, 1);
}
.wmap[data-case="on"] .wmap__case { opacity: 1; transform: none; pointer-events: auto; }
/* His case study's slot, so it carries his section's number. Counted on
   the section, not the card: under 1100px the card is display:none until
   its turn, and a hidden element does not count — every number below it
   would drop by one and come back mid-scroll. */
.wmap { counter-increment: sec; }
/* The head: his label and title on the left, the crate on the right,
   standing a little proud of the card's top edge — the consignment set
   down on the page rather than framed in it. */
.wmap__case-head {
  display: grid; grid-template-columns: minmax(0, 1fr) 92px;
  gap: 18px; align-items: start;
}
.wmap__case-t { display: grid; gap: 12px; }
/* A client photograph outside the home page's .ph frames — the case
   crate on its own page — takes the same grade and
   tint as the rest (tokens.css, --ph-*). The tint is an element and not
   a pseudo so that a cut-out can mask it to its own outline: the mask is
   the picture itself, set inline, where the markup knows its address.
   Before the map card's rules, so their small-screen position wins. */
.ph-tone { display: block; position: relative; isolation: isolate; }
.ph-tone > img { display: block; width: 100%; height: auto; filter: var(--ph-grade); }
.ph-tone__tint {
  position: absolute; inset: 0; pointer-events: none;
  background: var(--accent); mix-blend-mode: color; opacity: var(--ph-tint);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.case-photo .ph-tone { overflow: hidden; border-radius: var(--radius, 0); }

/* Our crate, the one in the first screen's clips, inside the card's top
   edge and level with the label. A render rather than a photograph, so
   it keeps its own wood and green label and skips the photographs' grade.
   It turns in on --crate (graphics.js, 0 to 1 over the card's stretch of
   the track): edge on when the card appears, then round to face the
   reader. */
.wmap__case-img {
  width: 100%; height: auto; display: block; margin: -6px -10px 0 0;
  filter: drop-shadow(0 10px 10px var(--shadow));
  perspective: 720px;
}
.wmap__case-img > * {
  --k: calc(1 - var(--crate, 1));
  transform-origin: 40% 60%;
  transform: translateY(calc(var(--k) * 14px)) rotateX(calc(var(--k) * 18deg)) rotateY(calc(var(--k) * 84deg));
  backface-visibility: hidden;
}
.wmap__case-img > img { width: 100%; height: auto; }
.wmap__case h3 {
  font-family: var(--display); font-weight: 700; letter-spacing: -.02em;
  font-size: clamp(1.1rem, .88rem + .5vw, 1.4rem); line-height: 1.14;
}
/* The route, out of the brackets in his title and set under it: where
   the consignment left and where it landed, each with the role we held
   there. Mono and in capitals like the labels; the dashes between are
   the map's leader line. */
.wmap__route {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px;
  font-family: var(--mono); font-size: .75rem; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase;
}
.wmap__route-i { display: inline-flex; align-items: center; gap: 8px; padding: 4px 0; color: var(--ink); }
.wmap__route-ic { flex: none; width: 18px; height: 18px; color: var(--accent); }
.wmap__route-r {
  padding: 3px 6px; border-radius: 5px; text-decoration: none;
  font-size: .625rem; letter-spacing: .08em;
  background: var(--accent-wash); color: var(--accent);
}
.wmap__route-to { flex: none; width: 34px; height: 8px; color: var(--ink-3); }
.wmap__case-sum { font-size: var(--fs-sm); line-height: 1.5; color: var(--ink-2); margin-top: 14px; }
/* The figures are his outcome — the ruling in about three business days,
   not a single customs hold — so they are set as figures, with the unit
   as their label. */
.wmap__case-f {
  display: grid; grid-template-columns: repeat(2, auto); justify-content: start;
  gap: 0 44px; margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--rule);
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .06em; color: var(--ink-2);
}
.wmap__case-f b {
  display: block; margin-bottom: 8px;
  font-family: var(--display); font-weight: 700; letter-spacing: -.03em; line-height: 1;
  font-size: clamp(1.9rem, 1.1rem + 1.3vw, 2.6rem); font-variant-numeric: tabular-nums;
  color: var(--accent);
}
.wmap__case-foot {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 8px 20px; margin-top: 22px;
}
/* the site's text link, zigzag and all (head.js), at the card's size;
   the room the line needs under it is lent back to the row */
.wmap__case-go { font-size: .6875rem; padding-bottom: 10px; margin-bottom: -10px; }
.wmap__case-d { font-family: var(--mono); font-size: .625rem; letter-spacing: .06em; color: var(--ink-3); }
/* Floating over the map, the card is the end of the section, not a
   caption to it: set at the size of a section's own head and body rather
   than a tooltip's. Only here — under 1100px it is a block under the map
   on a stage one screen tall, and the sizes above are what fit.
   The air is the card's, not the type's: padding and the gaps between its
   parts run on the viewport's height, which is what the card has to fit
   under the corridor, so a short desktop gets less of it rather than a
   card pushed up into Tokyo's name. */
@media (min-width: 1101px) {
  .wmap__case {
    --case-pt: clamp(36px, 5.8vh, 64px);
    width: min(42rem, 45vw);
    padding: var(--case-pt) clamp(40px, 3.9vw, 64px) clamp(34px, 5.4vh, 60px);
  }
  .wmap__case-head { grid-template-columns: minmax(0, 1fr) clamp(96px, 8.6vw, 132px); gap: 28px; }
  .wmap__case-t { gap: 16px; }
  .wmap__case-img { margin: -6px -16px 0 0; }
  .wmap__case h3 { font-size: clamp(1.45rem, .95rem + .95vw, 2rem); line-height: 1.1; }
  .wmap__route { font-size: .8125rem; }
  .wmap__route-ic { width: 20px; height: 20px; }
  .wmap__case-sum { font-size: var(--fs-body); line-height: 1.55; margin-top: clamp(18px, 3vh, 30px); max-width: 46ch; }
  .wmap__case-f { margin-top: clamp(24px, 4.2vh, 44px); padding-top: clamp(22px, 3.6vh, 36px); gap: 0 clamp(56px, 6vw, 96px); font-size: .8125rem; }
  .wmap__case-f b { font-size: clamp(2.5rem, 1.5rem + 1.9vw, 3.5rem); margin-bottom: 12px; }
  .wmap__case-foot { margin-top: clamp(26px, 4.2vh, 44px); }
  .wmap__case-go { font-size: .8125rem; }
  .wmap__case-d { font-size: .75rem; }
}

/* Below this width the copy cannot sit inside the map without landing on
   South America, so it stacks under it — and the stage gains the header's
   height back, otherwise the floating header covers the northern latitudes. */
@media (max-width: 1100px) {
  .wmap__panel {
    position: static; width: auto; max-width: 40rem;
    margin: 22px var(--gutter) 0;
  }
  .wmap__figure { display: grid; }
  .wmap__stage { padding-top: 86px; }
  /* No room to float the case over the arc, so the two blocks take
     turns in the same slot under the map — a swap, not a crossfade,
     because two stacked blocks would push the map off the stage. */
  .wmap__case {
    position: relative; width: auto; max-width: 40rem;
    margin: 22px var(--gutter) 0; display: none;
    padding: 22px 26px 20px;
  }
  .wmap:not([data-phase="0"]) .wmap__panel { display: none; }
  /* the corridor table takes the same slot in its turn, set tighter: no
     lede, and two columns of rows where the width allows, so the stage
     still holds the map above it */
  .wmap__corr {
    position: relative; z-index: auto; top: auto; left: auto; width: auto; max-width: 40rem;
    max-height: none; transform: none; box-shadow: none;
    margin: 22px var(--gutter) 0; padding: 18px 22px 8px; display: none;
  }
  .wmap[data-phase="1"] .wmap__corr { display: block; }
  .wmap__corr h2 { font-size: clamp(1.2rem, .9rem + 1vw, 1.6rem); max-width: none; }
  .wmap__corr .wmap__sub { display: none; }
  .wmap__corr .cov__list { margin-top: 12px; }
  .wmap__corr .cov__row { padding-block: 8px; }
  /* The stage is one screen tall and clips, so the card under the map is
     set tighter than the one floating over it: the link and the date move
     up beside the figures, on the same rule, instead of taking a row of
     their own — at 1024 × 768 that row was lost to the fold. */
  .wmap[data-case="on"] .wmap__case {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 24px;
  }
  .wmap__case-head, .wmap__case-sum { grid-column: 1 / -1; }
  .wmap__case-sum { margin-top: 10px; }
  .wmap__case-f { margin-top: 16px; padding-top: 14px; }
  .wmap__case-f b { font-size: 1.75rem; margin-bottom: 6px; }
  .wmap__case-foot {
    flex-direction: column; align-items: flex-end; justify-content: flex-end;
    margin: 16px 0 0 -24px; padding: 14px 0 0 24px; border-top: 1px solid var(--rule);
  }
  .wm__lead { display: none; }
}
/* A phone has no column to spare for the crate beside the label — the
   label wrapped onto two lines around it. The crate stands on the card's
   corner instead, mostly above it, and the head is one column. */
/* A short screen gives up the label and the column heads first, then the
   sector line under each corridor — the route and its days stay. */
@media (max-width: 1100px) and (max-height: 820px) {
  .wmap__corr .eyebrow, .wmap__corr .cov__row--head { display: none; }
  .wmap__corr h2 { margin-top: 0; }
  .wmap__corr .cov__row { padding-block: 6px; }
  .wmap__corr .cov__row:nth-child(2) { border-top: 1px solid var(--rule); }
}
@media (max-width: 1100px) and (max-height: 700px) {
  .wmap__corr .cov__sub { display: none; }
  .wmap__corr .cov__row { padding-block: 5px; }
}
@media (max-width: 1100px) and (min-width: 700px) {
  .wmap__corr .cov__list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 28px; }
  .wmap__corr .cov__row--head { display: none; }
  .wmap__corr .cov__row:nth-child(-n+3) { border-top: 1px solid var(--rule); }
}
@media (max-width: 560px) {
  .wmap[data-case="on"] .wmap__case { display: block; }
  .wmap__case-foot {
    flex-direction: row; align-items: baseline; justify-content: space-between;
    margin: 14px 0 0; padding: 0; border-top: 0;
  }
  .wmap__case-head { display: block; }
  .wmap__case-img { position: absolute; top: -84px; right: 14px; width: 68px; margin: 0; }
}

@media (max-width: 900px) {
  .wmap__track { height: 470vh; }
  .wmap__stage { min-height: 560px; }
  /* the map is drawn at about 0.6x here, so a four-unit mark lands
     under three device pixels — the marks scale with the map */
  .wmap {
    --fs-lbl: 22px; --r-mark: 6px; --r-hq: 6px; --r-end: 6px;
    --r-ring: 17px; --r-halo: 14px; --w-arc: 3px;
    --zm: calc(var(--zi) + (1 - var(--zi)) * .45);
  }
}

/* At phone width the map renders about a quarter of its viewBox, so a
   dot lands under one device pixel and a city name under five. The map
   takes the full width, the marks scale up, and the names come off —
   the readout carries the meaning at this size. */
@media (max-width: 640px) {
  .wmap__figure > svg { margin-inline: calc(var(--gutter) * -1); width: calc(100% + var(--gutter) * 2); }
  .wm__lbl { display: none; }
  .wmap {
    --w-dot: 5px; --r-mark: 8px; --r-hq: 8px; --r-end: 8px;
    --r-ring: 20px; --w-ring: 2.5px; --r-halo: 17px; --w-arc: 4px;
    --zm: calc(var(--zi) + (1 - var(--zi)) * .72);
  }
  /* the card becomes a sheet at the bottom of the stage */
  .wmap__pop {
    left: var(--gutter) !important; right: var(--gutter);
    top: auto !important; bottom: 16px; width: auto;
  }
}

/* No track to scroll through when motion is off: the map is simply
   finished, both steps resolved, and the section collapses to its
   own height. */
@media (prefers-reduced-motion: reduce) {
  .wmap__track { height: auto; }
  .wmap__stage { position: relative; height: auto; min-height: 0; padding-block: var(--section-y); }
  /* everything else on this map resolves through --z, --d and --dim,
     which paint() simply holds at their finished values; the card and the
     case are the two that run on their own time */
  .wmap__pop, .wmap__pop.is-open, .wmap__pop.is-glide.is-open, .wmap__case, .wm__lead { transition: none; }
  .wmap__pop.is-open .pop__in > * { animation: none; }
}
.wmap.is-static .wmap__track { height: auto; }
.wmap.is-static .wmap__stage { position: relative; height: auto; min-height: 0; padding-block: var(--section-y); }
/* With no scroll to hand the floor from one block to the other, both
   are simply on: the reach copy bottom-left, the case over its arc.
   Without this, a reduced-motion visitor never sees the reach copy. */
.wmap.is-static[data-case="on"] .wmap__panel {
  opacity: 1; transform: none; pointer-events: auto;
}
/* The corridor table has no frame of its own here, so it goes under the
   map as a block; the reach copy, which stands on the figure's bottom
   edge, is pinned to the map's bottom edge instead (the map is 540/1440
   of the figure's width) so the table does not carry it down. */
.wmap.is-static .wmap__anchor { top: 0; }
.wmap.is-static .wmap__corr {
  position: relative; top: auto; left: auto; transform: none; opacity: 1; pointer-events: auto;
  width: auto; max-width: 40rem; max-height: none; box-shadow: none; display: block;
  margin: clamp(32px, 4vw, 56px) var(--gutter) 0;
}
/* The case has no clear patch of the map to stand on once all seven
   corridors are on it, so here it goes under the map too: the table and
   the case side by side, the leader with nowhere to point. */
.wmap.is-static .wm__lead { display: none; }
@media (min-width: 1101px) {
  .wmap.is-static[data-case="on"] .wmap__panel {
    top: 0; bottom: auto; margin-top: 37.5%; transform: translateY(calc(-100% - 8px));
  }
  .wmap.is-static .wmap__figure {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(24px, 3vw, 48px); align-items: start;
  }
  .wmap.is-static .wmap__figure > svg { grid-column: 1 / -1; }
  .wmap.is-static .wmap__corr { max-width: none; margin-right: 0; }
  .wmap.is-static .wmap__case {
    position: relative; width: auto; margin: clamp(32px, 4vw, 56px) var(--gutter) 0 0;
  }
}
@media (max-width: 560px) {
  /* room for the crate, which stands on the card's corner */
  .wmap.is-static .wmap__case { margin-top: 76px; }
}
@media (max-width: 1100px) {
  .wmap.is-static[data-case="on"] .wmap__panel { display: block; }
}

/* ---------------- credentials ---------------- */

/* This was five flex items with a drawn glyph each, packed left and
   sized by their own words: IATA carried "Accredited cargo agent" and
   KLM carried "Carrier partner", so the gaps between them were all
   different and the row ran out two hundred pixels short of the page.
   The glyphs made it worse rather than better — a hexagon, a warning
   triangle, a window, four chevrons and a crosshair are five unrelated
   marks standing in for five names none of them actually draws.

   It is a register, so it is set as one: the label holds a fixed rail,
   the five entries take equal columns, and a hairline stands between
   them. Nothing is centred and nothing is packed — every entry starts
   on its own line, which is what makes the row read as a record rather
   than as a shelf of badges. The strip also drops its own fill: between
   the hero's rule and its own it is already a band, and a third value
   of grey behind it was the third horizontal stripe in a row. */
.creds { border-block: 1px solid var(--rule); }
.creds__in {
  display: grid;
  grid-template-columns: minmax(0, 21ch) minmax(0, 1fr);
  align-items: stretch;
  gap: clamp(20px, 3vw, 44px);
  padding-block: clamp(26px, 2.6vw, 36px);
}
.creds__lbl {
  font-family: var(--mono); font-size: .75rem; letter-spacing: .16em; line-height: 1.6;
  text-transform: uppercase; color: var(--ink-3);
  align-self: center; text-wrap: balance;
}
.creds__list { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); }
.cred {
  position: relative;
  padding-inline: clamp(14px, 1.6vw, 26px);
  border-left: 1px solid var(--rule);
}
.cred.is-open { z-index: 6; }
/* One curve for everything that moves here: it starts hard and stops
   dead, with nothing eased in on the way — a drive, not a float. */
.creds {
  --drive: cubic-bezier(.85,0,.15,1);
  margin-bottom: calc(var(--section-y) + var(--grow, 0px));
  transition: margin-bottom .46s var(--drive);
}
.cred__btn {
  display: grid; grid-template-rows: 58px auto; gap: 14px; align-content: start; justify-items: start;
  width: 100%; padding: 0; border: 0; background: none; font: inherit; text-align: left; cursor: default;
  color: color-mix(in srgb, var(--ink-3) 72%, var(--ground));
  transition: color .42s var(--drive), opacity .22s ease;
}
.cred__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 8px; border-radius: 4px; }
/* the open one comes up to ink and steps forward, and the rest step back */
.cred.is-open .cred__btn { color: var(--ink); }
.creds__list.has-open .cred:not(.is-open) .cred__btn { opacity: .45; }
/* the first entry keeps its rule too: the label is a rail, not the
   first column, and the row reads as six even cells beside it */
/* The issuers' marks, grey like the hero's proof strip, each sized by
   eye rather than by box: a wordmark runs long and low, a crest or a
   label stands tall, and each is set to the weight of the IATA mark. */
.cred__mark {
  display: block; align-self: center; width: auto; max-width: 100%; height: var(--mh);
  transform-origin: 0 50%; transition: transform .42s var(--drive);
}
.cred.is-open .cred__mark { transform: scale(1.14); }
.cred__mark--iata { --mh: 40px; }
.cred__mark--dg { --mh: 52px; }
.cred__mark--dg text { font-family: var(--display); font-weight: 700; font-size: 8.5px; fill: currentColor; }
.cred__mark--evo { --mh: 24px; }
.cred__mark--ndl { --mh: 56px; }
.cred__mark--dda { --mh: 35px; }
.cred__mark--klm { --mh: 42px; }
.cred__note { font-family: var(--mono); font-size: .75rem; letter-spacing: .03em; color: var(--ink-3); line-height: 1.35; }
.cred.is-open .cred__note { color: var(--ink); }

/* The note: what the accreditation is and what it lets CFL do, set in
   ink straight on the page in the empty band above or below the strip —
   no card, no shadow. graphics.js opens it on hover, focus or a tap,
   puts it above when the screen has room there and below when it does
   not (data-dir), lines it up with the mark's column, and measures the
   distance to the strip's rule (--edge) and its own height (--ch). The
   box sits on the rule and clips, so the text drives out from behind
   the line and back in. Moving along the row, the next one waits for
   the last to go back in (--wait). */
.cred__card {
  position: absolute; z-index: 1; left: var(--cx, 0px);
  width: min(30rem, calc(100vw - 2 * var(--gutter)));
  display: grid; gap: 8px;
  padding-bottom: 26px; overflow: hidden; background: var(--ground);
  visibility: hidden; pointer-events: none;
  transition: visibility 0s linear .2s;
}
.cred[data-dir="up"] .cred__card { bottom: calc(100% + var(--edge, 30px)); }
.cred[data-dir="down"] .cred__card { top: calc(100% + var(--edge, 30px)); padding: 26px 0 0; }
.cred__card > * { transition: transform .2s cubic-bezier(.6,0,.8,.2); }
.cred.is-placing .cred__card > * { transition: none; }
.cred[data-dir="up"] .cred__card > * { transform: translateY(var(--ch, 200px)); }
.cred[data-dir="down"] .cred__card > * { transform: translateY(calc(-1 * var(--ch, 200px))); }
.cred.is-open .cred__card {
  visibility: visible; pointer-events: auto;
  transition: visibility 0s linear var(--wait, 0ms);
}
.cred.is-open .cred__card > * {
  transform: none;
  transition: transform .46s var(--drive) var(--wait, 0ms);
}
.cred__card-h {
  font-family: var(--display); font-weight: 600; font-size: 1.0625rem; line-height: 1.25;
  letter-spacing: -.01em; color: var(--ink);
}
.cred__card-b { font-size: .9375rem; line-height: 1.55; color: var(--ink); text-wrap: pretty; }
@media (prefers-reduced-motion: reduce) {
  .cred__card > *, .cred.is-open .cred__card > * { transform: none; transition: none; }
  .creds, .cred__btn, .cred__mark { transition: none; }
}

/* the rail folds under the row before the entries have to fold on each
   other; below that they go two-up, which still keeps the columns even */
@media (max-width: 1180px) {
  .creds__in { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .cred:first-child { padding-left: 0; border-left: 0; }
}
@media (max-width: 820px) {
  .creds__list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 26px 0; }
  .cred:nth-child(3n+1) { padding-left: 0; border-left: 0; }
}
@media (max-width: 480px) {
  .creds__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cred:nth-child(3n+1) { padding-left: clamp(14px, 1.6vw, 26px); border-left: 1px solid var(--rule); }
  .cred:nth-child(odd) { padding-left: 0; border-left: 0; }
}

/* ---------------- how we work: five stages, two lanes ----------------

   His five stages are five columns, and across the top of them run the
   two ways of carrying them, named as his table names them. The reactive
   model has nothing on the first three — the freight is booked first —
   and then export, freight and customs in three pieces with a seam
   between each, and its questions at the border. CFL's lane is one bar
   from the first stage to the last, over the border and the moment the
   cargo moves without a break. The first screen's rail already says
   "one path, one party"; this says it as the comparison.

   Each stage draws its own piece of both lanes, so a lane is five pieces
   laid end to end. The columns have no gap for that reason — the space
   between them is the text's own right padding, which the lanes run
   across — and every piece reaches a pixel into the next, so no seam
   shows in the one lane that must not have one. On a narrow screen the
   same pieces stand in a gutter down the left and the stages stack. */

.flowb { --pad: clamp(18px, 2vw, 32px); }
.flowb__steps { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); row-gap: 10px; }
/* one row per part, shared across the five, so a short name that runs to
   two lines does not drop that stage's title below the others */
.flowb__step { display: grid; grid-row: span 5; grid-template-rows: subgrid; align-content: start; padding-right: var(--pad); }
/* a fade, not a slide: the lanes cross the columns and must not come apart */
.flowb__step[data-reveal] { transform: none; }
.flowb__stepn {
  font-family: var(--mono); font-size: .75rem; letter-spacing: .18em; color: var(--accent);
  display: flex; align-items: center; gap: 12px;
  margin-right: calc(12px - var(--pad));   /* the axis runs on to the next stage's number */
}
.flowb__stepn i { font-style: normal; height: 1px; flex: 1; background: var(--rule); }
.flowb__meta { font-family: var(--mono); font-size: .6875rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2); }
.flowb__step h3 { font-size: clamp(1.05rem, .9rem + .4vw, 1.3rem); line-height: 1.3; margin-top: 4px; }
.flowb__step p { color: var(--ink-2); font-size: var(--fs-sm); line-height: 1.6; }

.relay { position: relative; height: 124px; margin: 0 calc(-1 * var(--pad)) 6px 0; }
.relay > * { position: absolute; }
.relay__k, .relay__mark, .relay__flag, .relay__seg, .relay__c b {
  font-family: var(--mono); font-size: .625rem; letter-spacing: .12em; text-transform: uppercase;
  white-space: nowrap; font-weight: 400;
}
.relay__k { left: 0; color: var(--ink-3); }
.relay__k--r { top: 0; }
.relay__k--c { top: 70px; color: var(--accent); }

/* the lanes: 16px, one a row of dots where nothing is being done */
.relay__r, .relay__c { left: 0; right: -1px; height: 16px; z-index: 1; }
.relay__r { top: 20px; background: radial-gradient(circle, color-mix(in srgb, var(--ink-3) 55%, transparent) 1.1px, transparent 1.5px) 0 50% / 8px 8px repeat-x; }
.relay--run .relay__r { background: none; }
/* Wide, the dots for the first three stages are one run drawn from the
   first column, so their pitch does not restart at each column's edge. */
@media (min-width: 1024px) {
  .flowb__step:first-child .relay__r { right: auto; width: calc(300% + 1px); }
  .flowb__step:nth-child(2) .relay__r, .flowb__step:nth-child(3) .relay__r { background: none; }
}
.relay__c { top: 90px; background: var(--accent); transform-origin: left center; }
.flowb__step:first-child .relay__c { border-radius: 8px 0 0 8px; z-index: 2; }  /* its words run on over the next piece */
.flowb__step:last-child .relay__c { right: 0; border-radius: 0 8px 8px 0; }
.relay__c b { display: block; padding-left: 12px; line-height: 16px; letter-spacing: .1em; color: var(--ground); }

/* the reactive model's three pieces, and the seams between them */
.relay__seg {
  position: absolute; top: 0; height: 16px; line-height: 16px; text-align: center; letter-spacing: .08em;
  font-size: clamp(.5rem, .35rem + .3vw, .625rem);
  border-radius: 3px; color: var(--ink-2);
  background: color-mix(in srgb, var(--ink-3) 15%, var(--ground));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink-3) 24%, transparent);
}
.relay__seg--export  { left: 2%;  width: 24%; }
.relay__seg--freight { left: 29%; width: 29%; }
.relay__seg--customs { left: 66%; width: 30%; }

/* where the cargo moves, and where it crosses: two lines through both
   lanes, under them, so the one bar is seen to go straight over */
.relay__mark { top: 0; color: var(--ink-3); }
.relay__mark::before { content: ''; position: absolute; top: 0; height: 116px; width: 1px; }
.relay__mark--cargo { left: 0; }
.relay__mark--cargo { transform: translateX(calc(-100% - 8px)); }
.relay__mark--cargo::before { left: calc(100% + 8px); background: var(--ink-2); }
.relay__mark--border { left: calc(62% + 8px); }
.relay__mark--border::before {
  left: -8px; background: repeating-linear-gradient(to bottom, var(--ink-3) 0 3px, transparent 3px 7px);
}
.relay__flag { left: 62%; top: 42px; transform: translateX(-50%); padding: 2px 6px; background: var(--ground); color: var(--flag); }
.relay__flag::before {
  content: ''; position: absolute; left: 50%; top: -21px; width: 9px; height: 9px;
  margin-left: -4.5px; background: var(--flag); transform: rotate(45deg);
  box-shadow: 0 0 0 2px var(--ground);
}

/* the arrival: the CFL bar draws across the five, piece after piece; the
   reactive pieces and their question come after it */
.relay__c { transform: scaleX(0); transition: transform .3s linear; transition-delay: calc(var(--i, 0) * .3s + .2s); }
.flowb__step:first-child .relay__c { transition-timing-function: cubic-bezier(.5, 0, 1, 1); }
.flowb__step:last-child .relay__c { transition-timing-function: cubic-bezier(0, 0, .3, 1); }
.relay__seg, .relay__flag { opacity: 0; transition: opacity .45s ease; }
.relay__seg--export { transition-delay: .35s; }
.relay__seg--freight { transition-delay: .5s; }
.relay__seg--customs { transition-delay: .65s; }
.relay__flag { transition-delay: 1.1s; }
.flowb__step.is-in .relay__c { transform: none; }
.flowb__step.is-in .relay__seg, .flowb__step.is-in .relay__flag { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .relay__c { transform: none; transition: none; }
  .relay__seg, .relay__flag { opacity: 1; transition: none; }
}

/* narrow: the stages stack, and the lanes stand in a gutter down the left */
@media (max-width: 1023px) {
  .flowb__steps { grid-template-columns: minmax(0, 1fr); margin-top: 34px; row-gap: 0; }
  .flowb__step { position: relative; display: flex; flex-direction: column; gap: 10px; padding: 26px 0 30px 72px; border-top: 1px solid var(--rule); }
  .flowb__step:has(.relay--run) { border-top-color: var(--ink-2); }
  .flowb__stepn { margin-right: 0; }
  .relay { position: absolute; inset: 0 auto 0 0; width: 56px; height: auto; margin: 0; }
  .relay__r, .relay__c { top: 0; bottom: -1px; right: auto; width: 16px; height: auto; }
  .relay__r { left: 4px; background-position: 50% 0; background-size: 8px 8px; background-repeat: repeat-y; }
  .relay__c { left: 32px; transform-origin: center top; }
  .flowb__step:first-child .relay__c { border-radius: 8px 8px 0 0; }
  .flowb__step:last-child .relay__c { bottom: 0; border-radius: 0 0 8px 8px; }
  .relay__c b { writing-mode: vertical-rl; padding: 12px 0 0; line-height: 16px; }
  .relay__k { top: -30px; display: flex; align-items: center; gap: 8px; }
  .relay__k--r { left: 0; }
  .relay__k--c { left: 148px; }
  .relay__k::before { content: ''; width: 16px; height: 8px; border-radius: 2px; }
  .relay__k--r::before { background: radial-gradient(circle, color-mix(in srgb, var(--ink-3) 55%, transparent) 1.1px, transparent 1.5px) 0 50% / 8px 8px repeat-x; }
  .relay__k--c::before { background: var(--accent); }
  .relay__seg { left: 0; width: 16px; height: auto; writing-mode: vertical-rl; line-height: 16px; }
  .relay__seg--export  { top: 3%;  bottom: 75%; }
  .relay__seg--freight { top: 28%; bottom: 42%; }
  .relay__seg--customs { top: 66%; bottom: 6%; }
  /* the cargo line is the stage's top edge here; the border and the
     question keep their marks and drop their words, which the table
     under the stages has anyway */
  .relay__mark--cargo { top: 0; left: 72px; transform: translateY(-50%); padding-right: 8px; background: var(--ground); }
  .relay__mark--cargo::before { display: none; }
  .relay__mark--border { top: 62%; left: 0; font-size: 0; }
  .relay__mark--border::before { left: 0; top: 0; width: 56px; height: 1px; background: repeating-linear-gradient(to right, var(--ink-3) 0 3px, transparent 3px 7px); }
  .relay__flag { top: 62%; left: 12px; transform: none; padding: 0; background: none; font-size: 0; }
  .relay__flag::before { top: -4.5px; left: 0; }
  .relay__c { transform: scaleY(0); transition-delay: .15s; }
  .flowb__step:first-child .relay__c, .flowb__step:last-child .relay__c { transition-timing-function: linear; }
}

/* ---------------- who the services are for ----------------
   The five audiences the client's draft names, set as a band rather than
   as a sentence. A lede under this heading was a paragraph nobody reads
   standing between a claim and three cards; the same five words scan in
   a second and say the one thing the heading cannot — who this is for.

   They were five white capsules on a light ground, which is the weakest
   shape a list of five can take: five objects with nothing holding them,
   each ending wherever its word ends, none of them touching the page.
   The band is one object instead. It is ruled with the hairline the rest
   of the sheet is drawn with and divided into cells the way the key/value
   grids are, it runs the full measure, and it closes the head and hands
   the eye down to the three cards. The names are the site's own — the
   Industries block below calls them the same thing — and the band takes
   no accent except the 2px mark at its head, which is the same mark the
   rail above it starts on. */

.aud {
  display: flex; flex-wrap: wrap; align-items: stretch;
  gap: 1px; background: var(--rule);
  border-block: 1px solid var(--rule);
  margin-top: clamp(4px, .8vw, 12px);
}
.aud__lb, .aud__p {
  background: var(--ground);
  display: flex; align-items: center;
  padding: 13px clamp(14px, 1.4vw, 22px);
  font-family: var(--mono); text-transform: uppercase; white-space: nowrap;
}
.aud__lb {
  font-size: .625rem; letter-spacing: .18em; color: var(--ink-3);
  box-shadow: inset 2px 0 0 var(--accent);
}
.aud__p {
  flex: 1 1 auto; justify-content: center;
  font-size: .75rem; letter-spacing: .08em; color: var(--ink-2);
}

/* ---------------- solutions ----------------
   Three vertical cards on one row, split by the same hairline the rest
   of the sheet is drawn with. They were three horizontal bands before,
   which read as a list of three things a company happens to do; on one
   row they read as one offer with three parts, which is what they are.

   Each card is a subgrid of the row, so the name, the promise, the
   paragraph, the points and the link sit on shared baselines. That is
   not neatness for its own sake: the Freight paragraph runs a third
   longer than the other two, and without shared rows the row of links
   steps down like a staircase. Where subgrid is missing the card falls
   back to a flex column with the points pushed to the foot — top and
   bottom aligned, the middle allowed to float. */

.sols {
  --sol-gap: clamp(14px, 1.1vw, 20px);
  --sol-pad: clamp(26px, 2.1vw, 40px);
  --sol-space: clamp(6px, .55vw, 10px);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  /* The column gap is the space between the cards. The row gap is not:
     each card is a subgrid spanning every row of this grid, so the row
     gap falls inside the cards and is their internal rhythm. */
  column-gap: var(--sol-space); row-gap: var(--sol-gap);
}
.sol {
  background: var(--surface); padding: var(--sol-pad);
  border: 1px solid var(--rule); border-radius: var(--radius);
  display: flex; flex-direction: column; gap: var(--sol-gap);
  position: relative;
  transition: background .22s ease, border-color .22s ease;
}
.sol:hover { background: var(--surface-2); border-color: var(--accent-dim); }
.sol__pts { margin-top: auto; }

@supports (grid-template-rows: subgrid) {
  .sols { grid-template-rows: repeat(3, auto); }
  .sol { display: grid; grid-template-rows: subgrid; grid-row: span 3; }
  .sol__pts { margin-top: 0; }
}

/* below the three-up measure the cards stack and the hairline turns */
@media (max-width: 1079px) {
  /* stacked, the row gap goes back to being the space between cards */
  .sols { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; row-gap: var(--sol-space); }
  .sol { display: flex; grid-row: auto; }
  .sol__pts { margin-top: 0; }
}

.sol__b { color: var(--ink-2); font-size: var(--fs-sm); }
.sol__pts { display: grid; gap: 9px; align-content: start; }
.sol__pts li { font-family: var(--mono); font-size: .75rem; color: var(--ink-2); padding-left: 22px; position: relative; line-height: 1.55; }
.sol__pts li::before { content: ""; position: absolute; left: 0; top: .52em; width: 12px; height: 1px; background: var(--accent-dim); }
/* The three mono codes above the names - IOR/EOR, TC, FF - were a row of
   labels saying what the headline under each already said, and a row that
   exists on all three cards to carry two letters on two of them is a row
   of air. Gone. The client's Core tag is not a label though, it is a mark
   on one service, so it goes where a mark goes: the corner. */
.sol__tag { position: absolute; top: var(--sol-pad); right: var(--sol-pad); }

/* The three services are the headline's three functions, so on a wide
   screen they sit under it, in the column it leaves empty, rather than a
   whole head's height further down. Half a row is too narrow for three
   up, so they stack, and each turns on its side: the plate on the left,
   the prose beside it, the link under the prose. Below 1100px the head is
   one column again and they fall back to the row they always were. */
@media (min-width: 1100px) {
  .sols.sols--head { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; row-gap: var(--sol-space); }
  .sols--head .sol {
    display: grid; grid-row: auto;
    grid-template-columns: clamp(104px, 9vw, 150px) minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    column-gap: var(--sol-pad); row-gap: var(--sol-gap);
    padding: clamp(22px, 1.7vw, 30px);
  }
  .sols--head .sol__fig {
    grid-column: 1; grid-row: 1 / -1; align-self: start;
    padding: 0; border-bottom: 0;
  }
  .sols--head .sol__plate { width: 100%; }
  .sols--head .sol__txt { grid-column: 2; }
  .sols--head .sol > .txtlink { grid-column: 2; align-self: end; }
}

/* ---------------- sectors ---------------- */

.sectors { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(20px, 2.4vw, 32px); }
@media (max-width: 820px) { .sectors { grid-template-columns: minmax(0, 1fr); } }
.sector {
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: clamp(30px, 3.6vw, 52px); display: grid; gap: 18px; align-content: start;
  background: linear-gradient(180deg, var(--surface), var(--ground));
  scroll-margin-top: calc(var(--head-top) + var(--head-h) + 20px);
}
.sector--primary { border-color: var(--accent-dim); }
.sector__role { font-family: var(--mono); font-size: .625rem; letter-spacing: .2em; text-transform: uppercase; color: var(--accent); }
.sector p { color: var(--ink-2); font-size: var(--fs-sm); }
.sector__tags { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 6px; }
.sectors__rest { margin-top: 26px; font-size: var(--fs-sm); color: var(--ink-3); }

/* ---------------- cases ---------------- */

.cases { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(22px, 2.6vw, 34px); }
@media (max-width: 900px) { .cases { grid-template-columns: minmax(0, 1fr); } }
.case {
  border: 1px solid var(--rule); border-radius: var(--radius); background: var(--surface);
  padding: clamp(26px, 3vw, 38px); display: grid; gap: 18px; align-content: start;
  transition: border-color .22s ease, background .22s ease, transform .22s ease;
}
.case:hover { border-color: var(--accent-dim); background: var(--surface-2); transform: translateY(-2px); }
.case__arc { width: 100%; height: auto; }
.case__corr { font-family: var(--display); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.015em; }
.case__meta { font-family: var(--mono); font-size: .6875rem; color: var(--ink-3); letter-spacing: .04em; }
.case__v { font-family: var(--display); font-weight: 700; font-size: 2rem; letter-spacing: -0.045em; color: var(--accent); font-variant-numeric: tabular-nums; }
.case__h { font-size: var(--fs-sm); color: var(--ink-2); }

/* ---------------- references ----------------
   Five clients, in full and under their own names (the testimonials
   pack, 2026-10-06; the email gate that blurred them is parked). The
   widest of them leads, with the figures in it drawn out beside it.
   Each quote carries one phrase in the accent, the line the pack
   pulled out for a card. */

.refs .sec-head { margin-bottom: clamp(28px, 3vw, 48px); }

.ref {
  display: flex; flex-direction: column; gap: 22px;
  padding: clamp(22px, 2.2vw, 30px);
  background: var(--surface); border: 1px solid var(--rule); border-radius: var(--radius);
}
.ref__tag { font-family: var(--mono); font-size: .6875rem; letter-spacing: .06em; color: var(--ink-3); }
.ref__tag i { font-style: normal; color: var(--accent); }
.ref__q { flex: 1; font-size: 1.0625rem; line-height: 1.6; color: var(--ink); }
.ref__q p { text-wrap: pretty; }
.ref__q p + p { margin-top: .8em; }
.ref__q::before {
  content: "\201C"; display: block; margin-bottom: 10px;
  font-family: var(--display); font-weight: 700; font-size: 3rem; line-height: .75; color: var(--accent);
}
.ref__q b { font-weight: 600; color: var(--accent); }

.ref__by { display: flex; align-items: center; gap: 14px; padding-top: 18px; border-top: 1px solid var(--rule-2); }
.ref__who { display: grid; gap: 2px; min-width: 0; }
.ref__name { font-family: var(--display); font-weight: 700; font-size: .9375rem; }
.ref__role, .ref__org { font-size: .8125rem; line-height: 1.4; color: var(--ink-2); }
.ref__org { color: var(--ink); font-weight: 500; }
/* The client's mark, in the space the byline leaves on the right. Drawn
   as a mask in one ink, so five brands' colours do not fight the accent
   and the marks follow the theme. Each is sized to about the same ink
   area (--h from its aspect ratio --r, set inline), so a long wordmark
   and a squat one carry the same weight. Its name is beside it, so the
   mark is decoration to a screen reader. */
.ref__by { flex-wrap: wrap; row-gap: 14px; }
.ref__logo {
  flex: none; margin-left: auto;
  height: var(--h); width: calc(var(--h) * var(--r));
  background: var(--ink-2); opacity: .75;
  -webkit-mask: var(--logo) center / contain no-repeat;
          mask: var(--logo) center / contain no-repeat;
}

/* the lead: the quote, and beside it the figures and who said it */
.ref--lead {
  display: grid; grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr);
  gap: clamp(28px, 3.5vw, 56px);
  margin-bottom: 16px; padding: clamp(24px, 3.2vw, 48px);
}
.ref__main { display: flex; flex-direction: column; gap: 22px; }
.ref--lead .ref__q { font-size: clamp(1.125rem, .95rem + .5vw, 1.375rem); line-height: 1.55; }
.ref__side {
  display: flex; flex-direction: column; justify-content: space-between; gap: 32px;
  padding-left: clamp(28px, 3.5vw, 56px); border-left: 1px solid var(--rule-2);
}
.ref__facts { display: grid; }
.ref__facts div {
  display: flex; flex-direction: row-reverse; justify-content: flex-end; align-items: baseline; gap: 16px;
  padding: 14px 0; border-bottom: 1px solid var(--rule-2);
}
.ref__facts div:first-child { padding-top: 0; }
.ref__facts div:last-child { border-bottom: 0; }
.ref__facts dd {
  min-width: 1.35em;
  font-family: var(--display); font-weight: 700; font-size: clamp(2rem, 1.5rem + 1.3vw, 2.75rem);
  line-height: 1; letter-spacing: -0.045em; color: var(--accent); font-variant-numeric: tabular-nums;
}
.ref__facts dt { font-family: var(--mono); font-size: .75rem; letter-spacing: .04em; color: var(--ink-2); }

/* the other four. Joao's runs two and a half times the length of
   the others, so on a wide screen they go two to a row with the left
   column the wider one, which brings each row's two cards out near
   level; between that and a phone there is no width at which two
   columns stay level, so each card lays out as the lead does, the
   quote with who said it beside it, one to a row */
.refs__grid { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 16px; }
@media (max-width: 1279px) {
  .refs__grid { grid-template-columns: minmax(0, 1fr); }
  .refs__grid .ref {
    display: grid; grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr);
    grid-template-areas: "tag tag" "q by"; gap: 22px clamp(28px, 3.5vw, 56px);
  }
  .refs__grid .ref__tag { grid-area: tag; }
  .refs__grid .ref__q { grid-area: q; }
  .refs__grid .ref__by {
    grid-area: by; flex-direction: column; align-items: flex-start; justify-content: flex-end;
    flex-wrap: nowrap; padding: 0 0 0 clamp(28px, 3.5vw, 56px); border-top: 0; border-left: 1px solid var(--rule-2);
  }
  /* the byline stands at the foot of its column; the mark takes the top */
  .refs__grid .ref__logo { order: -1; margin: 0 0 auto; }
}
@media (max-width: 720px) {
  .ref--lead { grid-template-columns: minmax(0, 1fr); }
  .ref__side { padding: 22px 0 0; border-left: 0; border-top: 1px solid var(--rule-2); }
  .ref__facts { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
  .ref__facts div { flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 6px; padding: 0; border: 0; }
  .ref__facts dd { order: -1; }
  .ref__side .ref__by { padding-top: 0; border-top: 0; }
  .refs__grid .ref { display: flex; }
  .refs__grid .ref__by { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: flex-start; padding: 18px 0 0; border-left: 0; border-top: 1px solid var(--rule-2); }
  .refs__grid .ref__logo { order: 0; margin: 0 0 0 auto; }
}

.refs__note { margin-top: 20px; font-family: var(--mono); font-size: .6875rem; letter-spacing: .04em; line-height: 1.6; color: var(--ink-3); }

/* ---------------- coverage ---------------- */

.cov__list { display: grid; }
.cov__row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px 16px;
  padding: 13px 0; border-bottom: 1px solid var(--rule);
  font-family: var(--mono); font-size: .8125rem; color: var(--ink-2);
  cursor: default; transition: color .18s ease;
}
.cov__row:first-child { border-top: 1px solid var(--rule); }
.cov__row--head { font-size: .625rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); padding-bottom: 9px; }
.cov__row--head .cov__c, .cov__row--head:hover .cov__c { color: var(--ink-3); }
.cov__row--head .cov__sub { color: var(--ink-3); }
.cov__row:hover, .cov__row.is-hot { color: var(--accent); }
.cov__c { color: var(--ink); }
.cov__row:hover .cov__c, .cov__row.is-hot .cov__c { color: var(--accent); }
.cov__c i { font-style: normal; color: var(--accent); padding: 0 7px; }
.cov__sub { grid-column: 1 / -1; font-size: .6875rem; color: var(--ink-3); letter-spacing: .05em; }
.cov__d { white-space: nowrap; }

/* ---------------- who / legal ---------------- */

.who__in, .legal__in { display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); gap: clamp(28px, 5vw, 72px); }
@media (max-width: 880px) { .who__in, .legal__in { grid-template-columns: minmax(0, 1fr); } }
.who__body { display: grid; gap: 22px; }
/* one voice, no bold, the first paragraph included (client, 2026-10-07) */
.who__body p { font-size: var(--fs-lede); color: var(--ink-2); max-width: 56ch; }

.legal { border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); background: var(--ground-2); }
.legal__dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; background: var(--rule); border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden; }
@media (max-width: 620px) { .legal__dl { grid-template-columns: minmax(0, 1fr); } }
.legal__row { background: var(--surface); padding: 16px 18px; }
.legal__row:last-child:nth-child(odd) { grid-column: 1 / -1; }
.legal__k { font-family: var(--mono); font-size: .625rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); }
.legal__v { font-family: var(--mono); font-size: .8125rem; color: var(--ink); margin-top: 6px; overflow-wrap: anywhere; }
.legal__note { font-size: var(--fs-sm); color: var(--ink-3); max-width: 38ch; }
/* his headline here is a whole sentence (2026-10-07): set at h3 size, or
   the column runs it ten lines deep */
.legal__in > .sec-head > h2.legal__h { font-size: var(--fs-h3); line-height: 1.3; letter-spacing: -0.012em; max-width: 34ch; }
.legal__sub { font-size: .8125rem; line-height: 1.45; color: var(--ink-2); margin-top: 6px; }
/* a number he has not confirmed yet: the row stays, the number does not */
.legal__v--tbc { color: var(--ink-3); }

/* ---------------- closing ---------------- */

.closing { position: relative; overflow: hidden; border-top: 1px solid var(--rule); }
.closing__art { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; opacity: .85; }
.closing__in { position: relative; z-index: 2; display: grid; gap: 26px; max-width: 640px; }
.closing__note { font-family: var(--mono); font-size: .6875rem; letter-spacing: .06em; color: var(--ink-3); }

/* ---------------- footer ---------------- */

.site-foot { border-top: 1px solid var(--rule); background: var(--ground-2); }
.site-foot__top { display: grid; grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr)); gap: clamp(24px, 3vw, 48px); padding-block: clamp(44px, 5vw, 68px); }
@media (max-width: 860px) { .site-foot__top { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .site-foot__top { grid-template-columns: minmax(0, 1fr); } }
.foot-brand { display: grid; gap: 16px; align-content: start; }
.foot-brand .brand { --mark-h: calc(17px * var(--mark-scale, 1)); }
.foot-tag { font-size: var(--fs-sm); color: var(--ink-2); max-width: 34ch; }
.foot-col h3 { font-family: var(--mono); font-weight: 500; font-size: .625rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 15px; }
.foot-col li + li { margin-top: 10px; }
.foot-col a { font-size: var(--fs-sm); color: var(--ink-2); }
.foot-col a:hover { color: var(--accent); }
.site-foot__bot {
  border-top: 1px solid var(--rule); padding-block: 20px;
  display: flex; flex-wrap: wrap; gap: 12px 28px; align-items: center;
  font-family: var(--mono); font-size: .6875rem; color: var(--ink-3); letter-spacing: .03em;
}
.site-foot__bot a:hover { color: var(--accent); }
.site-foot__bot .sep { margin-left: auto; }
@media (max-width: 700px) { .site-foot__bot .sep { margin-left: 0; } }

/* ---------------- motion ---------------- */

/* --rd is written by the reveal observer in graphics.js: a block's
   place in its own group, so a row of cards assembles rather than
   flashes. Absent, it is zero and nothing waits. */
[data-reveal] { opacity: 0; transform: translateX(-14px); transition: opacity .6s ease, transform .6s cubic-bezier(.22,.61,.36,1); transition-delay: var(--rd, 0ms); }
[data-reveal].is-in { opacity: 1; transform: none; }
[data-reveal="up"] { transform: translateY(16px); }
[data-draw] { stroke-dasharray: var(--len, 1200); stroke-dashoffset: var(--len, 1200); transition: stroke-dashoffset 1.4s cubic-bezier(.33,.1,.2,1); }
[data-draw].is-in { stroke-dashoffset: 0; }

/* Everything the hand-placed reveals above do not reach arrives too —
   tagged and played by the last block of shared/head.js. Waiting is
   only a held opacity, so nothing moves in the layout; the way in is a
   Web Animation, which leaves no style behind once it has played, and
   while it plays the element's own transitions are held off, or a
   transition on opacity would race it. */
/* Not every hand-placed reveal is the same nudge. Once a section's head
   has landed, its headline rises from behind its own baseline; a card in
   a row lifts rather than slides; a photograph is uncovered across its
   frame like a print, the picture settling inside it, every other one
   from the far edge. a.sector's own hover transitions are carried along,
   or its rule would take the reveal's transition away. */
.sec-head[data-reveal] h2, .closing__in > h2[data-reveal] {
  clip-path: inset(-20% -5% 100% -5%); translate: 0 100%;
  transition: clip-path 1s cubic-bezier(.16,1,.3,1), translate 1s cubic-bezier(.16,1,.3,1);
  transition-delay: calc(var(--rd, 0ms) + 120ms);
}
.sec-head[data-reveal].is-in h2, .closing__in > h2[data-reveal].is-in { clip-path: inset(-20% -5% -20% -5%); translate: none; }
.closing__in > h2[data-reveal] { opacity: 1; transform: none; }
[data-reveal]:is(.sol, .sector, .why, .kc, .ref) {
  transform: translateY(36px);
  transition: opacity .8s ease, transform .9s cubic-bezier(.16,1,.3,1), background .22s ease, border-color .22s ease;
  transition-delay: var(--rd, 0ms), var(--rd, 0ms), 0s, 0s;
}
[data-reveal]:is(.sol, .sector, .why, .kc, .ref).is-in { transform: none; }
.ph[data-reveal] { transform: none; }
.ph[data-reveal] .ph__frame {
  clip-path: inset(-2px 100% -2px -2px);
  transition: clip-path 1.3s cubic-bezier(.77,0,.18,1) var(--rd, 0ms);
}
.ph[data-reveal]:nth-child(even) .ph__frame { clip-path: inset(-2px -2px -2px 100%); }
.ph[data-reveal] .ph__frame img { scale: 1.08; transition: scale 1.8s cubic-bezier(.16,1,.3,1) var(--rd, 0ms); }
.ph[data-reveal].is-in .ph__frame { clip-path: inset(-2px); }
.ph[data-reveal].is-in .ph__frame img { scale: 1; }
@media (prefers-reduced-motion: reduce) {
  .sec-head[data-reveal] h2, .closing__in > h2[data-reveal], .ph[data-reveal] .ph__frame { clip-path: none !important; translate: none !important; }
  .ph[data-reveal] .ph__frame img { scale: 1 !important; }
}

.rv-wait { opacity: 0 !important; }
.rv-run { transition: none !important; }
.rv-word { display: inline-block; }
@media print { .rv-wait { opacity: 1 !important; } }

/* The section rail draws rather than arrives. It is the same gesture the
   corridor diagrams make when they enter, at a quarter of the weight:
   the label lands, and the line leaves it for the far margin. */
[data-reveal] > .eyebrow::after {
  background-size: 0 1px;
  transition: background-size 1s cubic-bezier(.22,.61,.36,1);
  transition-delay: calc(var(--rd, 0ms) + 140ms);
}
[data-reveal].is-in > .eyebrow::after { background-size: 100% 1px; }
.sec-head[data-reveal].is-in > .eyebrow::after { background-size: calc(100% - 4ch) 1px; }

.pulse { animation: b-pulse 2.6s ease-in-out infinite; transform-origin: center; transform-box: fill-box; }
@keyframes b-pulse { 0%, 100% { opacity: .35; r: 4; } 50% { opacity: 1; r: 6.5; } }
@media (prefers-reduced-motion: reduce) {
  .pulse { animation: none; opacity: 1; }
  [data-reveal] > .eyebrow::after { background-size: 100% 1px; }
  .sec-head[data-reveal] > .eyebrow::after { background-size: calc(100% - 4ch) 1px; }
}

/* clearance for the collapsed review handle in the bottom-right corner —
   only where the handle is drawn; elsewhere the 34px stood under the
   footer as a band of the other ground */
body:has(> [data-cfl-ui="switcher"]) { padding-bottom: 34px; }

/* ============================================================
   LAYOUT ALTERNATIVES — first screen

   The same copy nodes serve every variant; only the layout moves,
   so there is nothing to keep in sync.

   The header is not an alternative any more: the glass island is
   the only one. Lockup, Band and the gather.ai Bar were cut.

   Applied as html[data-hero] by shared/prefs.js.

   Two first screens are left: Split, and Spell. Full, Film and Bars
   were cut on 2026-09-28.
   ============================================================ */

/* ---------------- first screen · SPLIT -------------------------
   The current first screen, rebuilt: the numbers are gone and the
   screen divides three to one.

   What left. The proof strip — $680m / 47 / €0 / 16 yrs — and the
   accreditation band under it were the bottom third of the screen,
   and both are figures a procurement team reads once it is already
   interested, not the thing that makes it interested. The band sits
   further down the page now, and the numbers are off the page
   altogether — Spell dropped them too.

   What arrived. A standing column of footage down the right edge, a
   quarter of the screen wide and the full height of it, running under
   the island rather than starting below it. The claim now has a whole
   screen to be the only thing on, and the picture is beside it rather
   than behind it — which is the one thing Split could never do while
   the copy sat on the page's full measure.
   ---------------------------------------------------------------- */

html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero {
  --q-w: 25%;                              /* the column, of the hero's own box */
  --q-gap: clamp(24px, 3.4vw, 80px);       /* and what the copy keeps clear of it */
  min-height: 100svh;
  display: grid; align-content: center;
  overflow: hidden;
}

/* The wrap stops centring itself here. It has to span the whole hero so
   that the right padding can be read off the same box the column is a
   quarter of; the left padding then puts the copy back on the page's own
   inset by hand — max-width plus auto margins, written out. Do it the
   other way round and the copy centres inside the remaining three
   quarters, which lands its left edge a hundred-odd pixels right of
   every other section on the page. */
html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__in {
  max-width: none; margin-inline: 0;
  padding-left: max(var(--gutter), calc((100% - var(--maxw)) / 2 + var(--gutter)));
  padding-right: calc(var(--q-w) + var(--q-gap));
  /* the island floats over this block, so the top pad carries its
     clearance; the rest is centred against the bottom pad */
  padding-block: calc(var(--head-top) + var(--head-h) + clamp(24px, 3vh, 44px)) clamp(48px, 6vw, 84px);
  /* The rail takes whatever height it asks for and the copy takes the
     rest, so the measure follows the drawing rather than a number: the
     rail is sized in svh, and at every width the headline gets what is
     left. Centred against each other, because the rail is a column of
     six evenly spaced things and hanging it off the top of a headline
     that is three lines on one screen and five on another reads as a
     mistake at one of them. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "copy rail";
  align-items: center;
  column-gap: clamp(24px, 2.8vw, 64px);
}
html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__copy { grid-area: copy; }

/* Split clears its own first screen of the four numbers. The credentials
   strip used to go with them, because it sat directly under the hero; it
   is below the client's sections now, so it shows on every first screen. */

.hero__quarter { display: none; }

html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__quarter {
  display: block;
  position: absolute; inset: 0 0 0 auto;
  width: var(--q-w);
  overflow: hidden;
  /* the blend inside must not reach the rest of the page */
  isolation: isolate;
  background: var(--ground-2);
  /* One edge to draw, and it has to paint over the clip and the tint
     rather than under them — the same reason the Bars windows are
     outlined and not bordered. Inset, so it costs the column no width. */
  box-shadow: inset var(--hair) 0 0 var(--rule);
}

.hero__quarter__clip {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* cover scales to the height in a column this narrow, so the y has
     nothing left to move and the crop is entirely in the x */
  object-position: var(--at, 30%) 50%;
  opacity: 0;
  transition: opacity 900ms linear;
  /* --bars-grade: nothing is read off this column, so it keeps its
     focus and its black point. The accent goes back over the greyscale as hue below,
     which is what stops the orange pallet trucks spending the colour
     the map needs. */
  filter: var(--bars-grade);
}
.hero__quarter__clip.is-live { opacity: 1; }
/* --at is written onto the column by the rail, one value per stage, and
   inherits down to whichever clip is live — see STAGE_SHOT in
   graphics.js. The fallback in object-position above is stage one's, so
   the column is aimed correctly before a line of script has run. */

/* The flip — the column's other mode (switcher COLUMN, f): stills cut
   from the clips with the crate held on one point, drawn on a canvas by
   graphics.js. Graded like the clips, and the clips step aside for it. */
.hero__quarter__flip { display: none; }
html[data-col="flip"] .hero__quarter__clip { display: none; }
html[data-col="flip"] .hero__quarter__flip {
  display: block;
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  filter: var(--bars-grade);
}

/* the accent, laid back over the greyscale as hue and nothing else */
.hero__quarter__tint {
  position: absolute; inset: 0;
  background: var(--accent);
  mix-blend-mode: color;
  opacity: var(--bars-tint);
}

/* Ground under the island, and nothing else. The column carries no copy,
   so it needs no veil and no wedge — only a quiet top, so the right end
   of the header has something to sit on rather than a moving rack. */
.hero__quarter__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    var(--ground) 0%,
    color-mix(in srgb, var(--ground) 55%, transparent) 7%,
    transparent 17%);
}

/* --- the six-stage rail, between the copy and the column ---------

   The same drawing as the one in How We Work, doing the opposite job:
   there it reports where the scroll has reached, here it is walked and
   the column follows. Sized by its height and not its width, because
   what it has to fit is the screen: the six stops are evenly spaced, so
   the height is the design and the width falls out of the viewBox. The
   grid column is auto, so the copy takes the rest. */

.hero__rail { display: none; }

html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__rail {
  display: block;
  grid-area: rail;
  justify-self: end;
  /* .hero__in reserves --q-gap between its content and the column, which
     is the distance a paragraph needs from a photograph. The rail is not
     a paragraph: it drives the column, and the two only read as one block
     if they are close enough to be one. So it takes a shorter gap of its
     own and pulls back the difference — the reserve in that padding is
     the copy's, and this is the rail opting out of part of it. What it
     gives up in margin the headline gets, because the copy column is the
     1fr. */
  --rail-gap: clamp(12px, 1.2vw, 26px);
  margin-right: calc(var(--rail-gap) - var(--q-gap));
}
.hero__rail__svg {
  display: block;
  /* The claim block made the drawing wider, so the height comes down to
     keep the rendered width where it was: the headline's measure is what
     this is really trading against, and 26ch at 1600 is three lines or
     four depending on about thirty pixels. */
  height: clamp(320px, 53svh, 530px);
  width: auto;
}

/* The marker's outer ring turns, slowly, and only while motion is wanted:
   it is the one thing on the first screen that says the rail is live
   rather than drawn. Everything else about the marker is still. */
.srail__orbit {
  animation: srail-orbit 11s linear infinite;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}
@keyframes srail-orbit { to { transform: rotate(360deg); } }
html.is-still .srail__orbit { animation: none; }
@media (prefers-reduced-motion: reduce) { .srail__orbit { animation: none; } }

/* A row is the whole band from outside the bracket to the end of the
   longest label, so the answer is the line and not the six characters of
   its name. The wash is the one the nav links use — a stage is a link to
   a frame, and it should feel like the same kind of object. */
.srail__row { cursor: pointer; }
.srail__row > rect { transition: fill .18s ease; }
@media (hover: hover) {
  .srail__row:hover > rect { fill: var(--accent-wash); }
}
.srail__row:focus { outline: none; }
.srail__row:focus-visible > rect {
  fill: var(--accent-wash);
  stroke: var(--accent); stroke-width: 1.5;
}

/* The lede under the headline is two columns now, and its right head —
   "move mission-critical infrastructure / internationally through" — has
   to keep to two lines to match the left. On laptop widths the rail gives
   up a little height for that, which is width, because it is sized by its
   height. */
@media (min-width: 1271px) and (max-width: 1400px) {
  html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__rail__svg { height: clamp(300px, 45svh, 420px); }
}

/* Below this the three columns stop being three: the rail wants about
   300px and the copy wants the rest, and somewhere under 1270 the rest
   stops being a measure for the two-column lede. The rail folds away
   rather than shrinking further — it is a path with eight named stops,
   and there is no smaller version of that which is still worth reading.
   The column keeps walking itself; what is lost is being able to walk it
   by hand. */
@media (max-width: 1270px) {
  html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__rail { display: none; }
  html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__in {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "copy";
  }
}

/* Below the width where a quarter stops being a column and starts being
   a gutter, the division turns through ninety degrees: the footage takes
   the floor of the first screen at full width and the copy sits above it.
   The crop comes back to the middle of the frame — a band that wide is
   showing the room again, not a slice of it. */
@media (max-width: 900px) {
  html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero { --q-h: 32svh; }
  html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__quarter {
    inset: auto 0 0 0; width: auto; height: var(--q-h);
    box-shadow: inset 0 var(--hair) 0 var(--rule);
  }
  html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__quarter__clip { object-position: 50% 45%; }
  html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__quarter__scrim {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--ground) 42%, transparent) 0%, transparent 30%);
  }
  html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .hero__in {
    padding-right: var(--gutter);
    padding-bottom: calc(var(--q-h) + clamp(28px, 5vh, 56px));
  }
}

/* The island floats over the column, so on the right of it the nav is
   read off moving footage rather than off the page — and it is the only
   text on the site in that position. Measured, it does not survive: the
   glass is a thin film, the brightest thing in both clips is the ceiling
   and its light panels, and "About" came out at 1.0:1 over a lit panel.

   So the glass takes a ground underneath it here, and keeps everything
   that makes it glass — the film, the rim, the specular, the blur, the
   drop. The footage still moves behind the pill; it is behind a window
   now rather than behind a pane of nothing. Over the three quarters that
   are already ground it is ground on ground, so the header does not
   change anywhere except where it had to.

   The stuck state gets the same treatment: the island passes over the
   column for the whole first screen, and --head-film-stuck is as thin. */
html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .site-head__in {
  background: var(--head-film), color-mix(in srgb, var(--ground) 80%, transparent);
}
html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .site-head.is-stuck .site-head__in,
html:is([data-hero="split"], [data-hero="lean"], [data-hero="refine"]) .site-head.is-menu-open .site-head__in {
  background: var(--head-film-stuck), color-mix(in srgb, var(--ground) 84%, transparent);
}

/* Under prefers-reduced-motion, and under ?static=1 for the captures,
   graphics.js never starts the sequence: the first clip holds its poster
   frame, so the column is the same picture every time. */
@media (prefers-reduced-motion: reduce) {
  .hero__quarter__clip { transition: none; }
}


/* ---------------- first screen · LEAN ---------------------------
   Split with the repeats taken out. The layout, the rail and the
   column are Split's — the rules above serve both — and each thing on
   the screen is said once:

     eyebrow           gone — the headline and the sector list say it,
                       and it was the second "cross-border" of three
     the services      gone — they are the paragraph and the rail; the
                       sectors stay, as the list they were, under the
                       paragraph rather than beside the services
     the paragraph     the lede now, without its bold: bold from "who
                       exports" to "should move" was most of the
                       sentence, so it stressed nothing
     the button        the header's words, Talk to an Expert: one action,
                       one name
     the rail's claim  No handovers, in the display face and set up the
                       brace, instead of the padlock and "CFL carries all
                       eight stages"

   Spell carries the same copy (2026-09-30) — everything here but the
   rail's claim applies to it too, and with no rail it keeps No handovers
   in the copy at every width.

   The nodes Lean adds are hidden on Split and Refine, so they render as
   they did.
   ---------------------------------------------------------------- */
.hero__nh, .btn__t--lean, .srail__big { display: none; }

html:is([data-hero="lean"], [data-hero="spell"]) .hero :is(.hero__eyebrow, .lp__head--how, .lp__how, .btn__t--split, .srail__claim) {
  display: none;
}
html:is([data-hero="lean"], [data-hero="spell"]) .hero .btn__t--lean { display: inline; }

html:is([data-hero="lean"], [data-hero="spell"]) .hero__copy { gap: 30px; }
/* the headline breaks after "the", not inside "cross-border" */
html:is([data-hero="lean"], [data-hero="spell"]) .hero__nb { white-space: nowrap; }

/* three lines, balanced, wherever the rail leaves it the width */
html:is([data-hero="lean"], [data-hero="spell"]) .hero__support {
  max-width: 64ch; text-wrap: balance;
  font-size: clamp(1rem, .95rem + .28vw, 1.14rem);
  line-height: 1.55;
}
html:is([data-hero="lean"], [data-hero="spell"]) .hero__support strong { font-weight: inherit; color: inherit; }

/* the paragraph leads, the sectors follow it: grid order, since the
   sectors come first in his sentence and so in the markup */
html:is([data-hero="lean"], [data-hero="spell"]) .hero__support { order: 1; }
html:is([data-hero="lean"], [data-hero="spell"]) .hero :is(.lede--parts, .hero__nh, .hero__cta, .hero__proof) { order: 2; }

/* who it is for: the sectors' half of his subheadline, on its own. Its
   head opened mid-sentence, after "CFL Worldwide coordinates …", so
   alone it takes a capital. */
html:is([data-hero="lean"], [data-hero="spell"]) .lede--parts {
  grid-template-columns: minmax(0, max-content);
  grid-template-areas: "hwho" "who";
  margin-top: -6px;
}
html:is([data-hero="lean"], [data-hero="spell"]) .lp__head--who { color: var(--ink-3); }
/* without "— including" the head is one line, so it stops breaking */
html:is([data-hero="lean"], [data-hero="spell"]) .lp__incl { display: none; }
html:is([data-hero="lean"], [data-hero="spell"]) .lp__nl { display: inline; }
html:is([data-hero="lean"], [data-hero="spell"]) .lp__head--who::first-letter { text-transform: uppercase; }

/* No handovers in the copy: on Spell, which has no rail to carry it, and
   on Lean below 1270, where the rail folds away */
.hero__nh {
  max-width: 50ch;
  padding-left: 14px; border-left: 2px solid var(--accent);
  font-size: var(--fs-sm); line-height: 1.5; color: var(--ink-2);
}
.hero__nh b {
  font-family: var(--display); font-weight: 700; font-size: 1.15em; color: var(--accent);
}
html[data-hero="spell"] .hero__nh { display: block; }
@media (max-width: 1270px) {
  html[data-hero="lean"] .hero__nh { display: block; }
}

html[data-hero="lean"] .srail__big {
  display: inline;
  font-family: var(--display); font-weight: 700;
  font-size: 24px; letter-spacing: -0.01em;
}


/* ---------------- first screen · REFINE -------------------------
   Split's copy, every word of it and in its order, set again. The
   layout, the rail and the column are Split's.

     the sentence      "CFL Worldwide coordinates [services] for companies
                       deploying … — including [sectors]" reads top to
                       bottom, as a sentence does, instead of as two
                       columns that each open mid-phrase
     the services      a ruled two-by-two in the weight of the name —
                       the framed cells read as buttons and fields, and
                       nothing in them answers a click
     the sectors       one quiet inline run under their lead-in: who it
                       is for is the qualifier, not a second list
     the paragraph     the client's emphasis kept, at a volume that is
                       still emphasis: plain words a step lighter, the
                       bold ones in ink at 500 rather than 600 on 80% of it
     the headline      breaks after "the", not inside "cross-border"
   ---------------------------------------------------------------- */
html[data-hero="refine"] .hero__copy { gap: 28px; }
html[data-hero="refine"] .hero__eyebrow { margin-bottom: -6px; }
html[data-hero="refine"] .hero__nb { white-space: nowrap; }

html[data-hero="refine"] .lede--parts {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "hhow" "how" "hwho" "who";
  row-gap: 0; max-width: 40rem;
}
html[data-hero="refine"] .lp__head {
  align-self: start; font-size: var(--fs-sm); line-height: 1.45; color: var(--ink-3);
}
html[data-hero="refine"] .lp__head--how b { display: inline; font-weight: 600; color: var(--ink); }
html[data-hero="refine"] .lp__nl { display: inline; }
html[data-hero="refine"] .lp__head--who { margin-top: 22px; }

html[data-hero="refine"] .lp__how {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-self: start; gap: 0 28px; margin-top: 8px;
}
/* ruled per cell, so the rules break at the gutter the same way top and
   bottom */
html[data-hero="refine"] .lp__how .lp__i {
  border: 0; border-top: 1px solid var(--rule); border-radius: 0; background: none;
  padding: 12px 0; font-size: 1.02rem; font-weight: 600;
}
html[data-hero="refine"] .lp__how .lp__i:nth-child(n+3) { border-bottom: 1px solid var(--rule); }

html[data-hero="refine"] .lp__who {
  display: flex; flex-wrap: wrap; align-self: start; gap: 6px 22px; margin-top: 8px;
}
html[data-hero="refine"] .lp__who .lp__i {
  border-top: 0; padding: 0; gap: 8px; font-size: var(--fs-sm); color: var(--ink-2);
}
html[data-hero="refine"] .lp__who .lp__ic { width: 16px; height: 16px; }

html[data-hero="refine"] .hero__support {
  max-width: 60ch; color: var(--ink-3); text-wrap: pretty;
}
html[data-hero="refine"] .hero__support strong { font-weight: 500; color: var(--ink); }
html[data-hero="refine"] .hero__cta { margin-top: 4px; }

@media (max-width: 560px) {
  html[data-hero="refine"] .lp__how { grid-template-columns: minmax(0, 1fr); }
  html[data-hero="refine"] .lp__how .lp__i:nth-child(n+3) { border-bottom: 0; }
  html[data-hero="refine"] .lp__how .lp__i:last-child { border-bottom: 1px solid var(--rule); }
}


/* ---------------- first screen · SPELL --------------------------
   Copy on the left, the mark on the right — drawn in footage rather
   than in ink. Three windows cut out of the footage re-cut themselves
   over eight seconds into C, then F, then L, and back: the logotype
   spelling itself out of one piece of footage. (It had a still sibling,
   Bars — the three windows standing as the C — cut with Full and Film.)

   The geometry is the mark's own, taken off the logo files rather than
   eyeballed. Every bar is 19 units wide and 14 from its neighbour, in a
   box 144 tall, and the three letters differ only in where each bar
   starts and stops inside that box:

     C   37 / 86 / 144, all three centred on one line
     F   144 / 86 / 38, all three hung from the top
     L   144 / 22 / 38, all three standing on the floor

   One custom property carries the height of the box and --hb-u divides
   it into the drawing's 144 units, so every other number here is a
   multiple of the drawing. Change the height and the mark stays the
   mark.

   That fixes the bar width at roughly an eighth of the box height,
   which is the whole point and also the constraint: these are slits,
   not screens. Nothing in them is meant to be read as a scene. They
   carry movement, depth and the accent, and the mark carries the
   meaning — which is why the crops matter more here than the clips do.

   It keeps the copy where a reader starts — top left, on ground, at the
   page's own contrast — and spends the picture on the other half of the
   screen, where it can be looked at rather than read through.

   The proof strip — $680m / 47 / €0 / 16 yrs — used to close this
   screen under a rule, and the screen was budgeted to keep it above the
   fold. It was cut on 2026-09-28, so the screen is the copy and the mark
   and nothing under them.
   ---------------------------------------------------------------- */

.hero__bars { display: none; }

html[data-hero="spell"] .hero {
  min-height: 100svh;
  display: grid; align-content: center;
  overflow: hidden;
}
html[data-hero="spell"] .hero__in {
  /* head clearance above, and the same air under the copy as over it */
  padding-block:
    calc(var(--head-top) + var(--head-h) + clamp(20px, 3vh, 40px))
    clamp(28px, 4vh, 56px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "copy bars";
  column-gap: clamp(32px, 5vw, 88px);
  align-items: center;
}
html[data-hero="spell"] .hero__copy { grid-area: copy; max-width: none; }

html[data-hero="spell"] .hero__bars {
  grid-area: bars;
  /* the box the letter is drawn in; every other measure is derived */
  --hb-h: clamp(260px, 66vh, 660px);
  --hb-u: calc(var(--hb-h) / 144);       /* one unit of the drawing */
  display: flex;
  align-items: center;                    /* the mark's common centre line */
  gap: calc(14 * var(--hb-u));
  height: var(--hb-h);
  /* The mark used to sit hard on the right edge of the measure, which
     left a hand's width of nothing between the last line of copy and
     the first window — the two halves of the screen were on the same
     row without being in the same composition. It comes in by one bar
     width, 19 units, so the space it leaves on the right is a distance
     the drawing already contains rather than one chosen by eye, and it
     scales with the mark. One pitch (33) was tried and is too far: at
     that point the ground left on the right is about the same as the
     gap on the left, and the mark stops reading as the right half of
     the screen and starts reading as an object floating in it. */
  margin-right: calc(19 * var(--hb-u));
}

.hbar {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: calc(19 * var(--hb-u));
  overflow: hidden;
  /* the blend inside must not reach the page behind it */
  isolation: isolate;
  background: var(--ground-2);
  /* The mark is a rectangle of solid ink and knows where it ends; a
     photograph does not. The tall bar opens on a lit ceiling and closes
     on a pale floor, and both of those are the same value as the
     paper — so without an edge the bar
     dissolves at exactly the two points that give it its height. An
     outline rather than an inset shadow, because it has to paint over
     the clip and the tint rather than under them; clip-path takes it
     with the rest of the box, so it traces whatever letter is up. */
  outline: var(--hair) solid var(--rule);
  outline-offset: calc(-1 * var(--hair));
}

/* three boxes of one height, cut by clip-path */
html[data-hero="spell"] .hbar { height: calc(144 * var(--hb-u)); }

.hbar__clip {
  width: 100%; height: 100%;
  object-fit: cover;
  /* the x is per bar, below; cover scales to the height here, so the y
     would have nothing left to move */
  object-position: var(--at, 50%) 50%;
  /* in focus and at the page's own brightness: nothing is read off it */
  filter: var(--bars-grade);
}
/* the accent laid back over the greyscale as hue and nothing else,
   one layer per window */
.hbar__tint {
  position: absolute; inset: 0;
  background: var(--accent);
  mix-blend-mode: color;
  opacity: var(--bars-tint);
}

/* The crops, which are a property of the variant and not of the clip.
   A window an eighth as wide as the box is high shows a slice of the
   frame, never the frame — and because cover scales to the height, the
   shorter the window the wider its slice. Every window here is full
   height, so every slice is the narrow one, and all three are aimed at
   a rack that fills it. */
html[data-hero="spell"] .hbar--1 { --at: 52%; }  /* the rack centre of the aisle */
html[data-hero="spell"] .hbar--2 { --at: 53%; }  /* the rack frame on the truck */
html[data-hero="spell"] .hbar--3 { --at: 20%; }  /* white rack, LED columns */

/* --- the windows open rather than arrive --------------------------

   Everything else on the page fades up and settles a few pixels; a
   window doing that would read as a photograph being placed. These are
   bars, so they are drawn: each is clipped to nothing at its own top
   edge and unfolds downward, which is the one direction that reads as
   the mark being struck rather than as an image loading.

   The observer watches .hero__bars and not the windows themselves,
   which is not a shortcut — it is the only thing that works. An
   IntersectionObserver measures its target after the target's own
   clip-path, and a window clipped to nothing has nothing to intersect:
   it reports itself off-screen forever and never opens. So the group is
   what is observed, and the three windows take their turn from --hb-d,
   written here rather than by the observer because their order is a
   designed sequence and not a generic cascade.
   ------------------------------------------------------------------ */
.hero__bars[data-reveal] { opacity: 1; transform: none; transition: none; }

.hbar--1 { --hb-d: 0ms; }
.hbar--2 { --hb-d: 150ms; }
.hbar--3 { --hb-d: 300ms; }
.hbar {
  transition: clip-path 1.05s cubic-bezier(.16, .84, .36, 1);
  transition-delay: var(--hb-d, 0ms);
}

/* The closed state is not the top of the box but the top of
   the bar this window is about to be: the C is the first letter, so
   each window opens straight into its share of it and the cycle picks
   it up from there without a step. */
html[data-hero="spell"] .hero__bars[data-reveal] .hbar--1 { clip-path: inset(36.806% 0 63.194% 0); }
html[data-hero="spell"] .hero__bars[data-reveal] .hbar--2 { clip-path: inset(20.139% 0 79.861% 0); }
html[data-hero="spell"] .hero__bars[data-reveal] .hbar--3 { clip-path: inset(0 0 100% 0); }
html[data-hero="spell"] .hero__bars[data-reveal].is-in .hbar--1 { clip-path: inset(36.806% 0 37.5% 0); }
html[data-hero="spell"] .hero__bars[data-reveal].is-in .hbar--2 { clip-path: inset(20.139% 0 20.139% 0); }
html[data-hero="spell"] .hero__bars[data-reveal].is-in .hbar--3 { clip-path: inset(0 0 0 0); }

/* --- spell: the letter cycle --------------------------------------

   Three letters in 8.4 seconds: two and a tenth on each, seven tenths
   turning into the next. The hold is what makes it a logotype and not a
   loading bar — a reader has to be able to land on the screen, read a
   letter, and see it change, rather than catch a shape mid-flight.

   Only clip-path moves. The footage inside never re-crops, so what the
   window shows stays put while the window travels over it: the rack
   under the middle bar is the same rack whether that bar is the middle
   of a C, the arm of an F or the foot of an L. Animating height instead
   would have dragged the picture up and down with the edge, which reads
   as the camera moving and undoes the whole conceit.

   The cycle starts after the windows have finished opening, so the
   first thing on screen is the mark as it is drawn.
   ------------------------------------------------------------------ */
@keyframes hb-spell-1 {
  0%,      25%    { clip-path: inset(36.806% 0 37.5% 0); }   /* C */
  33.333%, 58.333% { clip-path: inset(0 0 0 0); }            /* F */
  66.667%, 91.667% { clip-path: inset(0 0 0 0); }            /* L */
  100%            { clip-path: inset(36.806% 0 37.5% 0); }
}
@keyframes hb-spell-2 {
  0%,      25%    { clip-path: inset(20.139% 0 20.139% 0); } /* C */
  33.333%, 58.333% { clip-path: inset(0 0 40.278% 0); }      /* F */
  66.667%, 91.667% { clip-path: inset(84.722% 0 0 0); }      /* L */
  100%            { clip-path: inset(20.139% 0 20.139% 0); }
}
@keyframes hb-spell-3 {
  0%,      25%    { clip-path: inset(0 0 0 0); }             /* C */
  33.333%, 58.333% { clip-path: inset(0 0 73.611% 0); }      /* F */
  66.667%, 91.667% { clip-path: inset(73.611% 0 0 0); }      /* L */
  100%            { clip-path: inset(0 0 0 0); }
}

html[data-hero="spell"] .hero__bars.is-in .hbar {
  animation: 8.4s cubic-bezier(.65, 0, .35, 1) infinite;
  animation-delay: calc(var(--hb-d, 0ms) + 1.25s);
}
html[data-hero="spell"] .hero__bars.is-in .hbar--1 { animation-name: hb-spell-1; }
html[data-hero="spell"] .hero__bars.is-in .hbar--2 { animation-name: hb-spell-2; }
html[data-hero="spell"] .hero__bars.is-in .hbar--3 { animation-name: hb-spell-3; }

/* A capture and a reader who has asked for less motion get the same
   screen: the C, held. graphics.js sets .is-still for both. */
html.is-still .hbar { animation: none !important; }
@media (prefers-reduced-motion: reduce) {
  .hbar { transition: none; }
  html[data-hero="spell"] .hbar { animation: none !important; }
}

@media (max-width: 1100px) {
  html[data-hero="spell"] .hero__in {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "copy" "bars";
    row-gap: clamp(36px, 6vh, 64px);
    align-items: start;
  }
  /* Stacked, the mark is an object under the copy rather than the other
     half of the screen — and it is sized off the column it now sits in,
     not off the viewport height. Sized by height it came out 149px wide
     in a 350px column: legible as a mark, but reading as a leftover
     rather than as the thing the screen was built around. Nothing to
     pull away from either, so the pull goes. */
  html[data-hero="spell"] .hero__bars {
    --hb-h: calc(min(288px, 68vw) * 144 / 85);
    margin-right: 0;
  }
}

/* ============================================================
   BLOCKS ADDED FOR THE CLIENT CONTENT DRAFT (v1)

   Three services instead of four, a Why block, five industries
   instead of two sectors, and a Knowledge Centre. Everything else
   on the home page keeps the components it already had.
   ============================================================ */

/* ---------------- core services: the name leads, the client's
   benefit headline follows as the promise. It was the other way
   round — a mono label over a large headline — and the headline
   says what you get, not what it is called on an invoice. A card
   you cannot find again by name is a card you cannot buy. So the
   name takes the heading and the headline takes the line under
   it, which is also the order procurement reads them in. ------ */

.sol__name {
  font-size: clamp(1.15rem, .98rem + .52vw, 1.5rem);
  line-height: 1.14; text-wrap: balance;
}
/* the name and its promise are one block: the promise is a subtitle, and
   a subtitle that has been pushed away from its title by a shared
   baseline is a second heading. Everything below them aligns instead. */
.sol__hd { display: grid; gap: 10px; align-content: start; }
/* the paragraph and the points are one flowing block, so the slack that
   the shortest card has to give up lands above the link rather than as a
   hole between the prose and the points. */
.sol__body { display: grid; gap: var(--sol-gap); align-content: start; }
/* the name, its promise, the paragraph and the points read as one column
   of prose. They were four shared rows, which aligned the three cards at
   the cost of a hole under the one-line names — and a hole reads as a
   forgotten margin, not as a grid. Only the figure, the code and the
   link are held on shared lines now; the prose between them flows. */
.sol__txt { display: grid; gap: var(--sol-gap); align-content: start; }
.sol__lead {
  font-family: var(--body); font-size: var(--fs-body);
  line-height: 1.38; color: var(--ink); text-wrap: pretty;
}

/* ---------------- why CFL ----------------
   Four reasons on one row, each on a hairline that draws itself in
   from the left. */

.whys { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(20px, 2.4vw, 38px); }
@media (max-width: 1040px) { .whys { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .whys { grid-template-columns: minmax(0, 1fr); } }
.why {
  display: grid; gap: 13px;
  /* number, drawing, heading, body, key — the body takes the slack so the
     four key lines sit on one baseline across the row */
  grid-template-rows: auto auto auto 1fr auto;
  border-top: 1px solid var(--rule); padding-top: 22px; position: relative;
}
.why::before {
  content: ""; position: absolute; left: 0; top: -1px; width: 38px; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform .55s cubic-bezier(.22,.61,.36,1);
}
.why.is-in::before { transform: scaleX(1); }
.why__n { font-family: var(--mono); font-size: .6875rem; letter-spacing: .18em; color: var(--accent); }
.why__t { font-family: var(--display); font-weight: 700; font-size: clamp(1.05rem, .92rem + .45vw, 1.3rem); letter-spacing: -0.02em; line-height: 1.18; }
.why__b { color: var(--ink-2); font-size: var(--fs-sm); line-height: 1.68; }

/* Each reason gets the drawing it was missing. Every other block on the
   page pairs its claim with one — the services have a plate each, the
   industries have their cargo — and
   these four carried the best sentences in the section as four columns
   of prose. Same rule as everywhere else: the drawing has to say
   something the paragraph beside it cannot say as quickly, so each one
   is a document rather than an icon. */
.why__plate {
  width: 100%; height: auto; display: block; overflow: visible;
  margin: 3px 0 2px;
}
/* the key line, in the register the six stages already use */
.why__tag {
  font-family: var(--mono); font-size: .5625rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3);
  padding-top: 12px; border-top: 1px solid var(--rule-2);
}

/* ---------------- industries: his five (2026-10-06) ----------------
   Three across, then two, on a six-track grid so no row is a fraction of
   a row with a hole in it. On two tracks the first card — his first
   industry — takes the whole row, so the ones after it pair, and a last
   card left without a partner takes its row as well. */

.sectors--ind { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.sectors--ind > * { grid-column: span 3; }
.sectors--ind > *:nth-child(-n+3) { grid-column: span 2; }
.sectors--ind .sector { padding: clamp(24px, 2.6vw, 38px); gap: 15px; }
.sectors--ind .sector h3 { font-size: clamp(1.1rem, .95rem + .5vw, 1.35rem); }
.sectors--ind .sector p { font-size: clamp(1.0625rem, 1rem + .25vw, 1.1875rem); line-height: 1.55; }
@media (max-width: 1040px) {
  .sectors--ind { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sectors--ind > *, .sectors--ind > *:nth-child(-n+3) { grid-column: auto; }
  .sectors--ind > *:first-child { grid-column: 1 / -1; }
}
/* the Explore panel is the grid's last child, so the last card is found
   among the cards; grid and bento both pair up after a full-width first */
@media (min-width: 721px) and (max-width: 1040px) {
  .sectors--ind > .sector:nth-last-child(1 of .sector):nth-child(even of .sector) { grid-column: 1 / -1; }
}
@media (max-width: 720px) {
  .sectors--ind { grid-template-columns: minmax(0, 1fr); }
  .sectors--ind > *:first-child { grid-column: auto; }
}

/* every card goes somewhere now — each of his industries has a page behind
   it — so they answer the pointer the way the service cards do */
a.sector { transition: background .22s ease, border-color .22s ease; }
a.sector:hover { background: var(--surface-2); border-color: var(--accent-dim); }

/* The client's Why copy is a headline, three paragraphs and three points.
   The paragraphs stay together as the head's second column — left to the
   rule for a head's sentence, each would take a grid row of its own and
   sit a headline's distance from the next. The points carry no drawing
   and no key line: the figure above them is the section's drawing, so
   each is number, heading and body. */
.sec-head__prose { display: grid; gap: .85em; max-width: 60ch; }
@media (min-width: 1100px) { .sec-head > .sec-head__prose { grid-column: 2; margin-top: .34em; max-width: 54ch; } }
.whys--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.whys--three .why { grid-template-rows: auto auto 1fr; }

/* ---------------- the gates before the first mile ----------------
   The head's own drawing, in the column the headline leaves empty: the
   route, and what stands on it before the cargo has moved a metre. The
   gate that lifts is the same object the dual-use article draws, at
   board scale.

   It folds away under 1100px instead of shrinking. At a third of this
   width a 9px label is four pixels tall, and the section still has the
   accountability figure below it on a small screen. */
.gaunt { margin: clamp(14px, 1.8vw, 26px) 0 0; display: none; }
.gaunt__svg { width: 100%; max-width: 620px; height: auto; display: block; overflow: visible; }
.g-in { opacity: 0; transform: translateY(7px); transition: opacity .55s ease, transform .6s cubic-bezier(.22,.61,.36,1); }
.gaunt.is-in .g-in { opacity: 1; transform: none; }
@media (min-width: 1100px) {
  .gaunt { display: block; }
  :is(#why, #services) .sec-head > h2 { grid-row: 2; }
  :is(#why, #services) .sec-head > .sec-head__prose { grid-row: 2 / span 2; }
  :is(#why, #services) .sec-head > .gaunt { grid-column: 1; grid-row: 3; align-self: start; }
  /* Core Services reads as one argument down the right — headline, his
     introduction, the two lines that close it — with the three services
     it names on the left, from the headline down. The close sits straight
     under the introduction: set on the cards' foot it left a hole a card
     tall in the middle of the argument. The 1fr row takes the difference,
     so the headline and the prose keep their rows however much taller
     the cards run. */
  #services .sec-head { grid-template-rows: auto auto auto 1fr; }
  #services .sec-head > h2 { grid-column: 2; }
  #services .sec-head > .sec-head__prose { grid-column: 2; grid-row: 3; margin-top: 0; }
  #services .sec-head > .sols { grid-column: 1; grid-row: 2 / -1; align-self: start; margin-top: 0; }
  #services .sec-head > .sec-close { grid-column: 2; grid-row: 4; align-self: start; margin-top: clamp(12px, 1.4vw, 22px); max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
  .g-in { transition: none; }
}
@media (max-width: 1040px) { .whys--three { grid-template-columns: minmax(0, 1fr); } .whys--three .why__b { max-width: 68ch; } }

/* ---------------- knowledge centre ---------------- */

.kcs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(20px, 2.4vw, 32px); }
@media (max-width: 900px) { .kcs { grid-template-columns: minmax(0, 1fr); } }
.kc {
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: clamp(26px, 3vw, 40px); display: grid; gap: 13px; align-content: start;
  background: var(--surface); transition: background .22s ease, border-color .22s ease;
}
.kc:hover { background: var(--surface-2); border-color: var(--accent-dim); }
.kc__k { font-family: var(--mono); font-size: .625rem; letter-spacing: .18em; text-transform: uppercase; color: var(--accent); }
.kc__t { font-family: var(--display); font-weight: 700; font-size: clamp(1.1rem, .95rem + .5vw, 1.4rem); letter-spacing: -0.025em; line-height: 1.16; }
.kc__s { color: var(--ink-2); font-size: var(--fs-sm); line-height: 1.6; }
.kc__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  border-top: 1px solid var(--rule); margin-top: 6px; padding-top: 14px;
}
.kc__m { font-family: var(--mono); font-size: .625rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); }
.kc__go { font-family: var(--mono); font-size: .625rem; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); }
.kc__go i { font-style: normal; display: inline-block; transition: transform .18s ease; }
.kc:hover .kc__go i { transform: translateX(3px); }

/* Knowledge — the page fills in */
.navi :is(.ni-k1, .ni-k2) { transform-origin: 0% 50%; }
.ni-k2 { opacity: .45; }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-k1 { transform: scaleX(1.16); }
.site-nav > :is(a, button):is(:hover, :focus-visible, :active, [aria-current], [aria-expanded="true"]) .ni-k2 { opacity: 1; transform: scaleX(1.34); transition-delay: .08s; }

/* ============================================================
   SECTION ART — the four blocks the client's draft asked for

   Everything here is drawn: hairline SVG in the same 30-degree
   isometric and dot-matrix language as the map and the first
   screen.

   No figure carries information the copy does not. Each one is a
   second way of reading the same claim, for the reader who scans
   before they read. Everything resolves instantly under
   prefers-reduced-motion, because every transition here is
   triggered by .is-in and the reveal observer sets that at once.
   ============================================================ */

/* ---------------- service plates ----------------
   Each card opens on a drawing of what the service actually does:
   the name on the declaration, a bill of materials being classified,
   twelve racks becoming one entry. On the vertical card the plate is
   the first thing in the column and the mono code sits under it with
   the Core chip, so the three drawings read across the row as a set
   before any of the prose is read. */

.sol__fig {
  display: block;
  padding-bottom: var(--sol-gap);
  border-bottom: 1px solid var(--rule);
}
/* The plate was capped at 184px in a column nearly four hundred wide, so
   two thirds of every card's top was accidental white and the drawing read
   as a sticker. At the column's full width the same viewBox renders at
   2.4x, which is the difference between a six-point label and a legible
   one: the detail in these drawings is the argument, and detail that
   cannot be read at the size it ships at is only noise. */
.sol__plate {
  /* One number for all three: the plates share a viewBox, so one
     percentage of the column scales the set without touching a drawing.
     Written as a plain percentage on purpose — calc() around a var() is
     the kind of thing that resolves to auto in one engine and not in
     another, and an <svg> whose width falls back to auto draws itself at
     whatever size it likes. */
  width: 47%;
  height: auto; display: block; overflow: visible;
}

/* the plate's own parts, driven by the card's reveal state */
/* the field on the IOR plate fills with our name */
.plate-name { opacity: 0; transition: opacity .5s ease .45s; }
.sol.is-in .plate-name { opacity: 1; }
.plate-blank { opacity: 1; transition: opacity .3s ease .3s; }
.sol.is-in .plate-blank { opacity: 0; }
.plate-field { transition: stroke .45s ease .4s; }
.sol.is-in .plate-field { stroke: var(--accent); }

/* the classification rows resolve one after another */
.plate-row { opacity: 0; transform: translateX(-6px); transition: opacity .34s ease, transform .34s cubic-bezier(.22,.61,.36,1); }
.sol.is-in .plate-row { opacity: 1; transform: none; }
.plate-bar { transition: fill .4s ease; fill: var(--rule); }
.sol.is-in .plate-bar { fill: var(--accent-dim); }

/* The crate closes around the twelve units, and the spine gathers the
   cleared lines. Both draw themselves with a dashoffset, and the length
   is measured in script and handed over as --len.

   The transition lives on the revealed state, not on the base one. It
   used to sit on the base, which meant the fallback length in this rule
   animated to the real one the moment script set it — a change nobody
   asked for, a quarter of a second after load. On the crate that was an
   invisible 600-to-461. On the trade compliance spine, whose real length
   is 64, it was an offset travelling nine full dash cycles: the line
   strobed on and off before the reveal had even fired. Declared here,
   the handover is instant and the only animation is the one intended. */
.plate-crate { stroke-dasharray: var(--len, 600); stroke-dashoffset: var(--len, 600); }
.sol.is-in .plate-crate {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.1s cubic-bezier(.33,.1,.2,1) .25s;
}
.plate-unit { opacity: .5; transition: opacity .4s ease; }
.sol.is-in .plate-unit { opacity: .85; }
/* the stencil is painted last, after the crate has closed around it */
.plate-mark { opacity: 0; transition: opacity .45s ease .95s; }
.sol.is-in .plate-mark { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .plate-name, .plate-row, .plate-unit, .plate-mark { transition: none; }
  .plate-crate { transition: none; }
}

/* ---------------- industry icons ----------------
   The hero's industry glyphs, carried down to the cards they name, and
   three drawn on the same 20-unit grid for the industries the hero does
   not list. A tile each, at the head of the card; the card's pointer
   fills the tile and wakes the one part of the glyph that is its
   industry — the chip powers up, a server slides out, the vendors close
   on the project, the chillers spin, the trace runs, the anchor swings,
   the gear clicks round. */

.sector__ic {
  display: grid; place-items: center; justify-self: start;
  width: 64px; height: 64px; margin-bottom: 10px;
  border-radius: var(--radius); background: var(--accent-wash); color: var(--accent);
  transition: background .25s ease, color .25s ease;
}
.sector__ic svg { width: 36px; height: 36px; stroke-width: .8; overflow: visible; }
a.sector:is(:hover, :focus-visible) .sector__ic { background: var(--accent); color: var(--on-accent); }

:is(.sector__ic, .ind-panel__art, .nav-dd__ic) [class*="ii-"] {
  transform-box: fill-box; transform-origin: 50% 50%;
  transition: transform .4s cubic-bezier(.34, 1.3, .5, 1), fill .25s ease, opacity .25s ease;
}
.ii-core { fill: transparent; }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-core { fill: currentColor; }

/* Neoclouds — the chip powers up */
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-pins { transform: scale(1.14); }

/* Hyperscalers — one server pulled from the stack, the fleet blinking */
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-pull { transform: translateX(2px); }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-led { animation: ii-blink .9s steps(1, end) infinite; }
g:nth-of-type(2) > .ii-led { animation-delay: .3s; }
g:nth-of-type(3) > .ii-led { animation-delay: .6s; }
@keyframes ii-blink { 50% { opacity: .15; } }

/* System integrators — three vendors close on one project */
.ii-link { stroke-dasharray: 1; }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-link { animation: ii-draw .6s cubic-bezier(.45, 0, .18, 1); }
@keyframes ii-draw { from { stroke-dashoffset: 1; } }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-src { transform: translateX(1.4px); }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) rect.ii-src { transition-delay: .05s; }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) path.ii-src { transition-delay: .1s; }

/* Data centres — the chillers on the roof spin, and stop where they are */
.ii-fan { animation: ii-spin 1.6s linear infinite paused; }
.ii-fan + .ii-fan { animation-duration: 1.9s; }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-fan { animation-play-state: running; }
@keyframes ii-spin { to { transform: rotate(360deg); } }

/* Life sciences — the trace runs across the monitor */
.ii-trace { stroke-dasharray: 1 1; }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-trace { animation: ii-trace 1.5s cubic-bezier(.45, 0, .55, 1) infinite; }
@keyframes ii-trace { 0% { stroke-dashoffset: 1; } 50% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: -1; } }

/* Maritime — the anchor swings from its ring */
:is(.sector__ic, .ind-panel__art, .nav-dd__ic) .ii-swing { transform-origin: 50% 0; }
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-swing { animation: ii-swing 1.3s ease-out; }
@keyframes ii-swing {
  0% { transform: rotate(0); } 20% { transform: rotate(-13deg); } 45% { transform: rotate(9deg); }
  65% { transform: rotate(-5deg); } 83% { transform: rotate(2deg); } 100% { transform: rotate(0); }
}

/* Industrial — the gear clicks one tooth round */
:is(a.sector:is(:hover, :focus-visible), .ind-panel.is-awake, .nav-dd__list a:is(:hover, :focus-visible)) .ii-rays { transform: rotate(45deg); }

@media (prefers-reduced-motion: reduce) {
  :is(.sector__ic, .ind-panel__art, .nav-dd__ic) [class*="ii-"] { transition: fill .25s ease; animation: none !important; transform: none !important; }
}

/* ---------------- INDUSTRY LAYOUTS ----------------
   Review option ?ind= / key n (shared/prefs.js). The same cards in
   four arrangements, nothing in them rewritten. Grid is the three, then
   two and two, set above under .sectors--ind. */

/* Bento — three tracks, four rows, no hole. His first industry takes a
   2×2 cell, and its icon stops being a tile and is drawn large into the
   corner of it; the next two stack beside it; his last two each run the
   full width as one ruled line — icon, title and link, then his
   description. */
html[data-ind="bento"] .sectors--ind { grid-template-columns: repeat(3, minmax(0, 1fr)); }
html[data-ind="bento"] .sectors--ind > * { grid-column: span 1; }
html[data-ind="bento"] .sectors--ind > :nth-child(1) { grid-column: span 2; grid-row: span 2; }
html[data-ind="bento"] .sectors--ind > :nth-child(n+4) { grid-column: 1 / -1; }

html[data-ind="bento"] .sector:nth-child(1) { position: relative; overflow: hidden; padding: clamp(30px, 3.6vw, 56px); }
html[data-ind="bento"] .sector:nth-child(1) > :not(.sector__ic) { position: relative; max-width: 54%; }
html[data-ind="bento"] .sector:nth-child(1) h3 { font-size: clamp(1.6rem, 1.1rem + 1.5vw, 2.5rem); max-width: 13ch; }
html[data-ind="bento"] .sector:nth-child(1) .sector__ic {
  position: absolute; right: -7%; bottom: -9%; width: 52%; height: auto; aspect-ratio: 1; margin: 0;
  background: none; color: var(--accent-dim);
}
html[data-ind="bento"] a.sector:nth-child(1):is(:hover, :focus-visible) .sector__ic { background: none; color: var(--accent); }
html[data-ind="bento"] .sector:nth-child(1) .sector__ic svg { width: 100%; height: 100%; }
html[data-ind="bento"] .sector:nth-child(1) .sector__ic svg *,
.ind-panel__art svg * { vector-effect: non-scaling-stroke; stroke-width: 1.5px; }

html[data-ind="bento"] .sector:nth-child(n+4) {
  grid-template-columns: 64px minmax(0, 1fr) minmax(0, 1.15fr); column-gap: clamp(20px, 2.8vw, 48px); align-items: start;
}
html[data-ind="bento"] .sector:nth-child(n+4) > .sector__ic { grid-area: 1 / 1 / 3; margin: 0; }
html[data-ind="bento"] .sector:nth-child(n+4) > h3 { grid-area: 1 / 2; }
html[data-ind="bento"] .sector:nth-child(n+4) > p { grid-area: 1 / 3 / 3; }

@media (max-width: 1040px) {
  html[data-ind="bento"] .sectors--ind { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  html[data-ind="bento"] .sectors--ind > :nth-child(1) { grid-row: auto; min-height: 440px; }
  html[data-ind="bento"] .sectors--ind > :nth-child(n+4) { grid-column: auto; }
  html[data-ind="bento"] .sector:nth-child(n+4) { grid-template-columns: minmax(0, 1fr); }
  html[data-ind="bento"] .sector:nth-child(n+4) > :is(.sector__ic, h3, p) { grid-area: auto; }
  html[data-ind="bento"] .sector:nth-child(n+4) > .sector__ic { margin-bottom: 10px; }
}
@media (max-width: 720px) {
  html[data-ind="bento"] .sectors--ind { grid-template-columns: minmax(0, 1fr); }
  html[data-ind="bento"] .sectors--ind > * { grid-column: auto !important; }
  html[data-ind="bento"] .sector:nth-child(1) > :not(.sector__ic) { max-width: none; }
  html[data-ind="bento"] .sector:nth-child(1) { padding-bottom: 190px; }
  html[data-ind="bento"] .sector:nth-child(1) .sector__ic { width: 220px; right: -24px; bottom: -36px; }
}

/* Key — his first industry, AI Infrastructure, as the specialisation, the
   rest as what CFL also supports. It takes half the width and two rows,
   with the icon drawn large into the corner the way Bento draws it; the
   four after it drop to a quarter each, two by two beside it — icon,
   title and his description. Size alone says which is which: no labels
   and no rules between the two groups. The cards link nowhere since the
   industry pages were cut (2026-10-06); the menu and the footer go to
   each one by its id. */
html[data-ind="key"] .sectors--ind { grid-template-columns: repeat(4, minmax(0, 1fr)); }
html[data-ind="key"] .sectors--ind > * { grid-column: span 1; }
html[data-ind="key"] .sectors--ind > :nth-child(1) { grid-column: span 2; grid-row: span 2; }
html[data-ind="key"] .sector:nth-child(1) {
  position: relative; overflow: hidden; min-height: clamp(340px, 30vw, 440px);
  padding: clamp(30px, 3.4vw, 52px); border-color: var(--accent-dim);
}
html[data-ind="key"] .sector:nth-child(1) > :not(.sector__ic) { position: relative; max-width: 60%; }
html[data-ind="key"] .sector:nth-child(1) h3 { font-size: clamp(1.45rem, 1.05rem + 1.2vw, 2.2rem); max-width: 14ch; }
html[data-ind="key"] .sector:nth-child(1) .sector__ic {
  position: absolute; right: -6%; bottom: -10%; width: 46%; height: auto; aspect-ratio: 1; margin: 0;
  background: none; color: var(--accent-dim);
}
html[data-ind="key"] a.sector:nth-child(1):is(:hover, :focus-visible) .sector__ic { background: none; color: var(--accent); }
html[data-ind="key"] .sector:nth-child(1) .sector__ic svg { width: 100%; height: 100%; }
html[data-ind="key"] .sector:nth-child(1) .sector__ic svg * { vector-effect: non-scaling-stroke; stroke-width: 1.5px; }

html[data-ind="key"] .sectors--ind .sector:nth-child(n+2) { padding: clamp(18px, 1.8vw, 26px); gap: 12px; background: none; }
html[data-ind="key"] .sector:nth-child(n+2) h3 { font-size: clamp(.98rem, .9rem + .25vw, 1.1rem); }
html[data-ind="key"] .sector:nth-child(n+2) > p { font-size: var(--fs-sm); line-height: 1.5; }

@media (max-width: 1040px) {
  html[data-ind="key"] .sectors--ind { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  html[data-ind="key"] .sectors--ind > *,
  html[data-ind="key"] .sectors--ind > .sector:nth-last-child(1 of .sector):nth-child(even of .sector) { grid-column: span 1; }
  html[data-ind="key"] .sectors--ind > :nth-child(1) { grid-column: 1 / -1; grid-row: auto; }
  /* a narrow row has no room beside the text for the drawing, so it goes
     under it */
  html[data-ind="key"] .sector:nth-child(1) { min-height: 0; padding-bottom: 170px; }
  html[data-ind="key"] .sector:nth-child(1) > :not(.sector__ic) { max-width: none; }
  html[data-ind="key"] .sector:nth-child(1) .sector__ic { width: 200px; right: -24px; bottom: -40px; }
}
@media (max-width: 720px) {
  html[data-ind="key"] .sectors--ind { grid-template-columns: minmax(0, 1fr); }
}

/* Index — one ruled row each, numbered in his order: the icon, then the
   title with its link under it, then his description. Seven rows read as
   a list of what CFL covers rather than seven boxes of equal weight. */
html[data-ind="index"] .sectors--ind { grid-template-columns: minmax(0, 1fr); gap: 0; border-top: 1px solid var(--rule); counter-reset: ind; }
html[data-ind="index"] .sectors--ind > * { grid-column: auto; }
html[data-ind="index"] .sectors--ind .sector {
  counter-increment: ind;
  grid-template-columns: 2.4ch 64px minmax(0, 1fr) minmax(0, 1.15fr);
  column-gap: clamp(20px, 2.8vw, 48px); row-gap: 4px; align-items: start;
  padding: clamp(24px, 2.4vw, 34px) clamp(12px, 1.4vw, 20px);
  border: 0; border-bottom: 1px solid var(--rule); border-radius: 0; background: none;
}
html[data-ind="index"] a.sector:hover { background: var(--surface-2); }
html[data-ind="index"] .sector::before {
  content: counter(ind, decimal-leading-zero); grid-area: 1 / 1;
  font-family: var(--mono); font-size: .75rem; letter-spacing: .08em; color: var(--ink-3); padding-top: .45em;
}
html[data-ind="index"] .sector__ic { grid-area: 1 / 2 / 3; margin: 0; }
html[data-ind="index"] .sector h3 { grid-area: 1 / 3; }
html[data-ind="index"] .sector p { grid-area: 1 / 4 / 3; }
@media (max-width: 860px) {
  html[data-ind="index"] .sectors--ind .sector { grid-template-columns: 64px minmax(0, 1fr); row-gap: 12px; }
  html[data-ind="index"] .sector::before { display: none; }
  html[data-ind="index"] .sector__ic { grid-area: 1 / 1; }
  html[data-ind="index"] .sector h3 { grid-area: 1 / 2; align-self: center; }
  html[data-ind="index"] .sector p { grid-area: 2 / 1 / 3 / 3; }
}

/* Explore — the titles as a list, and the one under the pointer or
   focus opened in a panel beside it: its icon drawn large, its title, his
   description, its link. The panel is built by graphics.js as a copy for
   the eye (aria-hidden, out of the tab order); the cards stay the links,
   and their descriptions stay in them for a screen reader. Below 901px
   there is no room for a panel, and the cards go back to the grid. */
.ind-panel { display: none; }
@media (min-width: 901px) {
  html[data-ind="explore"] .sectors--ind {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); gap: 0 clamp(28px, 3.4vw, 60px); align-items: start;
  }
  html[data-ind="explore"] .sectors--ind > .sector { grid-column: 1; }
  html[data-ind="explore"] .sectors--ind .sector {
    grid-template-columns: 44px minmax(0, 1fr); column-gap: 18px; align-items: center;
    padding: 14px 16px; border: 0; border-bottom: 1px solid var(--rule); border-radius: 0; background: none;
    box-shadow: inset 0 0 0 transparent; transition: background .22s ease, box-shadow .22s ease;
  }
  html[data-ind="explore"] .sectors--ind .sector:first-child { border-top: 1px solid var(--rule); }
  html[data-ind="explore"] .sectors--ind .sector h3 { font-size: 1.05rem; }
  html[data-ind="explore"] .sector__ic { width: 44px; height: 44px; margin: 0; }
  html[data-ind="explore"] .sector__ic svg { width: 26px; height: 26px; }
  html[data-ind="explore"] .sector > p {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  html[data-ind="explore"] .sector.is-on { background: var(--surface); box-shadow: inset 2px 0 var(--accent); }
  html[data-ind="explore"] .sector.is-on .sector__ic { background: var(--accent); color: var(--on-accent); }

  html[data-ind="explore"] .ind-panel {
    display: grid; grid-column: 2; grid-row: 1 / span var(--ind-n, 10); align-self: stretch; align-content: start; gap: 18px;
    position: relative; overflow: hidden; min-height: 100%;
    border: 1px solid var(--rule); border-radius: var(--radius);
    padding: clamp(30px, 3.4vw, 56px);
    background: linear-gradient(180deg, var(--surface), var(--ground));
  }
}
.ind-panel > :not(.ind-panel__art) { position: relative; max-width: 56%; }
.ind-panel__n { font-family: var(--mono); font-size: .75rem; letter-spacing: .08em; color: var(--ink-3); }
.ind-panel h3 { font-size: clamp(1.5rem, 1.05rem + 1.3vw, 2.3rem); }
.ind-panel__p { color: var(--ink-2); font-size: var(--fs-sm); max-width: 46ch; }
.ind-panel__art {
  position: absolute; right: -6%; bottom: -10%; width: 44%; aspect-ratio: 1;
  color: var(--accent-dim); transition: color .25s ease;
}
.ind-panel:hover .ind-panel__art { color: var(--accent); }
.ind-panel__art svg { width: 100%; height: 100%; display: block; overflow: visible; }
.ind-panel.is-awake > :not(.ind-panel__art) { animation: ind-in .45s cubic-bezier(.22, .61, .36, 1) both; }
.ind-panel.is-awake > h3 { animation-delay: .04s; }
.ind-panel.is-awake > .ind-panel__p { animation-delay: .08s; }
@keyframes ind-in { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) { .ind-panel.is-awake > * { animation: none; } }

.cargo-floor { stroke: var(--rule); stroke-width: 2.6; stroke-linecap: round; fill: none; }

/* ---------------- knowledge figures ---------------- */

.kc__fig { width: 100%; max-width: 240px; height: auto; display: block; margin-bottom: 6px; overflow: visible; }
.fig-pulse { opacity: 0; transition: opacity .5s ease .35s; }
.kc.is-in .fig-pulse { opacity: 1; }
.fig-draw { stroke-dasharray: var(--len, 400); stroke-dashoffset: var(--len, 400); transition: stroke-dashoffset .95s cubic-bezier(.33,.1,.2,1) .2s; }
.kc.is-in .fig-draw { stroke-dashoffset: 0; }

/* the barrier lifts once the authorisation lands, which is the whole
   sequence the dual-use piece is about */
.kc__fig .fig-gate {
  transform-box: fill-box; transform-origin: 0% 50%;
  transition: transform .75s cubic-bezier(.34, 1.18, .5, 1) .5s;
}
.kc.is-in .fig-gate { transform: rotate(-34deg); }

@media (prefers-reduced-motion: reduce) {
  .fig-pulse, .fig-draw, .kc__fig .fig-gate { transition: none; }
}

/* ================================================================
   The client's 2026-09-30 documents on the home page.
   His six documents are long; the home page carries the head of each —
   his headline, his opening, his items by name — and a line to the page
   the whole document is on. Everything below styles that digest.
   ================================================================ */

/* ---- hero: his eyebrow, supporting line and proof strip ---- */
.hero__eyebrow { max-width: 44rem; }
.hero__support {
  max-width: 50ch; font-size: clamp(.98rem, .93rem + .22vw, 1.08rem); line-height: 1.6; color: var(--ink-2);
}
.hero__support strong { font-weight: 600; color: var(--ink); }
/* the proof strip as a row of marks — grey, the way a logo wall is, so
   three issuers' colours never compete with the one green on the page */
.hero__proof { display: flex; flex-wrap: wrap; gap: 14px 0; padding-top: clamp(16px, 2vw, 32px); }
.pf {
  display: flex; align-items: center; gap: 12px;
  color: color-mix(in srgb, var(--ink-3) 72%, var(--ground));
}
.pf + .pf { margin-left: 22px; padding-left: 22px; border-left: 1px solid var(--rule); }
.pf__mark { display: block; flex: none; width: auto; }
.pf__mark--iata { height: 27px; }
.pf__mark--dg { height: 34px; }
.pf__mark--dg text { font-family: var(--display); font-weight: 700; font-size: 8.5px; fill: currentColor; }
.pf__mark--dda { height: 25px; }
.pf__t {
  display: grid; font-family: var(--mono); font-size: .625rem; line-height: 1.4;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3);
}
@media (max-width: 560px) {
  .hero__proof { gap: 16px 20px; }
  .pf + .pf { margin-left: 0; padding-left: 0; border-left: 0; }
}
/* the first screen has more of his words on it now than it was cut for,
   so its gaps close up a step */
.hero__copy { gap: 24px; }

/* ---- the line under a digest to the page his whole document is on ---- */
.sec-more {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 16px;
  margin-top: clamp(36px, 4vw, 56px); padding-top: 22px; border-top: 1px solid var(--rule);
}
.sec-more__k { font-family: var(--mono); font-size: .625rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); }
.sec-more .txtlink { font-size: 1.05rem; }

/* his introduction closes under the three service cards — or, on a wide
   screen, beside their foot */
.sec-close { display: grid; gap: .6em; max-width: 62ch; margin-top: clamp(32px, 3.6vw, 52px); }
.sec-head:has(> .sec-close) { margin-bottom: 0; }
.sec-close p { color: var(--ink-2); }
.sec-close p:first-child { font-family: var(--display); font-weight: 700; font-size: var(--fs-h3); line-height: 1.2; color: var(--ink); letter-spacing: -0.02em; }

/* Core Services: his introduction and the close are one column of
   argument, set alike at the lede size; the line under each service
   matches the card's own subtitle. The three heads under the headline
   share one size. The paragraphs are balanced so none ends on a word or
   two left alone on the last line. */
#services :is(.sec-head__prose .lede, .sec-close p:not(:first-child)) {
  font-size: var(--fs-lede); line-height: 1.6; color: var(--ink-2); text-wrap: balance; max-width: 54ch;
}
#services .sol__b { font-size: var(--fs-body); line-height: 1.72; text-wrap: balance; }
#services .sol__name { font-size: var(--fs-h3); }

/* ---- Why: seven of his questions, and the reasons that open his page.
   Each row is keyed by a short accent rule, not a "?": the question mark
   is already at the end of every line. ---- */
.why-qs { display: grid; gap: 0; border-top: 1px solid var(--rule); margin-block: .3em; }
/* ruled rows, no dash in front (client, 2026-10-07) */
.why-qs li {
  padding: 9px 0; border-bottom: 1px solid var(--rule); position: relative;
  font-size: var(--fs-sm); color: var(--ink); line-height: 1.45;
}
.whys--five { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: clamp(18px, 2vw, 30px); }
.whys--five .why { grid-template-rows: auto auto 1fr auto; }
@media (max-width: 1240px) { .whys--five { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 860px) { .whys--five { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .whys--five { grid-template-columns: minmax(0, 1fr); } }
a.why { color: inherit; text-decoration: none; }
a.why .why__go { font-size: 1rem; }
a.why:hover .why__t { color: var(--accent); }
a.why:hover::before { transform: scaleX(1); width: 100%; }

/* ---- How We Work: his stage names link into his page ---- */
.flowb__step h3 a { color: inherit; text-decoration: none; }
.flowb__step h3 a:hover { color: var(--accent); }

/* his table, reactive against planned, set as the two runs it describes.
   Each column is one run in the order its steps happen, on a rail down
   its left; the cargo line crosses both, and everything under it happens
   with the goods already moving, on a ground of its own. So the argument
   is a height: the reactive run gets one step in above the line, CFL's
   five. Every li draws its own piece of the rail, like the stages above,
   so a step that wraps to two lines never pulls the rail apart.

   Reactive: a thin grey rail, broken at every step once the cargo moves
   (export, customs and freight handled separately); its discoveries
   carry the amber diamond the lanes above flag the border with; it ends
   on a bar at clearance. CFL: one accent rail, never broken, which runs
   on past its last step and fades out rather than stopping. */
.cmp { margin-top: clamp(48px, 5vw, 80px); display: grid; gap: 22px; max-width: 980px; }
.cmp__cap { font-family: var(--display); font-weight: 700; font-size: var(--fs-h3); letter-spacing: -0.02em; color: var(--ink); }
.runs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(24px, 3vw, 48px); align-items: start; }
/* each run is named in type that reads as its heading, over a rule in
   its own rail colour, keyed by its own step marker */
.run__k {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--display); font-weight: 700; font-size: 1.125rem; letter-spacing: -0.01em; line-height: 1.3;
  color: var(--ink-2); padding-bottom: 14px; border-bottom: 2px solid var(--rail);
}
.run__k::before { content: ''; flex: none; width: 10px; height: 10px; border-radius: 50%; box-sizing: border-box; margin-left: 3px; }
.run--r .run__k::before { border: 2px solid var(--ink-3); }
.run--c .run__k { color: var(--accent); }
.run--c .run__k::before { background: var(--accent); }
.run__list { list-style: none; margin: 0; padding: 0; }
.run__list > li {
  --bg: var(--ground); --y: 23px;       /* the centre of a step's first line */
  position: relative; padding: 12px 12px 12px 36px; background: var(--bg);
  font-size: var(--fs-sm); line-height: 1.5;
}
.run__list > li::before, .run__list > li::after { content: ''; position: absolute; }
.run__list > li::before { left: 7px; top: 0; bottom: 0; width: 2px; background: var(--rail); }
.run__list > li:first-child::before { top: var(--y); }
.run__list > li::after { left: 3px; top: calc(var(--y) - 5px); width: 10px; height: 10px; border-radius: 50%; }

.run--r { --rail: color-mix(in srgb, var(--ink-3) 60%, transparent); }
.run--r .run__s { --tx: var(--ink-3); color: var(--tx); }
.run--r .run__s::after { box-sizing: border-box; border: 2px solid var(--ink-3); background: var(--bg); }
.run--r .run__cargo ~ li::before { top: 8px; bottom: 8px; }
.run--r .run__s--q::after {
  left: 3.5px; top: calc(var(--y) - 4.5px); width: 9px; height: 9px; border: 0; border-radius: 0;
  background: var(--flag); transform: rotate(45deg); box-shadow: 0 0 0 2px var(--bg);
}
.run--r .run__s--end::before { bottom: auto; height: calc(var(--y) - 8px); }
.run--r .run__s--end::after { left: -1px; top: calc(var(--y) - 1px); width: 18px; height: 2px; border: 0; border-radius: 0; background: var(--ink-2); }

.run--c { --rail: var(--accent); }
.run--c .run__list > li::before { left: 6px; width: 4px; }
.run--c .run__s { --tx: var(--ink); color: var(--tx); font-weight: 500; }
.run--c .run__s::after { background: var(--accent); box-shadow: 0 0 0 3px var(--bg); }
.run--c .run__s--on { padding-bottom: 40px; }
.run--c .run__s--on::before { bottom: 0; background: linear-gradient(var(--accent) calc(var(--y) + 4px), transparent); }

/* the moment the cargo moves, and the ground everything after it is on */
.run__list > .run__cargo, .run__list > .run__cargo ~ li { --bg: var(--ground-2); }
.run__list > .run__cargo {
  --y: 22px;
  font-family: var(--mono); font-size: var(--fs-mono); font-weight: 500; letter-spacing: .12em; text-transform: uppercase; line-height: 20px;
  color: var(--ink); border-top: 1px solid var(--ink-2);
}
.run__list > li:last-child { border-radius: 0 0 var(--radius) var(--radius); }
.run__list > .run__cargo::after { left: 1px; width: 14px; height: 11px; top: calc(var(--y) - 5.5px); box-sizing: border-box; border: 1.5px solid var(--ink-2); border-radius: 1px; background: var(--bg); }

/* a row of his table is a step on each side: pointing at one lights the
   other, wherever the other run has put it */
@media (hover: hover) {
  .run__s { transition: color .2s ease; }
  .runs:has(.run__s:hover) .run__s { color: color-mix(in srgb, var(--tx) 32%, transparent); }
  .runs:has([data-pair="1"]:hover) [data-pair="1"],
  .runs:has([data-pair="2"]:hover) [data-pair="2"],
  .runs:has([data-pair="3"]:hover) [data-pair="3"],
  .runs:has([data-pair="4"]:hover) [data-pair="4"],
  .runs:has([data-pair="5"]:hover) [data-pair="5"],
  .runs:has([data-pair="6"]:hover) [data-pair="6"],
  .runs:has([data-pair="7"]:hover) [data-pair="7"] { color: var(--tx); }
}

/* narrow: the reactive run, then CFL's */
@media (max-width: 639px) {
  .runs { grid-template-columns: minmax(0, 1fr); row-gap: 36px; }
}


/* ============================================================
   PHONE — one pass over the home page at phone width (2026-10-06)

   Measured at 390 × 844, the home page ran to thirty screens, and the
   first of them was all copy: the button the first screen exists for
   sat just under the fold. What follows takes length out where it was
   air or repetition, never copy:

     the first screen    the button follows the paragraph it answers;
                         who it is for, No handovers and the proof
                         strip follow the button
     the rhythm          the gap between two sections drops from 128px
                         to 96px — at this width 128 was a screen's
                         seventh, every time
     the map             the scroll it is pinned for drops from 470vh
                         to 340vh: its three frames each still hold
                         most of a screen, without a blank one first
     the references      the four after the lead go into a row the
                         thumb swipes through, each in full, the next
                         one showing at the edge so the row reads as
                         one; it had been four screens of quotes
     the footer          two columns under Solutions, whose names are
                         too long to share a row; every link is a
                         thumb's height to hit, with no change to the
                         lines
   The photographs on the floor become a swiped row too — photos.css.
   ============================================================ */
@media (max-width: 560px) {
  :root { --section-gap: 96px; }

  html:is([data-hero="lean"], [data-hero="spell"]) .hero .hero__cta { order: 1; }

  .wmap__track { height: 340vh; }

  /* the row bleeds to the screen's edges, and snaps a card to the
     gutter; a card in it is already on screen, or arrives by the thumb,
     so it does not wait to be revealed */
  .refs__grid {
    grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 86%;
    align-items: start; gap: 12px;
    margin-inline: calc(var(--gutter) * -1); padding: 0 var(--gutter) 6px;
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory; scroll-padding-inline: var(--gutter);
    scrollbar-width: none;
  }
  .refs__grid::-webkit-scrollbar { display: none; }
  .refs__grid > .ref { scroll-snap-align: start; }
  .refs__grid > .ref[data-reveal] { opacity: 1; transform: none; }
}
@media (max-width: 520px) {
  .site-foot__top { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 36px; }
  .site-foot__top > .foot-brand, .site-foot__top > .foot-col:first-of-type { grid-column: 1 / -1; }
}
/* an inline box's padding grows what a finger can hit and leaves the
   line where it was; the menu's links and a chip that goes somewhere
   get the same */
@media (pointer: coarse) {
  .foot-col a, .site-foot__bot a, .closing__note a { padding-block: 9px; }
  .hm-col a { padding-block: 6px; margin-block: -6px; }
  a.chip { padding-block: 10px; }
}
/* the mono labels the inner pages also come up to 11px (pages.css) */
@media (max-width: 560px) {
  .hm-lbl, .foot-col h3 { font-size: .6875rem; }
}
