/**
 * BacInk design tokens — single source of truth for both the Vue SPA and the
 * static blog pages.
 *
 * Consumers:
 *   - SPA + static enhancement bundle: imported through src/styles/common.css
 *     (content-hashed by Vite or served as versioned enhance.css)
 *
 * Color tokens use light-dark(): the dark value activates where
 * color-scheme allows dark — enabled site-wide (common.css for the SPA,
 * static-styles.css for article pages). Manual override via
 * <html data-theme="light|dark"> (header toggle, src/utils/theme.ts).
 *
 * COLOR COMPOSITION PRINCIPLE (do not add per-opacity color variants):
 *   1. Base colors are opaque — one token per hue, no alpha baked in
 *      (--color-ink-base, --color-brand). The light/dark flip lives here.
 *   2. Alpha is a scale, not a magic number — use only the --alpha-* ramp
 *      (0.1 minimum unit: --alpha-1 = 10% … --alpha-9 = 90%). 1.0 = the base
 *      color itself (no mix). Never write a raw decimal/percentage alpha.
 *   3. Semantic tokens combine the two with color-mix():
 *        --token: color-mix(in srgb, var(--color-ink-base) var(--alpha-1), transparent);
 *      Consumers reference the semantic name, never a raw color-mix.
 *   4. Keep the alpha step the same in light and dark — let light-dark() swap
 *      the base color. Only a genuine per-mode look (e.g. opaque-in-dark rules)
 *      may light-dark() between two already-composed color-mix() colors.
 *   Exempt: elevation/effect tokens (--paper-shadow-*, --neumo-*,
 *   --paper-focus-ring) keep their literal alphas — a shadow recipe, not a fill.
 *
 * Never redeclare these values elsewhere. See
 * doc/design/static_spa_integration/STATIC_SPA_INTEGRATION_PLAN.md and .agents/skills/bacink-ui-guidelines/.
 */

:root {
  /* Base colors — opaque, one per hue; alpha is applied via color-mix + --alpha-* */
  --color-ink-base: light-dark(#000, #fff);

  /* Alpha scale — 0.1 minimum unit. Consume through color-mix, never as a raw
     alpha. 1.0 is not on the scale: that is the base color itself (no mix). */
  --alpha-1: 10%;
  --alpha-2: 20%;
  --alpha-3: 30%;
  --alpha-4: 40%;
  --alpha-5: 50%;
  --alpha-6: 60%;
  --alpha-7: 70%;
  --alpha-8: 80%;
  --alpha-9: 90%;

  /* Brand & semantic colors */
  --color-brand: #59c8e5;
  --color-brand-bright: #80dff5;
  /* brand hue brightened for dark surfaces */
  --color-brand-tint: light-dark(#eafaff, #16252b);
  --color-brand-soft: color-mix(in srgb, var(--color-brand) var(--alpha-1), transparent);
  /* was rgba(...,0.08) → α1 */
  /* Inline hyperlink text. Brand cyan (#59c8e5) is only ~1.9:1 on the light
     paper canvas and fails WCAG AA for text, so links use a deeper blue in
     light mode (5:1 on white/paper) while keeping the bright cyan on dark
     where it already contrasts well. Brand cyan stays for fills/accents. */
  --color-link: light-dark(#0e72a0, #59c8e5);
  --color-ink: light-dark(#2a2a2a, #d0cdc5);
  --color-heading: light-dark(#2c3e50, #e5e2da);
  --color-muted: light-dark(#666, #9a978f);
  --color-muted-strong: light-dark(#5f6b76, #bcc5d2);
  /* was .08/.14 → α1; the old -soft (.06/.10) also snapped to α1 and was merged in. */
  --color-surface-border: color-mix(in srgb, var(--color-ink-base) var(--alpha-1), transparent);
  /* Genuine per-mode need: borders stay denser on dark surfaces. was .16/.28 → α2/α3. */
  --color-surface-border-strong: light-dark(color-mix(in srgb, var(--color-ink-base) var(--alpha-2), transparent),
      color-mix(in srgb, var(--color-ink-base) var(--alpha-3), transparent));
  --color-accent-ink: light-dark(#12788d, #80dff5);
  /* Per-mode by design: the cyan tint needs more opacity to read on dark. was .12/.22 → α1/α2. */
  /* Deep brand cyan for text on the light book-stage mockup (BookHero).
     Scheme-invariant: the book illustration keeps its paper look in dark. */
  --color-brand-deep: #1f8fb0;
  --color-accent-surface: light-dark(color-mix(in srgb, var(--color-brand) var(--alpha-1), transparent),
      color-mix(in srgb, var(--color-brand) var(--alpha-2), transparent));
  /* Same alpha both modes; only the base hue flips (brand → bright). was .36 / .44 → α4. */
  --color-accent-border: color-mix(in srgb,
      light-dark(var(--color-brand), var(--color-brand-bright)) var(--alpha-4),
      transparent);
  /* Per-mode by design: frosted-white glass in light, cyan tint in dark. was .72 / .22 → α7 / α2. */
  --color-drawer-active-bg: light-dark(color-mix(in srgb, #fff var(--alpha-7), transparent),
      color-mix(in srgb, var(--color-brand) var(--alpha-2), transparent));
  /* Per-mode by design: dark ink outline in light, bright-cyan in dark. was .75 / .44 → α8 / α4. */
  --color-drawer-active-border: light-dark(color-mix(in srgb, var(--color-ink-base) var(--alpha-8), transparent),
      color-mix(in srgb, var(--color-brand-bright) var(--alpha-4), transparent));
  --color-error: #c53030;
  /* matches .paper-alert-error text */

  /* Solid-ink system: the black CTA / max-contrast fills used site-wide.
     ink-solid is the fill (pure black on paper, warm off-white on dark),
     on-ink is text/icons placed on that fill. */
  --color-ink-solid: light-dark(#000, #e8e5dd);
  --color-on-ink: light-dark(#fff, #111);
  /* Labels on scheme-invariant surfaces: the brand cyan and error red fills
     look the same in light and dark, so their text must NOT flip with the
     scheme (a flipping ink drops to ~1.2:1 in dark). Fixed dark on cyan
     (7.4:1) and fixed white on red (5.5:1). */
  --color-on-brand: #2a2a2a;
  --color-on-danger: #ffffff;
  --color-ink-solid-border: light-dark(#000, rgba(255, 255, 255, 0.4));
  /* Text inside neumo form controls (BaseInput) — teal-tinted ink. */
  --color-input-ink: light-dark(#0f4b5a, #9fd8e8);

  /* Modal backdrop — deliberately dark in both schemes. */
  --color-scrim: rgba(0, 0, 0, 0.5);
  /* Subtle ink tint for hover washes and quiet badge fills; flips to a white
     wash in dark where a black one would be invisible. Kept below the
     --alpha-* floor on purpose — it is an effect, not a fill. */
  --color-ink-wash: light-dark(rgba(0, 0, 0, 0.04), rgba(255, 255, 255, 0.08));

  /* Paper grain recipe shared by body and textured section shells. Like the
     shadow recipes, the literal alphas are part of the effect, not fills. */
  --paper-texture-image:
    radial-gradient(light-dark(rgba(120, 97, 60, 0.05), transparent) 0.6px, transparent 0.6px),
    linear-gradient(
      180deg,
      light-dark(rgba(255, 255, 255, 0.45), transparent),
      light-dark(rgba(236, 229, 214, 0.35), transparent)
    );
  --paper-texture-size:
    3px 3px,
    100% 100%;
  /* Fine dot grid laid over full-height marketing sections (homepage
     .paper-overlay). An effect recipe like the texture above, not a fill. */
  --paper-dot-overlay-image: radial-gradient(rgba(40, 40, 40, 0.08) 0.5px, transparent 0.5px);
  /* Review stars (testimonials). Gold in both modes; the ink-solid stroke
     around each glyph carries the mode contrast. */
  --color-rating-star: #ffd700;
  /* Near-black ink for large headings/strong text and emphasized borders. */
  --color-ink-strong: light-dark(#111, #e3e0d8);
  --color-ink-strong-border: light-dark(#111, rgba(255, 255, 255, 0.4));
  /* Plain white card surface (cooler than --paper-surface's warm #fffdf8). */
  --color-surface-card: light-dark(#fff, #232320);
  /* Warm low-emphasis ink (sidebar labels, archive lists). Sibling drifted
     variants still exist in BlogPage:757 / AboutPage:658 — consolidate later. */
  --color-muted-warm: light-dark(rgba(56, 47, 37, 0.62), rgba(214, 211, 201, 0.78));
  /* Soft gray panel surface (install-page steps, nav dropdown hover). */
  --color-surface-soft: light-dark(#f8f9fa, #262624);
  /* Softened ink for secondary labels/links (nav drawer, footer headings). */
  --color-ink-soft: light-dark(#555, #b8b5ad);

  /* Alert palette (paper-alert-* in common.css; auth pages share these). */
  --color-alert-error-bg: light-dark(#fff5f5, #2e1c1c);
  --color-alert-error-border: light-dark(#feb2b2, #7a3b3b);
  --color-alert-error-text: light-dark(var(--color-error), #f0a8a8);
  --color-alert-success-bg: light-dark(#f0fff4, #1b2a20);
  --color-alert-success-border: light-dark(#9ae6b4, #3f6e50);
  --color-alert-success-text: light-dark(#276749, #9ae6b4);

  /* Eink reader demo (/demo paged overlay) — mirrors the extension's reading
     surface: pure white paper / pure black in dark, like a real E Ink panel,
     deliberately outside the warm paper palette. The rule is the dashed
     column divider between the two page columns. */
  --eink-reader-surface: light-dark(#fff, #000);
  --eink-reader-ink: light-dark(#000, #fff);
  --eink-reader-rule: light-dark(#d3d3d3, #808080);
  /* Highlighter fill for the demo's marked passages, mimicking the extension's
     highlight feature (its default pen is pure yellow). Dark mode drops to a
     deep amber so the reader's light ink stays legible on top of it. */
  --eink-reader-highlight: light-dark(#ffff00, #4a3a00);

  /* Font stacks. The serif stack is deliberately platform-divergent, and the
     ORDER below is the whole design — do not "tidy" it.

     macOS keeps Songti TC, exactly as it renders today. Windows has no usable
     Traditional-Chinese Ming face at all (PMingLiU carries embedded bitmaps,
     thin stems and no bold, so every heading was synthesised), so it falls
     through to Noto Serif TC, shipped as a webfont from Google Fonts. A Mac
     never downloads it: the browser only fetches a webfont when it is actually
     needed to paint a glyph, and Songti TC covers the text first.

     The webfont <link> lives in index.html, backend/api/static-publisher.php
     and scripts/docs/render-page.mjs — all three emit independent <head>s and
     all three must load it, or that surface silently keeps PMingLiU.

     PMingLiU/MingLiU sit last as an emergency fallback for a failed font load;
     they must never move ahead of Noto Serif TC. Weights shipped: 400/700. */
  --font-serif:
    'SerifLatinLift', 'Songti TC', 'STSong', 'Noto Serif TC', 'PMingLiU', 'MingLiU', serif;

  /* The sans stack is platform-divergent for the same reason the serif one is,
     and the ORDER is again the whole design. Latin comes from Inter on every
     platform. For CJK, macOS keeps PingFang TC — named explicitly rather than
     left to the `sans-serif` default that used to resolve to it, so the face
     cannot drift with a browser setting — and Windows, which has nothing in
     that class (its default is Microsoft JhengHei, a visibly different
     humanist black), falls through to Noto Sans TC as a webfont.

     PingFang TC must stay AHEAD of Noto Sans TC: a Mac that matched the
     webfont first would download it and stop looking like a Mac, which is the
     one thing this stack exists to preserve.

     Unlike the serif webfont, Noto Sans TC is loaded ONLY by index.html, not
     by the two static <head>s — measured 2026-08-18, a css2 request for one
     CJK family is ~69KB gzipped and a second one doubles it, which is not
     worth paying on every blog article to style the one sans string those
     pages carry (the nav's user-name badge). Those pages fall through to
     Microsoft JhengHei there, exactly as they do today.

     Known limitation, shared with --font-serif: the TC faces are not scoped by
     :lang(), so ja/ko/zh-CN pages get Traditional glyph shapes for the Han
     they share. Pre-existing, and a per-locale stack is the real fix. */
  --font-sans:
    'Inter', -apple-system, 'Segoe UI', 'PingFang TC', 'Noto Sans TC', 'Helvetica Neue', Arial,
    sans-serif;

  /* Radius scale */
  --radius-sm: 6px;
  --radius-md: 12px;
  --radius-lg: 1rem;
  --radius-pill: 999px;

  /* Z-index layers (never use raw z-index values above component level) */
  --z-banner: 900; /* site-wide notices (e.g. session expiry) — above content, below header/drawer */
  --z-header: 1000;
  --z-drawer-backdrop: 999;
  --z-drawer: 1001;
  --z-modal: 1050;
  --z-consent: 1100;

  /* Header geometry (referenced by App.vue, AppNavigation, AppFooter) */
  --header-height: 80px;
  --gutter-line-x: 72px;

  /* Data-viz categorical series (admin analytics charts).
     Validated palette — each slot is one opaque base hue whose light/dark
     steps clear the CVD, chroma and lightness gates against the light
     (#fffdf8) and dark (#232320) chart surfaces. Assign slots in fixed order,
     never cycled, and cap a chart at 4 series: a 5th hue would not pass.
     Charts always ship the data table too, which discharges the sub-3:1
     contrast relief requirement on slots 3-4 in light mode. */
  --chart-series-1: light-dark(#2a78d6, #3987e5);
  --chart-series-2: light-dark(#eb6834, #d95926);
  --chart-series-3: light-dark(#1baf7a, #199e70);
  --chart-series-4: light-dark(#eda100, #c98500);

  --paper-bg: light-dark(#f6f8f8, #1d1d1b);
  --paper-surface: light-dark(#fffdf8, #232320);
  --paper-surface-bg:
    radial-gradient(light-dark(rgba(120, 97, 60, 0.035), transparent) 0.55px, transparent 0.55px),
    linear-gradient(180deg,
      light-dark(rgba(255, 255, 255, 0.72), transparent),
      light-dark(rgba(248, 241, 229, 0.72), transparent)),
    var(--paper-surface);
  --paper-shadow-raised:
    0 1px 0 rgba(255, 255, 255, 0.9) inset, 0 -1px 0 rgba(0, 0, 0, 0.04) inset,
    0 8px 18px rgba(68, 56, 35, 0.12);
  --paper-shadow-raised-hover:
    0 1px 0 rgba(255, 255, 255, 0.9) inset, 0 -1px 0 rgba(0, 0, 0, 0.06) inset,
    0 12px 24px rgba(68, 56, 35, 0.16);
  /* Letterpress emboss (BaseButton letterpress variants): the raised
     print-block look. The edge color paints the thick block-start/inline-start
     highlight border AND the soft counter-glow inside the shadow — keep the
     two in sync through this token. */
  --paper-shadow-emboss-edge: light-dark(rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.12));
  --paper-shadow-emboss:
    0 10px 18px rgba(0, 0, 0, 0.2),
    -4px -4px 10px var(--paper-shadow-emboss-edge);
  --neumo-inset:
    inset 4px 4px 8px light-dark(#d1d1d1, #121210),
    inset -4px -4px 8px light-dark(#ffffff, #30302c);
  --neumo-btn:
    5px 5px 10px light-dark(#d1d1d1, #121210),
    -5px -5px 10px light-dark(#ffffff, #30302c);
  --neumo-btn-hover:
    7px 7px 14px light-dark(#cecece, #121210),
    -6px -6px 12px light-dark(#ffffff, #30302c);
  --paper-focus-ring: 0 0 0 3px rgba(89, 200, 229, 0.16);
  /* rem (not em) so nested elements don't compound the size */
  --body-text-size: 1.3rem;

  /* Semantic accents (member center & status UI) */
  --color-brand-deep: light-dark(#16859a, #8ee2f3);
  /* Text/icon color placed on a brand-cyan fill (white fails WCAG AA on #59c8e5) */
  --color-on-brand: #1c2b30;
  --color-success: light-dark(#0f7a3d, #7fce9f);
  --color-success-soft: light-dark(rgba(15, 122, 61, 0.1), rgba(127, 206, 159, 0.14));
  --color-warning: light-dark(#9a6114, #e8b45a);
  --color-warning-soft: light-dark(rgba(245, 158, 11, 0.12), rgba(232, 180, 90, 0.16));
  --color-error-soft: light-dark(rgba(197, 48, 48, 0.08), rgba(229, 115, 115, 0.14));

  /* Ink (dark) action buttons — the site's established dark CTA pattern */
  --button-ink-bg: light-dark(#000, #e8e5dd);
  --button-ink-bg-hover: light-dark(#1c1c1c, #e3e0d8);
  --button-ink-text: light-dark(#fff, #111);

  /* Hairline borders on paper surfaces */
  --paper-border: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.1));
  --paper-border-strong: light-dark(rgba(0, 0, 0, 0.16), rgba(255, 255, 255, 0.22));
}

:root.android-mobile {
  --paper-bg: #fff;
  --paper-surface: #fff;
  --paper-surface-bg: #fff;
}

/**
 * SerifLatinLift — the Latin half of --font-serif on macOS, one size up.
 *
 * Songti TC is a CJK typeface: its Latin glyphs are drawn small relative to
 * the Han glyphs beside them, so in mixed copy digits and English words read a
 * size smaller than the Chinese around them. This face re-serves ONLY the
 * Latin range from that very same local font (typeface unchanged) with
 * size-adjust scaling it up ~2px at body size. Han glyphs are outside the
 * unicode-range and fall through untouched, so CJK rhythm does not move.
 *
 * CRITICAL: the src list must contain macOS-only faces. It used to lead with
 * local('PMingLiU'), local('MingLiU'), which was harmless while the whole
 * stack was PMingLiU on Windows — but now that Windows renders CJK in Noto
 * Serif TC, a PMingLiU src would paint Latin in PMingLiU *enlarged 112.5%*
 * next to Noto Serif TC Han glyphs: the ugly face, magnified, on the same
 * line as the good one. Windows must find nothing here and fall through to
 * Noto Serif TC, which needs no lift. Never add a Windows font to this src.
 *
 * No bold face is declared, so bold Latin is synthesised from this one. That
 * is what macOS already does today and this face exists to keep macOS
 * rendering unchanged — declaring a 700 face here would change it.
 *
 * size-adjust is Baseline (Chrome/Firefox 92, Safari 17); where unsupported
 * the descriptor is ignored and Latin renders as it did before.
 *
 * It is already the head of --font-serif — consume that token, never this
 * family name.
 *
 * unicode-range notes: dashes (U+2013-2014) and the ellipsis (U+2026) are
 * deliberately excluded — zh-TW copy uses them too, and scaling them would
 * stretch punctuation inside Chinese sentences. Curly quotes are included
 * because Chinese copy quotes with 「」 instead.
 */
@font-face {
  font-family: 'SerifLatinLift';
  src: local('Songti TC'), local('STSong');
  unicode-range: U+0020-007E, U+00A0-024F, U+1E00-1EFF, U+2018-201F;
  size-adjust: 112.5%;
}
