/* predicated.ai, the page at the apex.
 *
 * A different site from the forecast that lives under it, sharing the brand
 * and nothing else. It restates the token values rather than loading
 * `tokens.css`, and that is deliberate: this page is committed to one dark
 * palette with no theme switch, it is deployed on its own to the bucket root,
 * and a change made for a chart on the forecast site must not be able to
 * silently restyle the firm's front door.
 *
 * The three type roles are the forecast's, doing the same jobs. Space Grotesk
 * -- the wordmark face -- takes the display role here and is set at size
 * nowhere else on the brand, which is what keeps this page from reading as the
 * forecast with different copy. Newsreader carries the firm's own two
 * sentences, the hero's and the footer's, and nothing in between.
 */

:root {
  color-scheme: dark;

  --ground: #101619;
  --surface: #182126;
  --sunken: #0b1013;
  --ink: #e8eef1;
  --muted: #a2b3be;
  --rule: #2a3940;

  /* Sampled from the aurora inside the mark, so an accent on the page and the
     fill in the mark are the same three colours rather than a near miss. */
  --warm: #f2913f;
  --hot: #ef5a3c;
  --cool: #39b4c9;
  --teal: #3aada7;
  --violet: #a988e0;

  --prose: Newsreader, Georgia, serif;
  --display: Archivo, "Helvetica Neue", sans-serif;
  --body: Archivo, system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace;
  --wordmark: "Space Grotesk", Archivo, system-ui, sans-serif;

  --page: 1240px;
}

* { box-sizing: border-box; }

/* What shows outside the document when a touch device rubber-bands past
   either end. That area is painted from `html`, and there is only one of it,
   so both ends of the page have to agree on a colour: the hero sits on
   `--sunken` and the footer's ground is within a point of it. Reported from an
   iPad as a black band under the footer, back when the footer was pale.
   Stated here rather than left to inherit from `body`, so that changing either
   ground has to come past this note. */
html { background: var(--sunken); }

body {
  margin: 0;
  background: var(--sunken);
  color: var(--ink);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

/* The clip path the two marks are drawn through. It renders nothing and must
   take no room. */
.brand-defs { position: absolute; inline-size: 0; block-size: 0; overflow: hidden; }

.wrap { max-inline-size: var(--page); margin-inline: auto; padding-inline: clamp(1.2rem, 4vw, 3rem); }

/* The apparatus role -- eyebrows, column heads, metadata -- mono, tracked,
   upper, the way the forecast site sets the same things. */
.label {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
/* An eyebrow that lists several things wraps on a phone, and left to itself it
   breaks inside one of them: "machine / learning", "live & / public". Each
   item is wrapped in a span so the only break points are the separators
   between them. */
.label span { white-space: nowrap; }

/* Off-screen until focused, and drawn the way the forecast site draws it. */
.skip-link {
  position: fixed; inset-block-start: 0.6rem; inset-inline-start: 0.6rem; z-index: 40;
  transform: translateY(-250%);
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule); border-radius: 999px;
  padding: 0.45rem 1rem; font-family: var(--mono); font-size: 0.74rem; text-decoration: none;
}
.skip-link:focus { transform: none; }

/* The masthead is fixed, so focusing the landmark would otherwise put the top
   of the page underneath it. */
#content { scroll-margin-block-start: 5rem; outline: none; }

/* Every interactive thing keeps a visible ring. The page is dark and the
   default ring is nearly invisible on it. */
a:focus-visible {
  outline: 2px solid var(--cool);
  outline-offset: 3px;
  border-radius: 2px;
}

/* The firm's name set inside a sentence: the wordmark, so it is drawn in the
   wordmark's face rather than the paragraph's. A hair smaller than its
   surroundings, because Space Grotesk runs wide and large against a serif. */
.wm { font-family: var(--wordmark); font-weight: 600; font-size: 0.94em; letter-spacing: -0.02em; }

/* One treatment for the top-level domain, wherever the name is set on the
   dark ground: dimmed rather than coloured, so `.ai` reads as the quieter
   half of one word. It was the headline's cyan in the dek and dimmed in the
   masthead, which made the same name look like two different marks a screen
   apart. The footer is the deliberate exception -- it wears the brand accent
   there, the way the forecast site's footer does. */
/* The suffix is the one part of the name that is not the word, so it is set
   apart -- but it was set apart by being dimmed, which made the firm's own
   name the darkest thing in the masthead. It takes the page's cool token
   instead: already in the palette, so no new colour enters the system, and it
   is the colour at the base of the mark standing next to it. One rule for all
   three places the name is set, so the nav, the dek and the footer agree on
   every page. */
.top .wordmark .tld,
.wm .tld { color: var(--cool); }

/* ---- the field --------------------------------------------------------- */

/* One field of dots runs behind the whole page. It opens as a cloud, resolves
   into an ordered arc in a screen of its own, and scatters again for the
   close. The page never explains it and does not need to: the gesture is
   spreading out over what could happen and then landing on one thing, which
   is what all four practices have in common.

   Prose never sits on the field, with one exception. The sections that carry
   argument stand on their own opaque plates, so the dots read in the gutters
   and at the seams rather than through a paragraph. The exception is the
   interlude, and it is handled rather than tolerated: `landing.js` dims and
   softens the field for exactly as long as the paragraph is crossing the arc. */
#field { position: fixed; inset: 0; z-index: 0; display: block; }
.page { position: relative; z-index: 1; }

/* ---- masthead ---------------------------------------------------------- */

/* The lockup is drawn larger than it is on the forecast site, and the
   difference is a difference in job: there it is chrome above an article, here
   the firm is the subject of the page. It scales with the viewport rather than
   stepping at a breakpoint, because a lockup that jumps size mid-resize reads
   as a bug. */
.top {
  position: fixed; inset-block-start: 0; inset-inline: 0; z-index: 3;
  display: flex; align-items: center; gap: 1rem;
  padding: clamp(0.8rem, 1.4vw, 1.2rem) clamp(1.2rem, 4vw, 3rem);
  backdrop-filter: blur(14px);
  background: color-mix(in oklab, var(--sunken) 62%, transparent);
  border-block-end: 1px solid color-mix(in oklab, var(--rule) 60%, transparent);
}
.top .lockup { display: flex; align-items: center; gap: clamp(0.55rem, 0.9vw, 0.85rem); text-decoration: none; }
.top .brand-mark {
  display: block; inline-size: auto; block-size: clamp(30px, 2.9vw, 42px);
  aspect-ratio: 503 / 706; color: var(--cool);
}
.top .wordmark {
  font-family: var(--wordmark); font-weight: 600; letter-spacing: -0.02em;
  font-size: clamp(1.2rem, 1.75vw, 1.6rem); line-height: 1;
}
.top .lockup:hover .wordmark { text-decoration: underline; text-underline-offset: 5px; }
.top .cta {
  margin-inline-start: auto; font-family: var(--mono); font-size: 0.76rem;
  letter-spacing: 0.06em; text-decoration: none; white-space: nowrap;
  color: var(--sunken); background: var(--ink);
  padding: 0.55rem 1.1rem; border-radius: 999px;
}
.top .cta:hover { background: var(--cool); }

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

.hero { position: relative; min-block-size: 100svh; display: flex; align-items: center; }
/* The field, dimmed where the copy sits and left alone everywhere else.
   The hero's copy is the only text on the page set over the open field, and
   even at 84% ink with a halo it was reading against sharp dots at about 14px
   on a handset. This dims them rather than blurring them away or walling the
   copy off behind a panel: the stars stay stars, individually visible, just
   muted -- and they come back to full strength toward the edges.
   Two gradients, because they answer different questions. The background is
   how dark, and it is centred. The mask is where, and it is anchored over the
   copy rather than over the viewport, so the right-hand half of a wide screen
   keeps its dots. Without mask support the wash simply covers more, which is
   the same idea less precisely aimed rather than a broken layout.
   A wash rather than a `backdrop-filter`: the frosted versions of this read
   well too, but they recomposite a full-viewport region over a canvas that
   repaints every frame, and this costs nothing at all.
   Negative z-index rather than a layer on top. `.page` already establishes
   the stacking context, so the wash cannot fall behind the canvas -- and the
   copy needs no z-index of its own, which matters because the scroll hint is
   positioned against `.hero` from inside the copy block and giving that block
   a position of its own would move it. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(100% 100% at 50% 50%,
    rgba(9, 14, 17, 0.82) 0%, rgba(9, 14, 17, 0.62) 55%, rgba(9, 14, 17, 0) 100%);
  -webkit-mask-image: radial-gradient(125% 62% at 30% 52%, #000 0%, #000 42%, transparent 80%);
  mask-image: radial-gradient(125% 62% at 30% 52%, #000 0%, #000 42%, transparent 80%);
}
.hero .in { padding-block: 9rem 4rem; }
/* The eyebrow names four practices and is too long for one line on a phone.
   Balanced, it splits two and two rather than leaving one alone on the second
   line -- which works because `.label span` has already made the practices
   themselves unbreakable, without which the balancer counts characters and
   puts the line end inside "machine learning". */
.hero .label { display: block; text-wrap: balance; }
.hero h1 {
  font-family: var(--wordmark); font-weight: 600;
  font-size: clamp(2.4rem, 7vw, 5.4rem); line-height: 1.02; letter-spacing: -0.035em;
  margin: 0.8rem 0 0; max-inline-size: 15ch;
  text-shadow: 0 2px 40px rgba(6, 10, 12, 0.85);
}
/* The second half of the tagline is one clause and one colour, so it breaks
   before "One", never inside it. At the smallest viewport the page targets it
   measures 232px against 282px of measure, so holding it together costs no
   overflow. */
.hero h1 span { color: var(--cool); white-space: nowrap; }
/* The dek is the firm's one sentence about itself, so it is set in the prose
   face rather than the chrome face -- the same serif that closes the page in
   the footer, and set nowhere in between. */
.hero .dek {
  font-family: var(--prose); margin-block-start: 1.6rem;
  max-inline-size: 34ch; font-size: clamp(1.22rem, 2.1vw, 1.72rem); line-height: 1.42;
  color: var(--ink); text-shadow: 0 1px 24px rgba(6, 10, 12, 0.9);
}
/* This line is the smallest type on the page and the only type set over the
   open field, which is the pairing that made it hard to read on a handset:
   muted grey at about 14px with dots passing behind the glyphs, and dimmed a
   second time by the dek's own opacity. So it is near-ink rather than muted,
   dimmed once rather than twice, and carries a tight halo of its own -- the
   wide 24px shadow above softens a whole area, which does nothing about a dot
   sitting on a stem. */
.hero .dek .rest {
  display: block; margin-block-start: 0.7rem;
  font-size: 0.74em; line-height: 1.5; max-inline-size: 44ch;
  color: color-mix(in oklab, var(--ink) 84%, var(--ground));
  text-shadow: 0 1px 3px rgba(6, 10, 12, 0.95), 0 0 14px rgba(6, 10, 12, 0.92);
}
.scroll-hint {
  position: absolute; inset-block-end: 1.6rem; font-family: var(--mono);
  font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
}

/* ---- plates: prose stands on its own ground ---------------------------- */

.plate-band { background: var(--ground); border-block: 1px solid var(--rule); }
.plate-band.sunk { background: var(--surface); }

.practice { padding-block: clamp(3.5rem, 8vw, 6rem); }
.practice .head { margin-block-end: 1rem; }
.practice .head h2 {
  font-family: var(--wordmark); font-weight: 600; font-size: clamp(2.1rem, 4.6vw, 3.4rem);
  letter-spacing: -0.03em; line-height: 1.05; margin: 0.7rem 0 0;
  max-inline-size: 20ch; text-wrap: balance;
}

/* Four practices as full-width rows: apparatus left, argument right. The
   quadrant this replaced fitted all four onto one screen, which is exactly
   what made it small -- two columns of 54ch capped the headings at 1.3rem and
   pushed the body copy into muted grey, so the section read as a footnote to
   the masthead above it. Given the whole measure a practice can state itself
   at a size that answers the masthead and still hold a 60ch line. */
.cards { display: grid; grid-template-columns: 1fr; }
.card {
  display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  column-gap: clamp(1.5rem, 4vw, 3.5rem); padding-block: clamp(2rem, 4vw, 3rem);
  border-block-start: 1px solid var(--rule);
}
.card .dotmark { grid-column: 1; grid-row: 1; display: flex; gap: 5px; margin-block-start: 0.6rem; }
.card .dotmark i { inline-size: 11px; block-size: 11px; border-radius: 50%; display: block; background: var(--accent); }
.card .dotmark i:nth-child(2) { opacity: 0.6; }
.card .dotmark i:nth-child(3) { opacity: 0.3; }
/* The rail below the dotmark: the practice's name, and the mark that draws it.
   One cell rather than two, so the mark hangs off the label wherever the label
   ends up -- the label is aligned to the top of the paragraph beside it, not
   to the heading, and pinning the mark to a row of its own would have set it
   against the pills instead. */
.card .rail { grid-column: 1; grid-row: 2; align-self: start; }
.card .label { display: block; color: var(--accent); }
/* Hairline, `currentColor`, and no container: the mark takes the accent the
   label above it is already set in, so the rail reads as one apparatus block
   in one colour. The page spends its boldness on the field behind it, so
   these stay drawings rather than badges -- no tint, no ring, no chip. */
.card .glyph {
  display: block; inline-size: 64px; block-size: 64px;
  margin-block-start: 1.2rem; color: var(--accent);
}
.card.fcs { --accent: var(--warm); }
.card.mle { --accent: var(--violet); }
.card.llm { --accent: var(--cool); }
.card.eng { --accent: var(--teal); }
.card h3 {
  grid-column: 2; grid-row: 1;
  font-family: var(--wordmark); font-weight: 600; font-size: clamp(1.6rem, 3vw, 2.5rem);
  letter-spacing: -0.025em; line-height: 1.15; margin: 0 0 0.9rem;
}
.card p {
  grid-column: 2; grid-row: 2; margin: 0; max-inline-size: 60ch;
  font-size: 1.12rem; line-height: 1.62;
  color: color-mix(in oklab, var(--ink) 80%, var(--ground));
}
.card ul {
  grid-column: 2; grid-row: 3;
  list-style: none; margin: 1.4rem 0 0; padding: 0; font-family: var(--mono);
  font-size: 0.76rem; color: var(--muted); display: flex; flex-wrap: wrap; gap: 0.4rem;
}
.card li { border: 1px solid var(--rule); border-radius: 999px; padding: 0.25rem 0.75rem; }

/* ---- the interlude: the one stretch where the field is in the open ------
   The paragraph rises through the arc rather than beside it, and the field
   gets out of its way while it does. Because the dots dim under the text, the
   type needs far less of a shadow to survive them than it would otherwise. */
.interlude {
  min-block-size: 108svh; display: flex; align-items: flex-end;
  padding-block-end: clamp(2rem, 6vw, 4rem);
}
.interlude h2 {
  font-family: var(--wordmark); font-weight: 600; font-size: clamp(1.6rem, 3.6vw, 2.6rem);
  letter-spacing: -0.03em; line-height: 1.08; margin: 0.7rem 0 0.8rem; max-inline-size: 22ch;
  text-shadow: 0 2px 30px rgba(6, 10, 12, 0.7);
}
/* The second beat stays whole, the same rule the masthead's own second half
   keeps. It is short enough to hold together at 320px, so the only break the
   line can take is the one between the two sentences. */
.interlude h2 span { display: inline-block; }
.interlude p {
  margin: 0; max-inline-size: 52ch; color: var(--muted); font-size: 0.98rem;
  text-shadow: 0 1px 18px rgba(6, 10, 12, 0.8);
}

/* ---- work -------------------------------------------------------------- */

.work { padding-block: clamp(3.5rem, 8vw, 6rem); }
/* Same head as the practice section above it, at the same size. It used to be
   a baseline row -- eyebrow beside the heading -- which read as a caption once
   the practice heading grew, and made the section that closes the argument
   look like the smaller of the two. */
.work .head { margin-block-end: 2.4rem; }
.work .head h2 {
  font-family: var(--wordmark); font-weight: 600; font-size: clamp(2.1rem, 4.6vw, 3.4rem);
  letter-spacing: -0.03em; line-height: 1.05; margin: 0.7rem 0 0;
  max-inline-size: 20ch; text-wrap: balance;
}
/* A block with links in it rather than one big link, for the reason the
   entries below give: an anchor wrapping the heading, the paragraph and four
   figures announces all ninety words as its own name, and a reader using one
   cannot skim past it. The card still lights up as a whole on hover, which is
   the only thing wrapping it ever bought. */
.case {
  display: block; padding: clamp(1.6rem, 3.5vw, 2.6rem);
  border: 1px solid var(--rule); border-radius: 6px; background: var(--ground);
  position: relative; overflow: hidden;
}
/* The aurora as a four-pixel edge: the brand's own colours marking the one
   piece of work that is live rather than archived. */
.case::before {
  content: ""; position: absolute; inset-block: 0; inset-inline-start: 0; inline-size: 4px;
  background: var(--aurora-edge) center / cover;
}
.case:has(a:hover), .case:focus-within { border-color: var(--cool); }
.case h3 {
  font-family: var(--wordmark); font-weight: 600; font-size: clamp(1.5rem, 3vw, 2.2rem);
  letter-spacing: -0.03em; margin: 0.6rem 0 0.7rem;
}
.case h3 a { text-decoration: none; }
.case h3 a:hover { text-decoration: underline; text-underline-offset: 5px; }
.case p { margin: 0; color: var(--muted); max-inline-size: 62ch; font-size: 0.97rem; }
.case .row { display: flex; align-items: flex-end; gap: 2rem; flex-wrap: wrap; margin-block-start: 1.6rem; }
.case .row div b { display: block; font-family: var(--mono); font-size: 1.4rem; font-variant-numeric: tabular-nums; }
.case .row div span {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted);
}
.go { font-family: var(--mono); font-size: 0.78rem; color: var(--accent, var(--cool)); text-decoration: none; }
.go:hover { text-decoration: underline; text-underline-offset: 3px; }
.case .go { margin-inline-start: auto; }

/* A card is a block with links in it rather than one big link, because one of
   the three has two destinations -- the library and the paper about it -- and
   an anchor cannot hold another anchor. It also stops a screen reader reading
   a whole paragraph out as the name of a link. */
.entries { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-block-start: 1rem; }
.entry {
  display: flex; flex-direction: column;
  padding: clamp(1.4rem, 2.6vw, 1.9rem);
  border: 1px solid var(--rule); border-radius: 6px; background: var(--ground);
}
.entry:has(a:hover), .entry:focus-within { border-color: var(--accent); }
.entry.eng { --accent: var(--teal); }
.entry.llm { --accent: var(--cool); }
.entry.mle { --accent: var(--violet); }
.entry h3 {
  font-family: var(--wordmark); font-weight: 600; font-size: 1.2rem; letter-spacing: -0.025em;
  line-height: 1.2; margin: 0.7rem 0 0.6rem; overflow-wrap: anywhere;
}
.entry h3 a { text-decoration: none; }
.entry h3 a:hover { text-decoration: underline; text-underline-offset: 4px; }
.entry p { margin: 0 0 0.7rem; color: var(--muted); font-size: 0.92rem; }
.entry p:last-of-type { margin-block-end: 0; }
.entry code { font-family: var(--mono); font-size: 0.85em; color: var(--ink); overflow-wrap: anywhere; }
/* The project names are links now, and look like it without shouting. The
   code face has already lifted them out of the muted paragraph around them,
   so the rule underneath can stay faint until a reader wants it and take the
   card's own accent on hover.

   Four inline links rather than one anchor around the card, which is the same
   reasoning the case block above is built on: an anchor wrapping a paragraph
   announces the whole paragraph as its own name, and a reader moving by link
   cannot skim past it. Here it also means the card names four repositories and
   offers four ways in, instead of one that lands on the org. */
.entry p a { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 3px; text-decoration-color: color-mix(in oklab, var(--accent) 50%, transparent); }
.entry p a:hover, .entry p a:focus-visible { text-decoration-color: var(--accent); }
.entry p a code { color: inherit; }
.entry .links {
  margin-block-start: auto; padding-block-start: 1.1rem;
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.2rem;
}

/* ---- close: back onto the field ---------------------------------------- */

.close { min-block-size: 74svh; display: flex; align-items: center; }
.close h2 {
  font-family: var(--wordmark); font-weight: 600; font-size: clamp(2rem, 5.2vw, 3.6rem);
  line-height: 1.05; letter-spacing: -0.03em; margin: 0.8rem 0 1.6rem; max-inline-size: 17ch;
  text-shadow: 0 2px 40px rgba(6, 10, 12, 0.85);
}
.close .big-cta {
  display: inline-flex; align-items: center; gap: 0.7rem;
  font-family: var(--mono); font-size: 0.86rem; text-decoration: none;
  color: var(--sunken); background: var(--ink); padding: 0.9rem 1.6rem; border-radius: 999px;
}
.close .big-cta:hover { background: var(--cool); }

/* ---- footer ------------------------------------------------------------
   The forecast site's footer, in its dark palette, with different links in it.
   Same slab, same aurora wash, same generated grain, same lockup at the same
   size, and the values below are that footer's own dark-theme block copied
   rather than reinterpreted -- a reader arriving from the forecast should not
   be able to tell the two apart except by what they say.

   It was drawn pale first, on the reasoning that a change of ground is what
   separates a footer from a page. That is true on the forecast site, where
   the page above it is white in the light theme; here the page is near-black
   from the masthead down and the pale slab arrived as a flashbulb at the end
   of a dark scroll. The separation comes from the wash and the hairline
   instead, which is exactly how the forecast site does it in dark.

   Grain over a pale ground has to darken; over a dark one it has to lift --
   hence `overlay` and white rather than `multiply` and grey, and a stronger
   opacity because a dark ground swallows it. The wash alphas step up for the
   same reason. */
.pub-foot {
  --foot-ground: #0b1114;
  --foot-ink: #eef3f6;
  --foot-muted: #a7bac6;
  --foot-rule: rgba(238, 243, 246, 0.15);
  --foot-accent: #ffab63;
  --grain-ink: #ffffff;
  --grain-blend: overlay;
  --grain-opacity: 0.34;
  --wash-warm: rgba(252, 120, 56, 0.3);
  --wash-mid: rgba(195, 136, 139, 0.2);
  --wash-cool: rgba(61, 167, 189, 0.15);
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--foot-ground); color: var(--foot-ink);
  border-block-start: 1px solid var(--foot-rule);
}
.foot-wash, .foot-grain { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.foot-wash {
  background: radial-gradient(120% 150% at 92% 40%,
    var(--wash-warm) 0%, var(--wash-mid) 30%, var(--wash-cool) 55%, transparent 74%);
}
.foot-grain {
  inline-size: 100%; block-size: 100%;
  opacity: var(--grain-opacity); mix-blend-mode: var(--grain-blend);
}
.foot-grain rect { fill: var(--grain-ink); }

.foot-in {
  position: relative; display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 2.5rem 2rem; padding-block: clamp(2.8rem, 6vw, 3.5rem) 2.5rem;
}
.pub-foot a:focus-visible { outline-color: var(--foot-accent); }
.foot-brand { display: flex; flex-direction: column; align-items: flex-start; gap: 1.1rem; }
.brand-lockup { display: flex; align-items: center; gap: 1rem; text-decoration: none; color: inherit; }
.foot-brand .brand-mark {
  display: block; block-size: clamp(72px, 8vw, 100px); inline-size: auto;
  aspect-ratio: 503 / 706; color: var(--foot-accent);
}
.brand-word {
  font-family: var(--wordmark); font-weight: 600; font-size: clamp(1.6rem, 2.6vw, 2rem);
  letter-spacing: -0.02em; line-height: 1;
}
.brand-word .tld { color: var(--cool); }
.brand-lockup:hover .brand-word { text-decoration: underline; text-underline-offset: 4px; }
.brand-line {
  margin: 0; color: var(--foot-muted); font-family: var(--prose);
  font-size: 1rem; line-height: 1.55; max-inline-size: 34rem;
}
/* The same call the masthead and the close make, drawn the way the forecast
   site draws it: under the firm's own line, in the accent, underlined. Third
   time it is asked on this page, and the first time it is asked of a reader
   who has finished reading. */
.brand-cta {
  font-family: var(--display); font-weight: 600; font-size: 0.95rem;
  color: var(--foot-accent); text-decoration: none;
  border-block-end: 1px solid currentcolor; padding-block-end: 2px;
}
.brand-cta:hover { color: var(--foot-ink); }
.foot-col h2 {
  font-family: var(--display); font-weight: 600; font-size: 0.78rem;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--foot-muted); margin: 0 0 0.8rem;
}
.foot-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; }
.foot-col a { color: var(--foot-ink); text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem; }
.foot-col a:hover { text-decoration: underline; text-underline-offset: 3px; }
.foot-col .social-icon { inline-size: 15px; block-size: 15px; }
.foot-base {
  position: relative; grid-column: 1 / -1; display: flex; gap: 1.5rem; flex-wrap: wrap;
  padding-block-start: 1.5rem; border-block-start: 1px solid var(--foot-rule);
  font-size: 0.85rem; color: var(--foot-muted);
}
.foot-base p { margin: 0; }
.foot-base p:last-child { margin-inline-start: auto; }

/* ---- the page that is not there ---------------------------------------- */

/* Every 404 on this domain lands here, including the ones under the forecast's
   own path, so it points at both things rather than at one. It carries no
   field: a page that has to say something went wrong should not also be
   putting on the show. */
.gone { min-block-size: 100svh; display: flex; align-items: center; }
.gone .in { padding-block: 9rem 4rem; }
.gone h1 {
  font-family: var(--wordmark); font-weight: 600; font-size: clamp(2.2rem, 6vw, 4rem);
  line-height: 1.04; letter-spacing: -0.035em; margin: 0.8rem 0 1rem; max-inline-size: 16ch;
}
.gone p { margin: 0; color: var(--muted); max-inline-size: 46ch; font-size: 1.02rem; }
.gone ul { list-style: none; margin: 2rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.6rem 1.8rem; }

/* ---- narrow ------------------------------------------------------------ */

@media (max-width: 900px) {
  .entries { grid-template-columns: 1fr; }
  .card { display: block; }
  .card .dotmark { margin: 0 0 0.9rem; }
  /* Stacked, the rail is a line rather than a column: the mark goes beside
     the name at the size a phone can afford, and `order` moves it in front
     without moving it in the document -- it is `aria-hidden`, so there is
     nothing there to read out of order. The label still wraps to two lines at
     320px, which is why the mark is centred against it rather than topped. */
  .card .rail { display: flex; align-items: center; gap: 0.9rem; margin-block-end: 0.6rem; }
  .card .glyph { order: -1; flex: none; inline-size: 48px; block-size: 48px; margin-block-start: 0; }
  .foot-in { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .foot-brand { grid-column: 1 / -1; }
}

/* The field is the only motion on the page, so a reader who has asked for
   none gets no canvas at all rather than a still frame of one. */
@media (prefers-reduced-motion: reduce) {
  #field { display: none; }
  body { background: var(--ground); }
}
