@import './tokens.css'; /* * IBM Plex, self-hosted via @fontsource — offline/PWA safe, never a CDN * (docs/architecture/09 §3.1). Sans: 400 body, 600 emphasis/headings, 300 for * large display only. Mono: 400 editor/code, 500 for emphasis. Keep weights * minimal — add one only when a surface needs it. * * The family-level import pulls every script subset (latin, latin-ext, cyrillic, * greek, vietnamese). We keep them all: user data — snippet names, dataset * values, chart labels — can be in any language, and Plex must render it rather * than fall back to system-ui. @font-face `unicode-range` means the browser only * *downloads* the subset a glyph needs, so the unused subsets cost nothing at * runtime; they only add to the offline precache, which is the price of working * internationally offline. Vite bundles the woff2 and Workbox precaches them * (woff2 is in the PWA globPatterns — see vite.config.ts). */ @import '@fontsource/ibm-plex-sans/300.css'; @import '@fontsource/ibm-plex-sans/400.css'; @import '@fontsource/ibm-plex-sans/600.css'; @import '@fontsource/ibm-plex-mono/400.css'; @import '@fontsource/ibm-plex-mono/500.css'; * { box-sizing: border-box; } html, body, #app { height: 100%; margin: 0; } body { font-family: var(--font-sans); font-size: var(--font-size-base); /* Plex "requires space to breathe" — keep body line-height generous (doc §3.1). */ line-height: 1.45; color: var(--text); background: var(--bg); /* Repaint chrome on theme flip; neutralized under reduced-motion below. */ transition: background var(--dur-moderate) var(--ease), color var(--dur-moderate) var(--ease); } /* * Shared keyboard focus ring — always visible, accessibility is non-negotiable * (doc §4, principle 4). Components may override the offset (e.g. inset on * fields) but should not remove it. */ :where(button, input, select, textarea, a, [tabindex]):focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }