Project Invoice — render subsidiary Chinese name, "茲付金額", and amount-in-words in Zihi Kai Plain (was Iansui / Yuji Mai)
Why (owner, 2026-07-03)¶
The owner uploaded a modified BPMF-Kai face — "Zihi Kai Plain" — already used for the WOPC's Chinese rendering, and wants the same Kai face on the Project Invoice for a few Chinese elements, replacing the current Iansui / Yuji Mai.
Owner requests (verbatim):
"A while back I've uploaded a modified BPMF Kai font on the web app and it should currently be used on the rendering of Chinese characters for the WOPC. I'd like the Kai font to be used on the rendering of the Subsidiary name in Chinese for Project Invoice replacing the used of IanSui (If I'm not mistaken). Make sure all the facts are right before you making a move on the web app"
"Zihi Kai Plain is the one!! don't use the BPMF ones please" (the correct face is the self-hosted
public/fonts/ZihiKaiPlain-*.woff2, NOT the base64 BPMF blobs inlib/pdfTemplates/fontData.ts)"please also use zihi Kai font and render to 茲付金額 and the return string (like 貳仟伍佰元正)"
Scope chosen by the owner: subsidiary Chinese name = main header only; plus the "茲付金額" label and the Chinese amount-in-words return string (e.g. "貳仟伍佰元正").
Verified facts (before making the move)¶
- The live project invoice renders via
lib/invoiceInvoice→InvoicePage→ the Composed headers (InvoiceHeaderFullComposed/…VersionA) which renderHeaderBrandingVersionA/B. The standaloneInvoiceHeaderFull.tsx/…VersionA.tsxare legacy (exported but not on the live path). - Subsidiary Chinese name (main header) →
HeaderBrandingVersionB.tsx(variant B, default) +HeaderBrandingVersionA.tsx(variant A), inline"Iansui". - "茲付金額" label →
FooterFull.tsx(variant B/B2 last page), inline"Yuji Mai"(not Iansui). - Amount-in-words (
totalChinese) →TotalBox.tsx(all variants, last page) +FooterFull.tsx(variant B footer), inline"Iansui". - "Zihi Kai Plain" is self-hosted (
public/fonts/ZihiKaiPlain-{Regular,Bold}.woff2) and its@font-facewas declared only in the WOPC component — no invoice surface loaded it. Invoice surfaces load fonts via a Google-Fonts<link>(+ self-hostedgoogle-sans-mono.css); the PDF is a Puppeteer nav to the preview page that waits ondocument.fonts.ready, so a self-hosted<link>loads in the PDF too.
What changed¶
- Font swaps (Kai first, previous face kept as fallback so nothing renders as tofu):
HeaderBrandingVersionA.tsx+HeaderBrandingVersionB.tsx— subsidiary Chinese name:"Iansui"→"Zihi Kai Plain", "Iansui", sans-serif.FooterFull.tsx— "茲付金額:" label:"Yuji Mai"→"Zihi Kai Plain", "Yuji Mai", serif.TotalBox.tsx+FooterFull.tsx— amount-in-words:"Iansui"→"Zihi Kai Plain", "Iansui", sans-serif.- Font loading: new
public/fonts/zihi-kai-plain.css(@font-faceRegular + Bold), linked on the invoice preview client (browser + PDF) and the two public share views (pages/v/[id].tsx,pages/view/document/[token].tsx).
Scope notes / blast radius¶
- Other subsidiary-name instances (continuation header,
PageBranding, the Payment Details/Instructions supplementary pages used by variant A) were left on Iansui per the "main header only" choice. - Shared components (
TotalBox,FooterFull,HeaderBranding*) are also used by quotations and public share views; the Iansui/Yuji-Mai fallback means only surfaces that loadzihi-kai-plain.cssshow Kai — the quotation preview (font not added there) stays visually unchanged. - Left untouched: the base64 BPMF blobs in
lib/pdfTemplates/fontData.ts(owner: "don't use the BPMF ones").
Decision log¶
2026-07-03 — opened + implemented¶
- ✅ Attestation (Document Rendering Fine-Tuning): read
AGENTS.md; scope-scanned the board (by scope, not UID) — no covering task (T-092 = WOPC notes, done; T-070 = Contacts subsidiary tab). New scope → T-155. - Source: Document Rendering Fine-Tuning · https://claude.ai/code/session_01A2tdTGEeYDMDVhrceFpJ8D
- Proposed by: the owner (feature request, verbatim quotes above). Approved by: the owner.
- Deploy: owner — "Once you're done, commit to main and deploy the web app". Per the current 🚦 branch
policy
mainis 🟡🟡 (Vercel, manual): merge the PR on GitHub, then fire the Vercel deploy hook. - Type-check note: this sandbox has no
node_modules, so an unfilteredtsc --noEmitcould not be run here; the change is only style-object string literals + JSX<link>tags + a CSS file, which carry no TypeScript type surface. - Commits (append-only):
f99660d4— the change (font swaps +zihi-kai-plain.css+<link>s + this task doc)87cbdd5f— merge of PR #858 intomain
2026-07-03 — DONE (merged; deployed to Vercel)¶
- ✅ Attestation (Document Rendering Fine-Tuning): read
AGENTS.md; closing T-155 per the "close the loop" rule.done= merged (owner policy) — this is merged and the production deploy was fired. - Source: Document Rendering Fine-Tuning · https://claude.ai/code/session_01A2tdTGEeYDMDVhrceFpJ8D
- Verdict: all three targets now render in Zihi Kai Plain on the project invoice (subsidiary Chinese
name in the main header, the "茲付金額" label, and the Chinese amount-in-words), with Iansui / Yuji Mai kept
as fallback. Merged to
main(PR #858,87cbdd5f). Per the 🚦main🟡🟡 (Vercel, manual) policy, the production deploy was triggered manually via the Vercel deploy hook (VERCEL_DEPLOY_HOOKfrom GCP Secret Manager) — jobFoV3qBnjs4ijfgWjvXbN,201 PENDING. - Verification: could not run
tsc/render locally (nonode_modules); the change is CSS/string-only. Live check is the Vercel build + the invoice preview/PDF once deployed. - Blast radius: touches only the CJK font on the invoice subsidiary name (main header), "茲付金額", and
amount-in-words, plus a new self-hosted font CSS linked on the invoice preview + the two public share
views (
pages/v/[id],pages/view/document/[token]). Shared components (TotalBox,FooterFull,HeaderBranding*) are reused by quotations / share views, but the Iansui/Yuji-Mai fallback means only surfaces that loadzihi-kai-plain.cssshow Kai — the quotation preview is visually unchanged. Nothing else on the invoice, and the deadfontData.tsBPMF blobs, were touched.
2026-07-03 — FOLLOW-UP: supplementary Payment pages missed (owner reported 茲付金額 + amount still Iansui)¶
- ✅ Attestation (Document Rendering Fine-Tuning): read
AGENTS.md; same-scope fix, still T-155. - Source: Document Rendering Fine-Tuning · https://claude.ai/code/session_01A2tdTGEeYDMDVhrceFpJ8D
- Owner (2026-07-03): "'茲付金額' label and Chinese amount-in-words seems to be still rendered in Iansui".
- Root cause: the first pass only covered the variant-B main-page path (
FooterFull/TotalBox). The "茲付金額" label and amount-in-words also appear on the supplementary Payment Details / Instructions pages (invoice variants A / A2 / B2 / bundle), which render throughSchemePageRendererreading the font hardcoded in the*Data.tsscheme cells (Iansui / Yuji Mai).SchemeRendererapplies the cell'sfontFamilyto the page's value-override, so the fix is on the scheme cells. - Ruled out per-glyph fallback: rendered the target glyphs in Zihi Kai Plain via headless Chromium — 茲付金額, 貳仟伍佰元正, and the full 大寫 set 零壹貳參肆伍陸柒捌玖拾佰仟萬 all render in Kai (no tofu). The font has full coverage; the issue was purely the render path.
- What changed:
fontFamily→Zihi Kai Plainon 5 scheme cells:PaymentDetailsData.tsrow 180 (茲付金額A, amountD) andPaymentInstructionsData.tsrows 167 (amountD), 188 (茲付金額A), 189 (amountA). Other Iansui cells (bank info etc.) untouched. - Commits (append-only):
f99660d4,87cbdd5f(first pass) · this follow-up commit (scheme-cell fonts) - Blast radius: only the amount-in-words + 茲付金額 on the two supplementary Payment pages.
SchemeRendererfalls back tosans-serif(not Iansui) for these cells, but the font's full glyph coverage means the fallback never triggers.
2026-07-03 — FOLLOW-UP 3: app-wide — ALIAS Iansui → Zihi Kai Plain everywhere (owner request)¶
- ✅ Attestation (Document Rendering Fine-Tuning): read
AGENTS.md; same-scope extension, still T-155. - Source: Document Rendering Fine-Tuning · https://claude.ai/code/session_01A2tdTGEeYDMDVhrceFpJ8D
- Owner (2026-07-03): "Can you actually make it so that all usage of Iansui is replaced to using Zihi
Kai Plain?" + earlier "Including coaching invoices / all usage across the entire web app". Chose the
alias approach (via AskUserQuestion), leave xlsx as-is. (This also subsumes the presenter/work-type
ask — that CJK also used Iansui, so the alias covers it; the separate
claude/t155-presenter-worktype-kaibranch is superseded and left unmerged.) - What changed (alias, not rename): re-point the
Iansuifont family at the self-hostedZihiKaiPlain-{Regular,Bold}.woff2(no unicode-range) in the globally-loadedstyles/antd-reset.css(App-Router-wide), inpublic/fonts/zihi-kai-plain.css(Pages-Router public viewers), and inline inPaymentConfirmation.tsx(WOPC server render). Then removed the competing Google/jsdelivr Iansui loads from 10 surfaces (allfamily=Iansuilink/import params) so the alias always wins. Every existingfont-family: "Iansui"now renders Kai — invoices, WOPC, quotations, project screens — without renaming the ~75 call sites. - Left as-is: xlsx exports (Excel font-name string, no visual effect — owner's choice); the dead
GeneratedInvoice.tsx/lib/pdfTemplates/*(no live importers). - Coaching invoices (
lib/paymentRequest/*) don't use Iansui — they use Yuji Syuku / Yuji Mai / Klee One / Noto Serif TC for CJK. Per the owner's follow-up choice (AskUserQuestion: "Klee One / Noto Serif TC only") those two are also aliased to Kai (added toantd-reset.css+zihi-kai-plain.css, and their Google loads stripped from ToolsApp, the coaching preview, the payment-request preview, and the two public viewers). Yuji Mai / Yuji Syuku are deliberately kept so the calligraphic invoice titles and the coaching headers keep their look. Net: on coaching invoices, the Klee One / Noto Serif TC Chinese renders Kai; the Yuji-set Chinese stays. - Commits (append-only):
f99660d4,87cbdd5f,664e4198(prior) · this commit (Iansui + Klee One + Noto Serif TC → Kai alias)