fontisan 0.2.17 → 0.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +14 -90
  3. data/README.adoc +257 -1
  4. data/docs/.vitepress/config.ts +68 -8
  5. data/docs/.vitepress/theme/style.css +570 -272
  6. data/docs/CONVERSION_GUIDE.adoc +31 -8
  7. data/docs/EXTRACT_TTC_MIGRATION.md +1 -1
  8. data/docs/WOFF_WOFF2_FORMATS.adoc +53 -0
  9. data/docs/api/conversion-options.md +37 -14
  10. data/docs/cli/audit.md +337 -0
  11. data/docs/cli/convert.md +20 -1
  12. data/docs/cli/index.md +31 -0
  13. data/docs/guide/color.md +1 -1
  14. data/docs/guide/conversion/options.md +32 -3
  15. data/docs/guide/conversion/ttf-otf.md +1 -1
  16. data/docs/guide/conversion/type1.md +1 -1
  17. data/docs/guide/conversion/web.md +91 -32
  18. data/docs/guide/conversion.md +6 -5
  19. data/docs/guide/formats/woff.md +35 -11
  20. data/docs/guide/index.md +2 -2
  21. data/docs/guide/migrations/extract-ttc.md +1 -1
  22. data/docs/guide/quick-start.md +4 -4
  23. data/docs/guide/type1.md +4 -4
  24. data/docs/guide/woff.md +19 -17
  25. data/docs/index.md +2 -0
  26. data/docs/lychee.toml +5 -1
  27. data/docs/package.json +1 -1
  28. data/docs/public/robots.txt +4 -0
  29. data/docs/scripts/post-build.mjs +81 -0
  30. data/lib/fontisan/audit/codepoint_range_coalescer.rb +41 -0
  31. data/lib/fontisan/audit/context.rb +122 -0
  32. data/lib/fontisan/audit/differ.rb +124 -0
  33. data/lib/fontisan/audit/extractors/aggregations.rb +54 -0
  34. data/lib/fontisan/audit/extractors/base.rb +26 -0
  35. data/lib/fontisan/audit/extractors/color_capabilities.rb +141 -0
  36. data/lib/fontisan/audit/extractors/coverage.rb +48 -0
  37. data/lib/fontisan/audit/extractors/hinting.rb +197 -0
  38. data/lib/fontisan/audit/extractors/identity.rb +52 -0
  39. data/lib/fontisan/audit/extractors/language_coverage.rb +37 -0
  40. data/lib/fontisan/audit/extractors/licensing.rb +79 -0
  41. data/lib/fontisan/audit/extractors/metrics.rb +103 -0
  42. data/lib/fontisan/audit/extractors/opentype_layout.rb +69 -0
  43. data/lib/fontisan/audit/extractors/provenance.rb +29 -0
  44. data/lib/fontisan/audit/extractors/style.rb +32 -0
  45. data/lib/fontisan/audit/extractors/variation_detail.rb +99 -0
  46. data/lib/fontisan/audit/extractors.rb +27 -0
  47. data/lib/fontisan/audit/library_aggregator.rb +83 -0
  48. data/lib/fontisan/audit/library_auditor.rb +90 -0
  49. data/lib/fontisan/audit/registry.rb +60 -0
  50. data/lib/fontisan/audit/style_extractor.rb +80 -0
  51. data/lib/fontisan/audit.rb +20 -0
  52. data/lib/fontisan/base_collection.rb +23 -9
  53. data/lib/fontisan/binary/structures.rb +0 -2
  54. data/lib/fontisan/binary.rb +11 -0
  55. data/lib/fontisan/cldr/aggregator.rb +33 -0
  56. data/lib/fontisan/cldr/cache_manager.rb +110 -0
  57. data/lib/fontisan/cldr/config.rb +59 -0
  58. data/lib/fontisan/cldr/download_error.rb +9 -0
  59. data/lib/fontisan/cldr/downloader.rb +79 -0
  60. data/lib/fontisan/cldr/error.rb +8 -0
  61. data/lib/fontisan/cldr/index.rb +64 -0
  62. data/lib/fontisan/cldr/index_builder.rb +72 -0
  63. data/lib/fontisan/cldr/unicode_set_parser.rb +172 -0
  64. data/lib/fontisan/cldr/unknown_version_error.rb +9 -0
  65. data/lib/fontisan/cldr/version_resolver.rb +91 -0
  66. data/lib/fontisan/cldr.rb +23 -0
  67. data/lib/fontisan/cli/cldr_cli.rb +85 -0
  68. data/lib/fontisan/cli/ucd_cli.rb +97 -0
  69. data/lib/fontisan/cli.rb +201 -2
  70. data/lib/fontisan/collection/builder.rb +0 -4
  71. data/lib/fontisan/collection/dfont_builder.rb +0 -4
  72. data/lib/fontisan/collection/shared_logic.rb +0 -2
  73. data/lib/fontisan/collection/writer.rb +0 -3
  74. data/lib/fontisan/collection.rb +15 -0
  75. data/lib/fontisan/commands/audit_command.rb +123 -0
  76. data/lib/fontisan/commands/audit_compare_command.rb +66 -0
  77. data/lib/fontisan/commands/audit_library_command.rb +46 -0
  78. data/lib/fontisan/commands/base_command.rb +0 -3
  79. data/lib/fontisan/commands/convert_command.rb +25 -20
  80. data/lib/fontisan/commands/dump_table_command.rb +0 -3
  81. data/lib/fontisan/commands/export_command.rb +0 -4
  82. data/lib/fontisan/commands/features_command.rb +0 -3
  83. data/lib/fontisan/commands/instance_command.rb +0 -5
  84. data/lib/fontisan/commands/ls_command.rb +0 -6
  85. data/lib/fontisan/commands/optical_size_command.rb +0 -3
  86. data/lib/fontisan/commands/pack_command.rb +0 -5
  87. data/lib/fontisan/commands/scripts_command.rb +0 -2
  88. data/lib/fontisan/commands/subset_command.rb +0 -3
  89. data/lib/fontisan/commands/unicode_command.rb +0 -3
  90. data/lib/fontisan/commands/unpack_command.rb +0 -7
  91. data/lib/fontisan/commands/validate_command.rb +0 -8
  92. data/lib/fontisan/commands/variable_command.rb +0 -3
  93. data/lib/fontisan/commands.rb +29 -0
  94. data/lib/fontisan/config/cldr.yml +22 -0
  95. data/lib/fontisan/config/conversion_matrix.yml +38 -0
  96. data/lib/fontisan/config/ucd.yml +23 -0
  97. data/lib/fontisan/constants.rb +19 -0
  98. data/lib/fontisan/conversion_options.rb +30 -19
  99. data/lib/fontisan/converters/cff_table_builder.rb +0 -3
  100. data/lib/fontisan/converters/collection_converter.rb +0 -8
  101. data/lib/fontisan/converters/conversion_strategy.rb +161 -46
  102. data/lib/fontisan/converters/format_converter.rb +143 -32
  103. data/lib/fontisan/converters/glyf_table_builder.rb +0 -2
  104. data/lib/fontisan/converters/outline_converter.rb +0 -19
  105. data/lib/fontisan/converters/outline_extraction.rb +0 -5
  106. data/lib/fontisan/converters/outline_optimizer.rb +0 -5
  107. data/lib/fontisan/converters/svg_generator.rb +0 -4
  108. data/lib/fontisan/converters/table_copier.rb +0 -2
  109. data/lib/fontisan/converters/type1_converter.rb +0 -11
  110. data/lib/fontisan/converters/woff2_encoder.rb +49 -20
  111. data/lib/fontisan/converters/woff_writer.rb +211 -282
  112. data/lib/fontisan/converters.rb +21 -0
  113. data/lib/fontisan/dfont_collection.rb +29 -10
  114. data/lib/fontisan/export/exporter.rb +0 -6
  115. data/lib/fontisan/export/transformers/font_to_ttx.rb +0 -9
  116. data/lib/fontisan/export/transformers/head_transformer.rb +0 -2
  117. data/lib/fontisan/export/transformers/hhea_transformer.rb +0 -2
  118. data/lib/fontisan/export/transformers/maxp_transformer.rb +0 -2
  119. data/lib/fontisan/export/transformers/name_transformer.rb +0 -2
  120. data/lib/fontisan/export/transformers/os2_transformer.rb +0 -2
  121. data/lib/fontisan/export/transformers/post_transformer.rb +0 -2
  122. data/lib/fontisan/export/transformers.rb +17 -0
  123. data/lib/fontisan/export.rb +13 -0
  124. data/lib/fontisan/font_loader.rb +14 -19
  125. data/lib/fontisan/font_writer.rb +0 -1
  126. data/lib/fontisan/formatters/audit_diff_text_renderer.rb +122 -0
  127. data/lib/fontisan/formatters/audit_text_renderer.rb +324 -0
  128. data/lib/fontisan/formatters/library_summary_text_renderer.rb +99 -0
  129. data/lib/fontisan/formatters/text_formatter.rb +6 -0
  130. data/lib/fontisan/formatters.rb +12 -0
  131. data/lib/fontisan/hints/hint_converter.rb +0 -1
  132. data/lib/fontisan/hints/postscript_hint_applier.rb +0 -9
  133. data/lib/fontisan/hints/postscript_hint_extractor.rb +0 -2
  134. data/lib/fontisan/hints/truetype_hint_extractor.rb +0 -2
  135. data/lib/fontisan/hints.rb +16 -0
  136. data/lib/fontisan/metrics_calculator.rb +0 -2
  137. data/lib/fontisan/models/all_scripts_features_info.rb +0 -1
  138. data/lib/fontisan/models/audit/audit_axis.rb +30 -0
  139. data/lib/fontisan/models/audit/audit_block.rb +32 -0
  140. data/lib/fontisan/models/audit/audit_diff.rb +77 -0
  141. data/lib/fontisan/models/audit/audit_report.rb +153 -0
  142. data/lib/fontisan/models/audit/codepoint_range.rb +40 -0
  143. data/lib/fontisan/models/audit/codepoint_set_diff.rb +34 -0
  144. data/lib/fontisan/models/audit/color_capabilities.rb +93 -0
  145. data/lib/fontisan/models/audit/duplicate_group.rb +23 -0
  146. data/lib/fontisan/models/audit/embedding_type.rb +76 -0
  147. data/lib/fontisan/models/audit/field_change.rb +28 -0
  148. data/lib/fontisan/models/audit/fs_selection_flags.rb +61 -0
  149. data/lib/fontisan/models/audit/gasp_range.rb +63 -0
  150. data/lib/fontisan/models/audit/hinting.rb +93 -0
  151. data/lib/fontisan/models/audit/library_summary.rb +40 -0
  152. data/lib/fontisan/models/audit/licensing.rb +48 -0
  153. data/lib/fontisan/models/audit/metrics.rb +111 -0
  154. data/lib/fontisan/models/audit/named_instance.rb +41 -0
  155. data/lib/fontisan/models/audit/opentype_layout.rb +40 -0
  156. data/lib/fontisan/models/audit/script_coverage_row.rb +26 -0
  157. data/lib/fontisan/models/audit/script_features.rb +28 -0
  158. data/lib/fontisan/models/audit/variation_detail.rb +44 -0
  159. data/lib/fontisan/models/audit.rb +33 -0
  160. data/lib/fontisan/models/cldr/language_coverage.rb +31 -0
  161. data/lib/fontisan/models/cldr.rb +12 -0
  162. data/lib/fontisan/models/collection_brief_info.rb +0 -1
  163. data/lib/fontisan/models/collection_info.rb +0 -2
  164. data/lib/fontisan/models/collection_list_info.rb +0 -1
  165. data/lib/fontisan/models/collection_validation_report.rb +0 -2
  166. data/lib/fontisan/models/color_glyph.rb +0 -1
  167. data/lib/fontisan/models/font_report.rb +0 -1
  168. data/lib/fontisan/models/ttx/tables.rb +21 -0
  169. data/lib/fontisan/models/ttx/ttfont.rb +0 -8
  170. data/lib/fontisan/models/ttx.rb +14 -0
  171. data/lib/fontisan/models/ucd/ucd.rb +38 -0
  172. data/lib/fontisan/models/ucd/ucd_char.rb +67 -0
  173. data/lib/fontisan/models/ucd.rb +19 -0
  174. data/lib/fontisan/models.rb +47 -0
  175. data/lib/fontisan/open_type_collection.rb +6 -5
  176. data/lib/fontisan/open_type_font.rb +8 -2
  177. data/lib/fontisan/open_type_font_extensions.rb +9 -9
  178. data/lib/fontisan/optimizers/pattern_analyzer.rb +0 -1
  179. data/lib/fontisan/optimizers.rb +14 -0
  180. data/lib/fontisan/outline_extractor.rb +0 -2
  181. data/lib/fontisan/parsers/dfont_parser.rb +0 -1
  182. data/lib/fontisan/parsers.rb +10 -0
  183. data/lib/fontisan/pipeline/format_detector.rb +29 -102
  184. data/lib/fontisan/pipeline/output_writer.rb +11 -9
  185. data/lib/fontisan/pipeline/strategies/instance_strategy.rb +0 -4
  186. data/lib/fontisan/pipeline/strategies/named_strategy.rb +0 -4
  187. data/lib/fontisan/pipeline/strategies/preserve_strategy.rb +0 -2
  188. data/lib/fontisan/pipeline/strategies.rb +14 -0
  189. data/lib/fontisan/pipeline/transformation_pipeline.rb +0 -7
  190. data/lib/fontisan/pipeline/variation_resolver.rb +0 -7
  191. data/lib/fontisan/pipeline.rb +13 -0
  192. data/lib/fontisan/sfnt_font.rb +29 -14
  193. data/lib/fontisan/sfnt_table.rb +0 -4
  194. data/lib/fontisan/subset/builder.rb +0 -6
  195. data/lib/fontisan/subset.rb +13 -0
  196. data/lib/fontisan/svg/font_generator.rb +0 -4
  197. data/lib/fontisan/svg/glyph_generator.rb +0 -2
  198. data/lib/fontisan/svg.rb +12 -0
  199. data/lib/fontisan/tables/cbdt.rb +0 -1
  200. data/lib/fontisan/tables/cblc.rb +0 -1
  201. data/lib/fontisan/tables/cff/charset.rb +0 -1
  202. data/lib/fontisan/tables/cff/charstring.rb +0 -1
  203. data/lib/fontisan/tables/cff/charstring_rebuilder.rb +0 -4
  204. data/lib/fontisan/tables/cff/charstrings_index.rb +0 -3
  205. data/lib/fontisan/tables/cff/dict.rb +0 -1
  206. data/lib/fontisan/tables/cff/encoding.rb +0 -1
  207. data/lib/fontisan/tables/cff/header.rb +0 -2
  208. data/lib/fontisan/tables/cff/hint_operation_injector.rb +0 -2
  209. data/lib/fontisan/tables/cff/index.rb +0 -1
  210. data/lib/fontisan/tables/cff/private_dict.rb +0 -2
  211. data/lib/fontisan/tables/cff/private_dict_writer.rb +0 -2
  212. data/lib/fontisan/tables/cff/table_builder.rb +0 -6
  213. data/lib/fontisan/tables/cff/top_dict.rb +0 -2
  214. data/lib/fontisan/tables/cff.rb +22 -15
  215. data/lib/fontisan/tables/cff2/charstring_parser.rb +0 -2
  216. data/lib/fontisan/tables/cff2/table_builder.rb +0 -11
  217. data/lib/fontisan/tables/cff2/table_reader.rb +0 -2
  218. data/lib/fontisan/tables/cff2.rb +13 -14
  219. data/lib/fontisan/tables/cmap.rb +24 -2
  220. data/lib/fontisan/tables/cmap_table.rb +0 -3
  221. data/lib/fontisan/tables/colr.rb +0 -1
  222. data/lib/fontisan/tables/cpal.rb +0 -1
  223. data/lib/fontisan/tables/cvar.rb +0 -2
  224. data/lib/fontisan/tables/fvar.rb +0 -1
  225. data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +0 -2
  226. data/lib/fontisan/tables/glyf/glyph_builder.rb +0 -3
  227. data/lib/fontisan/tables/glyf.rb +0 -6
  228. data/lib/fontisan/tables/glyf_table.rb +0 -3
  229. data/lib/fontisan/tables/gpos.rb +0 -2
  230. data/lib/fontisan/tables/gsub.rb +0 -2
  231. data/lib/fontisan/tables/gvar.rb +0 -2
  232. data/lib/fontisan/tables/head.rb +0 -2
  233. data/lib/fontisan/tables/head_table.rb +0 -3
  234. data/lib/fontisan/tables/hhea.rb +0 -2
  235. data/lib/fontisan/tables/hhea_table.rb +0 -3
  236. data/lib/fontisan/tables/hmtx.rb +0 -2
  237. data/lib/fontisan/tables/hmtx_table.rb +0 -3
  238. data/lib/fontisan/tables/hvar.rb +0 -3
  239. data/lib/fontisan/tables/loca.rb +0 -2
  240. data/lib/fontisan/tables/loca_table.rb +0 -3
  241. data/lib/fontisan/tables/maxp.rb +0 -2
  242. data/lib/fontisan/tables/maxp_table.rb +0 -3
  243. data/lib/fontisan/tables/mvar.rb +0 -3
  244. data/lib/fontisan/tables/name.rb +0 -2
  245. data/lib/fontisan/tables/name_table.rb +0 -3
  246. data/lib/fontisan/tables/os2_table.rb +0 -3
  247. data/lib/fontisan/tables/post_table.rb +0 -3
  248. data/lib/fontisan/tables/sbix.rb +0 -1
  249. data/lib/fontisan/tables/svg.rb +0 -1
  250. data/lib/fontisan/tables/variation_common.rb +0 -1
  251. data/lib/fontisan/tables/vvar.rb +0 -3
  252. data/lib/fontisan/tables.rb +54 -0
  253. data/lib/fontisan/true_type_collection.rb +6 -14
  254. data/lib/fontisan/true_type_font.rb +8 -2
  255. data/lib/fontisan/true_type_font_extensions.rb +9 -9
  256. data/lib/fontisan/type1/afm_generator.rb +0 -4
  257. data/lib/fontisan/type1/conversion_options.rb +0 -2
  258. data/lib/fontisan/type1/encodings.rb +0 -2
  259. data/lib/fontisan/type1/generator.rb +0 -8
  260. data/lib/fontisan/type1/pfa_generator.rb +0 -3
  261. data/lib/fontisan/type1/pfb_generator.rb +0 -5
  262. data/lib/fontisan/type1/pfm_generator.rb +0 -4
  263. data/lib/fontisan/type1.rb +42 -69
  264. data/lib/fontisan/type1_font.rb +40 -11
  265. data/lib/fontisan/ucd/aggregator.rb +73 -0
  266. data/lib/fontisan/ucd/cache_manager.rb +111 -0
  267. data/lib/fontisan/ucd/config.rb +59 -0
  268. data/lib/fontisan/ucd/download_error.rb +9 -0
  269. data/lib/fontisan/ucd/downloader.rb +88 -0
  270. data/lib/fontisan/ucd/error.rb +8 -0
  271. data/lib/fontisan/ucd/index.rb +103 -0
  272. data/lib/fontisan/ucd/index_builder.rb +107 -0
  273. data/lib/fontisan/ucd/range_entry.rb +56 -0
  274. data/lib/fontisan/ucd/unknown_version_error.rb +9 -0
  275. data/lib/fontisan/ucd/version_resolver.rb +79 -0
  276. data/lib/fontisan/ucd.rb +23 -0
  277. data/lib/fontisan/utilities/checksum_calculator.rb +0 -1
  278. data/lib/fontisan/utilities.rb +10 -0
  279. data/lib/fontisan/utils.rb +10 -0
  280. data/lib/fontisan/validation/collection_validator.rb +0 -2
  281. data/lib/fontisan/validation.rb +9 -0
  282. data/lib/fontisan/validators/basic_validator.rb +0 -2
  283. data/lib/fontisan/validators/font_book_validator.rb +0 -2
  284. data/lib/fontisan/validators/opentype_validator.rb +0 -2
  285. data/lib/fontisan/validators/profile_loader.rb +0 -5
  286. data/lib/fontisan/validators/validator.rb +0 -2
  287. data/lib/fontisan/validators/web_font_validator.rb +0 -2
  288. data/lib/fontisan/validators.rb +14 -0
  289. data/lib/fontisan/variable/delta_applicator.rb +0 -4
  290. data/lib/fontisan/variable/instancer.rb +0 -3
  291. data/lib/fontisan/variable/static_font_builder.rb +0 -3
  292. data/lib/fontisan/variable.rb +16 -0
  293. data/lib/fontisan/variation/blend_applier.rb +0 -2
  294. data/lib/fontisan/variation/cache.rb +0 -2
  295. data/lib/fontisan/variation/converter.rb +0 -3
  296. data/lib/fontisan/variation/data_extractor.rb +0 -2
  297. data/lib/fontisan/variation/delta_applier.rb +0 -5
  298. data/lib/fontisan/variation/inspector.rb +0 -1
  299. data/lib/fontisan/variation/instance_generator.rb +0 -6
  300. data/lib/fontisan/variation/instance_writer.rb +0 -5
  301. data/lib/fontisan/variation/metrics_adjuster.rb +0 -4
  302. data/lib/fontisan/variation/optimizer.rb +0 -3
  303. data/lib/fontisan/variation/parallel_generator.rb +0 -3
  304. data/lib/fontisan/variation/subsetter.rb +0 -4
  305. data/lib/fontisan/variation/tuple_variation_header.rb +0 -2
  306. data/lib/fontisan/variation/variable_svg_generator.rb +0 -3
  307. data/lib/fontisan/variation/variation_context.rb +0 -3
  308. data/lib/fontisan/variation/variation_preserver.rb +0 -3
  309. data/lib/fontisan/variation.rb +31 -0
  310. data/lib/fontisan/version.rb +1 -1
  311. data/lib/fontisan/woff2.rb +13 -0
  312. data/lib/fontisan/woff2_font.rb +31 -9
  313. data/lib/fontisan/woff_font.rb +31 -2
  314. data/lib/fontisan.rb +124 -196
  315. metadata +114 -7
  316. data/fontisan.gemspec +0 -48
@@ -1,5 +1,12 @@
1
+ /* ================================================================
2
+ Fontist — Type Specimen design system (site-wide)
3
+ One palette + type system applied across every surface:
4
+ body, VPNav, VPFooter, and .vp-doc (all content pages).
5
+ ================================================================ */
6
+
7
+ /* ── Brand + specimen palette ─────────────────────────────────── */
1
8
  :root {
2
- /* Brand Colors from Logo */
9
+ /* Brand colors from the logo */
3
10
  --fontist-rose: #bf4e6a;
4
11
  --fontist-rose-light: #d4718a;
5
12
  --fontist-dark: #4d4b54;
@@ -8,323 +15,614 @@
8
15
  --fontist-beige: #bebbac;
9
16
  --fontist-pale: #dddac8;
10
17
 
11
- /* VitePress Theme Overrides - Light Mode */
12
- --vp-c-brand-1: var(--fontist-rose);
18
+ /* Specimen tokens LIGHT (default) */
19
+ --spec-paper: #f1ece1;
20
+ --spec-paper-deep: #e7e0d1;
21
+ --spec-ink: #1c1a18;
22
+ --spec-ink-soft: #4a4744;
23
+ --spec-mute: #75716c;
24
+ --spec-rose: #b8475f;
25
+ --spec-rose-soft: #d4718a;
26
+ --spec-rule: rgba(28, 26, 24, 0.16);
27
+ --spec-rule-strong: rgba(28, 26, 24, 0.5);
28
+ --spec-term-bg: #1c1a18;
29
+ --spec-term-ink: #ecdfd0;
30
+
31
+ /* VitePress brand wiring */
32
+ --vp-c-brand-1: var(--spec-rose);
13
33
  --vp-c-brand-2: #a3435a;
14
- --vp-c-brand-3: var(--fontist-dark);
15
- --vp-c-brand-soft: rgba(191, 78, 106, 0.14);
16
-
17
- /* Text Colors */
18
- --vp-c-text-1: var(--fontist-dark);
19
- --vp-c-text-2: var(--fontist-gray);
20
- --vp-c-text-3: #8a8888;
21
-
22
- /* Background Colors */
23
- --vp-c-bg-soft: #f8f7f4;
24
- --vp-c-bg-alt: #f2f1ed;
25
- --vp-c-bg: #ffffff;
26
-
27
- /* Hero */
28
- --vp-home-hero-name-color: var(--fontist-rose);
29
- --vp-home-hero-name-background: linear-gradient(
30
- 120deg,
31
- var(--fontist-rose) 30%,
32
- #d4718a
33
- );
34
+ --vp-c-brand-3: var(--spec-ink);
35
+ --vp-c-brand-soft: rgba(184, 71, 95, 0.12);
36
+ --vp-c-text-1: var(--spec-ink);
37
+ --vp-c-text-2: var(--spec-ink-soft);
38
+ --vp-c-text-3: var(--spec-mute);
39
+ --vp-c-bg: var(--spec-paper);
40
+ --vp-c-bg-soft: var(--spec-paper-deep);
41
+ --vp-c-bg-alt: var(--spec-paper-deep);
42
+ --vp-c-divider: var(--spec-rule);
43
+ --vp-c-gutter: var(--spec-rule);
44
+
45
+ /* Nav bg must be opaque (paper) so scrolled content doesn't leak through */
46
+ --vp-nav-bg-color: var(--spec-paper);
47
+ --vp-sidebar-bg-color: var(--spec-paper);
48
+
49
+ /* Type */
50
+ --spec-font-display: "Spectral", Georgia, serif;
51
+ --spec-font-body: "IBM Plex Sans", -apple-system, system-ui, sans-serif;
52
+ --spec-font-mono: "IBM Plex Mono", ui-monospace, "JetBrains Mono", monospace;
53
+
54
+ /* Override VitePress's default Inter with our specimen fonts everywhere */
55
+ --vp-font-family-base: var(--spec-font-body);
56
+ --vp-font-family-mono: var(--spec-font-mono);
34
57
 
35
58
  /* Buttons */
36
- --vp-button-brand-border: var(--fontist-rose);
59
+ --vp-button-brand-border: var(--spec-rose);
37
60
  --vp-button-brand-text: #ffffff;
38
- --vp-button-brand-bg: var(--fontist-rose);
61
+ --vp-button-brand-bg: var(--spec-rose);
39
62
  --vp-button-brand-hover-border: #a3435a;
40
63
  --vp-button-brand-hover-text: #ffffff;
41
64
  --vp-button-brand-hover-bg: #a3435a;
42
- --vp-button-brand-active-border: #8a3849;
43
- --vp-button-brand-active-text: #ffffff;
44
- --vp-button-brand-active-bg: #8a3849;
45
-
46
- /* Sidebar */
47
- --vp-sidebar-bg-color: var(--vp-c-bg-soft);
48
65
 
49
- /* Nav */
50
- --vp-nav-bg-color: var(--vp-c-bg);
66
+ --vp-button-alt-border: var(--spec-rule-strong);
67
+ --vp-button-alt-text: var(--spec-ink);
68
+ --vp-button-alt-bg: transparent;
69
+ --vp-button-alt-hover-border: var(--spec-rose);
70
+ --vp-button-alt-hover-text: var(--spec-rose);
71
+ --vp-button-alt-hover-bg: transparent;
51
72
  }
52
73
 
53
- /* Dark Mode */
74
+ /* ── Dark mode specimen ───────────────────────────────────────── */
54
75
  html.dark {
55
- --vp-c-brand-1: var(--fontist-rose-light);
76
+ --spec-paper: #161513;
77
+ --spec-paper-deep: #211f1c;
78
+ --spec-ink: #ecdfd0;
79
+ --spec-ink-soft: #b8ada0;
80
+ --spec-mute: #8a857f;
81
+ --spec-rose: #d4718a;
82
+ --spec-rose-soft: #e08a9e;
83
+ --spec-rule: rgba(236, 223, 208, 0.14);
84
+ --spec-rule-strong: rgba(236, 223, 208, 0.42);
85
+ --spec-term-bg: #0d0c0c;
86
+ --spec-term-ink: #ecdfd0;
87
+
88
+ --vp-c-brand-1: var(--spec-rose);
56
89
  --vp-c-brand-2: var(--fontist-rose);
57
- --vp-c-brand-3: #e1dfd2;
90
+ --vp-c-brand-3: var(--spec-ink);
58
91
  --vp-c-brand-soft: rgba(212, 113, 138, 0.14);
59
-
60
- /* Text Colors - Dark Mode */
61
- --vp-c-text-1: #e1dfd2;
62
- --vp-c-text-2: #bebbac;
63
- --vp-c-text-3: #8a8888;
64
-
65
- /* Background Colors - Dark Mode */
66
- --vp-c-bg: #1a1918;
67
- --vp-c-bg-soft: #222120;
68
- --vp-c-bg-alt: #2a2826;
69
- --vp-c-bg-elv: #2a2826;
70
-
71
- /* Hero - Dark Mode */
72
- --vp-home-hero-name-background: linear-gradient(
73
- 120deg,
74
- var(--fontist-rose-light) 30%,
75
- #e08a9e
76
- );
77
-
78
- /* Buttons - Dark Mode */
79
- --vp-button-brand-border: var(--fontist-rose-light);
80
- --vp-button-brand-text: #1a1918;
81
- --vp-button-brand-bg: var(--fontist-rose-light);
92
+ --vp-c-text-1: var(--spec-ink);
93
+ --vp-c-text-2: var(--spec-ink-soft);
94
+ --vp-c-text-3: var(--spec-mute);
95
+ --vp-c-bg: var(--spec-paper);
96
+ --vp-c-bg-soft: var(--spec-paper-deep);
97
+ --vp-c-bg-alt: var(--spec-paper-deep);
98
+ --vp-c-divider: var(--spec-rule);
99
+ --vp-c-gutter: var(--spec-rule);
100
+
101
+ --vp-button-brand-border: var(--spec-rose);
102
+ --vp-button-brand-text: #161513;
103
+ --vp-button-brand-bg: var(--spec-rose);
82
104
  --vp-button-brand-hover-border: var(--fontist-rose);
83
- --vp-button-brand-hover-text: #1a1918;
105
+ --vp-button-brand-hover-text: #161513;
84
106
  --vp-button-brand-hover-bg: var(--fontist-rose);
85
- --vp-button-brand-active-border: #a3435a;
86
- --vp-button-brand-active-text: #1a1918;
87
- --vp-button-brand-active-bg: #a3435a;
88
107
  }
89
108
 
90
- /* Feature cards on home page - Light */
91
- .VPFeature .title {
92
- color: var(--fontist-dark);
109
+ /* ── Site-wide body: paper + grain + base type ────────────────── */
110
+ body {
111
+ background-color: var(--spec-paper);
112
+ font-family: var(--spec-font-body);
113
+ color: var(--spec-ink);
114
+ }
115
+ /* paper grain overlay (every page) */
116
+ body::before {
117
+ content: "";
118
+ position: fixed;
119
+ inset: 0;
120
+ z-index: 200;
121
+ pointer-events: none;
122
+ opacity: 0.04;
123
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
124
+ mix-blend-mode: multiply;
125
+ }
126
+ html.dark body::before {
127
+ opacity: 0.06;
128
+ mix-blend-mode: screen;
129
+ }
130
+
131
+ /* ── Top navigation (VPNav) — specimen restyle ─────────────────── */
132
+ /* Use box-shadow for the rule, not border-bottom — VPNavBar is height:64px
133
+ with box-sizing:border-box, so border-bottom is inside the 64px and gets
134
+ overlapped by .content (also 64px). box-shadow renders outside the box. */
135
+ .VPNavBar {
136
+ background-color: var(--spec-paper) !important;
137
+ border-bottom: none !important;
138
+ box-shadow: 0 1px 0 var(--spec-rule) !important;
139
+ }
140
+ .VPNavBar.has-sidebar .content,
141
+ .VPNavBar .content {
142
+ background-color: transparent !important;
143
+ }
144
+ .VPNavBar .content .curtain {
145
+ display: none !important;
146
+ }
147
+
148
+ /* Nav links + dropdown groups: mono, tracked */
149
+ .VPNavBarMenuLink,
150
+ .VPNavBarMenuGroup {
151
+ font-family: var(--spec-font-mono) !important;
152
+ font-size: 12px !important;
153
+ letter-spacing: 0.14em !important;
154
+ text-transform: uppercase !important;
155
+ color: var(--spec-ink-soft) !important;
156
+ font-weight: 500 !important;
157
+ transition: color 0.2s ease;
158
+ }
159
+ .VPNavBarMenuLink:hover,
160
+ .VPNavBarMenuGroup:hover {
161
+ color: var(--spec-rose) !important;
162
+ }
163
+ .VPNavBarMenuLink .text,
164
+ .VPNavBarMenuGroup .text {
165
+ font-family: inherit !important;
166
+ }
167
+ /* Dropdown button — VitePress styles .button directly, so inheritance isn't enough */
168
+ .VPNavBarMenuGroup .button,
169
+ .VPNavBarExtra .button {
170
+ font-family: var(--spec-font-mono) !important;
171
+ font-size: 12px !important;
172
+ letter-spacing: 0.14em !important;
173
+ text-transform: uppercase !important;
174
+ font-weight: 500 !important;
175
+ color: var(--spec-ink-soft) !important;
176
+ line-height: 64px !important;
177
+ transition: color 0.2s ease;
178
+ }
179
+ .VPNavBarMenuGroup:hover .button,
180
+ .VPNavBarExtra:hover .button {
181
+ color: var(--spec-rose) !important;
182
+ }
183
+ /* VitePress renders the visible text in an inner .text span with its own
184
+ font-size (14px) and color — must target it directly, not via .button */
185
+ .VPNavBarMenuGroup .button .text,
186
+ .VPNavBarExtra .button .text {
187
+ font-size: 12px !important;
188
+ color: var(--spec-ink-soft) !important;
189
+ font-weight: 500 !important;
190
+ }
191
+ .VPNavBarMenuGroup:hover .button .text,
192
+ .VPNavBarExtra:hover .button .text {
193
+ color: var(--spec-rose) !important;
194
+ }
195
+
196
+ /* Flyout menu panel (shared by Integrations dropdown + ellipsis extra menu) */
197
+ .VPMenu {
198
+ background: var(--spec-paper) !important;
199
+ border: 1px solid var(--spec-rule) !important;
200
+ border-radius: 0 !important;
201
+ box-shadow: none !important;
202
+ }
203
+ .VPMenu .group-title {
204
+ font-family: var(--spec-font-mono) !important;
205
+ font-size: 11px !important;
206
+ letter-spacing: 0.14em !important;
207
+ text-transform: uppercase !important;
208
+ color: var(--spec-mute) !important;
209
+ }
210
+ /* Menu links — must out-specify VitePress's .VPLink.link (0-2-0),
211
+ so we chain .VPMenu parent to reach 0-2-1. Actual DOM:
212
+ .VPMenu > .items > .VPMenuLink > a.VPLink.link > span */
213
+ .VPMenu .VPMenuLink a,
214
+ .VPMenu .VPMenuLink a span {
215
+ font-family: var(--spec-font-mono) !important;
216
+ font-size: 12px !important;
217
+ letter-spacing: 0.14em !important;
218
+ text-transform: uppercase !important;
219
+ font-weight: 500 !important;
220
+ color: var(--spec-ink-soft) !important;
221
+ transition: color 0.2s ease, background 0.2s ease;
222
+ }
223
+ .VPMenu .VPMenuLink a:hover {
224
+ color: var(--spec-rose) !important;
225
+ background: var(--spec-paper-deep) !important;
226
+ }
227
+ .VPMenu .VPMenuLink a:hover span {
228
+ color: var(--spec-rose) !important;
229
+ }
230
+ /* Ellipsis menu: Appearance label + item labels — .VPMenu parent
231
+ boosts specificity above VitePress's scoped component styles */
232
+ .VPMenu .item .label,
233
+ .VPMenu .group .item {
234
+ font-family: var(--spec-font-mono) !important;
235
+ font-size: 12px !important;
236
+ letter-spacing: 0.14em !important;
237
+ text-transform: uppercase !important;
238
+ font-weight: 500 !important;
239
+ color: var(--spec-ink-soft) !important;
240
+ }
241
+
242
+ /* Logo / title */
243
+ .VPNavBar .VPNavBarTitle .title {
244
+ font-family: var(--spec-font-display) !important;
245
+ font-weight: 400 !important;
246
+ font-size: 18px !important;
247
+ letter-spacing: 0.02em !important;
248
+ color: var(--spec-ink) !important;
93
249
  }
94
-
95
- .VPFeature .details {
96
- color: var(--fontist-gray);
97
- }
98
-
99
- .VPFeature:hover {
100
- border-color: var(--fontist-rose);
250
+ .VPNav .VPImage {
251
+ height: 32px;
101
252
  }
102
253
 
103
- /* Feature cards - Dark */
104
- html.dark .VPFeature .title {
105
- color: var(--fontist-cream);
254
+ /* Social links + appearance toggle + search: quieter */
255
+ .VPNavBar .VPNavBarSocialLinks .social-link,
256
+ .VPNavBar .VPSocialLink,
257
+ .VPNavBarAppearance,
258
+ .VPNavBarExtra {
259
+ color: var(--spec-ink-soft) !important;
106
260
  }
107
-
108
- html.dark .VPFeature .details {
109
- color: var(--fontist-beige);
261
+ .VPNavBar .VPNavBarSearch {
262
+ --vp-nav-height: 56px;
110
263
  }
111
-
112
- /* Links */
113
- .vp-doc a {
114
- color: var(--fontist-rose);
264
+ .VPNavBarSearch .DocSearch-Button {
265
+ background: transparent !important;
266
+ border: 1px solid var(--spec-rule) !important;
267
+ font-family: var(--spec-font-mono) !important;
268
+ font-size: 12px !important;
269
+ letter-spacing: 0.06em !important;
115
270
  }
116
-
117
- .vp-doc a:hover {
118
- color: #a3435a;
271
+ .VPNavBarSearch .DocSearch-Button:hover {
272
+ border-color: var(--spec-rose) !important;
119
273
  }
120
274
 
121
- html.dark .vp-doc a {
122
- color: var(--fontist-rose-light);
275
+ /* ── Footer — specimen colophon ───────────────────────────────── */
276
+ .VPFooter {
277
+ border-top: 1px solid var(--spec-rule) !important;
278
+ background-color: transparent !important;
279
+ padding: 28px 24px 40px !important;
280
+ }
281
+ .VPFooter .copyright,
282
+ .VPFooter .message {
283
+ font-family: var(--spec-font-mono) !important;
284
+ font-size: 11px !important;
285
+ letter-spacing: 0.1em !important;
286
+ color: var(--spec-mute) !important;
287
+ }
288
+ .VPFooter .message a {
289
+ color: var(--spec-ink-soft) !important;
290
+ border-bottom: 1px solid var(--spec-rule-strong);
291
+ text-decoration: none;
292
+ }
293
+ .VPFooter .message a:hover {
294
+ color: var(--spec-rose) !important;
295
+ }
296
+
297
+ /* ── .vp-doc typography — every content page (about, blog, docs) ─ */
298
+ .vp-doc {
299
+ font-family: var(--spec-font-body);
300
+ color: var(--spec-ink);
301
+ }
302
+ .vp-doc h1,
303
+ .vp-doc h2,
304
+ .vp-doc h3,
305
+ .vp-doc h4,
306
+ .vp-doc h5,
307
+ .vp-doc h6 {
308
+ font-family: var(--spec-font-display);
309
+ color: var(--spec-ink);
310
+ letter-spacing: -0.018em;
311
+ font-weight: 380;
312
+ }
313
+ .vp-doc h1 {
314
+ font-size: clamp(40px, 6vw, 64px);
315
+ font-weight: 360;
316
+ font-variation-settings: "opsz" 120;
317
+ line-height: 1.02;
318
+ letter-spacing: -0.025em;
319
+ margin-top: 0;
320
+ }
321
+ .vp-doc h2 {
322
+ font-size: clamp(28px, 3.4vw, 40px);
323
+ font-variation-settings: "opsz" 60;
324
+ border-top: 1px solid var(--spec-rule);
325
+ padding-top: 1.6em;
326
+ margin-top: 2.2em;
327
+ }
328
+ .vp-doc h3 {
329
+ font-size: clamp(20px, 2vw, 24px);
330
+ font-variation-settings: "opsz" 36;
331
+ }
332
+ .vp-doc p,
333
+ .vp-doc li {
334
+ font-size: 16.5px;
335
+ line-height: 1.65;
336
+ color: var(--spec-ink-soft);
337
+ }
338
+ .vp-doc p {
339
+ margin: 1.1em 0;
123
340
  }
124
-
125
- html.dark .vp-doc a:hover {
126
- color: var(--fontist-rose);
341
+ .vp-doc a {
342
+ color: var(--spec-rose);
343
+ text-decoration: none;
344
+ border-bottom: 1px solid var(--spec-rule);
345
+ transition: color 0.2s, border-color 0.2s;
127
346
  }
128
-
129
- /* Footer */
130
- .VPFooter {
131
- background-color: var(--vp-c-bg-soft);
132
- border-top: 1px solid var(--fontist-pale);
347
+ .vp-doc a:hover {
348
+ color: var(--spec-rose);
349
+ border-color: var(--spec-rose);
133
350
  }
134
-
135
- html.dark .VPFooter {
136
- border-top-color: #3a3836;
351
+ .vp-doc strong {
352
+ color: var(--spec-ink);
353
+ font-weight: 600;
137
354
  }
138
-
139
- /* Code blocks - Light */
355
+ .vp-doc blockquote {
356
+ font-family: var(--spec-font-display);
357
+ font-style: italic;
358
+ font-weight: 380;
359
+ font-size: 1.25em;
360
+ line-height: 1.5;
361
+ color: var(--spec-ink);
362
+ border-left: 3px solid var(--spec-rose);
363
+ padding-left: 1.5em;
364
+ margin: 2em 0;
365
+ }
366
+ .vp-doc hr {
367
+ border: none;
368
+ border-top: 1px solid var(--spec-rule-strong);
369
+ width: 48px;
370
+ margin: 3em auto;
371
+ }
372
+ /* Drop cap — rose Spectral italic on the first paragraph of content pages */
373
+ .vp-doc > p:first-of-type::first-letter {
374
+ font-family: var(--spec-font-display);
375
+ font-style: italic;
376
+ font-weight: 460;
377
+ font-size: 3.4em;
378
+ float: left;
379
+ line-height: 0.8;
380
+ padding: 0.04em 0.1em 0 0;
381
+ color: var(--spec-rose);
382
+ font-variation-settings: "opsz" 72, "wght" 520;
383
+ }
384
+ /* Code blocks — light: warm paper-deep (matches Shiki light tokens);
385
+ dark: terminal ink (matches Shiki dark tokens) */
140
386
  .vp-doc [class*="language-"] {
141
- background-color: var(--fontist-dark);
387
+ background-color: var(--spec-paper-deep) !important;
388
+ border: 1px solid var(--spec-rule);
389
+ font-family: var(--spec-font-mono);
390
+ font-size: 13.5px;
142
391
  }
143
-
144
- /* Code blocks - Dark */
145
392
  html.dark .vp-doc [class*="language-"] {
146
- background-color: #0d0c0c;
147
- }
148
-
149
- /* Logo sizing */
150
- .VPNav .VPImage {
151
- height: 39px;
152
- }
153
-
154
- /* Search */
155
- .VPLocalSearchBox {
156
- --vp-c-brand-1: var(--fontist-rose);
157
- }
158
-
159
- /* Feature Comparison Tables */
160
- .feature-comparison {
161
- width: 100%;
393
+ background-color: var(--spec-term-bg) !important;
394
+ }
395
+ .vp-doc :not(pre) > code {
396
+ font-family: var(--spec-font-mono);
397
+ font-size: 0.9em;
398
+ background: var(--spec-paper-deep);
399
+ border: 1px solid var(--spec-rule);
400
+ padding: 0.1em 0.4em;
401
+ border-radius: 2px;
402
+ color: var(--spec-ink);
403
+ }
404
+ /* Tables */
405
+ .vp-doc table {
406
+ font-size: 14px;
162
407
  border-collapse: collapse;
163
- margin: 1.5rem 0;
164
- font-size: 0.9rem;
165
- }
166
-
167
- .feature-comparison th,
168
- .feature-comparison td {
169
- padding: 0.75rem 1rem;
170
- text-align: left;
171
- border-bottom: 1px solid var(--vp-c-divider);
172
- }
173
-
174
- .feature-comparison th {
175
- background: var(--vp-c-bg-soft);
176
- font-weight: 600;
177
- white-space: nowrap;
178
408
  }
179
-
180
- .feature-comparison .feature-name {
181
- font-weight: 500;
182
- }
183
-
184
- .feature-comparison .supported {
185
- color: var(--vp-c-green-1);
409
+ .vp-doc table th,
410
+ .vp-doc table td {
411
+ border: 1px solid var(--spec-rule);
412
+ padding: 8px 12px;
186
413
  }
187
-
188
- .feature-comparison .not-supported {
189
- color: var(--vp-c-red-1);
190
- }
191
-
192
- .feature-comparison .advantage {
193
- background: linear-gradient(
194
- 135deg,
195
- rgba(191, 78, 106, 0.1) 0%,
196
- rgba(212, 113, 138, 0.05) 100%
197
- );
198
- }
199
-
200
- /* Badge styles */
201
- .badge {
202
- display: inline-flex;
203
- align-items: center;
204
- padding: 0.125rem 0.625rem;
205
- border-radius: 9999px;
206
- font-size: 0.75rem;
207
- font-weight: 600;
208
- line-height: 1.25rem;
209
- vertical-align: middle;
210
- }
211
-
212
- .badge.pure-ruby {
213
- background: linear-gradient(135deg, var(--fontist-rose), #d4718a);
214
- color: white;
215
- font-weight: 700;
414
+ .vp-doc table th {
415
+ font-family: var(--spec-font-mono);
416
+ font-size: 11px;
417
+ letter-spacing: 0.1em;
216
418
  text-transform: uppercase;
217
- letter-spacing: 0.05em;
218
- }
219
-
220
- .badge.tip {
221
- background-color: var(--vp-c-brand-soft);
222
- color: var(--vp-c-brand-1);
223
- }
224
-
225
- .badge.preview {
226
- background-color: rgba(255, 197, 61, 0.14);
227
- color: #c4a226;
228
- }
229
-
230
- .badge.planned {
231
- background-color: rgba(96, 165, 250, 0.14);
232
- color: #60a5fa;
233
- }
234
-
235
- /* Home page enhancements */
236
- .my-index-page .VPHero .name {
237
- display: flex;
238
- align-items: center;
239
- gap: 0.5rem;
240
- }
241
-
242
- .my-index-page .VPHero .tagline {
243
- font-size: 1.25rem !important;
419
+ color: var(--spec-mute);
420
+ background: var(--spec-paper-deep);
421
+ }
422
+
423
+ /* ── Sidebar (if used) ────────────────────────────────────────── */
424
+ .VPSidebar {
425
+ background-color: var(--spec-paper) !important;
426
+ border-right: 1px solid var(--spec-rule) !important;
427
+ font-family: var(--spec-font-mono);
428
+ }
429
+ .VPSidebar .VPSidebarItem .text {
430
+ font-size: 13px;
431
+ }
432
+
433
+ /* ── 404 page — specimen ───────────────────────────────────────── */
434
+ .NotFound {
435
+ text-align: center;
436
+ padding: clamp(3rem, 8vw, 6rem) 1.5rem clamp(4rem, 10vw, 8rem);
437
+ font-family: var(--spec-font-body);
438
+ }
439
+ .NotFound .code {
440
+ font-family: var(--spec-font-display);
441
+ font-style: italic;
442
+ font-weight: 300;
443
+ font-variation-settings: "opsz" 144;
444
+ font-size: clamp(120px, 24vw, 280px);
445
+ line-height: 1;
446
+ color: var(--spec-rose);
244
447
  opacity: 0.9;
448
+ margin: 0 0 0.5rem;
245
449
  }
246
-
247
- /* Pure Ruby badge on hero */
248
- .pure-ruby-hero {
249
- display: inline-flex;
250
- align-items: center;
251
- gap: 0.5rem;
252
- padding: 0.25rem 0.75rem;
253
- background: linear-gradient(135deg, var(--fontist-rose), #d4718a);
254
- color: white;
255
- border-radius: 9999px;
256
- font-size: 0.85rem;
257
- font-weight: 700;
450
+ .NotFound .title {
451
+ font-family: var(--spec-font-mono);
452
+ font-size: 12px;
453
+ letter-spacing: 0.22em;
258
454
  text-transform: uppercase;
259
- letter-spacing: 0.05em;
260
- margin-top: 1rem;
261
- }
262
-
263
- /* Code comparison blocks */
264
- .code-comparison {
265
- display: grid;
266
- grid-template-columns: 1fr 1fr;
267
- gap: 1rem;
268
- margin: 1.5rem 0;
269
- }
270
-
271
- @media (max-width: 768px) {
272
- .code-comparison {
273
- grid-template-columns: 1fr;
274
- }
275
- }
276
-
277
- .code-comparison > div {
278
- background: var(--vp-c-bg-soft);
279
- border-radius: 8px;
280
- overflow: hidden;
281
- }
282
-
283
- .code-comparison h4 {
455
+ color: var(--spec-ink-soft);
284
456
  margin: 0;
285
- padding: 0.75rem 1rem;
286
- background: var(--vp-c-bg-alt);
287
- font-size: 0.85rem;
288
- font-weight: 600;
289
- border-bottom: 1px solid var(--vp-c-divider);
290
457
  }
291
-
292
- /* CLI command reference */
293
- .cli-command {
294
- padding: 1rem;
295
- margin: 1rem 0;
296
- background: var(--vp-c-bg-soft);
297
- border-radius: 8px;
298
- border-left: 3px solid var(--fontist-rose);
458
+ .NotFound .divider {
459
+ width: 60px;
460
+ border-top: 1px solid var(--spec-rule-strong);
461
+ margin: 2rem auto;
462
+ }
463
+ .NotFound .quote {
464
+ font-family: var(--spec-font-display);
465
+ font-style: italic;
466
+ font-weight: 380;
467
+ font-size: clamp(18px, 2.2vw, 26px);
468
+ line-height: 1.5;
469
+ color: var(--spec-ink);
470
+ border: none;
471
+ max-width: 32ch;
472
+ margin: 0 auto 2rem;
473
+ padding: 0;
474
+ }
475
+ .NotFound .action .link {
476
+ font-family: var(--spec-font-mono);
477
+ font-size: 13px;
478
+ letter-spacing: 0.12em;
479
+ text-transform: uppercase;
480
+ color: var(--spec-paper);
481
+ background: var(--spec-ink);
482
+ padding: 12px 22px;
483
+ text-decoration: none;
484
+ display: inline-block;
485
+ transition: background 0.25s ease;
299
486
  }
300
-
301
- .cli-command code {
302
- font-weight: 500;
487
+ .NotFound .action .link:hover {
488
+ background: var(--spec-rose);
303
489
  }
304
490
 
305
- /* Info boxes */
306
- .custom-block {
307
- margin: 1.5rem 0;
308
- padding: 1rem 1.25rem;
309
- border-radius: 8px;
310
- }
491
+ /* ================================================================
492
+ VitePress home layout — VPHero + VPFeatures specimen restyle
493
+ Used by the fontist/docs and fontisan/docs homepages.
494
+ ================================================================ */
311
495
 
312
- .custom-block.tip {
313
- border-left: 4px solid var(--fontist-rose);
314
- background: rgba(191, 78, 106, 0.08);
496
+ /* ── VPHero ───────────────────────────────────────────────────── */
497
+ .VPHero .container { gap: 48px; }
498
+ .VPHero .heading {
499
+ display: flex;
500
+ flex-direction: column;
501
+ margin-bottom: 0.5rem;
502
+ }
503
+ .VPHero .name {
504
+ font-family: var(--spec-font-display) !important;
505
+ font-weight: 360 !important;
506
+ font-variation-settings: "opsz" 72;
507
+ font-size: clamp(40px, 6vw, 72px) !important;
508
+ line-height: 1 !important;
509
+ letter-spacing: -0.02em !important;
510
+ background: none !important;
511
+ -webkit-background-clip: unset !important;
512
+ -webkit-text-fill-color: var(--spec-ink) !important;
513
+ color: var(--spec-ink) !important;
514
+ }
515
+ .VPHero .text {
516
+ font-family: var(--spec-font-display) !important;
517
+ font-weight: 340 !important;
518
+ font-size: clamp(28px, 4vw, 44px) !important;
519
+ line-height: 1.05 !important;
520
+ letter-spacing: -0.02em !important;
521
+ color: var(--spec-rose) !important;
522
+ margin-top: 0.1em !important;
523
+ display: block !important;
524
+ }
525
+ .VPHero .tagline {
526
+ font-family: var(--spec-font-body) !important;
527
+ font-size: 16px !important;
528
+ line-height: 1.6 !important;
529
+ color: var(--spec-ink-soft) !important;
530
+ max-width: 480px !important;
531
+ }
532
+ .VPHero .actions { gap: 12px; }
533
+ .VPHero .VPButton {
534
+ font-family: var(--spec-font-mono) !important;
535
+ font-size: 12px !important;
536
+ letter-spacing: 0.12em !important;
537
+ text-transform: uppercase !important;
538
+ border-radius: 0 !important;
539
+ }
540
+ .VPHero .VPButton.brand {
541
+ border: 1px solid var(--spec-ink) !important;
542
+ background: var(--spec-ink) !important;
543
+ color: var(--spec-paper) !important;
544
+ }
545
+ .VPHero .VPButton.brand:hover {
546
+ background: var(--spec-rose) !important;
547
+ border-color: var(--spec-rose) !important;
548
+ }
549
+ .VPHero .VPButton.alt {
550
+ border: 1px solid var(--spec-rule-strong) !important;
551
+ background: transparent !important;
552
+ color: var(--spec-ink) !important;
553
+ }
554
+ .VPHero .VPButton.alt:hover {
555
+ border-color: var(--spec-rose) !important;
556
+ color: var(--spec-rose) !important;
557
+ background: transparent !important;
558
+ }
559
+
560
+ /* ── HeroCodeBlock → specimen terminal plate ──────────────────── */
561
+ .hero-code-block {
562
+ background: var(--spec-term-bg) !important;
563
+ border: 1px solid var(--spec-rule) !important;
564
+ border-radius: 0 !important;
565
+ box-shadow: 12px 12px 0 -1px var(--spec-paper-deep), 12px 12px 0 var(--spec-rule) !important;
566
+ max-width: 520px !important;
567
+ }
568
+ .code-header {
569
+ background: rgba(0, 0, 0, 0.2) !important;
570
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
571
+ }
572
+ .code-title {
573
+ font-family: var(--spec-font-mono) !important;
574
+ font-size: 11px !important;
575
+ letter-spacing: 0.12em !important;
576
+ text-transform: uppercase !important;
577
+ color: var(--spec-rose-soft) !important;
578
+ }
579
+ .code-content {
580
+ font-family: var(--spec-font-mono) !important;
581
+ font-size: 13px !important;
582
+ line-height: 1.7 !important;
583
+ color: var(--spec-term-ink) !important;
584
+ }
585
+ .code-content .prompt { color: var(--spec-rose-soft) !important; font-weight: 500; }
586
+ .code-content .cmd { color: #93c5fd !important; }
587
+ .code-content .success { color: #8fb98a !important; }
588
+ .code-content .comment { color: rgba(236, 223, 208, 0.45) !important; }
589
+ .code-content .line { padding: 1px 0; }
590
+
591
+ /* ── VPFeatures — only style cards, DON'T override grid layout ─── */
592
+ /* VitePress renders responsive columns on .item.grid-N (child of .items).
593
+ Overriding .items as a grid fights VitePress's own engine. Only style .box. */
594
+ .VPFeatures .container { padding-top: 2rem; }
595
+
596
+ .VPFeature {
597
+ background: transparent !important;
598
+ border: none !important;
599
+ border-radius: 0 !important;
600
+ box-shadow: none !important;
601
+ }
602
+ .VPFeature .box {
603
+ background: var(--spec-paper) !important;
604
+ border: 1px solid var(--spec-rule) !important;
605
+ border-radius: 0 !important;
606
+ box-shadow: none !important;
607
+ height: 100% !important;
608
+ transition: border-color 0.2s ease, background 0.2s ease !important;
609
+ }
610
+ .VPFeature .box:hover {
611
+ background: var(--spec-paper-deep) !important;
612
+ border-color: var(--spec-rose) !important;
315
613
  }
316
-
317
- .custom-block.warning {
318
- border-left: 4px solid #ffc53d;
319
- background: rgba(255, 197, 61, 0.08);
614
+ .VPFeature .title {
615
+ font-family: var(--spec-font-display) !important;
616
+ font-weight: 400 !important;
617
+ font-size: 1.1rem !important;
618
+ color: var(--spec-ink) !important;
320
619
  }
321
-
322
- .custom-block.danger {
323
- border-left: 4px solid #f43f5e;
324
- background: rgba(244, 63, 94, 0.08);
620
+ .VPFeature .details {
621
+ font-family: var(--spec-font-body) !important;
622
+ font-size: 14px !important;
623
+ line-height: 1.55 !important;
624
+ color: var(--spec-ink-soft) !important;
325
625
  }
626
+ html.dark .VPFeature .box { background: var(--spec-paper) !important; }
627
+ html.dark .VPFeature .box:hover { background: var(--spec-paper-deep) !important; }
326
628
 
327
- .custom-block.info {
328
- border-left: 4px solid #60a5fa;
329
- background: rgba(96, 165, 250, 0.08);
330
- }