/* ==========================================================================
   ELVIS DS · web font layer
   Self-hosted so the browser never blocks on fonts.googleapis.com.

   2026-09-01: Lighthouse measured the Google Fonts stylesheet at 906ms of
   render blocking on this site. These are the same faces, latin subset,
   served from our own origin.

   Only the weights the site actually uses are here:
     Archivo            400 500 700 800   (display + label)
     Schibsted Grotesk  400 500 600       (body)
   Adding a weight to the CSS without adding it here means a silent
   synthetic bold, so check both.

   NOT merged into site.css on purpose: dist/sync.sh regenerates the design
   system's component layer from site.css, and these paths are site-specific.
   ========================================================================== */

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/archivo-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/archivo-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/archivo-700.woff2') format('woff2');
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/assets/fonts/archivo-800.woff2') format('woff2');
}
@font-face {
  font-family: 'Schibsted Grotesk';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/schibsted-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Schibsted Grotesk';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/schibsted-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Schibsted Grotesk';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/schibsted-600.woff2') format('woff2');
}

/* --------------------------------------------------------------------------
   Traditional Chinese stack. 2026-09-02, first Chinese on the site.

   System faces only, on purpose. A self-hosted CJK face is megabytes even
   subsetted, and the site just went from Performance 86 to 95 by removing a
   font download. PingFang TC ships on every Mac and iOS device, Microsoft
   JhengHei on Windows, Noto Sans TC covers the rest.

   The Latin label rule (+0.11em tracking, uppercase) is built for capitals and
   pulls Chinese characters apart, so Chinese never inherits it.
   -------------------------------------------------------------------------- */
:root {
  --font-zh: "PingFang TC", "Noto Sans TC", "Microsoft JhengHei",
             "Hiragino Sans CNS", "Heiti TC", sans-serif;
}
[lang="zh-Hant"] {
  font-family: var(--font-zh);
  letter-spacing: 0;
  text-transform: none;
}

/* One step up on every small size, Chinese only.

   A Han character carries far more stroke detail than a Latin letter, so the
   size that reads comfortably in Archivo is genuinely hard work in Chinese.
   11px labels became 12, 10px became 11. Latin pages are untouched. */
html[lang="zh-Hant"] {
  --label-size: 12px;
  --label-size-sm: 11px;
}
html[lang="zh-Hant"] .ds-nav { --nav-edge: 81px; }
html[lang="zh-Hant"] .site-footer__contact { font-size: 12px; }
html[lang="zh-Hant"] .site-footer__legal { font-size: 11px; }
@media (max-width: 900px) {
  html[lang="zh-Hant"] .ds-nav__links { font-size: 11px; }
}

/* --------------------------------------------------------------------------
   Chinese display face. 2026-09-02.

   PingFang TC stops at Semibold, so a Chinese heading asking for Archivo's 800
   simply cannot get there and reads limp next to the English. This is Noto
   Sans TC, subset to ONLY the characters that appear in Chinese h1/h2/display
   headings: 3MB down to 29KB. English pages never touch a Chinese glyph, so
   they never download it.

   ⚠ The subset is generated from the built Chinese pages by i18n/build.py.
   Change a heading and the file must be rebuilt, or the new character falls
   back to PingFang and one glyph in the line goes thin. build.py checks this.

   ⚠⚠ The ?v= on each URL is the file's own md5, and it is not decoration.
   Assets here are served with max-age=1 year and the filename never changes,
   so a rebuilt subset does not reach anyone who has already loaded the site:
   their browser keeps the old file and the characters added in the rebuild
   keep falling back to PingFang. That is exactly what happened on 2026-09-02.
   The subset was fixed, the fix was deployed, and Elvis still saw 零 為 掌 舵
   rendering lighter than the rest of the line, twice, because his browser
   never asked for the new file. Rebuild the subset, re-stamp these two hashes.
   i18n/build.py fails if they do not match the files on disk.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'ZH Display';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/zh-display-700.woff2?v=557cd487') format('woff2');
}
@font-face {
  font-family: 'ZH Display';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('/assets/fonts/zh-display-900.woff2?v=47b11549') format('woff2');
}

/* Chinese headings: heavier face, 30% smaller, and no negative tracking.

   The 30% is Elvis's call, and it is the right one: a Han character fills its
   em square, so it carries far more ink than a Latin letter at the same size
   and reads oversized beside the English. Negative tracking is a Latin fix and
   jams Chinese glyphs into each other. */
html[lang="zh-Hant"] {
  --display-size: clamp(39px, 9.4vw, 151px);
  --h1-size: clamp(24px, 3.9vw, 62px);
  --h2-size: clamp(20px, 2.1vw, 31px);
  --display-tracking: 0;
  --h1-tracking: 0;
  --h2-tracking: 0;
  --h1-leading: 1.18;
  --h2-leading: 1.3;
  --display-leading: 1.06;
}
html[lang="zh-Hant"] .ds-display,
html[lang="zh-Hant"] .ds-h1,
html[lang="zh-Hant"] .ds-h2 {
  font-family: 'ZH Display', var(--font-zh);
  font-weight: 900;
}
html[lang="zh-Hant"] .ds-h3 {
  font-family: 'ZH Display', var(--font-zh);
  font-weight: 700;
  letter-spacing: 0;
}

/* Homepage stack heading, Chinese.

   .ds-display--stack sets its own size and leading, so the token overrides
   above never reach it. Chinese needs looser leading than 0.98: at that value
   the Han characters of two lines nearly touch. */
html[lang="zh-Hant"] .ds-display--stack {
  font-size: min(3.7vw, 64px);
  /* 1.34 first, then Elvis asked for 30% tighter, which landed at 0.94 and
     was too tight once the mobile size went up: at 64px the Han glyphs of two
     lines nearly touch. Opened back to 1.12 on his call. */
  line-height: 1.12;
}

/* The same heading on a phone. 3.7vw is a desktop ratio and it does not survive
   the trip down: at 390px it computes to 14.4px, which is caption size for what
   is meant to be the loudest line on the page.

   The reason it collapses is that vw scaling assumes the line grows with the
   screen, and this line does not. The longest Chinese line is four Han
   characters plus the separator, about 5.2 ems all in, where the English line
   it replaces is twenty-two characters. So the Chinese is sized off the space
   it actually has rather than off the viewport: fill the column, stop at the
   64px the desktop rule already caps at. 320px gives 52px, 390px gives 64px. */
@media (max-width: 900px) {
  html[lang="zh-Hant"] .ds-display--stack {
    font-size: min(calc((100vw - 2 * var(--page-margin)) / 5.2), 64px);
  }
}
/* The Latin in a Chinese heading (AI, Bju Project) still comes from Archivo,
   which tops out at 800. Ask for 800 rather than the 900 the Han glyphs use,
   so the browser picks the real weight instead of synthesising one. */
html[lang="zh-Hant"] .ds-display,
html[lang="zh-Hant"] .ds-h1,
html[lang="zh-Hant"] .ds-h2 {
  font-family: 'ZH Display', Archivo, var(--font-zh);
}
/* The // separator needs no Chinese-specific nudge. The space before it comes
   from the same Archivo fallback the English uses, so the rhythm already
   matches. A negative margin here closed the gap completely: measured at
   -0.22em it read as 創意指導// with no air at all. */
/* Elvis asked for this line to hold on one line, with the name in bold. */
html[lang="zh-Hant"] .zh-nowrap { white-space: nowrap; }
html[lang="zh-Hant"] strong { font-weight: 900; }

/* Homepage approach-card teaser. 2026-09-02.

   This span's weight/line-height/font-family are set as inline styles on the
   shared markup (English and Chinese render off the same template), so a
   normal class rule loses the specificity fight. !important is used here on
   purpose, scoped to .ds-approach-headline under html[lang="zh-Hant"] only:
   the English page is never touched. */
html[lang="zh-Hant"] .ds-approach-headline {
  font-family: 'ZH Display', Archivo, var(--font-zh) !important;
  font-weight: 900 !important;
  line-height: 1.24 !important;   /* 1.5 tightened 30% gave 1.05; too tight, opened back */
}
/* The lead paragraph carries explicit <br> line breaks for a specific four-line
   layout Elvis asked for, so the ch-based max-width is dropped: at 34ch a
   CJK line wraps well before an English one does, and would re-wrap inside a
   line that was already broken deliberately. */
html[lang="zh-Hant"] .ds-approach-lead {
  line-height: 1.65;
}
/* Those hand-placed breaks are a desktop composition. Each of the four
   segments is about 24 Han characters, which needs roughly 380px; on a phone
   every one of them wraps again, so a four-line paragraph became nine ragged
   ones with 並 and 的 left hanging at line ends. Below 900 the card is full
   width and the text should just wrap to the column it has. The English page
   has no breaks here to lose. */
@media (max-width: 900px) {
  html[lang="zh-Hant"] .ds-approach-lead br { display: none; }
}


/* Approach diagram, Chinese. Swap the drawn English words for the text ones.
   The Han labels are sized off their own em box rather than the Latin cap
   height, which is why they are smaller numbers than they look. */
html[lang="zh-Hant"] .ds-fig__en { display: none; }
html[lang="zh-Hant"] .ds-fig__alt { display: block; }
html[lang="zh-Hant"] .ds-fig__cap { font-family: var(--font-zh); font-size: 15px; letter-spacing: 0; }
html[lang="zh-Hant"] .ds-fig__word { font-family: var(--font-zh); font-weight: 600; font-size: 17px; }


/* Chinese line breaking. 2026-09-04.

   The default breaker will end a Chinese line between any two Han characters,
   because there are no spaces to tell it otherwise, so it splits words in
   half: 工具能帶來效 / 率. keep-all stops that, and it is safe here only
   because i18n/wordbreak.py has put an explicit <wbr> at every word boundary
   on the way out. Without those, keep-all can only break at punctuation and
   the measured line fill drops to 55-64% with pages overflowing.

   overflow-wrap is the floor: a run with no boundary in it at all still has
   to break rather than push the page sideways. */
html[lang="zh-Hant"] body {
  word-break: keep-all;
  overflow-wrap: break-word;
}

/* Rail label, top-aligned with the body beside it. 2026-09-04.

   The label box and the body box already start on the same pixel. What is off
   is the ink inside them: the label sets 12px type in a 19.2px line box and
   the body sets 17px in a 27.2px one, so the body's larger half-leading
   pushes its first line down. Measured off a 2x render by finding the first
   inked row in each column: the label sat 2.0px high.

   Padding rather than line-height, so a label that wraps to two lines keeps
   its own leading. Only above 900px, where the rail is actually two columns;
   below that the label stacks on top and there is nothing to align to.

   Measured identically on the English page, which is left alone on Elvis's
   standing instruction. Same one-line fix if he wants it. */
@media (min-width: 901px) {
  html[lang="zh-Hant"] .ds-rail__label { padding-top: 2px; }
}
