/* Previse: warm editorial ground, emerald interaction system, flat forecast graphics. */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: rgba(14, 107, 82, .14);
  scroll-behavior: smooth;
  /* Flush, not offset. A section that carries its own top rule was landing 18px
     under the bar's rule, so every anchor jump ended on two parallel hairlines
     close enough to read as a printing error. Landed flush they coincide. */
  scroll-padding-top: var(--nav-height);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16.5px;
  font-weight: 400;
  line-height: 1.72;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, svg, canvas { display: block; max-width: 100%; }
h1, h2, h3, p, dl, dd, ol, ul, figure { margin: 0; }
a { color: inherit; }

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: 1px;
}

/* Scroll destinations, not tab stops. Focus is moved here so a keyboard reader carries
   on from the section they asked for instead of from the top of the document; both are
   page-width, so a ring around one is noise rather than a cue. */
main:focus-visible,
[data-section]:focus-visible { box-shadow: none; }

.skip-link {
  position: absolute;
  /* viewport-fit=cover is set on all three pages, so a sideways phone puts 44 to 59px of
     sensor housing over the left edge and the first third of this link was behind it.
     .shell and the menu panel already respect the inset. */
  top: 10px; left: max(10px, env(safe-area-inset-left));
  z-index: 60;
  transform: translateY(-180%);
  /* 13, not 10. At 12px uppercase the label's own line box is 19.9, which left the first
     control in the document at 39.9 tall. It is reached by keyboard rather than thumb,
     but it is still a control and the rest of the site is built to 44. */
  padding: 13px 16px;
  background: var(--emerald-deep);
  color: var(--ink-reverse);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-decoration: none;
}

.skip-link:focus { transform: translateY(0); }

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  /* viewport-fit=cover is set on every page, so the inset has to be respected in
     landscape on a notched phone. In portrait the page padding is already wider. */
  padding-inline: max(var(--page-pad), env(safe-area-inset-left)) max(var(--page-pad), env(safe-area-inset-right));
}

/* Type roles. */

.display {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -.014em;
  line-height: 1.1;
  font-size: clamp(30px, 3.5vw, 50px);
  /* Playfair defaults to old-style figures, where the zero sits at x-height and the one has
     no ascender. At body size that is the nicer setting. At 50px in a headline, "$100B"
     comes out as "$1ooB". */
  font-variant-numeric: lining-nums;
}

/* A quiet label, not a marker. Tracked caps in the accent meant this was doing two
   jobs the page had already given to something else: caps otherwise mean a filled
   control, and the accent otherwise means a link, a fill or the picked path in the
   figure. Counted on one phone screen, emerald was carrying six roles and tracked caps
   three. Set as a kicker it names its section and stops competing with the headline
   under it. It also used to carry a 24px green tick, which gave it the same silhouette
   as an underlined text action. */
.eyebrow {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--text-muted);
  line-height: 1;
}

.lead {
  font-size: clamp(17px, 1.35vw, 19.5px);
  line-height: 1.62;
  color: var(--ink-soft);
}

.prose p + p { margin-top: 1.15em; }


/* Marks and lockup. */

.lockup {
  display: inline-flex;
  align-items: center;
  gap: clamp(12px, 1.1vw, 17px);
  text-decoration: none;
  color: var(--ink);
}

/* Drawn inline rather than loaded as an <img>, the way the A in the wordmark and the
   figure already are. An <img> keeps its own colours in forced-colors mode, and the
   deep emerald in the upper chevron measured 1.88:1 against a black canvas, under the
   3:1 a graphic carrying meaning has to hold. Inline, both chevrons take their fill
   from here, and the reverse pair is a colour change rather than a second file. */
.lockup-mark {
  flex: none;
  width: auto;
  height: clamp(36px, 3.8vw, 55px);
  aspect-ratio: 96 / 172;
}

.lockup-up { fill: var(--emerald-deep); }
.lockup-low { fill: var(--sage); }

.lockup-name {
  font-size: clamp(15px, 1.52vw, 22px);
  font-weight: 400;
  letter-spacing: .3em;
  margin-right: -.3em;
  white-space: nowrap;
  line-height: 1;
}

.lockup-a {
  display: inline-block;
  height: .7em;
  width: auto;
  fill: currentColor;
  /* The SVG box carries no sidebearings, so restore them by hand: the negative
     left margin lands the apex where Urbanist's own A would sit. */
  margin-left: -.022em;
  margin-right: .3em;
  vertical-align: baseline;
}

/* Navigation. */

.nav {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(250, 248, 246, .92);
  background: color-mix(in srgb, var(--ground) 92%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  transition: background-color .22s var(--ease-out);
}

/* The bar takes the ground it is standing on. Cream at 92% over deep ground reads as a
   band of fog across the top of the screen, and the sections that put it there are the
   dark ones: the statement band, the advertisers pitch on a phone, the closing band, and
   the whole of the 404. The statement band asks for a full screen of dark and was getting
   90% of one with a pale cap. Script sets the attribute by reading what is actually under
   the bar's lower edge, so a section that is dark only under a media query is handled by
   the same rule as one that is always dark. Without script the bar stays cream, which is
   what it has always been. */
/* Opaque, where the paper bar is translucent. A translucent dark bar composites against
   whatever is actually painted under its own rectangle, and where a dark section begins
   flush with the bar that is the page background, not the section: on the 404 it came out
   several steps lighter than the headline field two pixels below it and read as a mismatched
   strip. Solid, it is the same ink as every dark ground on the site and the band it caps
   runs to the top edge of the screen. The hairline stays, because content still passes
   under this bar and the line is what says so. */
.nav[data-over="deep"] { background: var(--ground-deep); }
.nav[data-over="deep"] .nav-in::after { background: var(--hairline-reverse); }
.nav[data-over="deep"] .lockup { color: var(--ink-reverse); }
.nav[data-over="deep"] .lockup-up { fill: var(--ink-reverse); }
.nav[data-over="deep"] .lockup-low { fill: var(--sage-lift); }
.nav[data-over="deep"] .nav-links a { color: var(--text-reverse); }
.nav[data-over="deep"] .nav-links a::after { background: var(--emerald-lift); }
.nav[data-over="deep"] .nav-links a[aria-current] { color: var(--ink-reverse); }
.nav[data-over="deep"] .nav-burger span { background: var(--ink-reverse); }
/* The filled action keeps its own fill and only its rule changes, because emerald on deep
   ground is the same pairing the closing band and the menu panel already use. */
.nav[data-over="deep"] .nav-cta { border-color: var(--emerald-lift-rule); }

/* Restated, because the rules above are a class deeper than the pressed states further
   down the file and would otherwise leave the burger and the home link with no feedback
   on exactly the screens where the only route to navigation is the burger. The lift, not
   the deep: deep emerald measures 1.60:1 on this ground. */
.nav[data-over="deep"] .lockup:active { color: var(--emerald-lift); }
.nav[data-over="deep"] .lockup:active .lockup-up,
.nav[data-over="deep"] .lockup:active .lockup-low { fill: var(--emerald-lift); }
.nav[data-over="deep"] .nav-burger:active span { background: var(--emerald-lift); }
.nav[data-over="deep"] .nav-links a:active { color: var(--emerald-lift); }
@media (hover: hover) {
  .nav[data-over="deep"] .nav-links a:hover { color: var(--emerald-lift); }
}

.nav-in {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 56px);
  height: var(--nav-height);
  position: relative;
}

/* Full bleed. Every other structural join on the page (section to section, paper to
   dark band) runs edge to edge and only dividers inside a block are inset to the
   gutter. The bar is a structural edge, and drawn with the content rule it was the
   one boundary on the page that disagreed with the system, on every screen. */
.nav-in::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--hairline);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(18px, 2.6vw, 46px);
  margin-left: auto;
}

.nav-links a {
  position: relative;
  font-size: 15px;
  color: var(--ink-soft);
  text-decoration: none;
  padding-block: 6px;
  transition: color .2s var(--ease-out);
}

.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--emerald);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .28s var(--ease-out);
}

/* Every :hover on this stylesheet is gated on a device that has one. Ungated, a tap leaves
   the pointed-at state latched: the hero's action stayed on the hover fill with its arrow
   4px right of rest while the identical closing-band control sat at rest, which is the
   same one-control-in-two-greens split the note above exists to close. The primary action
   is a mailto, so the user comes back from Mail to exactly that. :active is never gated. */
.nav-links a:active { color: var(--emerald-deep); }
.nav-links a[aria-current]::after { transform: scaleX(1); }
@media (hover: hover) {
  .nav-links a:hover { color: var(--emerald-deep); }
  .nav-links a:hover::after { transform: scaleX(1); }
}
.nav-links a[aria-current] { color: var(--emerald-deep); }

.nav-cta { margin-left: clamp(12px, 1.6vw, 26px); }

.nav-burger {
  display: none;
  width: 40px; height: 40px;
  margin-left: auto;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.nav-burger span {
  display: block;
  width: 22px; height: 1px;
  margin: 5px auto;
  background: var(--ink);
}

/* Actions. */

.arrow {
  --arrow-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 9'%3E%3Cpath d='M0 4.5h21.4M17.6 1l3.8 3.5-3.8 3.5' fill='none' stroke='%23000' stroke-width='1.1' stroke-linecap='square'/%3E%3C/svg%3E");
  flex: none;
  width: 22px;
  height: 9px;
  background: currentColor;
  transition: transform .28s var(--ease-out);
  -webkit-mask: var(--arrow-src) no-repeat center / contain;
  mask: var(--arrow-src) no-repeat center / contain;
}

.button {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  padding: 15px 24px;
  background: var(--emerald-deep);
  color: var(--ink-reverse);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .15em;
  text-transform: uppercase;
  text-decoration: none;
  border: 0;
  transition: background .24s var(--ease-out);
}

.button--compact { padding: 13px 20px; font-size: 11px; gap: 13px; }
.button--compact .arrow { width: 18px; }
/* Pressed, not just pointed at. Every state on the page was written for a cursor, so
   on a phone nothing answered a tap: the primary action is a mailto, and the wait for
   the mail app to come up read as a dead button. */
.button:active { background: var(--emerald-hover); }
.button:active .arrow { transform: translateX(2px); }
@media (hover: hover) {
  .button:hover { background: var(--emerald-hover); }
  .button:hover .arrow { transform: translateX(4px); }
}

/* Sentence case. Tracked caps were the only emphasis the page had, and on a phone
   an eyebrow, an action and the wordmark landed in one screen, all three of them
   caps and two of them green. Caps now mean a filled control and nothing else; a
   sentence-case word over a rule reads as a link, which is what these are. The
   footer headings were taken out of caps for the same reason. */
.text-action {
  display: inline-flex;
  align-items: center;
  gap: 15px;
  color: var(--emerald-deep);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0;
  text-decoration: none;
  padding-bottom: 9px;
  position: relative;
}

/* Inside the hero the two actions are a pair of controls, so the second one keeps
   the button's case and tracking. Anywhere else it is a link in a section. */
.hero-actions .text-action {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .15em;
  text-transform: uppercase;
}

/* The arrow belongs to the outline-button treatment, which only exists on a portrait
   phone. Everywhere else this is an underlined link, and the rule ran 37px past the
   last letter to clear an arrow, which reads as a hairline that overshot. */
.hero-actions .text-action .arrow { display: none; }

/* Full width at rest. The rule used to stop 37px short to leave room for a trailing
   arrow and close on hover, which meant a touch screen only ever saw the short state.
   Arrows now mark the three places the page asks for a decision, and the inline
   actions are carried by the underline alone. */
.text-action::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
}

.text-action { transition: color .25s var(--ease-out); }
.text-action:active { color: var(--emerald); }
.text-action:active .arrow { transform: translateX(2px); }
@media (hover: hover) {
  .text-action:hover { color: var(--emerald); }
  .text-action:hover .arrow { transform: translateX(4px); }
}

/* The stippled ridge field. */

.ridge {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: clamp(215px, 42vh, 420px);
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.1s ease-out;
}

.ridge[data-painted] { opacity: 1; }

.ridge--reverse {
  --dot-tone: var(--dot-tone-reverse);
  --dot-gain: var(--dot-gain-reverse);
  height: clamp(110px, 16vh, 190px);
}

/* The closing band is short, so its terrain sits lower than the hero's. */
.ridge--closing { height: clamp(140px, 24vh, 250px); }

/* The hero's terrain is the one ridge that has something sitting on top of it. Once
   the layout stacks, the figure is directly above the crest and the two run into each
   other: at 390 the ridge was climbing 148px into the drawing, so Purchase and Install
   read as labels floating in speckle. Height and the hero's bottom pad both come off
   --hero-terrain, which keeps the clearance between them fixed as the screen changes.
   Doubled up so it outranks the plain .ridge heights the breakpoints below still set. */
.ridge.ridge--hero { height: var(--hero-terrain, clamp(215px, 42vh, 420px)); }

/* Hero. */

.hero {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-start;
  padding-block: clamp(36px, 5vh, 64px) clamp(120px, 22vh, 230px);
  /* Fill exactly to the fold so the ridge runs off the bottom edge rather than
     stopping short of it. vh first for browsers that do not know svh, where the
     declaration would otherwise drop and take the min-height with it. */
  min-height: calc(100vh - var(--nav-height));
  min-height: calc(100svh - var(--nav-height));
}

.hero-layout {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr);
  align-items: center;
  gap: clamp(44px, 4.6vw, 86px);
  width: 100%;
}

.hero h1 {
  margin-top: 18px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(44px, 5.92vw, 118px);
  line-height: .95;
  letter-spacing: -.019em;
  color: var(--ink);
}

.hero h1 span { display: block; }
.hero h1 .accent { color: var(--emerald-deep); }

/* Optical left edge. Playfair's cap S carries more left sidebearing than the h and n
   below it, so the three lines set flush measured 26 / 23 / 24 against body copy at
   23 and the buttons at 22: the largest thing on the page was the only thing not on
   the column. Set in em so it holds from 44px to 118px. Keyed to these initials. */
.hero h1 span:first-child { margin-left: -.044em; }
.hero h1 .accent { margin-left: -.015em; }

.hero-body {
  margin-top: clamp(20px, 1.9vw, 28px);
  max-width: 42ch;
  font-size: 15.5px;
  line-height: 1.56;
}

.hero-body p + p { margin-top: .9em; }

.hero-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(18px, 2.3vw, 36px);
  margin-top: clamp(24px, 2.5vw, 34px);
}

.hero-figure {
  display: flex;
  justify-content: center;
  min-width: 0;
}

/* Section rhythm. */

.quality, .loop, .audience, .about {
  position: relative;
  padding-block: clamp(68px, 8.4vw, 128px);
}

.about { padding-block: clamp(56px, 6.4vw, 96px); }

/* One rule left on the page, at the turn from the two audience sections to what the company
   is. It used to sit on .audience too, which put a hairline on one of the two 88px seams and
   one of the three 120px seams: the advertisers/publishers pair then opened on a rule and
   closed without one, and the surviving 88 sat immediately before the unruled 88 it was
   meant to match. Unruled seams are now always the 88s. */
.about { border-top: 1px solid var(--hairline); }
.audience--publishers { padding-top: 0; }
/* No rule under the hero. The terrain runs to the bottom of that section and a hairline
   there cuts the stipple off dead straight, which is the one thing a drawn field must
   never do. The change of section is legible from the terrain ending on its own. */

.split {
  display: grid;
  grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  gap: clamp(32px, 6vw, 110px);
  align-items: start;
}

.split .display { max-width: 12ch; }
.split .prose { max-width: 60ch; }
.split .text-action { margin-top: 30px; }

/* The one dark band. */

.thesis {
  position: relative;
  overflow: hidden;
  background: var(--ground-deep);
  color: var(--text-reverse);
  padding-block: clamp(84px, 11vw, 168px) clamp(190px, 24vh, 280px);
}

.thesis-in { position: relative; z-index: 1; }

/* No top margin: this is the first thing in the band now that the eyebrow is gone, and the
   band centres its own content, so a leading margin only pushed the block below centre. */
.thesis .display {
  color: var(--ink-reverse);
  max-width: 20ch;
  font-size: clamp(32px, 4.4vw, 66px);
  line-height: 1.06;
}

/* One measure, stacked, not the two side-by-side columns this used to be. The band's two
   paragraphs are no longer parallel statements about two audiences: the first makes the claim
   and the second gives the precedent behind it. Set side by side, a one-line claim next to a
   four-line argument read as a column that had lost its content. */
.thesis-say {
  margin-top: clamp(48px, 6vw, 84px);
  max-width: 54ch;
}

.thesis-say p + p { margin-top: 1.3em; }

/* The training loop. */

.loop-rail {
  position: relative;
  margin-top: clamp(48px, 5.6vw, 84px);
}

.loop-steps {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(22px, 3vw, 54px);
  list-style: none;
  padding: 0;
  border-top: 1px solid var(--hairline-firm);
}

.loop-steps li { padding-top: 26px; }

.loop-steps h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(21px, 1.9vw, 27px);
  line-height: 1.2;
  color: var(--ink);
  margin-bottom: 10px;
}

.loop-steps p { font-size: 15.5px; line-height: 1.66; max-width: 30ch; }


/* Advertisers and publishers. */

.audience-in {
  display: grid;
  grid-template-columns: minmax(0, 190px) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 68px);
  align-items: start;
}

.audience .display { max-width: 21ch; }
.audience .lead { margin-top: 24px; max-width: 56ch; }
.audience-body > p:not(.lead) { margin-top: 1.1em; max-width: 62ch; }

.points {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: clamp(26px, 3.4vw, 60px);
  margin-top: clamp(38px, 4vw, 58px);
  max-width: 960px;
}

.points dt {
  font-family: var(--font-display);
  font-size: 20px;
  color: var(--ink);
  margin-bottom: 9px;
}

.points dd { margin: 0; font-size: 15.5px; line-height: 1.68; }

.audience .text-action { margin-top: clamp(34px, 3.6vw, 52px); }

/* Closing. */

.closing {
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--hairline);
  padding-block: clamp(78px, 10vw, 140px) clamp(150px, 20vh, 260px);
}

.closing-in {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
  gap: clamp(32px, 5vw, 96px);
  align-items: end;
}

.closing .display { font-size: clamp(34px, 4.6vw, 62px); line-height: 1.06; max-width: 13ch; }
.closing p { max-width: 40ch; }
.closing .button { margin-top: 34px; }

/* Footer. */

.footer {
  border-top: 1px solid var(--hairline);
  padding-block: clamp(48px, 5vw, 76px) 32px;
}

.footer-main {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  gap: clamp(30px, 4vw, 64px);
}

.footer-brand p {
  margin-top: 22px;
  max-width: 30ch;
  font-size: 15px;
  color: var(--text-muted);
}

.footer-col { display: flex; flex-direction: column; align-items: flex-start; gap: 11px; }

/* Not letter-spaced caps. Three of them landed in the same viewport as two more
   sets of caps on a phone, which is texture rather than seasoning, and at 10.5px
   with .2em tracking they were the hardest thing on the page to scan. */
.footer-col h2 {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 6px;
}

.footer-col a {
  font-size: 15px;
  color: var(--text);
  text-decoration: none;
  /* jack@previselabs.com is one 20-character token with no break opportunity in it.
     Under the text-spacing metrics a reader is entitled to set, it grew past its
     column and took the whole document sideways: 31px of horizontal scroll at 320,
     12 at 360, 4 at 375. Same treatment the careers contact line already gets. */
  overflow-wrap: anywhere;
  transition: color .2s var(--ease-out);
}

/* Deep on hover, core when pressed, which is what .text-action already does. Held at deep
   for both, the pressed state was a 1.095:1 change against the resting grey: the faintest
   feedback on the site, and fainter than the tap highlight this file rejects as inadequate
   two rules below. The core green measures 1.577:1. */
.footer-col a:active { color: var(--emerald); }
@media (hover: hover) { .footer-col a:hover { color: var(--emerald-deep); } }

/* The three controls that had no pressed state. Their only feedback was the tap
   highlight, which composites to a 1.19:1 change on paper, and one of them is the
   only route to navigation on a phone. */
.lockup:active { color: var(--emerald-deep); }
.nav-burger:active span { background: var(--emerald-deep); }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  margin-top: clamp(48px, 5vw, 78px);
  padding-top: 24px;
  border-top: 1px solid var(--hairline);
  font-size: 13px;
  color: var(--text-muted);
}

/* Interior pages. */

/* Interior editorial page: heading block, banded sections, role list, closing line. */
.page-head {
  position: relative;
  overflow: hidden;
  padding-block: clamp(50px, 6vw, 92px) clamp(120px, 17vh, 210px);
}

.page-head-in { position: relative; z-index: 1; }
.page-head .ridge { height: clamp(120px, 20vh, 230px); }
.page-head h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(37px, 5.4vw, 68px);
  line-height: 1.06;
  letter-spacing: -.018em;
  color: var(--ink);
}
.page-head h1 .accent { color: var(--emerald-deep); }
.page-head .lead p + p { margin-top: 1.05em; }
.page-band { border-top: 1px solid var(--hairline); padding-block: clamp(56px, 7vw, 110px); }
/* Same reason the hero has no rule under it: the page head carries a terrain field, and a
   hairline a few dozen pixels below where the dots run out reads as a lid on them. The
   rule stays between the bands that follow, which have nothing above them but type. */
.page-head + .page-band { border-top: 0; }
.role-list { margin-top: clamp(30px, 3.4vw, 52px); }
.role {
  display: grid;
  grid-template-columns: minmax(0, 268px) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 68px);
  align-items: start;
  padding-block: clamp(32px, 3.4vw, 54px);
  border-top: 1px solid var(--hairline);
}
.role:first-child { border-top-color: var(--hairline-firm); }
.role:last-child { padding-bottom: 0; }
.role h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(26px, 2.6vw, 38px);
  line-height: 1.08;
  color: var(--ink);
}
.role-kicker { margin-top: 11px; font-size: 14.5px; color: var(--text-muted); }
.role-body p { max-width: 62ch; }
.role-body p + p { margin-top: 1.05em; }
.role-body .text-action { margin-top: clamp(26px, 2.6vw, 38px); }
.contact-line {
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--hairline);
  padding-block: clamp(52px, 6vw, 86px) clamp(160px, 22vh, 260px);
}

.contact-line .ridge { height: clamp(120px, 18vh, 200px); }
.contact-line p { position: relative; z-index: 1; max-width: 54ch; overflow-wrap: anywhere; }
/* Inline in a sentence, and it is this page's only way to make contact, so it gets a
   box rather than the 19px its own type gives it. Left inline, not inline-block: vertical
   padding on an inline box is hit-tested and painted without entering the line box, so the
   target is 35px either way. As inline-block it was inflating its own line to 42.5px inside
   a 26.56px leading, which opened the third and fourth lines of a four-line paragraph by
   30% and left the darkest band on the page with uneven leading in its only copy. */
.contact-line a { padding-block: 13px; color: var(--emerald-deep); text-decoration-color: var(--emerald); text-underline-offset: 4px; }

/* Not found: one serif line, one sentence, one way back. */
.page-miss {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  min-height: min(560px, calc(100vh - var(--nav-height) - 60px));
  min-height: min(560px, calc(100svh - var(--nav-height) - 60px));
  padding-block: clamp(64px, 11vh, 130px) clamp(150px, 22vh, 250px);
}

.page-miss-in { position: relative; z-index: 1; }
.page-miss h1 { font-size: clamp(34px, 4.4vw, 58px); max-width: 12ch; }
.page-miss p { margin-top: clamp(20px, 2vw, 28px); max-width: 42ch; }
.page-miss .text-action { margin-top: clamp(28px, 3vw, 40px); }

/* Mobile menu. */

.mobile-menu {
  position: fixed;
  inset: 0;
  /* iOS sizes a fixed box to the large viewport, so with inset alone the panel runs
     under Safari's own chrome and the one thing it is for ends up below the screen.
     dvh tracks the toolbar: pinned to svh the panel stopped 86px short of the glass the
     moment the bars retracted, and the page showed through underneath, including the
     hero's underlined action, which looked live while main was inert.
     --svh is the conservative height and is what the contents are sized against, so the
     panel fits with the bars showing and grows when they go. */
  --svh: 100vh;
  --svh: 100svh;
  height: 100vh;
  height: 100svh;
  height: 100dvh;
  z-index: 50;
  display: flex;
  flex-direction: column;
  /* Same inline padding expression as .shell, so the panel clears the notch when
     the phone is held sideways. Nothing above the links: the head takes the full
     bar height instead, so the lockup does not jump when the panel opens. */
  padding: 0 max(var(--page-pad), env(safe-area-inset-right))
             calc(40px + env(safe-area-inset-bottom))
             max(var(--page-pad), env(safe-area-inset-left));
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--ground-deep);
  color: var(--ink-reverse);
  opacity: 0;
  visibility: hidden;
  /* Visibility flips at the end of the close fade and at the start of the open
     one, so the panel is focusable on the same frame the burger is pressed. */
  transition: opacity .3s var(--ease-out), visibility 0s linear .3s;
}

.mobile-menu[data-open="true"] {
  opacity: 1;
  visibility: visible;
  transition: opacity .3s var(--ease-out), visibility 0s;
}

.mobile-menu-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 0 0 auto;
  height: clamp(48px, var(--svh) * .0804 + 11px, var(--nav-height));
}

/* 7px, the same the nav bar and the footer give their copies of this lockup. Without it the
   menu's was 31px tall against their 45, which made the one home link inside the panel the
   shortest target on the site. */
.mobile-menu .lockup { color: var(--ink-reverse); padding-block: 7px; }
.mobile-menu .lockup-up { fill: var(--ink-reverse); }
.mobile-menu .lockup-low { fill: var(--sage-lift); }

/* Both halves of the lockup, and scoped to the panel. The page's .lockup:active is an
   equal-specificity rule earlier in the file, so the reverse colour above was winning in
   the pressed state too and the one home link inside the menu had no feedback at all.
   The chevrons carry their own fills here rather than currentColor, so a colour on the
   anchor alone would only have moved the wordmark. */
.mobile-menu .lockup:active { color: var(--emerald-lift); }
.mobile-menu .lockup:active .lockup-up,
.mobile-menu .lockup:active .lockup-low { fill: var(--emerald-lift); }

/* The lift, not the deep. On the carbon panel the deep emerald is 1.60:1 against the
   reverse ink it replaces, which is not a visible change; the lift reads at 9.05:1. */
.mobile-menu-close:active span { background: var(--emerald-lift); }

/* Same box and same overhang as .nav-burger. When these disagreed, pressing the
   burger slid the wordmark down 11px and the control itself 8px sideways. */
.mobile-menu-close {
  position: relative;
  width: 44px; height: 44px;
  margin-right: -11px;
  border: 0;
  background: none;
  cursor: pointer;
}

.mobile-menu-close span {
  position: absolute;
  left: 11px;
  top: 21px;
  width: 22px;
  height: 1px;
  background: var(--ink-reverse);
}

.mobile-menu-close span:first-child { transform: rotate(45deg); }
.mobile-menu-close span:last-child { transform: rotate(-45deg); }

/* Anchored top and bottom rather than centred. Centring the list in the leftover height
   put 113px of empty dark under the bar and 139px more above the CTA: 44% of the panel,
   in two holes that read as a layout that has not been positioned.

   The list is the scroller, not the panel. It used to grow to its content and the CTA was
   held down by margin-bottom:auto, which resolves to 0 the moment a flex container
   overflows: on a 4.7" phone the one destination in the menu opened with 18px of itself
   visible. Growing the list instead pins the CTA for real and lets the links take any
   overflow, so the one gap left is the one separating the five from the single
   destination. */
.mobile-menu-links {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Room for the focus ring inside the scroll box. overflow-y computes overflow-x to auto,
     which clipped all four edges of a 4px ring on links stretched to the container width. */
  padding-inline: 4px;
  margin-inline: -4px;
  /* And on the block axis, where the first and last rows were flush with the scroll box
     and their rings came out three-sided. */
  padding-block: 4px;
  margin-top: 26px;
  margin-bottom: -4px;
  display: flex;
  flex-direction: column;
}

/* Bigger than the desktop nav asks for, because this panel is the whole screen and
   the five items were 25px sitting in 380px of leftover dark. Type that fills what it
   is given is a better answer than a list that has been centred in a hole. */
.mobile-menu-links a {
  /* Off svh, not off a height breakpoint. At a flat 29/17 the five links were 415px in the
     373px an iPhone 14 gives them, so the fifth was cut through the middle of its glyphs:
     the max-height:620px branch that was meant to catch this is answered at 745 on that
     phone and never fired. */
  padding-block: clamp(6px, var(--svh) * .0568 - 25.4px, 17px);
  /* Grow into whatever the panel has, never shrink below the type. The box is sized on dvh
     and its contents on svh, so opening the menu after the browser bars have retracted
     left the five rows at their svh height inside a box 185px taller, and all of it
     collected as one hole above the call to action. Sharing the slack across the rules
     spaces them instead. */
  flex: 1 0 auto;
  /* Centred in whatever the row grows to. Left at the top of the box, the extra height
     all landed under the word: each label sat hard against the rule above it with 60px
     of dark below, so the rules read as glued to the foot of five voids. */
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--hairline-reverse);
  font-family: var(--font-display);
  font-size: clamp(17px, var(--svh) * .0227 + 12px, 29px);
  text-decoration: none;
  color: var(--ink-reverse);
}

/* A rule under the last item separates nothing from nothing. Transparent rather than
   removed, so the last row is not 1px shorter than the four above it: at the padding a
   short panel drops to, that one pixel was the difference between 44.2 and 43.2. */
.mobile-menu-links a:last-child { border-bottom-color: transparent; }

.mobile-menu-links a:active { color: var(--emerald-lift); }
.mobile-menu-links a[aria-current] { color: var(--emerald-lift); }

/* The panel's one destination. Set as row six of six it was indistinguishable
   from About, and the list ended on 240px of empty dark. */
.mobile-menu-cta {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin-top: clamp(10px, var(--svh) * .0804 - 27px, 26px);
  padding: 17px 22px;
  background: var(--emerald);
  /* The fill is the only thing separating this from the five plain links above it, and
     the core emerald on the panel is 2.76:1, under the 3:1 a control's boundary needs.
     The lift rule carries the edge at 4.63:1 rather than lightening the fill, which
     would put a pale block at the foot of a dark panel. */
  border: 1px solid var(--emerald-lift-rule);
}

/* Deep, for the same reason the hero's copy of this button presses to it. */
.mobile-menu-cta:active { background: var(--emerald-deep); }
@media (hover: hover) { .mobile-menu-cta:hover { background: var(--emerald-hover); } }

/* Sideways the panel is 844 wide, and a serif word with a rule running the whole way
   across it reads as a divider rather than a link. Width, not height: what this branch is
   for is the shape of a wide panel, and every vertical measure in here now comes off svh.
   Two columns, because five full-width rows do not fit a panel that is 344px tall: the
   list was scrolling 83px with the last destination below the cut. Down the first column
   and then down the second, so the order still reads the way the list is written. */
@media (orientation: landscape) and (max-height: 520px) {
  .mobile-menu-links {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(3, auto);
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 40px;
    max-width: 560px;
  }
  /* The foot of each column, not only the foot of the list. */
  .mobile-menu-links a:nth-child(3) { border-bottom-color: transparent; }
  /* The list's own width, not a number of its own. At 420 in an 844px panel the block
     overshot the left column by 164px and stopped 132px short of the right one, so it
     lined up with neither and the lower right quadrant read as a gap rather than a margin. */
  /* 552 and not 560. The list carries 4px of inline padding either side so its focus
     ring has somewhere to land, and under border-box that comes out of its 560, so a
     button set to the same number overhung the rule above it by 8px. */
  .mobile-menu-cta { max-width: 552px; }
  /* The vertical trims that go with this live in the sideways-phone block near the foot
     of the file: the 640px rules are later than this one and set the panel's padding with
     a shorthand, so a padding-block written here would be overridden at 568 and not at
     844, which is the one width where it looked like it worked. */
}

body[data-menu-open="true"] { overflow: hidden; }

/* Predicted action. */

/* Predicted action — hero diagram.
   Tokens consumed: --ground --emerald --emerald-deep --sage --viz-line
                    --hairline-firm --ink --ink-soft --text-muted --font-display */

.forecast {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.forecast--compact { display: none; }

/* Type -------------------------------------------------------------------- */

/* Muted, not ink and not green: the title belongs to the figure, and at ink weight
   it competed with the headline across the fold. It was in the accent for a while,
   which made a caption the fifth thing on the page drawn in the one hue. */
.forecast-title {
  font: 400 26px/1 var(--font-display);
  fill: var(--text-muted);
}

.forecast-label {
  font: 400 15px/1 var(--font-display);
  fill: var(--ink-soft);
}

.forecast-label--active { fill: var(--ink); }

/* The one piece of text on the drawing that names a branch rather than a point, so it
   is set in the sans at caption weight: in the display face at label size it would have
   read as a fourth outcome sitting under the three. */
.forecast-note {
  font: 400 15px/1 var(--font-sans);
  fill: var(--text-muted);
  letter-spacing: .01em;
}

/* Rule under the title ---------------------------------------------------- */

.forecast-rule {
  stroke: var(--hairline-firm);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* Probability field ------------------------------------------------------- */

.forecast-field circle {
  fill: none;
  stroke: var(--viz-line);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* Filaments --------------------------------------------------------------- */

.forecast-path {
  fill: none;
  stroke: var(--viz-line);
  /* A hair over one device pixel, so the filaments hold their weight against the
     stipple instead of dropping away to nothing next to it. */
  stroke-width: 1.3;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.forecast-path--active { stroke: var(--emerald); }

/* Node -------------------------------------------------------------------- */

.forecast-node {
  fill: var(--ground);
  stroke: var(--viz-node);
  /* Heavier than the filaments so the model reads as the one solid object. */
  stroke-width: 1.6;
  vector-effect: non-scaling-stroke;
}

.forecast-mark-up  { fill: var(--emerald-deep); }
.forecast-mark-low { fill: var(--sage); }

/* Rails ------------------------------------------------------------------- */

/* Neutral. Seven green dots with the highlighted one in a second green two shades
   off it meant the figure's one claim was carried by a 1px radius difference, and
   it put six more dots in the accent colour on a page that was already leaning on
   it. Grey inert, emerald for the outcome the model picked. */
.forecast-dot { fill: var(--viz-node); }
.forecast-dot--active { fill: var(--emerald); }

.forecast-dot--muted {
  fill: var(--text-muted);
  opacity: .6;
}

/* Compact -----------------------------------------------------------------
   The compact figure is portrait, so it is capped rather than allowed to fill
   the column: past about 400px the labels would outgrow the body copy, which is
   what made the old landscape variant read as a diagram bolted on from a
   different page. The sizes below are set so the labels land near 14px and the
   title near 22px at every phone width, not so they look right at one. */

.forecast-rail--stacked text { text-anchor: middle; }

/* A caption, not a heading. Set in the display face at the size of a section
   sub-head, in the deepest green on the page, over its own rule, it read as the
   start of a new section rather than as the name of the drawing under it.
   Centred over the rail it names, because the drawing's other caption is centred under
   the branch it names. Flush left at the figure's edge it was the one piece of text in
   the portrait figure not on the drawing's own axis: grey sans at x=0 above centred
   Playfair labels, with the desktop layout's short rule under it switched off. Two
   captions, one logic, top and bottom. */
.forecast--compact .forecast-title {
  font: 500 13.5px/1 var(--font-sans);
  fill: var(--text-muted);
  letter-spacing: .01em;
  text-anchor: middle;
}

.forecast--compact .forecast-rule { display: none; }

/* Every size below is an svg user unit riding a 360-unit viewBox, so what the reader sees
   is the number here times the drawing's own scale. Three fixed steps could not track that:
   the labels ran 12.2px at 320 up to 15.6px at 480, a 27% spread on type meant to land at
   one size, and at 320 and 360 the caption came out larger than the labels it captions.
   The drawing renders at min(100vw - 2 x gutter, 400), so the user unit that holds a fixed
   rendered size is target x 360 / that width. CSS cannot divide by a length, but 1/x over
   the 284-to-400 range a phone actually spans is close enough to a line that a ramp in vw
   tracks it inside a tenth of a pixel. Two ramps, because the gutter steps from 18 to 22
   at 360 and the drawing's width steps with it, and a flat value above 444 where the
   400px cap takes over and the scale stops moving. */
@media (max-width: 720px) {
  .forecast--desktop { display: none; }
  .forecast--compact { display: block; max-width: 400px; }
  .forecast-label { font-size: 12.6px; }
  .forecast--compact .forecast-title,
  .forecast--compact .forecast-note { font-size: 11.7px; }
}

@media (max-width: 444px) {
  .forecast-label { font-size: calc(28.88px - 3.7vw); }
  .forecast--compact .forecast-title,
  .forecast--compact .forecast-note { font-size: calc(26.82px - 3.44vw); }
}

/* The four input labels used to be lifted onto two alternating lines here, the ordinary
   fix for a crowded category axis. They are not crowded, even at the one size where the
   row is tightest: at 320 the closest pair sits 9.5px apart and "Commerce" clears the
   drawing's own edge by 2.6. What the lift bought was a zigzag across the one row on the
   drawing whose meaning is that the four inputs are alike, and the caption had to be
   nudged 7px clear of the raised label next to it. Both are gone. */
@media (max-width: 360px) {
  .forecast-label { font-size: calc(35.28px - 5.48vw); }
  .forecast--compact .forecast-title,
  .forecast--compact .forecast-note { font-size: calc(32.76px - 5.09vw); }
}

/* Motion ------------------------------------------------------------------
   Each flow group is clipped by a <rect> whose width animates from zero, so
   the filaments draw left to right and every hairline keeps
   vector-effect: non-scaling-stroke. Two things this deliberately avoids:
   stroke-dasharray, which Chrome measures in screen space once a stroke is
   non-scaling and so cannot draw a path at any scale but 1:1; and an animated
   CSS clip-path on the group, which leaves a stale clip region in Chrome and
   can strand a half-drawn line.

   The markup carries the finished widths, so with motion turned off, or if the
   stylesheet never arrives, the diagram is simply complete.

   The wipe is gated on [data-draw], which the page drops once the last flow has
   landed. Blink restarts a running animation when the document is re-laid out,
   which replayed the draw on every window resize and left the figure half-drawn
   in anything that captures at a second size. The fill is backwards only, so
   the resting width is always the one in the markup. */

@media (prefers-reduced-motion: no-preference) {
  .forecast[data-draw] .forecast-wipe {
    animation: forecast-draw 660ms cubic-bezier(.2, .58, .32, 1) backwards;
  }

  /* The phone figure flows downward, so its wipe opens on height instead. Same
     specificity as the rule above, so it has to stay after it. */
  .forecast--compact[data-draw] .forecast-wipe { animation-name: forecast-draw-down; }

  .forecast[data-draw] .forecast-wipe--out  { animation-delay: 210ms; }
  .forecast[data-draw] .forecast-wipe--pick { animation-delay: 400ms; animation-duration: 700ms; }
}

@keyframes forecast-draw {
  from { width: 0; }
}

@keyframes forecast-draw-down {
  from { height: 0; }
}

/* Forced colours ---------------------------------------------------------- */

@media (forced-colors: active) {
  .forecast-path,
  .forecast-field circle,
  .forecast-node,
  .forecast-rule { stroke: CanvasText; }
  .forecast-field circle { opacity: 1; }
  .forecast-dot,
  .forecast-mark-up,
  .forecast-mark-low,
  .forecast-title,
  .forecast-note,
  .forecast-label { fill: CanvasText; opacity: 1; }
  /* The drawing's one claim is that the model picked sign-up, and it is carried by
     colour: emerald branch, emerald dot, ink label against grey. Forced to CanvasText
     all three outcomes came out identical and the only cue left was a 1.9px radius
     difference. The system accent keyword is honoured here, so it carries the claim. */
  .forecast-path--active { stroke: Highlight; }
  .forecast-dot--active,
  .forecast-label--active { fill: Highlight; }
}

/* Responsive. */

/* The hero holds two columns for as long as the desktop nav does. Stacking it
   earlier left a full desktop chrome above a single narrow column and an empty
   right half of the fold. */
@media (max-width: 1180px) {
  .hero-layout { grid-template-columns: minmax(0, .74fr) minmax(0, 1.26fr); gap: clamp(26px, 3vw, 48px); }
  /* Two columns on a short screen is the one case where the hero is bound by its
     min-height, so the bottom pad buys no clearance: the terrain box is pinned to a
     fold that is not moving and the headline is pinned to the top of it. At 1024x768
     the crest reached six pixels into the last line of the copy. The terrain itself
     is what has to give here, and 42vh is too much of a screen this short. */
  .hero { --hero-terrain: clamp(170px, 30vh, 300px); }
}

/* Holding two columns to 980 keeps the forecast and the ridge on the first screen at
   1024 and on a landscape tablet. Below 1100 the figure has to give a little width
   back to the copy or the pair will not fit the content box. */
@media (max-width: 1100px) and (min-width: 981px) {
  .hero-layout { grid-template-columns: minmax(0, .78fr) minmax(0, 1.22fr); gap: clamp(20px, 2.4vw, 33px); }
  .hero-copy h1 { font-size: clamp(40px, 5.2vw, 62px); }
}

@media (max-width: 980px) {
  .hero {
    --hero-terrain: clamp(190px, 26vh, 260px);
    min-height: 0;
    padding-bottom: calc(var(--hero-terrain) + 14px);
  }
  .hero-layout { grid-template-columns: minmax(0, 1fr); gap: clamp(44px, 7vw, 76px); }
  .hero-copy { max-width: 640px; }
  /* Stacked, the headline has to be set to the measure it now owns, the same way the
     phone rules do it. Left on the viewport-width clamp it came out at 45px inside a
     640px column: three lines filling 173, 167 and 96px, with 467px of paper down the
     right of the largest type on the site. */
  .hero-copy h1 { font-size: clamp(56px, 8.1vw, 76px); line-height: .98; }
  /* Once the figure is under the copy rather than beside it, its caption is a caption:
     at 26 user units it rendered 21px over a 33px rule, above 12.7px labels, so inside
     one drawing the caption outranked the thing it labelled and the standfirst too. */
  .forecast--desktop .forecast-title { font-size: 17px; }
  .forecast--desktop .forecast-rule { display: none; }
  /* One column, so the measure is the column's job. At 768 the publishers paragraph
     was running 83 characters to the line against 47 on a phone. */
  .prose, .thesis-say, .audience .lead, .audience-body > p, .points dd,
  .loop-steps p, .page-head p, .role p, .hero-body { max-width: 62ch; }
  /* Stacked, the figure would stretch the full column and its type would grow as
     the viewport shrank. */
  .hero-figure { justify-content: flex-start; }
  /* Scoped to the landscape variant. Unscoped this outranked .forecast--compact's own
     400px cap, so between about 440 and 720 wide the phone figure ran to 648px and its
     labels rendered at 25px against 15.5px body copy. */
  .hero-figure .forecast--desktop { max-width: 720px; }
  html.js .nav-links, html.js .nav-cta { display: none; }
  html.js .nav-burger { display: block; }

  /* Opaque, and keyed here rather than at 640 because a phone held sideways is
     844x390 and never sees the width-keyed phone rules. At 92% a headline passing
     under the bar showed through it, and the remaining 3% was enough to tint the
     whole bar eight levels darker as it crossed the dark band, which is more
     visible than the ghost it was hiding. */
  .nav {
    background: var(--ground);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* -11px, not -9: the box is 44 and the ink inside it 22, so this is what puts
     the ink's right edge on the same line as the bar's rule and the lockup. */
  .nav-burger { width: 44px; height: 44px; margin-right: -11px; }
  .split { grid-template-columns: minmax(0, 1fr); gap: clamp(22px, 4vw, 40px); }
  .split .display, .audience .display { max-width: 22ch; }
  .role { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .audience-in { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .loop-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 40px; }
  /* Four tracks, not two. In a 2x2 the brand cell is short and the Platform cell tall,
     so the brand line sat over 125px of empty against 44px in the column beside it. */
  .footer-main { grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr)); column-gap: 34px; row-gap: 0; }
  .closing-in { grid-template-columns: minmax(0, 1fr); gap: 30px; align-items: start; }
  .closing .display { max-width: 18ch; }

  /* The burger needs script to open anything, so where there is none the links have
     to stay on the page. They drop to their own row and the bar grows to fit, rather
     than running off the side and taking the whole document sideways with them. */
  html:not(.js) .nav-in {
    flex-wrap: wrap;
    height: auto;
    min-height: var(--nav-height);
    padding-block: 13px;
    row-gap: 12px;
  }

  html:not(.js) .nav-links {
    order: 3;
    width: 100%;
    margin-left: 0;
    flex-wrap: wrap;
    gap: 12px 22px;
  }

  html:not(.js) .nav-cta { margin-left: auto; }

  /* Three rows of bar is 215px, a third of a 568px screen, and it is worth that much
     less when it is stuck there. Unpinned it scrolls away like any other block, and
     the scroll padding that offsets an anchor for a sticky bar goes with it: it was
     still set to 64px, so every in-page jump landed 151px behind the bar. */
  html:not(.js) { scroll-padding-top: 0; }
  html:not(.js) .nav { position: static; }
}

/* Phones. Not the desktop layout narrowed: the column is the only measure there
   is, so the things that carry structure on a wide screen (a second column, a
   long gap, a figure sitting beside the copy) have to be re-earned vertically. */

@media (max-width: 640px) {
  :root {
    /* A 74px bar costs a tenth of the fold on a phone and buys nothing. */
    --nav-height: 64px;
  }

  body { font-size: 16px; line-height: 1.66; }

  .nav-in::after { background: var(--hairline-edge); }
  .lockup-mark { height: 31px; }
  .lockup-name { font-size: 14.5px; }
  /* 31px of artwork in a 64px bar, and the whole link was only as tall as the mark. */
  .nav .lockup { padding-block: 7px; }

  /* Hero */

  /* Also on the root, because the seam under the figure is set by two rules that sit
     outside .hero and it has to compress with the same screen the hero is fitted to. */
  :root {
    --svh: 100vh;
    --svh: 100svh;
  }

  /* No terrain under the hero on a phone. Two columns give the ridge a job: it runs
     under both of them and reads as the ground the whole section stands on. Stacked,
     it lands directly beneath the figure, and the two become one 519px run of grey
     abstraction with no sentence in it, which is 61% of a 390x844 screen. The figure
     is the one carrying meaning, so the terrain is the one that goes. The motif still
     appears twice below, under the dark band and under the closing line. */
  .ridge--hero { display: none; }
  /* Every vertical number in the hero comes out of this one, and nothing comes out of a
     height breakpoint. There used to be two more branches below, gated on max-height 740
     and 640, and they were unreachable on the phones they were written for: a media query
     is answered against the large viewport while svh resolves to the small one, so a
     390x844 iPhone asks the query at 745 and lays out at 659. The short-screen hero was
     therefore excluded from every screen that needed it, and the block overran the fold by
     46px on the three commonest iPhone portraits with the second action underneath the cut.
     Headless Chrome resolves the two to the same number, which is why eight rounds of
     capture showed the box landing on the fold exactly.
     Sized off svh instead, there is no threshold left to answer against the wrong viewport:
     the block shrinks continuously with the space it is actually given. Measured against
     each device's real pair, the second action now clears by 22px at 390x844, 25 at
     360x780, 20 at 430x932 and 8 at 375x667.
     A 320x568 phone is the exception and stays one: 460px of small viewport against a
     476px block, with the argument alone running nine lines at a 34-character measure. The
     headline is already at its 36px floor there. What the ramps buy is the order it breaks
     in, with the filled action 21px under the fold and the link below that.
     vh first, for browsers that would drop an svh declaration outright. */
  .hero {
    --svh: 100vh;
    --svh: 100svh;
    /* Each of these ramps is a line through two known-good points, 553px of small viewport
       (a 4.7" phone with its bars up) and 745px (a 6.1"), rather than a plain ratio: a
       ratio through the origin barely moves across the range phones actually occupy and
       left the 4.7" class 32px over its own fold. */
    --hero-top: clamp(8px, var(--svh) * .0625 - 26.6px, 20px);
    /* The drawing belongs to the hero, and on a stacked page proximity is the only thing
       that can say so. Above it the gap has a job and a fixed size; below it the gap is
       free, so it is the one that opens. At 390 the pair reads 108 above and 144 below
       ink to ink, at 430 it is 108 and 195. A 4.7" screen is the exception and stays at
       roughly even, because there the space below the drawing is also what carries the
       next heading over the second fold, and 553px of viewport cannot pay for both. */
    padding-block: var(--hero-top) clamp(32px, var(--svh) * .247423 - 104.82px, 80px);
  }
  /* Flat, not ramped. This gap has one job, keeping the drawing clear of the sticky bar
     when the second screen opens, and the bar is 64px tall at every width here, so there
     is nothing for it to scale with. Ramped it also grew the wrong half of the pair. */
  .hero-layout { gap: 72px; }

  /* The copy takes the first screen and the drawing takes the second. Stacked, the
     figure used to start 72px under the last action, which put the fold at 390x844
     straight through the input dots: the opening screen ended on four disconnected
     words and four half-circles with the curves below them cut off, and one full
     swipe then buried that same row behind the nav bar. That is the "standalone
     weird graphic, and then the dots" in the brief, and it is a framing problem
     rather than a spacing one. Holding the copy column to the fold height sends the
     figure to 100svh + 64, so it opens clear of the bar with all 342px of it on
     screen at once. The measure is a floor, so a long headline or a text-spacing
     override just pushes the figure down instead of overflowing.
     The box has to end ON the fold, not past it. Nav plus this section's own top pad is
     the whole of what sits above the box, so that is the whole of what comes off, and both
     terms are read from the same place the height is: written as svh minus a constant of
     its own the box ran 48px past the fold, which centring then split.
     The clearance under it is the layout gap, below the fold, where it costs nothing.
     A floor, not a height, so a text-spacing override pushes the figure down the page
     rather than overflowing the box. */
  .hero-copy {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: calc(var(--svh) - var(--nav-height) - var(--hero-top));
  }

  /* The lines are fixed by the markup, so the type can be set to the measure
     instead of to a viewport ratio inherited from the desktop rule. At 52px
     "See what" filled 55% of the column and the biggest words on the site sat in
     a huddle with 150px of paper down their right. */
  .hero h1 { margin-top: 0; font-size: clamp(36px, min(17.6vw, var(--svh) * .11875 - 17.7px), 78px); line-height: .94; }

  /* A deck and then the argument. Set at one size the two paragraphs read as nine
     lines of the same thing under a headline with no step down to them.
     34, because 20 was the same interval as the gap between two paragraphs: a 69px
     Playfair headline was separated from 16px body copy by 1.19 times a paragraph
     break, while the largest interval in the block, 40px, sat between the copy and
     the button. The block's widest breath was between two secondary things and its
     narrowest at the one place the eye has to step down.
     Every interval in the block is now fitted through the same two points the rest of
     the hero uses, 553px of small viewport and 745px, because the ladder had been holding
     at one end only. The two block gaps were ramps and the paragraph gap was 1.05em, so
     as the screen got shorter the ramps fell to their floors while the em barely moved:
     on a 4.7" phone it went 13 / 15.8 / 13, with the widest interval in the hero sitting
     between the two halves of one paragraph block. It reads 15 / 9 / 19 there now and
     34 / 17 / 38 on a 6.1". */
  .hero-body { margin-top: clamp(12px, var(--svh) * .0677083 - 22.44px, 28px); }
  /* Two sizes on a phone and no more: 19 for a standfirst and 16 for body. The deck
     and the argument used to sit at 18 and 15.8, which put six sizes inside a 4px band
     and read as drift rather than a scale. Flattening that left a third size, 15.5,
     for a "secondary register" that turned out to be four different regions playing
     the same role: the hero's argument, the four steps and the two terms all sat 3%
     under the paragraphs two screens away, which is a step no reader can find a reason
     for. Colour carries that distinction instead, which is what it was already doing. */
  /* Floor 16.5. At 15.5 against the argument's 15 the step down was 3% on the two phones
     where the block is most crowded, where a 6.1" gets 19 over 16. Weight and colour were
     carrying it alone. It costs 2.9px of the 7.5 the 4.7" fold has left.
     The slope is 19/659 and 16/659, so the two sizes reach the page's own 19 and 16 on the
     commonest phone rather than only on the largest one. At .0255 a 6.1" screen set the
     standfirst at 16.8 and the argument at 15, which made the opening block the smallest-set
     region on a page whose every other lead is 19: six sizes inside a 2.4px band, which is
     the drift these notes keep rejecting elsewhere. It is free, because the copy column is
     bound by its own floor rather than by its contents there: 539px of type in a 580px box,
     and the two sizes add 14 of the 41 spare. Nothing moves at 553 or below, where both
     terms are already under their floors. */
  .hero-body p { font-size: clamp(16.5px, var(--svh) * .028832, 19px); line-height: 1.46; color: var(--ink-soft); }
  /* Balanced, because the natural break left "TV." alone on a third line at 8% of the
     measure: 27px of ink under 319px of hole, at the second-largest size on the page, in
     the sentence that says what the company is. It happens at 320, 360 and 390 and not at
     430, which is why it survived this long. Balancing holds the line count at three. */
  .hero-body p:first-child { text-wrap: balance; font-weight: 500; }
  .hero-body p + p { margin-top: clamp(7px, var(--svh) * .0208333 - 2.52px, 13px); font-size: clamp(15px, var(--svh) * .024279, 16px); line-height: 1.6; color: var(--text); }

  /* One block and one link. Matching the pair box for box was an attempt to answer
     "the buttons are mismatched" by making them identical, and it kept producing a
     subtler version of the same fault: first the labels ended 18px apart while the
     arrows lined up, then, centred, the arrow inside the centring group shoved the
     primary's label 18px left of a centre the secondary hit exactly. Two same-width
     slabs whose words do not line up read as an error however they are tuned. Ranked
     instead, there is nothing left to misalign: a full-width filled block, and under
     it the underlined action this page already uses everywhere else.
     The 6px that sat between them made the link look like a second control that had
     come loose from the first. At 20 it is a link under a button. */
  .hero-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: clamp(12px, var(--svh) * .0260417 - 2.4px, 17px);
    /* The widest interval in the hero, which is what it was on a tall screen and was not
       on a short one. Same two fitted points as the rest of the block. */
    margin-top: clamp(14px, var(--svh) * .0677083 - 18.44px, 32px);
    max-width: 400px;
  }

  /* Label and arrow centred together as one group. The arrow was pinned to the right
     edge for a while, to stop it dragging the label off the box's centre; what that
     bought was a 346px block reading as five zones, 115 void, 116 label, 73 void, a
     22px hairline arrow and 20 more void, with the lightest mark on the screen sitting
     73px from the words it belongs to. The 18px the label sits left of centre only
     ever mattered when there was a second box beside it to fail to line up with, and
     there is not one any more: the action under this is an underlined link at the page
     gutter. One object, centred. */
  .hero-actions .button, .closing .button, .mobile-menu-cta {
    display: flex;
    align-self: stretch;
    justify-content: center;
    gap: 14px;
    padding: 17px 22px;
    /* One fill for the control. A phone sees this exact block three times, same words
       and same shape, and it was arriving in two greens: the deep one on paper and the
       core one on the two dark grounds, where the deep one measures 2.0:1 and stops
       being a block at all. The core green is the one that survives both, at 6.1:1 on
       paper and 5.63:1 for its own label, so it is the one that gets the job. Deep
       stays where it is type rather than fill: the headline's last word, and links. */
    background: var(--emerald);
  }
  /* Deep, not the hover green. --emerald-hover was drawn to sit a step under
     --emerald-deep, which is the fill this button carries on a desktop, so reusing it
     over the core green gave the page's most important control a 1.30:1 press against
     1.73 on the text actions, 1.58 in the footer and 1.59 on the burger. It is the one
     control where the feedback has to carry a wait: the label opens a mail app. Deep is
     the green the rest of the page presses to and it lands on the same 1.73. */
  .hero-actions .button:active, .closing .button:active { background: var(--emerald-deep); }
  /* Deep on paper is a darker block. Deep on the closing band is 1.60:1 against its own
     ground, which is a block losing its edge at the moment a thumb is on it. The menu's
     copy of this button already carries the lift rule for the same reason; here it only
     has to appear for the press, and inset so the box does not resize under the thumb. */
  .closing .button:active { box-shadow: inset 0 0 0 1px var(--emerald-lift-rule); }
  @media (hover: hover) {
    .hero-actions .button:hover, .closing .button:hover { background: var(--emerald-hover); }
  }
  /* Only the hero's copy is fold-bound, so only the hero's block gives height back. The
     floor is 13, which holds the target at 45px with the label's own line box. */
  .hero-actions .button { padding-block: clamp(13px, var(--svh) * .0228, 17px); }

  /* Back to the register every other action on the site uses. In the hero it kept the
     button's caps and tracking to read as the second half of a pair, and once the pair
     stopped being a pair that left an 11.5px tracked-caps label sitting under a filled
     block in the same green: a control by its type, a link by its size. The rule under
     a sentence-case word is what says link here, and it says it four times below. */
  .hero-actions .text-action {
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
  }

  /* The section-level ones keep the underline, but they were 28px tall against a
     44px thumb. The rule is lifted off the bottom of the box rather than the box
     being made shorter: at 19px under the label it had stopped reading as an
     underline and started reading as the next section's rule. */
  .text-action { padding-block: 13px 17px; }
  .text-action::after { bottom: 8px; }

  /* Sections */

  /* A wider step between a section head and the sub-heads inside it. At 30 and 21
     in the same serif the two sizes were close enough that four screens of the
     page read as one repeated block. */
  .display { font-size: 34px; }

  /* Two joins, not one and not five. These were 60, 56, 52 and 64 once, which is
     small enough per pair to look like a measurement error; flattening them to a
     single 60 fixed that and left the page with the same 120px seam five times in a
     row for 4,000px of scrolling. So the size of a join now says what kind of join it
     is: 88px inside a movement, where one section continues the last, and 120px where
     the argument turns over. How it works, advertisers and publishers are one stretch;
     where things stand is the company talking about itself, and gets the wider seam
     either side of it.
     The two inside-a-movement joins were 80 and 64, and the 64 was the one between the
     advertisers pitch and the publishers pitch: the weakest seam on the page sat under
     the one place two parallel arguments change over. They are both 88 now, which
     leaves two numbers rather than four. */
  /* The one seam in that scheme that is not a constant, because it is the only one a fold
     lands in. On a 4.7" phone the drawing ended 105px above the second cut and "Model
     quality." began 15px below it, so the whole of screen two was one figure and a field of
     cream. Ramped through the same two screens the hero is, this and the two intervals above
     the figure take 56px out of a 553px phone and nothing out of a 745px one, which puts the
     heading and its first line over the cut. The joins between sections stay at 88 and 120. */
  .quality { padding-block: clamp(48px, var(--svh) * .319588 - 128.73px, 110px) 56px; }
  .loop { padding-block: 64px 40px; }
  .audience { padding-block: 48px; }
  .about { padding-block: 72px 44px; }

  /* Last-line orphans, across the body copy. Every one of these blocks runs the full
     width of the screen here, so a paragraph ending on one word ends on a line that is
     97% empty. Not the audience leads, which are balanced above for a different reason. */
  .prose p, .thesis-say p, .audience-body > p, .points dd,
  .loop-steps p, .page-head p, .role p, .hero-body p,
  /* The three that were missed, and they are the last sentence in their block every
     time: the one before the page's primary call to action, the one before the mail
     link on careers, and the whole of what the 404 has to say. On a 4" phone they were
     ending on "you manage." at 32% of the measure, "you have built." at 35% and
     "moved." at 28%. */
  .closing p, .contact-line p, .page-miss p { text-wrap: pretty; }

  /* The one short section on the page, and the only place a sentence gets to be the
     whole of what is there. Set at body size it was the fourth screen in a row of
     head-then-paragraph and the page had been running one register for 4,000px. */
  /* No measure cap. At 26ch the paragraph ran to a fourth line holding two words, with
     "go-to-" hyphenated across the break right above it: a split compound and a stub in a
     four-line block that is the whole of the section. At full width it sets as three. */
  .about .prose p { font-size: 19px; line-height: 1.5; color: var(--ink-soft); text-wrap: balance; }
  .split { gap: 18px; }
  .split .display, .audience .display { max-width: 20ch; }
  /* Pretty, not balance. Balancing these evened the fills and paid for it twice: the
     publishers head broke as "for connected-" / "TV inventory", splitting a compound on
     its own hyphen, and the advertisers head came out 212 / 211 / 319 with the last line
     half again as wide as the two above it. Two sections built as a mirrored pair were
     stepping in opposite directions. Pretty leaves the rag descending, 310 / 261 / 149
     and 297 / 274 / 171, and keeps connected-TV whole. */
  .audience .display { text-wrap: pretty; }
  .split .text-action { margin-top: 20px; }

  /* The third band to be pinned to its terrain rather than sized next to it. Left as
     two independent numbers the reverse field was starting 14px above the last line of
     the paragraph at 390 and 12px above it at 430, so the closing sentence of the
     strongest screen on the phone was set over stipple. Deep enough, too, that the
     terrain finishes inside its own canvas: at 126 the field was still at a fifth of
     its crest density one pixel above the band's edge, which is the one taper on the
     page that ends by being cut. */
  .thesis {
    --band-terrain: clamp(120px, 15vh, 150px);
    padding-block: 84px calc(var(--band-terrain) + 34px);
    /* Big, and capped. A plain 100svh made the band a piece of the screen rather than a
       piece of the page: it sets its own height at 506 to 533 across the phone widths, so
       the lock was inflating it by 34px on a 4.7" screen and 241px on a 6.7" one, all of
       it empty. The lead above the headline came out 84 / 101 / 152 / 173 / 205 going up
       the range while the closing band, the same idiom, sits at a flat 76 everywhere.
       At 560 the slack tops out at 54px and the lead holds between 84 and 111, and the
       band still runs most of a screen: with the bar taking the dark ground the reader
       sees 624px of it on a 659px phone. The min() keeps it off the floor of a 4" screen,
       where the band is already taller than the viewport.
       Centred, because top-aligned the slack collected under the last line as one hole.
       The paddings are deliberately uneven, 84 over the headline against 34 over the
       terrain: the terrain is 120px of ink, not margin, so the foot of the band is not
       the empty side it looks like in a stylesheet. */
    min-height: 560px;
    min-height: min(100svh, 560px);
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .ridge.ridge--reverse { height: var(--band-terrain); }
  /* Playfair's dollar sign runs the full ascender and hangs below the baseline, so
     at .06 leading the "$" in "$100B" touched the line above it. */
  /* The two full-band statements are the largest type on a wide screen and had ended
     up smaller than the section heads on a phone, which put the page's climaxes a
     size below its signposts. */
  /* 11.1vw. At 11.3 a 430-wide screen sets this at 48.6 and takes the cap, while 390 sets
     it at 44.1: the largest phone got a smaller headline relative to its gutter than the
     one below it, and the four-line break landed on "a" at 425 and up. */
  /* Floor 36, not 38. At 320 the floor is what binds, 11.1vw being 35.5, and at 38 the
     largest statement on the page broke 99 / 247 / 237 in a 284px measure: a first line
     holding a third of the column. At 36 it sets as two, 277 and 284. Nothing else moves,
     because 360 and up never reach the floor. */
  .thesis .display { font-size: clamp(36px, 11.1vw, 48px); line-height: 1.1; text-wrap: balance; }
  .thesis-say { margin-top: 30px; }

  /* Four steps down one column with nothing between them is four paragraphs.
     The rules give each one a top edge, which is the whole of what the row of
     columns was doing on a wide screen. One weight of rule: the firmer one at the
     top of the list and the lighter ones between items were 19 levels apart on a
     250 ground, too close to read as hierarchy and close enough to read as a slip. */
  .loop-rail { margin-top: 30px; }
  .loop-steps { grid-template-columns: minmax(0, 1fr); row-gap: 0; border-top-color: var(--hairline); }
  .loop-steps li { padding-block: 20px 22px; border-top: 1px solid var(--hairline); }
  .loop-steps li:first-child { border-top: 0; }
  .loop-steps li:last-child { padding-bottom: 0; }
  .loop-steps h3 { font-size: 21px; }
  .loop-steps p { max-width: none; font-size: 16px; line-height: 1.66; }
  .points dd { font-size: 16px; line-height: 1.66; }

  /* The third ground change, and the one that breaks the middle of the page. Between the
     dark band and the closing one a phone was scrolling 2,704px, four screens, of the same
     thing: four sections of a 34px serif head over 16px paragraphs on one gutter, with the
     two structures that could have differed, the four ruled steps and the two terms, both
     set at 21 over 16. Turning the closing band over cut that run from 3,600px and did not
     break it. This does: 825 cream, 880 dark, 998 cream, and then the close.
     Of the four it is the only one that works. The steps run straight on from the dark band
     above and would merge with it; the company note is 333px and would merge with the band
     below. The cost is that the two audience pitches no longer look alike, which is a real
     cost and the reason this sat unfixed for two rounds. They are addressed to different
     readers and only one of them is the argument the page has been making since the hero,
     so it is the one that gets the ground. */
  /* 48 above, where it was 76. A ground change already does the work a seam does, so a
     band edge is the one join on the page that should be narrower than its neighbours
     rather than wider. At 76 the join above this measured 116 against the 88 the scheme
     above claims for a join inside a movement, and the one below it measured 144, which
     made the widest seam on the page the changeover between two parallel pitches. */
  .audience--advertisers {
    padding-block: 48px 72px;
    background: var(--ground-deep);
    color: var(--text-reverse);
  }
  .audience--advertisers .eyebrow,
  .audience--advertisers .lead { color: var(--text-reverse); }
  .audience--advertisers .display,
  .audience--advertisers .points dt { color: var(--ink-reverse); }
  /* Same pair the 404 and the careers contact line already use on this ground. */
  .audience--advertisers .text-action { color: var(--emerald-lift); }
  .audience--advertisers .text-action:active { color: var(--ink-reverse); }
  /* 48, under a band edge, which puts this join at 120: the same number the two joins
     either side of the company section use, and where the argument does turn over. */
  .audience--publishers { padding-top: 48px; }

  /* 16 and 22, where both were 14 measured ink to ink. The kicker was no more bound to
     its headline than the headline was to the paragraph under it, and both intervals were
     tighter than the 16 to 17 every other section on the page puts between a heading and
     its body. */
  .audience-in { gap: 16px; }
  /* Not balanced. It was, because the publishers lead ended on "is worth." at a fifth of
     the measure, and balancing five lines to do it left line 3 at 68% with a 109px hole
     beside it in the middle of the paragraph. The stub is bound in the markup instead,
     which is the narrower fix for what was a narrow problem. */
  .audience .lead { margin-top: 22px; font-size: 19px; line-height: 1.5; }
  /* Not the ruled stack the four steps use. Two things are a pair, not a list, and
     giving them the same rules made the two halves of the page identical.
     44 above, against the 26 that separates the two terms inside it: at 30 and 26 the
     pair did not read as one block under the paragraph, it read as two more paragraphs. */
  .points { grid-template-columns: minmax(0, 1fr); gap: 26px; margin-top: 44px; }
  /* Back to the 21px the loop steps use. Held a step below at 18.5 it was 2.5px from the
     16px paragraph under it, which is drift rather than a step, and a term that sits
     almost at body size stops reading as the thing being defined. Two sections running
     a heading over a paragraph at the same level should set it at the same size. */
  .points dt { font-size: 21px; }
  /* Clear of the 26px inside the pair above it, for the same reason the pair is clear of
     the paragraph: at 24 the link out of the section was closer to the block than the
     block's own halves were to each other. */
  .audience .text-action { margin-top: 40px; }

  /* Same trap the hero terrain was in. Stacked, the call to action is a full-width
     block sitting directly on the closing terrain instead of beside it, and the crest
     was climbing 9px into it at 390 and 14px at 360. Height and pad come off one value
     so the clearance holds wherever the band lands. */
  /* Dark, and the second thing on the page that is. A phone gets 5,970px of this site
     where a laptop gets 2,400, and stretched to that length one change of ground at 28%
     left 3,600px of unbroken cream after it: five section heads at one size on one
     gutter, each with a paragraph under it, for four and a half screens. That is the
     "and then all the rest of it" in the brief, and no amount of seam-tuning inside it
     fixes a run that long with nothing to see. Turning the last band over closes the
     page on the ground the argument opened on. It also gives the closing terrain
     somewhere to live: on cream that stipple read as dust, and the footer's hairline
     was slicing it off square. */
  .closing {
    --closing-terrain: clamp(120px, 15vh, 160px);
    /* 64 above the terrain, not 40. The band's own head clears at 76, so at 40 the last
       thing on the page sat nearer the stipple than the first thing sat to the seam. */
    padding-block: 76px calc(var(--closing-terrain) + 64px);
    background: var(--ground-deep);
    color: var(--text-reverse);
    border-top: 0;
  }
  .closing .display { color: var(--ink-reverse); }
  .closing-in { gap: 24px; }
  /* 11.1 and a 36 floor, the same as the statement band. The two full-bleed statements are
     the same rank and were set 0.78px apart at 390, which is not a step and not a match. */
  .closing .display { font-size: clamp(36px, 11.1vw, 48px); line-height: 1.1; max-width: 15ch; }
  /* The fill is 2.76:1 against this band, under the 3:1 a control's boundary needs, so
     the edge comes off the lift rule at 4.63:1. Same answer the menu panel's call to
     action already carries, for the same reason. */
  .closing .button { margin-top: 28px; max-width: 400px; border: 1px solid var(--emerald-lift-rule); }

  /* Footer */

  /* The home indicator sits in the last 21px of the screen when Safari's toolbar
     collapses, which put it on the copyright line. */
  /* No rule across the top. Every one of the three pages ends on a band of terrain,
     and stacked, that band runs the full width right up to this border: a 1px line
     across the foot of a stipple field cuts it off square, which is the one thing the
     hero's comment says a drawn field must never do. The ground changing, or the
     terrain running out, is the boundary. */
  /* 56 over 36. There is no rule above the footer on a phone, so what separates it from the
     page is this gap alone, and on two of the three pages the thing directly above it is a
     band of stipple at its densest. At 36 the mark sat closer to the crest of that terrain
     than any two paragraphs on the site sit to each other, which made it the tightest join
     on the page at the one boundary that is also a change of ground. */
  .footer { border-top: 0; padding-block: 56px calc(24px + env(safe-area-inset-bottom)); }
  .footer-main { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 20px; row-gap: 22px; }
  /* The tap target stays 44px; the pitch comes back to 44px by dropping the gap that
     was sitting between the boxes rather than inside them. */
  .footer-col { gap: 0; }
  .footer-brand { grid-column: 1 / -1; }
  /* 15, not 15.5. Half a pixel from the link size beside it is drift, not a step. */
  .footer-brand p { margin-top: 11px; }

  /* Three groups in two columns leaves one cell empty, and it fell in the bottom
     right where it read as a grid that had run out of content. The contact pair is
     the one group that is a line of detail rather than a list of pages, so it runs
     across the foot of the block instead of standing as a third column. */
  /* One column, like the two above it. Split across the footer's own two tracks the
     email landed under the "Company" heading with nothing naming it, so a block whose
     whole job is to group two links under one heading was reading as a third column
     that had lost its label. */
  .footer-col--access {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    padding-top: 22px;
    border-top: 1px solid var(--hairline);
  }

  /* Same 6px the other two groups use. At 1px the third label read as glued to its link
     while the two beside it were not, which is one spacing value doing nothing but making
     three identical groups look like two kinds of thing. */
  .footer-col--access h2 { margin-bottom: 6px; }
  .footer-col a { padding-block: 9px; }
  /* 31px, where the same lockup in the bar above is 45. It is a duplicate home link
     with a lot of clear space around it, so nothing was landing on the wrong thing,
     but it was the one target on the site under the 44 the rest are built to. */
  .footer-brand .lockup { padding-block: 7px; }
  /* 22/22, matching the access rule above it. At 34 above and 24 below, the footer's two
     hairlines carried different clearances and the wider one left a hole under the email. */
  .footer-bottom { flex-direction: column; gap: 6px; margin-top: 22px; padding-top: 22px; }

  /* The terrain is drawn to absolute depths, so spreading it over 350px of a
     phone screen thins it to a haze. A shorter band holds the crest. There is a
     floor to that, though: the last 118px above the base is a fade, so a canvas
     much under 140px is all fade and no crest. The reverse ridge sat at 112px
     and had gone invisible, which is what left the dark band's foot reading as
     an empty third of a screen. */
  .ridge { height: clamp(170px, 25vh, 250px); }
  /* Light dots on the dark band now, like the reverse ridge two sections up. */
  .ridge--closing {
    --dot-tone: var(--dot-tone-reverse);
    --dot-gain: var(--dot-gain-reverse);
    height: var(--closing-terrain);
  }
  /* Careers and 404 */

  /* No terrain on paper. Measured across the 404's field the row means run 242 to 248
     against a 248 ground: a 2.6% difference, which is a smudge rather than a landform,
     and it was costing 219px of a 1,097px page to be one. The home page's hero ridge
     came off for the same reason two hundred lines up. What is left is the two bands
     that are dark, where the same field reads as terrain, and the two pages below now
     end on one of those as well. */
  .page-head .ridge { display: none; }
  .page-head { padding-block: 34px 56px; }
  /* Same standfirst size as the home page's. It was one step down, so careers and
     the 404 opened quieter than the page they are reached from. */
  .page-head .lead { font-size: 19px; line-height: 1.5; }
  /* 12vw, not 10.6. At 41.3px against a 34px .display the interior title was 1.22 times
     its own section heads, where the home page runs 1.78 and the desktop pair runs 1.36,
     and both sizes land on the first careers screen together. Nothing here is fold-bound:
     .page-head has padding and no minimum height. */
  .page-head h1 { font-size: clamp(40px, 12vw, 58px); }
  .page-band { padding-block: 56px; }
  .role { padding-block: 28px; gap: 14px; }
  .role h3 { font-size: 26px; }
  /* Careers closes the way the home page does: the last band turns over, and the field
     that was invisible on paper has a ground it can be seen on. */
  .contact-line {
    --contact-terrain: clamp(120px, 15vh, 160px);
    padding-block: 76px calc(var(--contact-terrain) + 40px);
    background: var(--ground-deep);
    color: var(--text-reverse);
    border-top: 0;
  }
  .contact-line a { color: var(--emerald-lift); text-decoration-color: var(--emerald-lift-rule); }
  /* Its own pressed state, because the rule above it is a later, equal-specificity colour
     and was winning in :active as well: the mail link on the dark band computed the same
     green pressed as at rest, and the tap highlight over this ground is 1.02:1. */
  .contact-line a:active { color: var(--ink-reverse); }
  .contact-line .ridge {
    --dot-tone: var(--dot-tone-reverse);
    --dot-gain: var(--dot-gain-reverse);
    height: var(--contact-terrain);
  }
  /* Top-aligned, so the deep pad plus the centring stops opening the page on 380px
     of paper before the first word. It still takes the fold, because at 425px of
     content the footer began inside it and the wordmark appeared twice on one
     screen, the second time larger than the first. */
  /* Filling the fold is what keeps the footer and its second, larger wordmark off a
     screen that has already said there is nothing here. The cost was 480px of empty
     paper between the way back and the first dot. The terrain takes that space instead:
     the lower two fifths of the screen is landscape, which is the one image this page
     is entitled to. */
  /* Centred in whatever is left rather than pinned to the top of it. A short screen
     has no slack and looks the same either way; a 390x844 one had 243px of bare paper
     between the link and the first dot, which is 29% of the fold and the first thing
     on the page. */
  /* The fourth band pinned to its terrain. A 300px floor on the ridge against a 150px
     floor on the pad meant that at 320x568 half the canvas was behind the copy before
     anything moved; at 390x844 the opposite happened and the fold opened with 243px of
     bare paper between the link and the first dot. One value drives both, and the copy
     centres in what is left over. */
  .page-miss {
    /* No fold-height floor on a phone. Held to the full fold the section had 209px of
       slack in it and nowhere to put it: centred, half opened above the headline and
       half below the link, and pinned to the top it all fell in one hole before the
       terrain. Sized to what is in it, the copy and the landform fill 84% of a 390x844
       fold and the footer's top edge shows under them, which is the truer signal that
       the page continues. */
    /* Dark, like the last band of the other two pages. This page is only one band, so
       turning it over turns the whole thing over, which is the right amount of signal
       for a page whose job is to say the address was wrong. */
    --miss-terrain: clamp(150px, 22vh, 220px);
    min-height: 0;
    padding-block: 56px calc(var(--miss-terrain) + 36px);
    background: var(--ground-deep);
    color: var(--text-reverse);
  }
  .page-miss .ridge {
    --dot-tone: var(--dot-tone-reverse);
    --dot-gain: var(--dot-gain-reverse);
    height: var(--miss-terrain);
  }
  /* Same step, and this page is a headline and a link. At 37.4 it was the smallest
     headline on the site. */
  .page-miss h1 { font-size: clamp(38px, 11.4vw, 50px); color: var(--ink-reverse); }
  .page-miss .text-action { color: var(--emerald-lift); }
  .page-miss .text-action:active { color: var(--ink-reverse); }
}

/* One column in the footer, on every phone held upright. Two tracks were carrying a
   three-item list beside a two-item one with the contact pair spanning underneath, so the
   block was a grid with a hole in its lower right and a rule across it to explain the
   third group's odd position. Stacked, the three groups are three groups. */
@media (max-width: 430px) {
  .footer-main { grid-template-columns: minmax(0, 1fr); row-gap: 26px; }
  /* The rule went with the second column. It was there to say the contact pair was not a
     lost third column, and there is no column for it to be lost from now. */
  .footer-col--access { display: flex; padding-top: 0; border-top: 0; }
}

@media (max-width: 400px) {
  .button { padding: 15px 20px; gap: 12px; letter-spacing: .12em; }
  /* Inline only. The hero's block padding is set against svh above and a shorthand here
     would put a fixed number back on the one button whose height is fold-bound. */
  .hero-actions .button, .closing .button, .mobile-menu-cta { padding-inline: 18px; }
  .closing .button, .mobile-menu-cta { padding-block: 16px; }
  /* Six lines at 360 and 375, and the block has to clear the fold. 26px comes back out
     of the argument for a step the deck above it can carry. */
  .hero-body p + p { line-height: 1.45; }
}

/* At 320 the 22px gutter leaves a 34-character measure, which is short enough that
   the second hero paragraph runs to nine lines and pushes the button off the fold. */
@media (max-width: 360px) {
  :root { --page-pad: 18px; }
  /* 16.4vw rather than 17.6, because the gutter drops to 18 here and the column is a
     larger share of the screen. The svh term carries through from the rule above: without
     it this override reinstated a width-only headline, which on a 320x568 phone is 52px
     against the 44 its 460px of small viewport can hold. */
  .hero h1 { font-size: clamp(36px, min(16.4vw, var(--svh) * .11875 - 17.7px), 56px); }
  .display { font-size: 31px; }
}

/* The 4" phone, which is the one screen the hero cannot fit whole: 388px of fold against
   474px of headline, standfirst, argument and two actions. Every ordering of those five
   things leaves something below the line, so the only question is which thing, and the
   first two answers were both bad. Setting the headline small enough to make room bought
   15px and spent the largest words on the site to do it, then cut the filled action along
   the baseline of its own label. Setting it at the size the column wants moved the cut off
   the button and onto the link instead, 35px of it, on the same phone with its bars up.
   So the argument moves under the button here, and the second action stays where it was,
   at the foot of the block. Screen one becomes the headline, the standfirst, the filled
   action and the first three lines of the argument running past the fold, which is a
   paragraph being interrupted rather than a control being sliced.
   Putting both actions above the argument was the first try and it was worse: an emerald
   phrase with a rule under it, sitting directly on top of a paragraph, is the shape of a
   section heading, and the words are "How it works", which is a section heading further
   down this page.
   display:contents dissolves the two wrappers so the paragraphs and the two actions are
   siblings in the copy column's own flex order and can be interleaved. The wrappers were
   carrying a top margin, a gap and two caps that never bound at this width: 42ch is 328px
   in a 284px column, and so is 400px.
   Reading order is unchanged, so the argument still follows the standfirst for a screen
   reader, and the button is the first thing in the hero that takes focus either way.
   Gated on width, not height: a height query is answered against the large viewport while
   svh resolves to the small one, so there is no media query that can ask "is this window
   short". */
@media (max-width: 340px) {
  .hero h1 { font-size: clamp(36px, 16.4vw, 56px); }
  .hero-body, .hero-actions { display: contents; }
  .hero-body p:first-child { margin-top: clamp(12px, var(--svh) * .0677083 - 22.44px, 28px); }
  .hero-actions .button { margin-top: clamp(14px, var(--svh) * .0677083 - 18.44px, 32px); }
  /* The interval this margin used to draw was between two paragraphs. Now it is between
     the filled action and the body text under it, which is a wider seam and takes the
     figure the block above it uses. */
  .hero-body p + p { order: 1; margin-top: 26px; }
  /* Last, and back to sitting under body text rather than over it. align-self, because
     the cap that used to hold this to the left of a stretched column went with the
     wrapper. 22 is flat and it is doing two jobs: it is the seam between a paragraph and
     the link that follows it, and it is what clears this box past the fold on a 4" phone
     with its bars retracted, where at 12 the fold cut 3px off the top of it. Both of the
     heights that phone has are below it now, 564 and 574 against folds of 460 and 568. */
  .hero-actions .text-action { order: 2; align-self: flex-start; margin-top: 22px; }
}

/* Everything above keys touch sizing off width, on the assumption that a narrow screen
   is a thumb and a wide one is a mouse. An iPad breaks it: 768 to 820 wide, no mouse,
   and it was taking the desktop targets. A footer link measured 25.8px there and a
   section link 28.8px, which clears 2.5.8's 24px floor by a hair and misses the 44pt
   one by 18. Keyed on the primary pointer, so a desktop with a touchscreen attached
   keeps its mouse sizing. */
@media (pointer: coarse) {
  .text-action { padding-block: 13px 17px; }
  .text-action::after { bottom: 8px; }
  /* The column stretches nothing, so a short label is as wide as its word: "About" was a
     40px target between two full-width ones. Gated on the pointer rather than on width,
     because a phone held sideways is 844 wide and never matched the 640px block this
     started in. */
  .footer-col a { padding-block: 10px; min-width: 44px; }
  /* The no-script fallback, and an iPad, where the burger only appears under 980 and this
     bar is the navigation on a touchscreen. At 6px of padding these were 36.9px tall, and
     "About" is a 40px word. */
  .nav-links a { padding-block: 10px; min-width: 44px; }
  /* The home link, which this block had missed: 36px tall on an unfolded foldable and on
     an iPad in portrait, against 45 on a phone and 50 inside the menu panel at those very
     widths. Same 7px the phone branch gives it. */
  .lockup { padding-block: 7px; }
}

/* A phone held sideways is 844x390: it misses the width-keyed phone rules entirely
   and takes the tablet path, which gives it a 74px bar over a fifth of the fold, a
   79-character measure and a menu panel taller than the screen. */
@media (max-width: 1024px) and (max-height: 520px) and (orientation: landscape) {
  :root { --nav-height: 56px; }

  /* The nav bar's and footer's copies of the lockup take their padding from the 640px
     block, which a sideways phone never matches: both measured 36px here against the 45
     they hold in portrait. The menu's copy carries its own and is unaffected. */
  .lockup { padding-block: 7px; }

  body { font-size: 16px; line-height: 1.62; }

  /* Sideways the figure is back beside the copy, so the terrain has nothing above it
     and takes the same height as every other ridge here. */
  .ridge--hero { display: none; }
  .hero { min-height: 0; padding-block: 14px 40px; }
  .hero-layout {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
    gap: 40px;
    /* Top, not centre. The copy column runs 446 to 526 tall against a fold of 218 to
       334, so centring the shorter figure against it pushed the drawing 66 to 127px
       down and put the fold through the middle of the model node on every landscape
       phone on the list. Started at the headline it clears the fold outright on a 6.7"
       screen and loses only its caption on a 6.1". The hole this leaves under the
       figure sits beside the actions, which are below the fold anyway. */
    align-items: start;
  }
  /* The figure is beside the copy here, not under it, so the rule that holds the copy
     column to the fold height has nothing to push past and is switched off. */
  .hero-copy { max-width: none; min-height: 0; display: block; }
  .hero h1 { margin-top: 0; font-size: clamp(38px, 6.2vw, 58px); line-height: .96; }
  .hero-body { margin-top: 16px; }
  /* Ranked here too. Side by side the two were both 11.5px tracked caps at 48.6px tall
     with the link's rule sitting 7px above the button's bottom edge, which is the pair
     portrait stopped trying to build. Centred on each other, one filled control and one
     sentence-case link, there is nothing left to line up. */
  .hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 26px; margin-top: 24px; }
  .hero-actions .text-action {
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
  }
  .hero-figure { justify-content: center; }
  /* The landscape figure column is about 380px wide and the fold is 390px tall, so
     the portrait variant is the one that fits. */
  .hero-figure .forecast--desktop { display: none; }
  /* 268 and not 272, with 4px also taken off the padding above: the drawing is 1.088
     times as tall as it is wide, so those 8px are what puts its foot at 362 on a 6.7"
     screen whose fold is at 366. Four pixels of clearance is not much, but the
     alternative was four pixels of the caption sliced off. */
  .hero-figure .forecast--compact { display: block; max-width: 268px; }
  /* Both sizes are svg user units riding a 360-unit viewBox, so what a reader sees is
     the unit times the drawing's own scale. Flat numbers had been set one at a time and
     the caption ended up above the labels it captions: 13.03px against 11.54 at the cap,
     and 10.88 against 9.64 on a 4" phone sideways, where the column is narrower than the
     cap. That is the same inversion the portrait ramps exist to remove.
     17.75 is the unit size portrait uses at its tightest, at 320 wide, so the input row
     cannot collide at any landscape width; 16.5 puts the captions a step under the labels
     at the .93 ratio the rest of the figure holds. Rendered, that is 13.2 / 12.3 at the
     cap and 11.0 / 10.3 at 568, ranked the right way round at both ends. */
  .forecast--compact .forecast-title,
  .forecast--compact .forecast-note { font-size: 16.5px; }
  .forecast--compact .forecast-label { font-size: 17.75px; }

  /* Rotating the phone was shrinking the section heads from 34 to 30 while the measure
     grew from 346px to 589px: a bigger canvas getting smaller type over longer lines. */
  .display { font-size: 34px; }
  .loop-steps h3 { font-size: 21px; }
  /* 21, the same as the sub-head it is parallel to. Held a step below at 18.5 it sat 3px
     off the paragraph under it, which is drift rather than a step. The portrait branch
     settled this and this branch had kept the number portrait rejected. */
  .points dt { font-size: 21px; }

  /* Everything under here is the phone's type ranking, which stops at 640 wide and so
     never reached a phone held sideways: at 641 the statement bands dropped by a third,
     the hero's two paragraph sizes tied, and the 15.5-against-16 drift the portrait
     branch collapsed came back across six regions. Turning a phone does not make it a
     different document. */
  .thesis .display, .closing .display { font-size: clamp(40px, 5.4vw, 48px); }
  /* 17.5 over 16, which is the phone's 19-over-16 step at the size this canvas sets. Both
     were 15.5 here, so the one place the hero asks the eye to step down did not step, and
     the opening block was set smaller than every other paragraph on the page. */
  .hero-body p { font-size: 17.5px; line-height: 1.5; }
  .hero-body p + p { font-size: 16px; line-height: 1.6; }
  .loop-steps p, .points dd { font-size: 16px; }
  /* The careers page opened a size quieter than the page it is reached from: 17 against
     the 19 this branch already gives the home page's leads. */
  .page-head .lead { font-size: 19px; }
  /* A measure, which this heading has nowhere else. Sideways it ran the whole shell and
     broke "perform." onto a line of its own at 23% of the width. */
  .page-head h1 { max-width: 15ch; }

  /* The orphan rule, which lives in the 640 block and so was off across this whole
     branch. Sideways is where it is needed most: the measure is at its longest here. */
  .prose p, .thesis-say p, .audience-body > p, .points dd, .loop-steps p,
  .page-head p, .role p, .hero-body p, .closing p, .contact-line p,
  .page-miss p { text-wrap: pretty; }

  .quality, .loop, .audience, .about { padding-block: 48px; }
  .thesis { padding-block: 56px 120px; }
  /* Same band as portrait. Turning the phone sideways should not turn the last screen
     of the page from dark back to cream. */
  .closing {
    --closing-terrain: clamp(130px, 30vh, 170px);
    padding-block: 48px calc(var(--closing-terrain) + 26px);
    background: var(--ground-deep);
    color: var(--text-reverse);
    border-top: 0;
  }
  .closing .display { color: var(--ink-reverse); }

  /* The same ground the portrait branch gives this section, for the same reason: a phone
     held sideways runs the middle of the page at 11 screens rather than 4. */
  .audience--advertisers {
    padding-block: 56px 52px;
    background: var(--ground-deep);
    color: var(--text-reverse);
  }
  .audience--advertisers .eyebrow,
  .audience--advertisers .lead { color: var(--text-reverse); }
  .audience--advertisers .display,
  .audience--advertisers .points dt { color: var(--ink-reverse); }
  .audience--advertisers .text-action { color: var(--emerald-lift); }
  .audience--advertisers .text-action:active { color: var(--ink-reverse); }
  .audience--publishers { padding-top: 56px; }

  /* Both buttons, not just the closing one. Turned sideways the page was serving the same
     control in two greens: the hero's on the base --emerald-deep and the closing band's on
     --emerald, which is the exact split the portrait branch exists to collapse. */
  /* 40px under the panel and 30 over the list are portrait numbers. On a 4" phone held
     sideways they are a fifth of the whole height, and the list was giving up 34px of
     itself to hold them. The head cannot go under 44, because the close button is 44
     square and sets its own floor; the call to action can, down to the same 44 once its
     label's line box is counted. Together the list gets 141px on that phone rather than
     91, which is the difference between three rows of 47 and three rows of 42 with the
     list scrolling under them. */
  .mobile-menu { padding-bottom: calc(20px + env(safe-area-inset-bottom)); }
  .mobile-menu-links { margin-top: 16px; }
  .mobile-menu-head { height: 44px; }
  .mobile-menu-cta { margin-top: 8px; padding-block: 12px; }

  .hero-actions .button, .closing .button, .mobile-menu-cta { background: var(--emerald); }
  .hero-actions .button:active, .closing .button:active { background: var(--emerald-deep); }
  .closing .button:active { box-shadow: inset 0 0 0 1px var(--emerald-lift-rule); }
  @media (hover: hover) {
    .hero-actions .button:hover, .closing .button:hover { background: var(--emerald-hover); }
  }
  .footer { border-top: 0; }
  /* 41ch, where this said 58. The ch unit is the advance of a zero, and in Urbanist that
     is .636em against a real average advance closer to .44, so 58ch resolved to 591px and
     set 83 characters to the line. Rotating the phone was taking the measure from 48
     characters to 83 at the same 16px type, which is what this cap was added to stop. */
  .prose, .thesis-say, .audience .lead, .audience-body > p, .points dd,
  .loop-steps p, .page-head p, .role p { max-width: 41ch; }
  .audience .lead { font-size: 19px; }
  .about .prose p { font-size: 19px; color: var(--ink-soft); }

  /* Symmetric, so the label sits on the same line as the label in the filled button
     beside it. Asymmetric they were 2px apart, which is the amount that reads as a
     mistake rather than a step. 15 also puts the box at 44 tall. */
  /* Its own grid. Landscape was taking the 980px two-column footer, which put the
     first column heading in the right-hand track and the second one below it on the
     left, so the reading order ran across and then back. It also stopped at x=584 on
     an 844 screen. Four tracks across the measure puts it back in order. */
  .footer-main {
    grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
    column-gap: 34px;
    row-gap: 0;
  }
  .footer-brand { grid-column: auto; }
  .footer-col--access {
    grid-column: auto;
    display: flex;
    flex-direction: column;
    padding-top: 0;
    border-top: 0;
  }
  .footer-col--access h2 { margin-bottom: 6px; }

  .text-action { padding-block: 15px; }
  .text-action::after { bottom: 6px; }
  /* Left to the pointer-coarse rule, which sets 10 to hold these at 44 on a touchscreen.
     At 8 they measured 40.3px here purely because this block is later in the file. */

  .ridge { height: clamp(140px, 34vh, 190px); }
  .ridge--closing {
    --dot-tone: var(--dot-tone-reverse);
    --dot-gain: var(--dot-gain-reverse);
    height: var(--closing-terrain);
  }
  /* Same three decisions as portrait, for the same reasons: no terrain on paper, and
     the last band of each page turns over. */
  .page-head .ridge { display: none; }
  .contact-line, .page-miss {
    background: var(--ground-deep);
    color: var(--text-reverse);
    border-top: 0;
  }
  .contact-line a { color: var(--emerald-lift); text-decoration-color: var(--emerald-lift-rule); }
  /* Its own pressed state, because the rule above it is a later, equal-specificity colour
     and was winning in :active as well: the mail link on the dark band computed the same
     green pressed as at rest, and the tap highlight over this ground is 1.02:1. */
  .contact-line a:active { color: var(--ink-reverse); }
  .page-miss h1 { color: var(--ink-reverse); }
  .page-miss .text-action { color: var(--emerald-lift); }
  .page-miss .text-action:active { color: var(--ink-reverse); }
  .contact-line .ridge, .page-miss .ridge {
    --dot-tone: var(--dot-tone-reverse);
    --dot-gain: var(--dot-gain-reverse);
    height: clamp(130px, 30vh, 170px);
  }
}

/* Windows high-contrast mode strips backgrounds, which would erase the arrow
   masks and the hairlines that carry most of the structure here. */

@media (forced-colors: active) {
  .arrow { background: CanvasText; }
  .button { border: 1px solid ButtonText; }
  .nav-in::after,
  .text-action::after,
  .nav-burger span,
  .mobile-menu-close span,
  .nav-links a::after { background: CanvasText; forced-color-adjust: none; }
  .lockup-up, .lockup-low { fill: CanvasText; }
  /* Doubled up: the per-breakpoint caption sizes are set on .forecast--compact
     .forecast-title, which outranks a bare .forecast-title and was keeping the
     authored grey. On a black canvas that measured 4.30:1 at 13.8px. */
  .forecast .forecast-title { fill: CanvasText; }
  /* The nodes are filled with paper so the line reads as passing behind them. On a
     dark high-contrast canvas a white fill would be a row of bright dots. */
  .forecast-node { fill: Canvas; }
  /* Twelve rings authored between 0.030 and 0.170 opacity, which reads as a soft
     halo. Forced to full CanvasText they come out as a solid disc with the node, the
     mark and every filament crossing it lost inside, so the figure ends up less
     legible in high contrast than in normal mode. The halo is atmosphere; drop it. */
  .forecast-field { display: none; }
  .ridge { display: none; }
  :focus-visible { outline: 2px solid Highlight; outline-offset: 2px; }
}
