/*
 * Self-hosted fonts (Inter, Space Grotesk, Geist, Geist Mono), plus one local
 * metric-compatible stand-in for Geist ('Geist Fallback', at the bottom).
 * Replaces fonts.googleapis.com / fonts.gstatic.com requests so a single
 * connection serves both the embed JS and its fonts.
 *
 * Every self-hosted family is one face built from the variable TTF vendored in
 * devops/base/fonts/, subset to latin plus latin-ext, with no unicode-range.
 * Google's css2 output splits a family into several @font-face that differ
 * only by unicode-range; on WebKit 26 that collision resolves the wght
 * variation instance to NaN and drops the uppercase A. Don't re-fetch any of
 * these from css2. That directory's README has the build command.
 *
 * Keep each `font-weight` range equal to the woff2's real `wght` axis. A
 * narrower range doesn't clamp gracefully: the browser pins the weight to the
 * declared bound and then fakes the rest with synthetic bold, so the requested
 * weight silently never renders. Geist and Geist Mono once shipped as
 * `300 700` while their axis is `100 900`, which made the `font-weight: 900`
 * section headers in MetricsGrid.tsx render identical to 700.
 * backend/monolith/tests/inline_font_face_sync_unit_test.py reads each woff2's
 * `fvar` table and fails CI on a range that disagrees, and fails CI on a
 * self-hosted family that declares more than one face or any unicode-range.
 * Space Grotesk's 300-700 is genuinely its axis -- leave it.
 */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(inter/Inter-Variable.woff2) format('woff2');
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(spacegrotesk/SpaceGrotesk-Variable.woff2) format('woff2');
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(geist/Geist-Variable.woff2) format('woff2');
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(geistmono/GeistMono-Variable.woff2) format('woff2');
}
/* Arial / Arial Bold re-metricized to occupy Geist's space (slightly over,
 * never under). Plain Arial paints text ~6-9% smaller than Geist, and LCP
 * re-records on any strictly larger paint — so on loads where Geist arrives
 * after first paint but before React hydration (the common slow-mobile
 * ordering), the hydrated marketing h1 first-paints in Geist as a new, larger
 * LCP candidate, and LCP moves from first-paint time to hydration time. The
 * stacks in app/frontend/src/index.css put this family between Geist and the
 * generic family; onsen/index.html carries a parse-time copy because this file
 * loads async there.
 *
 * TWO faces, because Geist is a variable font and its glyphs are ~9% wider at
 * 800 than at 400 — the prose h1 routes (/about/, /blog/, the legal pages)
 * render headings at 800, so one 400-fitted scalar left them undershooting and
 * the LCP re-record armed. Weights 100-500 get adjusted Arial, 600-900 get
 * adjusted Arial Bold, each fitted to the Geist weight its range serves most.
 *
 * The values come from the fonts' metric tables, never from eyeballing:
 *   size-adjust     = (Geist avgWidth/upm) / (fallback avgWidth/upm), avgWidth
 *                     frequency-weighted over a-z + space, at wght 400 (regular
 *                     face) / wght 800 (bold face). The bold face then adds the
 *                     margin that makes every in-repo h1 string paint at >= its
 *                     Geist-800 width x 1.005. The worst is "Better answers,
 *                     backed by trusted data" in content/knowledge_search.json,
 *                     not any of the template literals — most prose routes take
 *                     their h1 from content/*.json, so a fit measured against
 *                     the templates alone undershot two live routes. One scalar
 *                     cannot fit every string, and an undershoot re-arms the LCP
 *                     re-record while an overshoot only shrinks slightly at
 *                     swap -- except at the bottom of this face's range, where
 *                     weight 600 runs ~6.9% wide. Card, chart and data-hub
 *                     titles come from the database and can exceed any fixed
 *                     margin; blog titles do NOT -- content/blog/*.json is in
 *                     the repo and all of it is in the fit.
 *   ascent-override = Geist hhea.ascent / upm / size-adjust  (descent, line-gap
 *                     same; overridden metrics are scaled by size-adjust per
 *                     css-fonts-5, hence the division)
 * inline_font_face_sync_unit_test.py recomputes the derivation from the Geist
 * woff2 (instantiated at each face's fit weight) on every CI run, so a Geist
 * refresh that moves the metrics fails the build instead of silently un-fixing
 * LCP.
 *
 * ANDROID, and it differs per face. local('Arial') resolves through the `arial`
 * family alias to Roboto, within 0.32% of Arial on weighted advance, so the
 * regular face works there. local('Arial Bold') does NOT: it is a full-font-name
 * lookup, Android's font config aliases only family names, and Blink resolves
 * local() through SkFontMgr::matchFamilyStyle, which matches family names only.
 * That face therefore has no source on Android and weights 600-900 fall through
 * to the unadjusted generic family, which leaves the re-record armed on the
 * prose-h1 routes there. Not a regression -- it is what those routes did before
 * this family existed -- and not fixable without a device: see TAKO-4628, which
 * carries the Blink measurements and the repairs already refuted.
 *
 * These two faces are the one place a unicode-range belongs: unlike the faces
 * above, the family is a stand-in, and the ratio is derived from Latin glyph
 * widths, so outside the Latin script it means nothing. The range covers every
 * codepoint the Geist face carries and stops before Greek, Cyrillic, and
 * everything after — Arial has those glyphs and Geist does not, so without the
 * range this face would win over the generic family for non-Latin text
 * permanently, not just until Geist lands. */
@font-face {
  font-family: 'Geist Fallback';
  font-style: normal;
  font-weight: 100 500;
  src: local('Arial');
  size-adjust: 105.71%;
  ascent-override: 95.07%;
  descent-override: 27.91%;
  line-gap-override: 0%;
  unicode-range: U+0000-036F, U+1D00-1DBF, U+1E00-1EFF, U+2000-2122, U+2190-2215, U+2C60-2C7F, U+A720-A7FF, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist Fallback';
  font-style: normal;
  font-weight: 600 900;
  src: local('Arial Bold');
  size-adjust: 109.06%;
  ascent-override: 92.15%;
  descent-override: 27.05%;
  line-gap-override: 0%;
  unicode-range: U+0000-036F, U+1D00-1DBF, U+1E00-1EFF, U+2000-2122, U+2190-2215, U+2C60-2C7F, U+A720-A7FF, U+FEFF, U+FFFD;
}
