/* Zoho Puvi - the house typeface for the whole web application.
 *
 * Self-hosted rather than CDN-loaded: this replaces the Google Fonts links that
 * every page used to carry. Nothing here reaches a third party, the pages keep
 * their type with no network, and there is no render-blocking request to a host
 * we do not control. Puvi is not on Google Fonts in any case.
 *
 * ---------------------------------------------------------------------------
 * WHY THIS FILE LOOKS THE WAY IT DOES: the pages used to visibly flicker, every
 * single load, from the fallback face to Puvi. Three things caused it and all
 * three are addressed here and in StaticCacheConfig.
 *
 *   1. The fonts were never cached. Spring Security stamps
 *      "no-cache, no-store, max-age=0" on every response by default, so all five
 *      weights were re-downloaded on EVERY page view and the swap happened every
 *      time. StaticCacheConfig now serves /fonts/*.woff2 with a one-year
 *      immutable cache. That is the actual fix; the rest is refinement.
 *
 *   2. font-display:swap paints the fallback at 0ms and then repaints. That IS
 *      the flicker, by design. It is now "optional": the browser gives the font
 *      a short window, and if it misses it, the page keeps the fallback for that
 *      load rather than swapping mid-read. The font still downloads and is
 *      cached, so the next view has it before first paint. A cold first visit
 *      may therefore render in the fallback; every view after it is Puvi, and no
 *      view ever changes face underneath the reader.
 *
 *   3. Nothing told the browser to start fetching the fonts early. The pages now
 *      carry <link rel="preload"> for all five weights, so the download begins
 *      with the HTML instead of waiting for this file to be parsed first. All
 *      five are preloaded deliberately: if only some arrived in time, a page
 *      would render its headings and its body in different families.
 *
 * The files are subset to the Latin ranges the pages can actually produce
 * (ASCII, Latin-1, Latin Extended-A for European names, arrows, punctuation and
 * the rupee sign), which took all five weights from 331 KB to 147 KB. The subset
 * was verified against every character present in every page: nothing that Puvi
 * can draw and a page asks for was dropped. Note that a few marks on the pages
 * (checks, crosses, box-drawing, the padlock) are not in Puvi at all and always
 * came from the system font.
 *
 * The ?v= token is how a font change is published. The files are immutable for a
 * year, so a new cut of Puvi MUST arrive under a new token or browsers will keep
 * the old one. Bump it here and nowhere else; this stylesheet itself revalidates
 * on every load, so a bump propagates immediately.
 *
 * Latin only. Anything outside Latin falls back to the system stack.
 */

@font-face {
	font-family: 'Zoho Puvi';
	font-style: normal;
	font-weight: 400;
	font-display: optional;
	src: url('/fonts/ZohoPuvi-Regular.woff2?v=2') format('woff2');
}

@font-face {
	font-family: 'Zoho Puvi';
	font-style: normal;
	font-weight: 500;
	font-display: optional;
	src: url('/fonts/ZohoPuvi-Medium.woff2?v=2') format('woff2');
}

@font-face {
	font-family: 'Zoho Puvi';
	font-style: normal;
	font-weight: 600;
	font-display: optional;
	src: url('/fonts/ZohoPuvi-SemiBold.woff2?v=2') format('woff2');
}

@font-face {
	font-family: 'Zoho Puvi';
	font-style: normal;
	font-weight: 700;
	font-display: optional;
	src: url('/fonts/ZohoPuvi-Bold.woff2?v=2') format('woff2');
}

@font-face {
	font-family: 'Zoho Puvi';
	font-style: normal;
	font-weight: 800;
	font-display: optional;
	src: url('/fonts/ZohoPuvi-ExtraBold.woff2?v=2') format('woff2');
}

/* A metric-matched stand-in for the cold-cache case above.
 *
 * Puvi's x-height is 0.5230 of its em; Arial's is 0.5186. Scaling Arial by
 * 100.86% makes the two read at the same visual size, and overriding the ascent
 * and descent to Puvi's own (divided by that same scale factor, so they land
 * correctly after it is applied) gives a line of fallback text the same line box
 * HEIGHT that Puvi produces. So on the one load that may not have the font yet,
 * the vertical rhythm is already right and the text is the right size rather
 * than obviously the wrong font.
 *
 * Height, not width. The scale is derived from x-height because that is what
 * governs perceived size; measured on the deployed page, Puvi still sets about
 * 3% wider than the adjusted Arial, so a fallback render can break lines at
 * slightly different points. Matching advance width instead would hold the line
 * breaks but leave the type visibly too large, which is the worse trade. This
 * only ever shows on a cold first load: font-display:optional means the two are
 * never composited in the same page view, so nothing shifts under the reader.
 *
 * Arial and Helvetica are near-identical in these metrics and one of them is
 * present on Windows, macOS, iOS and Android. Anywhere neither exists, this
 * @font-face simply does not match and the stack falls through to system-ui
 * unadjusted, which is the behaviour we had before.
 *
 * These numbers are derived from the font binary, not estimated. See the
 * computation in the deploy notes if Puvi is ever re-cut. */
@font-face {
	font-family: 'Puvi Fallback';
	font-style: normal;
	src: local('Arial'), local('Helvetica');
	size-adjust: 100.86%;
	ascent-override: 92.01%;
	descent-override: 21.71%;
	line-gap-override: 0%;
}

/* The two type roles every page uses. Pages that predate the shared file set
 * these themselves; both definitions agree, so either order resolves the same.
 *
 * --font-mono stays monospace on purpose. It is used for school codes,
 * registration numbers, percentages and table figures, where column alignment
 * and telling 0 from O matter more than house style. The system stack keeps
 * that without a second webfont download. */
:root {
	--font-ui: 'Zoho Puvi', 'Puvi Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
	--font-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
}
