/* BourneWise typography.
   Two voices + brand mark, strictly cast — no other font may appear
   anywhere in the app:
   BioRhyme  — headings, AI verdicts, display numbers, the brand wordmark
   Spinnaker — body copy, UI labels, metadata, counters, CTAs (everything else) */

:root {
  --font-serif: "BioRhyme", Georgia, serif;
  --font-sans: "Spinnaker", system-ui, -apple-system, sans-serif;
  --font-mono: "Spinnaker", ui-monospace, monospace;
  --font-brand: 'Pacifico', cursive;
  /* ⭐ THE CHINESE FACE. Owner, 2026-09-02: 中文用华文中宋.
     None of the three locked families has a single CJK glyph, so Chinese has
     always been set by whatever the operating system reached for — the reading
     is the product, and it was being typeset by accident. This names the face.

     ⚠️ It is NOT a fourth webfont and does not touch the lock: nothing is
     downloaded, there is no @font-face, and assets/fonts still holds exactly
     three files. tests/font-lock.mjs gates @font-face declarations rather than
     family names for this reason — a system fallback the three families
     genuinely need is not a fourth family.

     ⚠️ The Latin faces are listed AFTER it, not before, and that is what
     settles the punctuation. A Chinese sentence carries ASCII marks as well as
     full-width ones; if the Latin face leads, the comma in "2026, 秋" comes
     from Spinnaker and the 。from the Song face, and the two do not sit on the
     same baseline or the same weight. Whichever face leads supplies EVERY
     codepoint it has, so leading with the Song face is what makes one Chinese
     paragraph read as one typeface.

     ⚠️⚠️ AND THE LIST HAS TO BE LONG, because the generic at the end is where
     the split comes back. Measured with CSS.getPlatformFontsForNode on a box
     with none of the named faces installed: the first version of this stack
     ended `… "Source Han Serif SC", serif`, and one Chinese paragraph rendered
     as `Liberation Serif: 1 glyph` + `WenQuanYi Zen Hei: 54`. The generic
     resolved to a Latin face, which HAS the ASCII marks and therefore claimed
     them, while the characters fell past it to the system's last-resort CJK
     font. Two families, one sentence — exactly the fault this token exists to
     remove, arriving through the fallback rather than through the lead.
     So the real families are listed until it is unlikely any machine reaches
     the generic: the Song faces first, then Noto/Source Han, then the sans CJK
     faces that Windows, Android and Linux actually ship. A sans CJK is a
     compromise; a split paragraph is a defect. */
  --font-zh: "STZhongsong", "华文中宋", "STSong", "SimSun", "宋体", "Songti SC",
             "Noto Serif CJK SC", "Source Han Serif SC", "Noto Serif SC",
             "Microsoft YaHei", "PingFang SC", "WenQuanYi Zen Hei",
             "Noto Sans CJK SC", serif;

  /* scale */
  --text-poster: clamp(40px, 6vw, 76px); /* hero/poster display */
  --text-display: 32px;   /* page titles */
  --text-title: 24px;     /* section titles */
  --text-verdict: 17px;   /* AI verdict prose (serif) */
  --text-body: 15px;      /* body, chat input */
  --text-small: 12px;     /* secondary UI */
  --text-meta: 11px;      /* mono metadata, pills */
  --text-micro: 10px;     /* mono CTAs, ledger labels */

  /* weights */
  --weight-poster: 800;   /* BioRhyme extrabold for posters only (heaviest weight we load) */
  --weight-display: 700;  /* BioRhyme bold for headings */
  --weight-em: 500;       /* BioRhyme italic emphasis */
  --weight-body: 400;
  --weight-medium: 500;
  --weight-strong: 600;

  /* rhythm */
  --leading-poster: .92;
  --leading-tight: 1.2;
  --leading-verdict: 1.55;
  --leading-body: 1.5;

  /* mono labels are tracked but NEVER all-caps — sentence case everywhere */
  --tracking-poster: -.03em;
  --tracking-label: .06em;
  --tracking-meta: .08em;
  --tracking-wide: .12em;

  /* ⚠️ 第二个缺口:BioRhyme 的梯级是 13·15·16.5·18,再往上直接跳到 hero 的
     clamp()。中间那一档从来没有过,于是七个地方各自发明了一个数 ——
     21 / 22 / 22 / 22 / 24 / 25 / 27。22 出现最多,而且沿着 13→15→16.5→18
     那条约 1.1 的比例往上走正好落在这儿。
     ⚠️ 不许把它吸到 20:20 是 Spinnaker 的档,三套字体各有各的梯,不许互串。 */
  --serif-title: 22px;
}

/* ── Woven weight ─────────────────────────────────────────────────────────
   The display device: one variable face, weight alternating between words.
   BioRhyme carries wght 200–800 in a single woff2, so this adds no font and
   no bytes — the three-font rule is untouched.

   Site-wide on purpose. It used to be scoped to .blocks-route, which left the
   glass routes' headlines single-weight and made the two halves of the site
   read as two typographic voices. Material differs by route (see blocks.css);
   type does not.

   300/700 rather than 400/700: against 700 a 400 barely separates at display
   size, and the gap is the whole effect. The split is done in
   assets/weave.js — deterministic by word index, and skipped on one-word
   headings, where alternation is not a pattern. */
[data-weave] [data-w="0"] { font-weight: 300; }
[data-weave] [data-w="1"] { font-weight: 700; }

/* ── Glow ─────────────────────────────────────────────────────────────────
   Display type emits a little light. Which light depends entirely on what it
   is sitting on, and the two cases are opposites — one token each, so no page
   has to decide.

   --glow-on-color   White, for type on a saturated field (the block routes:
                     cream letterforms on brick, olive, teal). This is the
                     reference case — light against colour.

   --glow-on-paper   The glyph's OWN colour, for type on paper or glass. Here
                     the type is dark ink on a near-white ground, so a white
                     halo has no contrast to give and simply disappears; the
                     visible bloom is the letterform's own hue spreading.
                     currentColor means it follows the rotating palette by
                     itself and no colour is ever written down.

   Both keep the tight ring under .06em and the outer halo under .34em. Past
   that the counters fill in and it stops reading as light and starts reading
   as fog — the same limit the method page found. Static: nothing pulses. */
:root {
  /* Turned up 2026-08-26 (owner). Both keep their shape — a tight edge under
     .06em and an outer bloom under .34em, past which the counters silt up and
     it stops being light and starts being fog — and gain roughly half again in
     strength. --glow-on-paper matters more than it used to: the white plates
     behind on-landscape type are gone, so the halo the glyph throws in its own
     colour is now the only thing separating it from the ridge behind it. */
  /* ⚠️⚠️ NOT WHITE any more. A white halo behind coloured type is a white edge
     drawn around every letter — and white is the one colour on the block routes
     that does not rotate with the 114 groups, on pages whose whole material
     rule is flat palette colour. What separates the type instead is the
     group's OPPOSING hue: the engine already picks two hues per card that
     measure 161° apart, and a block filled from one of them gets its glow from
     the other. Legibility from colour contrast rather than from a rim of
     light. Fallback stays neutral so a browser without relative colour, or a
     card with only one hue, loses the effect rather than the text. */
  --glow-on-color:
    0 0 .05em rgb(255 255 255 / .30),
    0 0 .30em rgb(255 255 255 / .18);
  --glow-on-paper:
    0 0 .06em color-mix(in srgb, currentColor 38%, transparent),
    0 0 .34em color-mix(in srgb, currentColor 24%, transparent);
}

/* The opposing hue, per block. A block that takes hue A glows in B and vice
   versa, so the halo is always the colour furthest from the ground it sits on.
   Lightness is pinned away from the fill's band — the glow has to read against
   the block, not melt into it. */
@supports (color: oklch(from red l c h)) {
  body.blocks-route :is([data-fill="dusty"], [data-fill="olive"], [data-fill="brick"]) {
    --glow-hue: var(--bw-hue-b, #C79A55);
  }
  body.blocks-route :is([data-fill="ochre"], [data-fill="teal"], [data-fill="clay"],
                        [data-fill="bone"], [data-fill="cream"]) {
    --glow-hue: var(--bw-hue-a, #5A6B45);
  }
  body.blocks-route [data-fill] {
    --glow-on-color:
      0 0 .05em oklch(from var(--glow-hue) .62 clamp(.10, c, .21) h),
      0 0 .26em oklch(from var(--glow-hue) .54 clamp(.09, c, .19) h);
  }
}

/* Glass routes: woven headings and the display numerals. The numerals are the
   page's loudest objects — a price, a unit count, a balance — and they are the
   one thing a reader looks for, so they are what earns the light. */
html[data-skin="paper"] body:not(.blocks-route) :is(
  /* display type — the largest words on each route */
  [data-weave], h1, .hero h1, #greetLine, .pagetitle,
  .section-head h2, .empty h2, .legal-poster-intro h1,
  /* the loud numerals: a price, a count, a balance — what a reader is
     actually hunting for on the page, so they are what carry the light */
  .plan-price b, .rate-number b, .pack .u, .ledgerstrip b,
  .count, .amount, .grant-amount, .u, .unit-price b, .units-big,
  /* the brand wordmark, which is display type by any other name */
  .brand b, .brand-word
) {
  text-shadow: var(--glow-on-paper);
}
