/* /trans-afrique — the expedition.
 *
 * Dramatic by scale and contrast, not by effects. The house rules the rest of
 * the site keeps apply here too: no gradients, no glass, no glow, no 3D. What
 * makes this feel different from every other page is that the type is bigger,
 * the ground is darker, and the route reads as a line of countries with arrows
 * between them rather than as a list.
 */
.tf-body{background:var(--fj-basalt);color:var(--fj-onbasalt)}
.tf-page{max-width:var(--frame);margin:0 auto;padding:var(--sp-6) 44px var(--sp-7)}

/* ---- 01 the opening spread ------------------------------------------------ */
/* THE FIXED-WINDOW BAND, AND IT MUST NOT BE BROKEN.
   ---------------------------------------------------------------------------
   .tf-band-pic holds one viewport of photograph at position:fixed; the band is
   given clip-path:inset(0), which clips that fixed child to the band's own box.
   Scrolling slides the window down the picture and the picture never moves.
   There is no script — this is two properties.

   NOTHING between .tf-band and .tf-band-pic, or on either, may carry
   transform, filter, backdrop-filter, perspective, will-change or contain.
   Each makes an element a containing block for fixed descendants, which turns
   that `fixed` into `absolute` and ends the effect with no error and nothing
   obviously wrong — the picture simply starts scrolling like any other image.
   A hover effect added here in two years is enough to do it, which is why
   tools/browser-checks.js asserts the mechanism rather than the appearance.

   background-attachment:fixed is the other way to do this and is not used: iOS
   Safari ignores it outright, on the platform most of these readers are on.

   88svh, and svh rather than vh: on a phone vh changes as the toolbars hide,
   which would resize the window while the reader is inside it. */
.tf-band{position:relative;clip-path:inset(0);min-height:96svh;
  display:flex;align-items:flex-end}
.tf-band-pic{position:fixed;inset:0;z-index:0}
/* FOUR SLIDES, CROSS-FADED WITH OPACITY AND NOTHING ELSE.
   opacity makes a stacking context; transform, filter, perspective,
   will-change and contain make a containing block for fixed descendants and
   would silently demote the picture above from fixed to absolute. A fade
   written with any of those would have looked identical in the browser and
   killed the effect, so this animation is deliberately the dullest one that
   works.
   Each image is stacked in the same cell and lit for a quarter of the cycle
   with a short overlap, so the band dissolves rather than cuts. --i is the
   slide index, written by the generator, so adding a fifth photograph needs no
   CSS at all beyond the cycle length. */
.tf-band-pic img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block;opacity:0;
  animation:tf-slide 32s linear infinite;
  animation-delay:calc(var(--i) * -8s)}
.tf-band-pic img:first-of-type{opacity:1}
@keyframes tf-slide{
  0%{opacity:1} 22%{opacity:1} 25%{opacity:0}
  97%{opacity:0} 100%{opacity:1}}
/* Refused motion gets the first frame and no dissolve at all. */
@media(prefers-reduced-motion:reduce){
  .tf-band-pic img{animation:none;opacity:0}
  .tf-band-pic img:first-of-type{opacity:1}
}
/* Fixed WITH the picture, not laid on the band: a tint on the band would
   travel while the photograph stood still and the shading would crawl across
   the frame. Graded, not flat — this photograph is a bright orange road under
   a pale sky, and a flat wash heavy enough for the worst pixel is the dark
   scrim this is built not to be. Weight goes where the type is and is released
   over the hills, which is the half of the picture worth keeping luminous. */
/* HORIZONTAL, AND IT HAS TO BE.
   The first version graded this vertically — heavy at the foot where the type
   sits, released at the top over the hills — and it measured 3.55 to 4.48:1
   against a 4.5 bar. The reason is the same one the homepage band records: the
   tint is fixed to the viewport and the copy travels the whole height of it, so
   every line passes through every band of a vertical gradient. Grading it that
   way does not fix anything, it only chooses which scroll position fails.
   Horizontally there is no such problem, because the copy is pinned to the left
   column and stays there for the whole travel — weight put on the left is under
   the type at every scroll position. So the left is carried and the right, which
   is hills and sky and the best part of the photograph, keeps its light. */
.tf-band-tint{position:absolute;inset:0;background:
  linear-gradient(100deg,
    color-mix(in srgb,var(--c-primary) 92%,transparent) 0%,
    color-mix(in srgb,var(--c-primary) 89%,transparent) 40%,
    color-mix(in srgb,var(--c-primary) 80%,transparent) 60%,
    color-mix(in srgb,var(--c-primary) 40%,transparent) 82%,
    color-mix(in srgb,var(--c-primary) 14%,transparent) 100%)}
.tf-band-copy{position:relative;z-index:1;width:100%;max-width:var(--frame);
  margin:0 auto;padding:0 44px var(--sp-6)}
.tf-band-in{max-width:min(720px,58%)}
.tf-band-mark{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  letter-spacing:.16em;font-size:clamp(13px,1.2vw,16px);color:var(--c-bg);margin:0}
.tf-band-series{font-family:var(--fj-mono);font-size:9.5px;letter-spacing:.28em;
  text-transform:uppercase;color:var(--c-bg);margin:8px 0 0}
.tf-band-sub{margin:22px 0 0;font-family:var(--fj-display);font-weight:700;
  font-size:clamp(16px,1.6vw,22px);line-height:1.25;color:var(--c-bg);max-width:24ch}
.tf-band-chain{margin:20px 0 0;display:flex;flex-wrap:wrap;
  font-family:var(--fj-mono);font-size:9.5px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--c-bg);line-height:2}
.tf-band-chain span+span::before{content:"\2192";margin:0 .5em}
/* Ivory fill, deep-forest ink: the one thing to do from a very dark screen has
   to be the brightest object on it, and terracotta over this photograph
   measured 3.15:1 where 4.5 was needed. */
.tf-band-go{margin-top:30px;background:var(--c-bg);border-color:var(--c-bg);
  color:var(--c-primary)}
.tf-band-go:hover{background:var(--c-accent-lit);border-color:var(--c-accent-lit)}
@media(max-width:900px){
  .tf-band{min-height:92svh}
  .tf-band-copy{padding:0 24px var(--sp-5)}
  .tf-band-in{max-width:none}
  /* Heavier on the small screen than the desktop gradient. The band's copy
     travels the full height of a FIXED photograph, so every line meets every
     part of the picture at some scroll position — and the four slides cross-
     fade, so it meets each of them too. 72% held until the page was shortened
     and a sticky bar was added above it, which moved every line to a different
     part of the frame. A tint on a fixed picture is not tuned to a composition,
     it is tuned to the brightest pixel any line can reach — measured at 5.8:1
     over pure white at 72%, so 80 is margin rather than rescue. What actually
     broke the band here was the sticky bar above it, not the tint. */
  .tf-band-tint{background:color-mix(in srgb,var(--c-primary) 80%,transparent)}
}

/* Widened when the headline grew. 'Don't just cross Africa. Experience it
   with a team.' at 13ch set it in eight lines down a narrow column, which
   reads as a poem rather than a statement. In the hero column it is narrower
   again, so the size is cut with it — 88px against a 37% column would set the
   same eight lines the 13ch measure did. */
.tf-h1{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(36px,5.4vw,82px);line-height:.94;letter-spacing:-.028em;
  color:var(--c-bg);margin:18px 0 0;max-width:15ch}
.tf-lede{margin-top:24px;font-size:clamp(17px,1.7vw,22px);line-height:1.5;
  color:var(--fj-onbasalt);max-width:56ch}

/* ---- 02 the idea ---------------------------------------------------------- */
/* Asymmetric on purpose: the statement holds the left column and the argument
   runs down the right, so the page reads as a spread rather than as a stack of
   centred blocks. The argument is in distances rather than adjectives, which is
   the only version that survives a reader who has never been. */
.tf-idea-in{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.1fr);
  gap:var(--sp-4) var(--sp-6);align-items:start}
.tf-idea-line{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(30px,3.8vw,58px);line-height:1;letter-spacing:-.025em;
  color:var(--c-bg);margin:0;max-width:13ch}
.tf-idea-say{margin:0 0 20px;font-size:clamp(16px,1.4vw,18.5px);line-height:1.65;
  color:var(--fj-onbasalt);max-width:60ch}
/* The turn, and it sits between the paragraphs rather than after them: the
   first says a week in one country is a week in one country, this says what the
   alternative actually is, and the second then has something to explain. */
.tf-idea-pull{margin:26px 0 26px;padding-left:22px;
  border-left:2px solid var(--c-accent-lit);
  font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(19px,2vw,28px);line-height:1.1;letter-spacing:-.01em;
  color:var(--c-bg);max-width:20ch}
@media(max-width:900px){.tf-idea-in{grid-template-columns:minmax(0,1fr)}}

.tf-block{margin-top:var(--sp-7)}
.tf-h2{font-family:var(--fj-mono);font-size:10px;letter-spacing:.24em;
  text-transform:uppercase;color:var(--c-accent-lit);font-weight:400;
  border-top:2px solid var(--c-accent-fill);padding-top:18px;margin:0 0 var(--sp-4)}



/* ---- the crossings, on the continent -------------------------------------- */
}

/* The crossings. The country chain is the picture — arrows between names, which
   is what an expedition actually looks like on paper. */
.tf-routes{display:grid;gap:var(--sp-3);
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.tf-route-in{border-top:2px solid var(--c-accent-fill);padding-top:20px;height:100%;
  display:flex;flex-direction:column}
.tf-route-name{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(21px,2.1vw,29px);line-height:1.04;color:var(--c-bg);margin:0}
.tf-route-where{margin-top:12px;font-family:var(--fj-mono);font-size:10px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--c-gold);line-height:2}
.tf-route-where a{color:inherit;border-bottom:1px solid transparent;transition:border-color .2s}
.tf-route-where a:hover{border-color:var(--c-gold)}
.tf-route-say{margin-top:14px;font-size:15.5px;line-height:1.5;
  color:var(--fj-onbasalt-dim);max-width:34ch;flex:1 1 auto}
.tf-route-facts{margin:20px 0 0;padding-top:14px;border-top:var(--fj-rule-dark)}
.tf-route-facts div{display:grid;grid-template-columns:78px 1fr;gap:14px;padding:6px 0}
.tf-route-facts dt{font-family:var(--fj-mono);font-size:9px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--fj-onbasalt-dim)}
.tf-route-facts dd{margin:0;font-size:15px;color:var(--c-bg)}

.tf-fine{margin-top:var(--sp-4);font-size:14px;line-height:1.6;
  color:var(--fj-onbasalt-dim);max-width:70ch}

.tf-end{margin-top:var(--sp-7);padding-top:var(--sp-5);border-top:var(--fj-rule-dark);
  display:flex;flex-wrap:wrap;align-items:center;gap:20px 28px}
.tf-end p{font-family:var(--fj-display);font-size:clamp(19px,2vw,27px);
  color:var(--c-bg);margin:0;max-width:26ch}

@media(max-width:760px){
  .tf-page{padding:var(--sp-5) 18px var(--sp-6)}
  .tf-block{margin-top:var(--sp-6)}
  .tf-team-row{grid-template-columns:1fr;gap:7px}
  .tf-routes{grid-template-columns:1fr}
}

/* Three ways to cross. The middle one is recommended and says so on the card,
   the way the ground journey's tiers do — the same idea in the same language,
   because they are the same company's way of offering a choice. */
.tf-levels{display:grid;gap:var(--sp-3);
  grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.tf-level{border-top:2px solid var(--c-accent-fill);padding-top:20px;
  display:flex;flex-direction:column;position:relative}
.tf-level.is-rec{box-shadow:inset 0 3px 0 var(--c-accent-lit)}
.tf-level.is-rec::after{content:"Most crossings";position:absolute;top:-9px;right:0;
  background:var(--c-accent);color:var(--c-bg);font-family:var(--fj-mono);
  font-size:8.5px;letter-spacing:.18em;text-transform:uppercase;padding:3px 8px}
.tf-level-name{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(19px,1.9vw,25px);line-height:1.05;color:var(--c-bg);margin:0}
.tf-level-line{margin-top:10px;font-family:var(--fj-display);font-size:16px;
  color:var(--c-accent-lit)}
.tf-level-say{margin-top:12px;font-size:15px;line-height:1.55;
  color:var(--fj-onbasalt-dim);flex:1 1 auto}
.tf-level-seats{margin-top:11px;font-family:var(--fj-mono);font-size:9.5px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--c-gold)}
.tf-level-facts{margin:18px 0 0;padding-top:13px;border-top:var(--fj-rule-dark)}
.tf-level-facts div{display:grid;grid-template-columns:88px 1fr;gap:12px;padding:5px 0}
.tf-level-facts dt{font-family:var(--fj-mono);font-size:9px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--fj-onbasalt-dim)}
.tf-level-facts dd{margin:0;font-size:15px;color:var(--c-bg);
  font-variant-numeric:tabular-nums}
.tf-level-who{margin-top:12px;font-size:13.5px;color:var(--fj-onbasalt-dim)}

/* CHOOSING ONE IS A LINK, AND THE WHOLE CARD IS THAT LINK.
   The three were read-only columns with no way out of them: a reader who had
   decided had to scroll past all three, find the enquiry in the footer and
   start again in their own words. The action carries the choice — its name,
   length and band pre-written into the enquiry letter — so deciding and asking
   are one gesture.

   The anchor is a real link at the foot with a sentence for its name, stretched
   over the card by a pseudo-element. The other way round — a card-shaped <a>
   wrapping four paragraphs — makes the accessible name four paragraphs long,
   which is how a screen reader reads out an entire tier before saying "link". */
.tf-level{cursor:pointer;transition:border-color .2s ease,background .2s ease}
.tf-level-go{margin-top:auto;padding-top:20px;
  display:inline-flex;align-items:center;min-height:24px;gap:9px;
  font-family:var(--fj-mono);font-size:10px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--c-accent-lit);align-self:flex-start}
.tf-level-go::after{content:"";position:absolute;inset:0;z-index:1}
.tf-level-go i{font-style:normal;transition:transform .2s ease}
/* The hover and focus states are on the CARD, because the card is the target.
   A hover that only lights the sentence at the bottom tells the reader the
   sentence is clickable and the four inches above it are not. */
.tf-level:hover{border-top-color:var(--c-accent-lit);
  background:color-mix(in srgb,var(--c-bg) 4%,transparent)}
.tf-level:hover .tf-level-go i{transform:translateX(4px)}
.tf-level:has(.tf-level-go:focus-visible){outline:2px solid var(--c-accent-lit);
  outline-offset:6px}
.tf-level-go:focus-visible{outline:none}
/* The recommended card already carries a flag in the top right; the action
   line is where the eye lands last, so it gets the emphasis instead. */
.tf-level.is-rec .tf-level-go{color:var(--c-bg)}

/* The lenses a crossing leads on, under the country chain. */
.tf-route-strands{margin-top:9px;font-family:var(--fj-mono);font-size:9px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--c-accent-lit)}
/* Nine countries and two oceans deserves the width. */
.tf-route--great{grid-column:1/-1}
.tf-route--great .tf-route-name{font-size:clamp(24px,2.6vw,36px)}
.tf-route--great .tf-route-where{line-height:2.1}
.tf-route--great .tf-route-say{max-width:52ch}

/* What the fee is and is not, in three columns rather than one long list, so
   the split between what we earn and what Africa costs is visible at a glance
   rather than read. */
.tf-money{display:grid;gap:var(--sp-4) var(--sp-5);
  grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.tf-money-h{font-family:var(--fj-mono);font-size:9.5px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--c-accent-lit);font-weight:400;margin:0 0 12px}
.tf-money-col.is-not .tf-money-h{color:var(--fj-onbasalt-dim)}
.tf-money-list{list-style:none;margin:0;padding:0}
.tf-money-list li{padding:9px 0 9px 20px;border-bottom:var(--fj-rule-dark);
  position:relative;font-size:15px;color:var(--fj-onbasalt)}
.tf-money-list li::before{content:"\002B";position:absolute;left:0;top:9px;
  color:var(--c-accent-lit);font-family:var(--fj-mono);font-size:11px}
.tf-money-col.is-not .tf-money-list li{color:var(--fj-onbasalt-dim)}
.tf-money-col.is-not .tf-money-list li::before{content:"\2013";color:var(--fj-onbasalt-dim)}

.tf-sub{display:block;font-family:var(--fj-display);font-weight:700;
  font-size:clamp(18px,1.9vw,26px);color:var(--c-accent-lit);margin-bottom:12px}

@media(max-width:760px){
  .tf-levels{grid-template-columns:1fr}
  .tf-money{grid-template-columns:1fr}
}

/* SOME JOURNEYS NEED A GUIDE. TRANS AFRIQUE NEEDS A TEAM.
   The line the section turns on, so it is set at statement scale rather than
   as copy. */
.tf-motto{font-family:var(--fj-display);font-weight:700;
  font-size:clamp(20px,2.3vw,32px);line-height:1.14;color:var(--c-accent-lit);
  max-width:30ch;margin:var(--sp-4) 0 0}

/* Expedition support: six domains, and the roles inside each. Naming what is
   covered rather than who is on the bus — which individual travels changes with
   the route, and what has to be covered does not. */
.tf-sup-head{margin-top:var(--sp-6)}
.tf-sup-title{font-family:var(--fj-mono);font-size:10px;letter-spacing:.24em;
  text-transform:uppercase;color:var(--c-accent-lit);font-weight:400;
  border-top:2px solid var(--c-accent-fill);padding-top:18px;margin:0}
.tf-sup-lede{margin-top:12px;font-size:15.5px;color:var(--fj-onbasalt-dim);max-width:52ch}
.tf-support{margin-top:var(--sp-4);display:grid;gap:var(--sp-3) var(--sp-4);
  grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.tf-sup{border-top:var(--fj-rule-dark);padding-top:16px}
.tf-sup-name{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:17px;line-height:1.06;color:var(--c-bg);margin:0}
.tf-sup-say{margin-top:9px;font-size:14.5px;line-height:1.5;color:var(--fj-onbasalt-dim)}
.tf-sup-roles{list-style:none;margin:12px 0 0;padding:0}
.tf-sup-roles li{font-family:var(--fj-mono);font-size:9px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--c-gold);padding:3px 0}

/* The medical offer and its limit, in one block. The limit is not a footnote:
   it is the sentence a traveller would be most entitled to be angry about later
   if it were buried, so it sits directly under the offer and is marked. */
.tf-med{margin-top:var(--sp-4);border-left:3px solid var(--c-accent-lit);
  padding:2px 0 2px 20px;max-width:66ch}
.tf-med-h{font-family:var(--fj-mono);font-size:9.5px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--c-accent-lit);font-weight:400;margin:0}
.tf-med-say{margin-top:10px;font-size:15.5px;line-height:1.55;color:var(--fj-onbasalt)}
.tf-med-but{margin-top:10px;font-size:14px;line-height:1.6;color:var(--fj-onbasalt-dim)}

@media(max-width:760px){.tf-support{grid-template-columns:1fr}}


/* ---- 03 the dossier, 04 the medical note ---------------------------------- */
/* 01 / MEDICAL reads as a manifest; six unnumbered boxes read as a features
   grid. The number is the whole premiumisation and it costs one element. */
.tf-sup-no{display:flex;align-items:center;gap:12px;margin:0 0 12px}
.tf-sup-no b{font-family:var(--fj-mono);font-size:10px;letter-spacing:.2em;
  color:var(--c-accent-lit);font-weight:400}
.tf-sup-no i{flex:1 1 auto;height:1px;background:var(--fj-rule-dark);display:block}
.tf-sup{border-top:0;padding-top:0}
.tf-med{position:relative}
.tf-med-stamp{margin:18px 0 0;font-family:var(--fj-mono);font-size:9px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--c-gold)}

/* ---- 05 three ways to cross ----------------------------------------------- */
/* Not a pricing table. The number and the promise come first and the figures
   come last in the metadata voice, because the figure is a consequence of the
   product rather than the product. */
.tf-level-no{font-family:var(--fj-mono);font-size:10px;letter-spacing:.2em;
  color:var(--c-accent-lit);margin:0 0 12px}

/* ---- 06 the crossings, on the map ----------------------------------------- */
/* Four colours, and the widths carry the truth the colours alone could not:
   East is the Continental spine's first four countries and South is its last
   five, so the three lines are coincident. Drawn in descending width — spine
   widest and underneath, then South, then East on top — the overlap reads as
   the regional crossings sitting inside the trunk, which is exactly what they
   are. Four equal lines would have read as a collision. */
/* The legend names every country, because "6 countries" raises exactly the
   question it then refuses to answer — and it doubles as the map's control. */
/* Hovering a legend row lifts its line and dims the rest. Transitions only —
   no transform anywhere near the band, and nothing here is fixed anyway. */

/* ---- 08 the flagship ------------------------------------------------------ */
/* Full width and out of the grid the other three sit in. East, West and South
   are three crossings; this is the one they are portions of, which is a
   different kind of thing and must not read as a fourth option. */
.tf-great{margin:var(--sp-7) auto 0;max-width:var(--frame);padding:var(--sp-6) 44px;
  border-top:2px solid var(--c-accent-fill);border-bottom:2px solid var(--c-accent-fill)}
.tf-great-eyebrow{font-family:var(--fj-mono);font-size:10px;letter-spacing:.28em;
  text-transform:uppercase;color:var(--c-accent-lit);margin:0}
.tf-great-name{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(32px,5.6vw,86px);line-height:.94;letter-spacing:-.03em;
  color:var(--c-bg);margin:14px 0 0;max-width:14ch}
.tf-great-strands{margin:20px 0 0;font-family:var(--fj-mono);font-size:10px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--c-gold)}
.tf-great-where{margin:16px 0 0;font-family:var(--fj-mono);font-size:10px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--fj-onbasalt);line-height:2.1}
.tf-great-where a{color:inherit;border-bottom:1px solid transparent}
.tf-great-where a:hover{border-color:var(--c-gold)}
.tf-great-say{margin:24px 0 0;font-size:clamp(16px,1.5vw,20px);line-height:1.6;
  color:var(--fj-onbasalt);max-width:60ch}
.tf-great-facts{display:flex;flex-wrap:wrap;gap:0 var(--sp-5);margin:var(--sp-4) 0 0;
  padding-top:var(--sp-3);border-top:var(--fj-rule-dark)}
.tf-great-facts div{padding:6px 0}
.tf-great-facts dt{font-family:var(--fj-mono);font-size:9px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--fj-onbasalt-dim)}
.tf-great-facts dd{margin:6px 0 0;font-size:17px;color:var(--c-bg)}
.tf-great-close{margin:var(--sp-4) 0 0;font-family:var(--fj-display);font-weight:700;
  text-transform:uppercase;font-size:clamp(17px,1.8vw,25px);letter-spacing:.01em;
  color:var(--c-accent-lit)}

/* ---- 10 ready to cross ---------------------------------------------------- */
/* An invitation, not a checkout. Two buttons because the two audiences are
   genuinely different, and the ground changes to deep forest so the end of the
   page is a room rather than another paragraph. */
.tf-close{background:var(--c-primary);margin-top:var(--sp-7)}
.tf-close-in{max-width:var(--frame);margin:0 auto;padding:var(--sp-7) 44px}
.tf-close-h{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(34px,5vw,72px);line-height:.96;letter-spacing:-.028em;
  color:var(--c-bg);margin:0}
.tf-close-say{margin:22px 0 0;font-size:clamp(16px,1.5vw,19px);line-height:1.6;
  color:var(--fj-onbasalt);max-width:54ch}
.tf-close-acts{display:flex;flex-wrap:wrap;gap:14px;margin-top:var(--sp-4)}
.tf-close-go{background:var(--c-bg);border-color:var(--c-bg);color:var(--c-primary)}
.tf-close-go:hover{background:var(--c-accent-lit);border-color:var(--c-accent-lit)}
.tf-close-alt{border-color:var(--c-bg);color:var(--c-bg);background:none}
.tf-close-alt:hover{background:var(--c-bg);color:var(--c-primary)}
.tf-close-stamp{margin:var(--sp-4) 0 0;font-family:var(--fj-mono);font-size:9px;
  letter-spacing:.24em;text-transform:uppercase;color:var(--c-gold)}
.tf-page--foot{padding-top:var(--sp-5)}

@media(max-width:860px){
  .tf-great{padding:var(--sp-5) 24px}
  .tf-close-in{padding:var(--sp-6) 24px}
}


/* ---- 03 the philosophy band ----------------------------------------------- */
/* The page's thesis on a photograph, and deliberately NOT the fixed-window
   band: that technique belongs to the opening spread, and a second fixed
   picture two screens later turns a signature into a tic. An ordinary
   full-bleed image with the type over its darkened left is enough here — the
   sentence is doing the work, the picture only has to make it literal. */
.tf-phil{position:relative;isolation:isolate;min-height:min(62svh,620px);
  display:flex;align-items:center;overflow:hidden;background:var(--c-primary)}
.tf-phil-pic{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  display:block}
.tf-phil-tint{position:absolute;inset:0;background:
  linear-gradient(100deg,
    color-mix(in srgb,var(--c-primary) 90%,transparent) 0%,
    color-mix(in srgb,var(--c-primary) 84%,transparent) 38%,
    color-mix(in srgb,var(--c-primary) 58%,transparent) 62%,
    color-mix(in srgb,var(--c-primary) 26%,transparent) 100%)}
.tf-phil-in{position:relative;width:100%;max-width:var(--frame);margin:0 auto;
  padding:var(--sp-6) 44px}
.tf-phil-pull{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(28px,4.2vw,62px);line-height:1;letter-spacing:-.025em;
  color:var(--c-bg);margin:0;max-width:15ch}
.tf-phil-say{margin:26px 0 0;font-size:clamp(16px,1.4vw,18.5px);line-height:1.65;
  color:var(--c-bg);max-width:48ch}
.tf-page--after{padding-top:var(--sp-7)}
@media(max-width:860px){.tf-phil-in{padding:var(--sp-5) 24px}
  .tf-phil-tint{background:color-mix(in srgb,var(--c-primary) 76%,transparent)}}

/* ---- 06 the medical strip -------------------------------------------------- */
/* A photographic strip rather than a boxed note. The picture is vehicles drawn
   up together on an open pan — expedition logistics, which is what this section
   is actually about — and it sits beside the words rather than behind them, so
   nothing here has to fight a photograph for contrast. */
.tf-med{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.55fr);
  gap:0;margin-top:var(--sp-5);border-left:0;padding:0;max-width:none;
  border:1px solid var(--fj-rule-dark)}
/* CAPPED, because nothing else here sets the row's height. The frame is a tall
   portrait and the note beside it is four short paragraphs, so on its own page
   the picture drove a row twice the height of the words next to it and the
   strip stopped reading as a strip. On the old long page the surrounding copy
   happened to be tall enough to hide that. */
.tf-med-pic{overflow:hidden;background:var(--c-primary);min-height:100%;
  max-height:min(62svh,560px)}
/* The vehicles sit about two thirds down a tall portrait frame, so a centred
   cover crop showed nothing but cloud. Measured off the picture, not guessed. */
.tf-med-pic img{width:100%;height:100%;object-fit:cover;display:block;
  object-position:50% 64%}
.tf-med-in{padding:var(--sp-5) var(--sp-5) var(--sp-5) var(--sp-4)}
.tf-med-in p{max-width:62ch}
@media(max-width:860px){
  .tf-med{grid-template-columns:minmax(0,1fr)}
  .tf-med-pic{aspect-ratio:16/9;min-height:0}
  .tf-med-in{padding:var(--sp-4) 24px}
}

/* ---- 12 the closing spread ------------------------------------------------- */
/* The page ends on a photograph, not another dark text block. */
.tf-close{position:relative;isolation:isolate;overflow:hidden;
  background:var(--c-primary);margin-top:var(--sp-7)}
.tf-close-pic{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:50% 40%;display:block}
.tf-close-tint{position:absolute;inset:0;background:
  linear-gradient(100deg,
    color-mix(in srgb,var(--c-primary) 92%,transparent) 0%,
    color-mix(in srgb,var(--c-primary) 88%,transparent) 40%,
    color-mix(in srgb,var(--c-primary) 62%,transparent) 66%,
    color-mix(in srgb,var(--c-primary) 30%,transparent) 100%)}
.tf-close-in{position:relative}
.tf-close-eyebrow{font-family:var(--fj-mono);font-size:10px;letter-spacing:.26em;
  text-transform:uppercase;color:var(--c-bg);margin:0 0 18px}
@media(max-width:860px){
  .tf-close-tint{background:color-mix(in srgb,var(--c-primary) 82%,transparent)}
}

/* ---- 05 six tracks, and 07 Signature carries the weight -------------------- */
/* Six specialists, six columns on a wide screen. auto-fit gave four and then
   two, which reads as a grid that ran out rather than as a manifest of six. */
@media(min-width:1180px){
  .tf-support{grid-template-columns:repeat(6,minmax(0,1fr));gap:var(--sp-3)}
}
/* Signature is the middle proposition and the one most people are actually
   buying; three equal panels make the reader do the comparing. It takes the
   accent rule, the badge and a step up in name size — not a different colour of
   card, which would have been a pricing table admitting what it is. */
.tf-level.is-rec{border-top-color:var(--c-accent-lit)}
.tf-level.is-rec .tf-level-name{font-size:clamp(21px,2.3vw,30px)}
@media(min-width:900px){
  .tf-levels{grid-template-columns:minmax(0,1fr) minmax(0,1.22fr) minmax(0,1fr)}
}

/* ---- 11 the fee, with a marker per column ---------------------------------- */
/* Three columns, three markers: a rule for what is in the fee, a dot for what
   we arrange at cost, a dash for what stays yours. Three different symbols
   because the three are three different promises, and a reader scanning should
   be able to tell them apart without reading the headings again. */
.tf-money-list li::before{content:"\002B"}
.tf-money-col:nth-child(2) .tf-money-list li::before{content:"\25CB";font-size:9px}
.tf-money-col.is-not .tf-money-list li::before{content:"\2013"}

/* ===========================================================================
   THE SERIES — nine pages where there was one.
   ---------------------------------------------------------------------------
   One page was answering nine questions in one scroll, so a reader met the
   medical protocol before deciding they wanted a crossing. Length was never
   the fault; every question sharing a page with every other question was.
   What follows is the chrome that makes nine pages read as one thing: a bar
   that is always there, an opening that is a still photograph rather than a
   second fixed band, and a way on at the foot of every page.
   =========================================================================== */

/* The bar. Sticky under the masthead — a series a reader can leave at any
   point but never fall out of. */
/* STICKS BELOW THE MASTHEAD, NOT ON TOP OF IT. Both are sticky at top:0 and
   this one carries the higher z-index, so on any scroll it parked itself over
   the ivory masthead and hid the logo and the primary nav completely. The
   masthead is 78px and says so in a token; the bar starts where it ends. */
.tf-series{position:sticky;top:var(--mast);z-index:36;display:flex;flex-wrap:wrap;
  align-items:baseline;gap:0 26px;
  background:color-mix(in srgb,var(--fj-basalt) 94%,var(--c-bg));
  border-bottom:1px solid color-mix(in srgb,var(--c-bg) 14%,transparent);
  padding:12px max(20px,calc((100vw - var(--frame)) / 2 + 44px))}
/* 24px of height on every link in this bar. At 22 they were a hair under the
   minimum tap target and the gate caught all four pages at once — a nine-pixel
   label needs padding, not a bigger font, or the bar stops being a bar. */
.tf-series-mark{display:inline-flex;align-items:center;min-height:24px;
  font-family:var(--fj-display);font-weight:700;font-size:13px;
  text-transform:uppercase;letter-spacing:.02em;color:var(--c-bg);
  white-space:nowrap;margin-right:auto}
/* The current page is marked by the RULE, not by tinting the ink. Set in
   accent-lit the label measured 3.94:1 on this ground, under the 4.5 a
   nine-pixel label needs — and dimming the one item the reader is on is
   backwards anyway. Ivory ink, accent underline. */
/* The door's bar sits UNDER the band rather than over it, and sticks from
   there. See overview_body() in transafrique.py for why it cannot be above:
   the opening spread's headline travels the full height of a fixed picture,
   so a bar pinned in front of it is a bar the copy slides beneath. */
.tf-series--top{border-top:1px solid color-mix(in srgb,var(--c-bg) 14%,transparent)}
.tf-series-mark.is-on{color:var(--c-bg);
  border-bottom:2px solid var(--c-accent);padding-bottom:4px}
.tf-series-list{list-style:none;display:flex;flex-wrap:wrap;gap:4px 22px;
  margin:0;padding:0}
/* min-width as well as min-height: "Why" sets to 22 pixels wide at 9.5px mono,
   and the criterion is a target, not a line height. */
.tf-series-list a{display:inline-flex;align-items:center;justify-content:center;
  min-height:24px;min-width:24px;
  font-family:var(--fj-mono);font-size:9.5px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--fj-onbasalt-dim);white-space:nowrap;
  padding:6px 0;border-bottom:2px solid transparent;transition:color .2s,border-color .2s}
.tf-series-list a:hover{color:var(--c-bg)}
.tf-series-list a[aria-current]{color:var(--c-bg);
  border-bottom-color:var(--c-accent)}
/* ON A PHONE THE BAR STOPS BEING STICKY, AND THAT IS THE WHOLE FIX.
   Wrapped into three rows at 390px it stood 126 pixels tall; under a 78-pixel
   masthead that is 204 pixels — a quarter of an 844-pixel screen — permanently
   occupied before any content, with the opening spread's headline sliding
   underneath it. The band pass caught it as a contrast failure, because the
   bright pixels behind the band's copy were this bar's own nav links; the
   contrast was the symptom and the height was the fault.

   Static, and one row that scrolls sideways. The series is still complete and
   still one gesture away; it simply no longer sits on top of the page. */
@media(max-width:760px){
  .tf-series{position:static;flex-wrap:nowrap;align-items:center;gap:0 18px;
    padding-left:20px;padding-right:20px;overflow-x:auto;
    scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .tf-series::-webkit-scrollbar{display:none}
  .tf-series-mark{margin-right:0;width:auto;flex:none}
  .tf-series-list{flex-wrap:nowrap;gap:0 18px}
  .tf-series-list li{flex:none}
}

/* A child page's opening. DELIBERATELY NOT THE BAND: the fixed-window effect
   belongs to the door and earns its cost once. Repeated on eight more pages it
   stops being an event and starts being a habit. Still photograph, hard scrim,
   type on top. */
.tf-top{position:relative;display:flex;align-items:flex-end;min-height:52svh;
  overflow:hidden;background:var(--fj-basalt)}
.tf-top.no-pic{min-height:auto;padding-top:var(--sp-6)}
.tf-top-pic{position:absolute;inset:0}
.tf-top-pic img{width:100%;height:100%;object-fit:cover;display:block}
/* The scrim is a sibling span rather than a filter on the picture: filter on
   an ancestor of a fixed element kills the band elsewhere on this page shape,
   and keeping one rule for both means nobody has to remember which is which. */
/* Heavier than it looks like it needs to be, on purpose. These photographs
   are chosen for the subject and not for a dark corner to set type in, and
   three of the five have open sky exactly where the eyebrow lands. The scrim
   has to hold ivory over the brightest frame in the set, not the average one —
   and this is the one contrast the automatic pass cannot see, because it walks
   up to the section's own background and never looks at the picture. */
.tf-top-tint{position:absolute;inset:0;background:linear-gradient(to top,
  color-mix(in srgb,var(--fj-basalt) 96%,transparent) 0%,
  color-mix(in srgb,var(--fj-basalt) 84%,transparent) 40%,
  color-mix(in srgb,var(--fj-basalt) 58%,transparent) 72%,
  color-mix(in srgb,var(--fj-basalt) 34%,transparent) 100%)}
.tf-top-in{position:relative;z-index:2;width:100%;max-width:var(--frame);
  margin:0 auto;padding:var(--sp-6) 44px var(--sp-5)}
.tf-top-eyebrow{font-family:var(--fj-mono);font-size:9.5px;letter-spacing:.24em;
  text-transform:uppercase;color:var(--c-accent-lit);margin:0}
.tf-top-h{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(34px,5.4vw,76px);line-height:.98;letter-spacing:-.022em;
  color:var(--c-bg);margin:14px 0 0;max-width:16ch}
.tf-top-sub{margin-top:18px;font-size:clamp(16px,1.5vw,20px);line-height:1.5;
  color:var(--fj-onbasalt);max-width:46ch}
@media(max-width:760px){.tf-top-in{padding-left:20px;padding-right:20px}}

/* The overview's own lead: the chain, straight under the band, because the
   door promises a crossing and this is the first thing that makes it real. */
.tf-lead{padding-top:var(--sp-5)}
.tf-lead-sub{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(22px,2.6vw,38px);line-height:1.04;letter-spacing:-.015em;
  color:var(--c-bg);margin:0;max-width:22ch}
.tf-lead-chain{margin-top:20px;font-family:var(--fj-mono);font-size:10px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--c-gold);
  line-height:2.1}
.tf-lead-chain a{color:inherit;border-bottom:1px solid transparent}
.tf-lead-chain a:hover{border-color:var(--c-gold)}
.tf-lead-go{margin-top:26px}

/* Reading pages — why, and a crossing's own account. One measure, no columns:
   these are the pages somebody actually reads rather than scans. */
.tf-read{max-width:60ch}
.tf-say{font-size:clamp(16px,1.5vw,19px);line-height:1.62;
  color:var(--fj-onbasalt);margin-top:20px}
.tf-read .tf-h2{color:var(--c-accent-lit)}

.tf-crossing-strands{font-family:var(--fj-mono);font-size:9.5px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--c-gold);margin:0}
.tf-crossing-say{margin-top:20px;font-size:clamp(17px,1.7vw,23px);line-height:1.5;
  color:var(--c-bg)}
.tf-crossing-chain{margin-top:24px;font-family:var(--fj-mono);font-size:10px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--fj-onbasalt-dim);
  line-height:2.1}
.tf-crossing-chain a{color:var(--c-gold);border-bottom:1px solid transparent}
.tf-crossing-chain a:hover{border-color:var(--c-gold)}
.tf-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:0 26px;margin-top:30px;border-top:var(--fj-rule-dark);padding-top:18px}
.tf-facts dt{font-family:var(--fj-mono);font-size:9px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--fj-onbasalt-dim)}
.tf-facts dd{margin:7px 0 18px;font-family:var(--fj-display);font-size:19px;
  color:var(--c-bg)}

/* The way on. A page in a series that ends without one has handed the reader
   back to the browser's Back button, which is the failure mode of every site
   that splits a long page and stops there. */
.tf-next{margin-top:var(--sp-6);padding-top:var(--sp-4);
  border-top:var(--fj-rule-dark);max-width:56ch}
.tf-next-say{font-size:17px;line-height:1.55;color:var(--fj-onbasalt-dim);
  margin:0 0 20px}

/* The table of contents, and the three questions a crossing raises. Same
   object twice because they do the same job: name the page, say what is on it,
   and go there. */
.tf-index,.tf-onward{margin-top:var(--sp-6);padding-top:var(--sp-4);
  border-top:var(--fj-rule-dark)}
.tf-index-h{font-family:var(--fj-mono);font-size:10px;font-weight:400;
  letter-spacing:.24em;text-transform:uppercase;color:var(--c-gold);margin:0}
.tf-index-list{list-style:none;margin:20px 0 0;padding:0;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:0 34px}
.tf-index-list a{display:grid;grid-template-columns:minmax(0,1fr) auto;
  align-items:baseline;gap:10px;padding:16px 0;
  border-bottom:1px solid color-mix(in srgb,var(--c-bg) 13%,transparent)}
.tf-index-list b{grid-column:1;font-family:var(--fj-display);font-weight:700;
  text-transform:uppercase;font-size:19px;line-height:1.1;color:var(--c-bg)}
.tf-index-list span{grid-column:1;font-size:14px;line-height:1.5;
  color:var(--fj-onbasalt-dim);margin-top:6px}
.tf-index-list i{grid-column:2;grid-row:1;font-style:normal;
  color:var(--c-accent-lit);transition:transform .2s}
.tf-index-list a:hover i{transform:translateX(4px)}

/* The route card is a way in now, not the whole record. */
.tf-route-name a{color:inherit;display:inline-block;min-height:24px}
.tf-route-name a:hover{color:var(--c-accent-lit)}
/* 24px tall like every other control here. The chain links above it are
   exempt — the criterion allows an inline link inside a sentence — but this is
   a standalone call to action in a div, and it was 17. */
.tf-route-go{display:inline-flex;align-items:center;min-height:24px;gap:8px;margin-top:20px;
  font-family:var(--fj-mono);font-size:9.5px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--c-accent-lit);
  border-bottom:1px solid color-mix(in srgb,var(--c-accent-lit) 45%,transparent)}
.tf-route-go:hover{border-bottom-color:var(--c-accent-lit)}

/* A page's first block sits under its own opening, not under two stacked
   spacers. .tf-page--after already pads the top of the column and .tf-block
   adds a margin of its own on top of that, which on the child pages put most
   of a screen of nothing between the headline and the first sentence. */
.tf-page--after > .tf-block:first-child,
.tf-page--after > .tf-next:first-child,
.tf-page--after > .tf-index:first-child{margin-top:0}

/* ===========================================================================
   THE CROSSINGS PLATE
   ---------------------------------------------------------------------------
   The map was a small SVG beside a legend, and it read as an infographic
   dropped onto a dark page. It is now the object the section is about: the
   continent takes roughly three fifths of the measure and the four journeys
   sit beside it as an editorial index rather than a key.

   THE INDEX IS NOT A LEGEND. A legend explains a picture; this one chooses
   what the picture shows. Which is also why the four route cards that used to
   repeat under the map are gone — they printed the same four names, the same
   four country chains and the same three figures one screen further down.

   ALL THE STATE IS `data-lit` ON THE FIGURE, set by scripts/crossings.js and
   read only here. Nothing below is applied from JavaScript, so the whole look
   can change without touching the script, and the map and the panel cannot
   disagree about what is selected.
   =========================================================================== */
.tf-atlas{margin:0;display:grid;gap:var(--sp-5);
  grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);align-items:start}
.tf-atlas-art{min-width:0;position:relative}
.tf-plate-svg{width:100%;height:auto;display:block;overflow:visible}
.tf-atlas-panel{margin:0;min-width:0}

/* ---- the continent ------------------------------------------------------ */
/* Two tones and one hairline. Everything Afrinkong does not cross is the
   quietest thing on the plate; the countries the roads enter come up a step
   and carry a border, so the road has something to run between. Picking every
   border out would make this an atlas index, and picking none out leaves the
   road crossing an undifferentiated shape. */
.tf-plate-rest path{fill:color-mix(in srgb,var(--c-bg) 10%,transparent);stroke:none}
.tf-plate-in path{fill:color-mix(in srgb,var(--c-bg) 19%,transparent);
  stroke:color-mix(in srgb,var(--c-bg) 27%,transparent);stroke-width:1.1;
  transition:fill .3s ease}

/* ---- the roads ---------------------------------------------------------- */
/* The halo is a wider stroke of the GROUND colour under each road, so a
   narrower crossing running along a wider one is separated by a hair of dark
   instead of merging into it. A filter would do the same job and is banned
   anywhere near this page — see docs/window-band.md. */
.tf-plate-halo{fill:none;stroke:var(--fj-basalt);stroke-linecap:round;
  stroke-linejoin:round;opacity:.85;transition:opacity .3s ease}
.tf-plate-road{fill:none;stroke-linecap:round;stroke-linejoin:round;
  transition:opacity .3s ease,stroke-width .3s ease}
/* Four accents, all cut from the palette, and the spine is the warmest and
   widest because it is the journey the other three are lengths of. */
.tf-plate-road[data-route="great"]{stroke:var(--c-gold)}
.tf-plate-road[data-route="south"]{stroke:#E0A24A}
.tf-plate-road[data-route="east"]{stroke:var(--c-accent-lit)}
.tf-plate-road[data-route="west"]{stroke:#7FA88B}

/* ---- nodes and termini -------------------------------------------------- */
.tf-plate-nodes circle{transition:opacity .3s ease,r .3s ease}
.tf-node{stroke:var(--fj-basalt);stroke-width:2}
.tf-node[data-route="great"]{fill:var(--c-gold)}
.tf-node[data-route="south"]{fill:#E0A24A}
.tf-node[data-route="east"]{fill:var(--c-accent-lit)}
.tf-node[data-route="west"]{fill:#7FA88B}
/* A ring, not a dot with a name. The anchor is a country's centroid, so a
   larger marker saying "the road starts here" is true and "NAIROBI" printed on
   it would be false by about two hundred kilometres. The city pair is in the
   panel, in words, claiming nothing about a position. */
.tf-term{fill:none;stroke-width:2.4;opacity:0}
.tf-term[data-route="great"]{stroke:var(--c-gold)}
.tf-term[data-route="south"]{stroke:#E0A24A}
.tf-term[data-route="east"]{stroke:var(--c-accent-lit)}
.tf-term[data-route="west"]{stroke:#7FA88B}

/* Country names: in the SVG for every road, shown for the chosen one. Drawn
   once and revealed by state rather than re-rendered, so the plate is whole
   with scripting off and selecting is a repaint and not a rebuild. */
.tf-name{font-family:var(--fj-mono);font-size:15px;letter-spacing:1.6px;
  fill:var(--fj-onbasalt);opacity:0;transition:opacity .3s ease;
  paint-order:stroke;stroke:var(--fj-basalt);stroke-width:3.5;
  stroke-linejoin:round}

/* ---- the terminus cities ------------------------------------------------ */
/* Four of the eight route ends are cities this repository holds a real
   position for, so those four are marked AT the city and named. The road runs
   on to meet them, which is the journey rather than a liberty: the Continental
   Expedition starts in Nairobi, not at Kenya's centroid. Arusha and Victoria
   Falls have no position and keep a plain country ring instead. */
.tf-town circle{fill:none;stroke:var(--c-bg);stroke-width:1.8;opacity:.55;
  transition:opacity .3s ease}
.tf-town .tf-town-pip{fill:var(--c-bg);stroke:none;opacity:.75}
.tf-town text{font-family:var(--fj-mono);font-size:15px;letter-spacing:1.8px;
  fill:var(--c-bg);opacity:.62;transition:opacity .3s ease;
  paint-order:stroke;stroke:var(--fj-basalt);stroke-width:3.5;
  stroke-linejoin:round}

/* ---- the furniture ------------------------------------------------------ */
/* What makes it a plate rather than a diagram, and every piece is a fact or a
   derivation: the oceans and the continent are named because they are, the
   rose points north because the projection does, and the bar is computed at
   the latitude it is drawn at — see furniture() for why a continental scale
   that does not say its latitude is not true anywhere in particular. */
.tf-sea{font-family:var(--fj-mono);font-size:15px;letter-spacing:4px;
  fill:color-mix(in srgb,var(--c-bg) 30%,transparent)}
.tf-land{font-family:var(--fj-mono);font-size:26px;letter-spacing:16px;
  fill:color-mix(in srgb,var(--c-gold) 34%,transparent)}
.tf-rose line,.tf-rose circle{stroke:color-mix(in srgb,var(--c-bg) 34%,transparent);
  stroke-width:1.4;fill:none}
.tf-rose text{font-family:var(--fj-mono);font-size:14px;
  fill:color-mix(in srgb,var(--c-bg) 46%,transparent)}
.tf-scale line{stroke:color-mix(in srgb,var(--c-bg) 40%,transparent);stroke-width:1.6}
.tf-scale text{font-family:var(--fj-mono);font-size:13px;letter-spacing:1px;
  fill:color-mix(in srgb,var(--c-bg) 46%,transparent)}
.tf-scale .tf-scale-at{font-size:11px;
  fill:color-mix(in srgb,var(--c-bg) 30%,transparent)}

/* ---- what selection does ------------------------------------------------ */
/* Nothing until the script says the figure is live: with no JavaScript the
   plate shows all four roads at full strength and the panel lists all four
   journeys open, which is a complete answer rather than a degraded one. */
.tf-atlas[data-lit] .tf-plate-road:not([data-route]),
.tf-atlas[data-lit] .tf-plate-road{opacity:.16}
.tf-atlas[data-lit] .tf-plate-halo{opacity:.35}
.tf-atlas[data-lit] .tf-node{opacity:.18}
/* A country that is not on the chosen road falls back to the rest of the
   continent's tone — not below it. Dimmed past the ground it sits on, an
   unselected crossing reads as a hole cut in Africa. */
.tf-atlas[data-lit] .tf-plate-in path{fill:color-mix(in srgb,var(--c-bg) 10%,transparent)}
.tf-atlas[data-lit="east"] .tf-plate-road[data-route="east"],
.tf-atlas[data-lit="west"] .tf-plate-road[data-route="west"],
.tf-atlas[data-lit="south"] .tf-plate-road[data-route="south"],
.tf-atlas[data-lit="great"] .tf-plate-road[data-route="great"]{opacity:1}
.tf-atlas[data-lit="east"] .tf-node[data-in~="east"],
.tf-atlas[data-lit="west"] .tf-node[data-in~="west"],
.tf-atlas[data-lit="south"] .tf-node[data-in~="south"],
.tf-atlas[data-lit="great"] .tf-node[data-in~="great"]{opacity:1;r:5.6}
.tf-atlas[data-lit="east"] .tf-plate-in path[data-in~="east"],
.tf-atlas[data-lit="west"] .tf-plate-in path[data-in~="west"],
.tf-atlas[data-lit="south"] .tf-plate-in path[data-in~="south"],
.tf-atlas[data-lit="great"] .tf-plate-in path[data-in~="great"]{
  fill:color-mix(in srgb,var(--c-bg) 24%,transparent)}
.tf-atlas[data-lit="east"] .tf-term[data-route="east"],
.tf-atlas[data-lit="west"] .tf-term[data-route="west"],
.tf-atlas[data-lit="south"] .tf-term[data-route="south"],
.tf-atlas[data-lit="great"] .tf-term[data-route="great"]{opacity:1}
.tf-atlas[data-lit="east"] .tf-name[data-in~="east"],
.tf-atlas[data-lit="west"] .tf-name[data-in~="west"],
.tf-atlas[data-lit="south"] .tf-name[data-in~="south"],
.tf-atlas[data-lit="great"] .tf-name[data-in~="great"]{opacity:1}
/* A terminus city that is not on the chosen road goes quiet with the road. */
.tf-atlas[data-lit] .tf-town circle,
.tf-atlas[data-lit] .tf-town text{opacity:.14}
.tf-atlas[data-lit="east"] .tf-town[data-in~="east"] circle,
.tf-atlas[data-lit="west"] .tf-town[data-in~="west"] circle,
.tf-atlas[data-lit="south"] .tf-town[data-in~="south"] circle,
.tf-atlas[data-lit="great"] .tf-town[data-in~="great"] circle{opacity:.9}
.tf-atlas[data-lit="east"] .tf-town[data-in~="east"] text,
.tf-atlas[data-lit="west"] .tf-town[data-in~="west"] text,
.tf-atlas[data-lit="south"] .tf-town[data-in~="south"] text,
.tf-atlas[data-lit="great"] .tf-town[data-in~="great"] text{opacity:1}

/* ---- the panel ---------------------------------------------------------- */
.tf-atlas-head{margin-bottom:var(--sp-4)}
.tf-atlas-h{font-family:var(--fj-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(22px,2.2vw,32px);line-height:1.04;letter-spacing:-.015em;
  color:var(--c-bg);margin:0;max-width:16ch}
.tf-atlas-say{margin:14px 0 0;font-size:15px;line-height:1.6;
  color:var(--fj-onbasalt-dim);max-width:44ch}

.tf-picks{list-style:none;margin:0;padding:0;border-top:var(--fj-rule-dark)}
.tf-pick{border-bottom:1px solid color-mix(in srgb,var(--c-bg) 13%,transparent)}
/* A row, not a card. Thin rules and type, the way an index in a book is set;
   four boxes with borders would be the "collection of coloured cards" this
   section is explicitly not. */
.tf-pick-hit{display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:baseline;gap:4px 12px;width:100%;padding:18px 0;
  background:none;border:0;text-align:left;cursor:pointer;
  font:inherit;color:inherit}
.tf-pick-dot{grid-row:1;width:9px;height:9px;border-radius:50%;
  align-self:center;transition:transform .25s ease}
.tf-pick[data-route="great"] .tf-pick-dot{background:var(--c-gold)}
.tf-pick[data-route="south"] .tf-pick-dot{background:#E0A24A}
.tf-pick[data-route="east"] .tf-pick-dot{background:var(--c-accent-lit)}
.tf-pick[data-route="west"] .tf-pick-dot{background:#7FA88B}
.tf-pick-name{grid-row:1;font-family:var(--fj-display);font-weight:700;
  font-size:clamp(16px,1.5vw,20px);line-height:1.15;color:var(--c-bg);
  transition:color .2s ease}
.tf-pick-n{grid-row:1;font-family:var(--fj-mono);font-size:9px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--fj-onbasalt-dim);
  white-space:nowrap}
.tf-pick-where{grid-row:2;grid-column:2/-1;margin-top:7px;font-size:13.5px;
  line-height:1.55;color:var(--fj-onbasalt-dim)}
/* The flagship is the one the other three are lengths of, and the index says
   so in weight rather than in a badge. */
.tf-pick.is-great .tf-pick-name{font-size:clamp(18px,1.7vw,23px)}
.tf-pick-hit:hover .tf-pick-name,
.tf-pick-hit:focus-visible .tf-pick-name{color:var(--c-accent-lit)}
.tf-pick-hit:hover .tf-pick-dot{transform:scale(1.35)}
.tf-pick-hit[aria-current] .tf-pick-name{color:var(--c-accent-lit)}
.tf-pick-hit:focus-visible{outline:2px solid var(--c-accent-lit);outline-offset:3px}

.tf-sum{padding:0 0 20px}
.tf-sum-say{margin:0;font-size:15px;line-height:1.6;color:var(--fj-onbasalt);
  max-width:44ch}
/* Shape takes the whole row. "Nairobi to Arusha, the long way round." is a
   sentence and the other two are a number each; in three equal columns the
   sentence broke over three lines beside two that used one. */
.tf-sum-facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:0 20px;margin:18px 0 0}
.tf-sum-facts>div:first-child{grid-column:1/-1}
.tf-sum-facts dt{font-family:var(--fj-mono);font-size:8.5px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--fj-onbasalt-dim)}
.tf-sum-facts dd{margin:6px 0 12px;font-family:var(--fj-display);font-size:16px;
  color:var(--c-bg)}
.tf-sum-go{display:inline-flex;align-items:center;min-height:24px;gap:8px;
  margin-top:6px;font-family:var(--fj-mono);font-size:9.5px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--c-accent-lit);
  border-bottom:1px solid color-mix(in srgb,var(--c-accent-lit) 45%,transparent)}
.tf-sum-go:hover{border-bottom-color:var(--c-accent-lit)}

/* The accordion exists only once the script says so. Before that every summary
   is open and the buttons control nothing, which is why the markup ships
   aria-expanded="true" and the script is what makes it false. */
.tf-atlas.is-live .tf-sum{display:none}
.tf-atlas.is-live .tf-pick-hit[aria-expanded="true"]+.tf-sum{display:block}

.tf-atlas-go{margin-top:var(--sp-4)}

@media(prefers-reduced-motion:reduce){
  .tf-plate-road,.tf-plate-halo,.tf-plate-in path,.tf-plate-nodes circle,
  .tf-name,.tf-pick-dot,.tf-pick-name{transition:none}
}

/* Tablet: the plate stays the dominant object and the index moves under it,
   two columns wide so four journeys are still one glance. */
@media(max-width:1000px){
  .tf-atlas{grid-template-columns:minmax(0,1fr);gap:var(--sp-4)}
  .tf-atlas-head{margin-bottom:var(--sp-3)}
  .tf-picks{columns:2;column-gap:var(--sp-4)}
  .tf-pick{break-inside:avoid}
  .tf-name{font-size:19px;letter-spacing:2px}
  .tf-town text{font-size:18px}
}
/* Phone: one column, and the country names come up again because the plate is
   now 390 pixels wide rather than 700 and 15px of viewBox type is four. */
@media(max-width:640px){
  .tf-picks{columns:1}
  .tf-name{font-size:26px;letter-spacing:2.6px;stroke-width:5}
  .tf-town text{font-size:24px;stroke-width:5}
  .tf-sea{font-size:22px;letter-spacing:5px}
  .tf-land{font-size:34px;letter-spacing:20px}
  .tf-scale text{font-size:18px}.tf-rose text{font-size:19px}
  .tf-plate-road{stroke-width:inherit}
  /* The countries come before the count of them. Wrapped the other way round
     the row read "4 COUNTRIES / Kenya, Uganda, Rwanda, Tanzania", which is the
     caption above the thing it captions. */
  .tf-pick-where{grid-row:2;grid-column:1/-1}
  .tf-pick-n{grid-row:3;grid-column:1/-1;margin-top:8px}
}

/* The Journey Fund's one line on a crossing card.
   Set at the fine-print size and in the muted ink, because it is a footnote to
   the fee above it rather than a second offer competing with it. The link
   carries the accent the rest of this page uses for a way onward, so it reads
   as the same kind of thing as "See this crossing" without borrowing its
   weight. */
.tf-route-kept{margin:14px 0 0;font-size:13.5px;line-height:1.5;
  color:var(--c-muted)}
.tf-route-kept a{color:var(--c-accent-lit)}

/* The Journey Fund's one line, directly under a crossing's fee.
   Fine-print size and muted ink: a footnote to the figure above it rather
   than a second offer competing with it. The link takes the accent this page
   already uses for a way onward, so it reads as the same kind of thing as
   "See this crossing" without borrowing its weight. */
.tf-kept{margin:18px 0 0;font-size:13.5px;line-height:1.55;color:var(--c-muted);
  max-width:56ch}
.tf-kept a{color:var(--c-accent-lit)}

/* The plate on the door page. It spans both columns under the claim and the
   paragraph, because the sentence above it is about the whole continent and a
   drawing of the whole continent in a half column is a thumbnail. Capped
   rather than full-bleed: past about 760 the roads stop gaining anything and
   the section starts being a map with a paragraph attached. */
.tf-idea-map{grid-column:1/-1;margin:var(--sp-4) 0 0;max-width:760px}
.tf-idea-map svg{display:block;width:100%;height:auto}
.tf-idea-map figcaption{margin-top:14px;font-family:var(--fj-mono);font-size:9.5px;
  letter-spacing:.18em;text-transform:uppercase;color:var(--fj-onbasalt-dim)}
