/* website/public/pixelos/style/pixelos-fonts.css
 * PixelOS 自托管字体 — 零外部 CDN(替代原型 home-v3.html 的 Google Fonts + cdnfonts 外链)。
 * 家族名严格对齐原型字体栈(base.css 的 --font-body/--font-hero/--font-mono):
 *   'Geist' / 'Geist Mono' / 'Noto Sans SC'。
 *
 * - Geist / Geist Mono:变量字 100–900,latin + latin-ext 子集(Fontsource @fontsource-variable/geist,SIL OFL 1.1)。
 * - Noto Sans SC / TC / JP / KR:从官方 NotoSansXX[wght] variable TTF 各自子集化
 *   (变量字重轴 100–900 保留),源在 app/assets/fonts/noto_sans_{sc,tc,jp,kr}/,SIL OFL 1.1。
 *   字符集 = dist 里对应 locale **全部页面的实际用字** + 常用 CJK 标点/全角符号,
 *   四份各 55–170KB(替代 Fontsource 的 ~100 块 4.6MB)。
 *   重产:`pnpm build && node scripts/build-cjk-subset.mjs`(可加 `--only tc,jp` 只产其中几份);
 *   缺字门禁:`pnpm precheck:dist` 的 cjkSubsetCoverage + tests/unit/font-subset-coverage.test.ts。
 *   ⚠ 改 CJK 文案后必须重产,否则新字静默回退系统字体、同段话两套字形混排(2026-07-30
 *   就是这样烂到 zh 覆盖 63% / zh-hant 覆盖 39% 才被发现,故加了上面那道门禁)。
 * - D-DIN(原型 --font-hero/--font-mono 首选,DIN 1451 工业体):npm 无 OFL 包,暂缺;
 *   字体栈自动回退 'Geist'/'Geist Mono'/'Noto Sans SC' —— 不引入外链。TODO: 后补本地 D-DIN。
 *
 * 本文件同时是「CJK 字体按需加载」的单一事实源:@font-face 的 unicode-range
 * + 文件末尾按 locale 收窄的字体栈,两者缺一不可(原因见各自注释)。
 */

/* ── Geist(正文/标题)── */
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/geist-variable-latin.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/geist-variable-latin-ext.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* ── Geist Mono(数字/eyebrow/code-meta,D-DIN 回退）── */
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/geist-mono-variable-latin.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/geist-mono-variable-latin-ext.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* ── Noto Sans SC / TC / JP / KR(CJK 四语,变量字 100–900,各一份页面子集)──
 *
 * 为什么是四份而不是一份:同一个汉字码位在简中 / 繁中 / 日文 / 韩文有不同的**地区
 * 字形**(骨 過 今 角 雪 直 者 海 …… 实测四份字体的轮廓数据互不相同)。一份 SC 顶四语
 * 会让繁中读者看到简中字形的「骨」、日文读者看到简体字形的汉字 —— 字虽然对,形是错的。
 *
 * ⚠ 分流只能靠 locale 字体栈(文件末尾),不能靠 unicode-range 互相回避:四份字体的
 * 汉字区段本来就大面积重叠,谁负责哪个字取决于**读者的语言**,不取决于码位。所以下面
 * 四个 face 的汉字区段是**故意相同**的,差异只在各自的表音文字区段:
 *   SC/TC 汉字区段一致;JP 多假名(U+3040-30FF、U+31F0-31FF);KR 多谚文(U+1100-11FF、
 *   U+3130-318F、U+A960-A97F、U+AC00-D7FF)。
 * 反过来说:ja 页面上的「한국어」(语言菜单)不在 JP 的区间里,照旧走系统韩文字体 —— 这是
 * 有意的,为一个导航词把谚文塞进 JP 子集不划算。
 *
 * unicode-range 必须显式写:缺省声明区间是 U+0-10FFFF,于是页面里任何超出 Geist
 * latin/latin-ext 的字符(✓ U+2713、⏺ U+23FA、⎿ U+23BF、▸ U+25B8、▶ U+25B6、→ U+2192)
 * 都把它当候选而触发下载 —— 而子集 cmap 里根本没有这些符号,下完仍回退系统字体,
 * 215KB 纯浪费(实测占非 CJK 语言页首访传输 70%)。故只声明 CJK 区段;刻意不含
 * U+2000-206F(通用标点,Geist 已覆盖)与上面那批符号。 */
@font-face {
  font-family: 'Noto Sans SC';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/noto-sans-sc-subset.woff2') format('woff2-variations');
  unicode-range: U+2E80-2EFF,U+3000-303F,U+3400-4DBF,U+4E00-9FFF,U+F900-FAFF,U+FF00-FFEF;
}
@font-face {
  font-family: 'Noto Sans TC';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/noto-sans-tc-subset.woff2') format('woff2-variations');
  unicode-range: U+2E80-2EFF,U+3000-303F,U+3400-4DBF,U+4E00-9FFF,U+F900-FAFF,U+FF00-FFEF;
}
@font-face {
  font-family: 'Noto Sans JP';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/noto-sans-jp-subset.woff2') format('woff2-variations');
  unicode-range: U+2E80-2EFF,U+3000-303F,U+3040-30FF,U+31F0-31FF,U+3400-4DBF,U+4E00-9FFF,U+F900-FAFF,U+FF00-FFEF;
}
@font-face {
  font-family: 'Noto Sans KR';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/noto-sans-kr-subset.woff2') format('woff2-variations');
  unicode-range: U+1100-11FF,U+2E80-2EFF,U+3000-303F,U+3130-318F,U+3400-4DBF,U+4E00-9FFF,U+A960-A97F,U+AC00-D7FF,U+F900-FAFF,U+FF00-FFEF;
}

/* ── 字体栈按 locale 收窄:CJK webfont 只进「需要 CJK 字形」的语言页 ──
 * 单靠上面的 unicode-range 不够:导航语言菜单在**所有**语言页都渲染「日本語」
 * 「繁體中文」(.h-nav-lang-item),收起态是 opacity:0 + visibility:hidden ——
 * 仍生成盒子、仍参与布局塑形,栈里有 CJK webfont 就会被抓。实测只加 unicode-range
 * 时 en/es/fr/it 首页依旧下 215KB。故非 CJK locale 栈里根本不放 CJK webfont,
 * 那 7 个字走系统 CJK 字体(한국어 今天已经是这个待遇)。
 * cascade:html[data-lang] (0,1,1) > base.css 的 :root (0,1,0),与 <link> 顺序无关;
 * 下面各 locale 规则与兜底同 specificity,靠书写顺序取胜(必须写在兜底之后)。
 * data-lang 由 src/layouts/PixelOS.astro 写在 <html> 上(值 = 真实 locale)。
 * 新增语言默认落进兜底(= 不下 CJK webfont);需要 CJK 字形的新语言必须在下面显式
 * 补一条 —— tests/unit/font-locale-stack.test.ts 守这条约定。
 * --font-mono 不含 CJK webfont(沿用 base.css),CJK 在 mono 语境走 ui-monospace。 */
html[data-lang] {
  --font-body: 'Geist', system-ui, -apple-system, sans-serif;
  --font-serif: 'Geist', system-ui, -apple-system, sans-serif;
  --font-quote: 'Geist', system-ui, -apple-system, sans-serif;
  --font-hero: 'D-DIN', system-ui, -apple-system, sans-serif;
}
/* CJK 四语各绑一份自己的子集 —— 每条规则里**有且只有一个** CJK 家族,
 * 于是「读者语言 → 地区字形」是确定的,而且一个 locale 只会下载一份 CJK 字体。
 * 绝不要写成 'Noto Sans TC', 'Noto Sans SC' 这种并列兜底:那会让繁中页面在
 * 首个 TC 缺字时静默掉进简中字形,还多下一整份 SC。缺字由 precheck 的
 * cjkSubsetCoverage 硬门禁负责,不靠字体栈兜。 */
/* 简中 → SC */
html[data-lang='zh'] {
  --font-body: 'Geist', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
  --font-serif: 'Geist', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
  --font-quote: 'Geist', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
  --font-hero: 'D-DIN', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
}
/* 繁中 → TC。曾经错用 SC:骨/過/今/角/雪 等字的台湾标准字形与简中不同,
 * 读者看到的是「字对形错」—— 2026-08-01 修正。 */
html[data-lang='zh-hant'] {
  --font-body: 'Geist', 'Noto Sans TC', system-ui, -apple-system, sans-serif;
  --font-serif: 'Geist', 'Noto Sans TC', system-ui, -apple-system, sans-serif;
  --font-quote: 'Geist', 'Noto Sans TC', system-ui, -apple-system, sans-serif;
  --font-hero: 'D-DIN', 'Noto Sans TC', system-ui, -apple-system, sans-serif;
}
/* 日文 → JP(汉字 + 假名同源于一套设计,不再是「汉字系统字体 A + 假名系统字体 B」) */
html[data-lang='ja'] {
  --font-body: 'Geist', 'Noto Sans JP', system-ui, -apple-system, sans-serif;
  --font-serif: 'Geist', 'Noto Sans JP', system-ui, -apple-system, sans-serif;
  --font-quote: 'Geist', 'Noto Sans JP', system-ui, -apple-system, sans-serif;
  --font-hero: 'D-DIN', 'Noto Sans JP', system-ui, -apple-system, sans-serif;
}
/* 韩文 → KR(谚文 + 汉字同源) */
html[data-lang='ko'] {
  --font-body: 'Geist', 'Noto Sans KR', system-ui, -apple-system, sans-serif;
  --font-serif: 'Geist', 'Noto Sans KR', system-ui, -apple-system, sans-serif;
  --font-quote: 'Geist', 'Noto Sans KR', system-ui, -apple-system, sans-serif;
  --font-hero: 'D-DIN', 'Noto Sans KR', system-ui, -apple-system, sans-serif;
}
