flexr 1.0.0

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 (161) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +33 -0
  3. data/CONTRIBUTING.md +39 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +116 -0
  6. data/Rakefile +468 -0
  7. data/benchmark/baselines/json.json +34 -0
  8. data/benchmark/baselines/json_handwritten.rb +43 -0
  9. data/benchmark/baselines/json_rexical.rex +25 -0
  10. data/benchmark/corpora/README.md +11 -0
  11. data/benchmark/corpora/generate_json.rb +26 -0
  12. data/benchmark/golden/calculator_lexer.sha256 +1 -0
  13. data/benchmark/golden/json_lexer.sha256 +1 -0
  14. data/benchmark/golden/regexp_tokenizer.sha256 +1 -0
  15. data/benchmark/golden/ruby_subset_lexer.sha256 +1 -0
  16. data/benchmark/golden/toy_lang_lexer.sha256 +1 -0
  17. data/benchmark/golden/with_lrama_lexer.sha256 +1 -0
  18. data/benchmark/golden/with_racc_lexer.sha256 +1 -0
  19. data/benchmark/run.rb +254 -0
  20. data/docs/README.md +64 -0
  21. data/docs/RELEASING.md +30 -0
  22. data/docs/adr/0001-byte-level-dfa.md +5 -0
  23. data/docs/adr/0003-leftmost-longest.md +4 -0
  24. data/docs/adr/0006-accel-not-scanner.md +4 -0
  25. data/docs/adr/0008-what-pure-ruby-means.md +5 -0
  26. data/docs/adr/0016-spec-is-plain-ruby.md +4 -0
  27. data/docs/adr/0017-static-analysis-by-default.md +5 -0
  28. data/docs/adr/0018-prism-for-generator-only.md +4 -0
  29. data/docs/adr/0019-measured-performance-floor.md +26 -0
  30. data/docs/adr/0020-vendored-unicode-contract.md +21 -0
  31. data/docs/explanation/backends.md +33 -0
  32. data/docs/explanation/matching-semantics.md +20 -0
  33. data/docs/explanation/runtime-vs-generated.md +22 -0
  34. data/docs/explanation/security-model.md +18 -0
  35. data/docs/explanation/unicode-and-encoding.md +20 -0
  36. data/docs/how-to/deploy-a-standalone-lexer.md +23 -0
  37. data/docs/how-to/generate-a-lexer.md +39 -0
  38. data/docs/how-to/handle-errors.md +32 -0
  39. data/docs/how-to/integrate-with-lrama.md +21 -0
  40. data/docs/how-to/integrate-with-racc.md +25 -0
  41. data/docs/how-to/migrate-from-flex.md +21 -0
  42. data/docs/how-to/migrate-from-rexical.md +23 -0
  43. data/docs/how-to/run-a-lexer-at-runtime.md +29 -0
  44. data/docs/how-to/track-token-locations.md +27 -0
  45. data/docs/how-to/tune-performance.md +23 -0
  46. data/docs/how-to/use-states.md +36 -0
  47. data/docs/how-to/use-trailing-context.md +22 -0
  48. data/docs/internals/README.md +14 -0
  49. data/docs/perf-log.md +56 -0
  50. data/docs/reference/README.md +23 -0
  51. data/docs/reference/actions.md +47 -0
  52. data/docs/reference/cli.md +80 -0
  53. data/docs/reference/compatibility.md +38 -0
  54. data/docs/reference/diagnostics.md +41 -0
  55. data/docs/reference/dsl.md +81 -0
  56. data/docs/reference/errors.md +27 -0
  57. data/docs/reference/generated-artifacts.md +50 -0
  58. data/docs/reference/public-api.md +42 -0
  59. data/docs/reference/regexp.md +39 -0
  60. data/docs/reference/runtime.md +49 -0
  61. data/docs/reference/tokens-and-locations.md +33 -0
  62. data/docs/tutorial/build-a-calculator-lexer.md +96 -0
  63. data/examples/calculator/README.md +27 -0
  64. data/examples/calculator/lexer.flexr.rb +17 -0
  65. data/examples/json/README.md +30 -0
  66. data/examples/json/lexer.flexr.rb +24 -0
  67. data/examples/ruby_subset/README.md +17 -0
  68. data/examples/ruby_subset/lexer.flexr.rb +22 -0
  69. data/examples/toy_lang/README.md +17 -0
  70. data/examples/toy_lang/lexer.flexr.rb +18 -0
  71. data/examples/with_lrama/README.md +17 -0
  72. data/examples/with_lrama/lexer.flexr.rb +13 -0
  73. data/examples/with_racc/README.md +17 -0
  74. data/examples/with_racc/lexer.flexr.rb +13 -0
  75. data/exe/flexr +7 -0
  76. data/lib/flexr/automaton/accel.rb +39 -0
  77. data/lib/flexr/automaton/analysis.rb +38 -0
  78. data/lib/flexr/automaton/byte_class_set.rb +29 -0
  79. data/lib/flexr/automaton/compiler.rb +413 -0
  80. data/lib/flexr/automaton/dfa.rb +103 -0
  81. data/lib/flexr/automaton/minimizer.rb +70 -0
  82. data/lib/flexr/automaton/nfa.rb +92 -0
  83. data/lib/flexr/cli.rb +342 -0
  84. data/lib/flexr/codegen/base.rb +17 -0
  85. data/lib/flexr/codegen/direct.rb +52 -0
  86. data/lib/flexr/codegen/firstmatch.rb +17 -0
  87. data/lib/flexr/codegen/table.rb +158 -0
  88. data/lib/flexr/codegen/table_packer.rb +61 -0
  89. data/lib/flexr/diagnostics.rb +94 -0
  90. data/lib/flexr/dsl.rb +182 -0
  91. data/lib/flexr/errors.rb +28 -0
  92. data/lib/flexr/generated.rb +125 -0
  93. data/lib/flexr/generator.rb +400 -0
  94. data/lib/flexr/importer.rb +560 -0
  95. data/lib/flexr/ir.rb +36 -0
  96. data/lib/flexr/lexer.rb +10 -0
  97. data/lib/flexr/options.rb +47 -0
  98. data/lib/flexr/rake_task.rb +27 -0
  99. data/lib/flexr/regexp/ast.rb +45 -0
  100. data/lib/flexr/regexp/char_class.rb +7 -0
  101. data/lib/flexr/regexp/normalizer.rb +117 -0
  102. data/lib/flexr/regexp/parser.rb +517 -0
  103. data/lib/flexr/regexp/tokenizer.flexr.rb +27 -0
  104. data/lib/flexr/regexp/tokenizer.rb +168 -0
  105. data/lib/flexr/regexp/unsupported.rb +7 -0
  106. data/lib/flexr/runtime/buffer.rb +112 -0
  107. data/lib/flexr/runtime/core.rb +388 -0
  108. data/lib/flexr/runtime/errors.rb +22 -0
  109. data/lib/flexr/runtime/interpreter.rb +505 -0
  110. data/lib/flexr/runtime/location.rb +26 -0
  111. data/lib/flexr/runtime/token.rb +7 -0
  112. data/lib/flexr/source/passthrough.rb +31 -0
  113. data/lib/flexr/source/prism_reader.rb +283 -0
  114. data/lib/flexr/source/static_eval.rb +145 -0
  115. data/lib/flexr/unicode/case_fold.rb +45 -0
  116. data/lib/flexr/unicode/data/LICENSE-UNICODE.txt +5 -0
  117. data/lib/flexr/unicode/data/UNICODE_VERSION +1 -0
  118. data/lib/flexr/unicode/data/case_folding.rb +9 -0
  119. data/lib/flexr/unicode/data/properties.rb +10 -0
  120. data/lib/flexr/unicode/property.rb +107 -0
  121. data/lib/flexr/unicode/reference_regexp.rb +102 -0
  122. data/lib/flexr/unicode/utf8_splitter.rb +109 -0
  123. data/lib/flexr/version.rb +5 -0
  124. data/lib/flexr.rb +81 -0
  125. data/site/README.md +22 -0
  126. data/site/astro.config.mjs +57 -0
  127. data/site/package.json +19 -0
  128. data/site/pnpm-lock.yaml +5029 -0
  129. data/site/pnpm-workspace.yaml +6 -0
  130. data/site/public/playground.js +189 -0
  131. data/site/scripts/verify-site.mjs +42 -0
  132. data/site/src/content/docs/benchmarks.md +8 -0
  133. data/site/src/content/docs/concepts/matching-semantics.md +15 -0
  134. data/site/src/content/docs/concepts/regexp-model.md +18 -0
  135. data/site/src/content/docs/concepts/runtime-vs-generated.md +15 -0
  136. data/site/src/content/docs/concepts/security-model.md +15 -0
  137. data/site/src/content/docs/examples.md +17 -0
  138. data/site/src/content/docs/learn/generation.md +29 -0
  139. data/site/src/content/docs/learn/getting-started.md +56 -0
  140. data/site/src/content/docs/learn/parser-integration.md +27 -0
  141. data/site/src/content/docs/learn/runtime-mode.md +32 -0
  142. data/site/src/content/docs/reference/action-context.md +20 -0
  143. data/site/src/content/docs/reference/cli.md +22 -0
  144. data/site/src/content/docs/reference/diagnostics.md +16 -0
  145. data/site/src/content/docs/reference/dsl.md +19 -0
  146. data/site/src/content/docs/reference/public-api.md +18 -0
  147. data/site/src/content/docs/reference/regexp.md +16 -0
  148. data/site/src/content/docs/reference/runtime.md +16 -0
  149. data/site/src/content/docs/reference/tokens-and-locations.md +16 -0
  150. data/site/src/content.config.ts +12 -0
  151. data/site/src/env.d.ts +1 -0
  152. data/site/src/layouts/SiteLayout.astro +39 -0
  153. data/site/src/pages/index.astro +174 -0
  154. data/site/src/pages/playground.astro +64 -0
  155. data/site/src/styles/custom.css +711 -0
  156. data/site/tsconfig.json +5 -0
  157. data/tools/coverage.rb +32 -0
  158. data/tools/docs_verify.rb +116 -0
  159. data/tools/gen_unicode_tables.rb +202 -0
  160. data/tools/regexp_tokenizer_reference.rb +60 -0
  161. metadata +205 -0
@@ -0,0 +1,711 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --sl-color-accent-low: #e2f1ec;
4
+ --sl-color-accent: #176b5b;
5
+ --sl-color-accent-high: #0d4a3e;
6
+ --sl-color-white: #17271f;
7
+ --sl-color-gray-1: #2d4037;
8
+ --sl-color-gray-2: #52645b;
9
+ --sl-color-gray-3: #718178;
10
+ --sl-color-gray-4: #a5b2ab;
11
+ --sl-color-gray-5: #d3ddd8;
12
+ --sl-color-gray-6: #edf2ef;
13
+ --sl-color-black: #f7f9f7;
14
+ --flexr-ink: #17271f;
15
+ --flexr-muted: #617069;
16
+ --flexr-faint: #8b9891;
17
+ --flexr-canvas: #f7f9f7;
18
+ --flexr-paper: #ffffff;
19
+ --flexr-paper-muted: #f0f4f1;
20
+ --flexr-line: #dbe5df;
21
+ --flexr-line-strong: #c7d5cd;
22
+ --flexr-green: #176b5b;
23
+ --flexr-green-dark: #0d4a3e;
24
+ --flexr-green-soft: #e2f1ec;
25
+ --flexr-yellow: #a16207;
26
+ --flexr-blue: #28658a;
27
+ --flexr-red: #b54848;
28
+ --flexr-code: #192a24;
29
+ --sl-font: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
30
+ --sl-font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, "Liberation Mono", monospace;
31
+ }
32
+
33
+ html {
34
+ background: var(--flexr-canvas);
35
+ }
36
+
37
+ body {
38
+ margin: 0;
39
+ color: var(--flexr-ink);
40
+ background: var(--flexr-canvas);
41
+ font-family: var(--sl-font);
42
+ text-rendering: optimizeLegibility;
43
+ }
44
+
45
+ ::selection {
46
+ color: #ffffff;
47
+ background: var(--flexr-green);
48
+ }
49
+
50
+ :focus-visible {
51
+ outline: 3px solid #63a99a;
52
+ outline-offset: 3px;
53
+ }
54
+
55
+ a {
56
+ color: var(--flexr-green);
57
+ }
58
+
59
+ button,
60
+ select,
61
+ textarea {
62
+ font-family: inherit;
63
+ }
64
+
65
+ .skip-link {
66
+ position: fixed;
67
+ top: 0.75rem;
68
+ left: 0.75rem;
69
+ z-index: 50;
70
+ padding: 0.7rem 0.9rem;
71
+ color: #ffffff;
72
+ border-radius: 0.3rem;
73
+ background: var(--flexr-green-dark);
74
+ transform: translateY(-160%);
75
+ transition: transform 160ms ease;
76
+ }
77
+
78
+ .skip-link:focus {
79
+ transform: translateY(0);
80
+ }
81
+
82
+ .site-nav {
83
+ position: sticky;
84
+ top: 0;
85
+ z-index: 30;
86
+ border-bottom: 1px solid rgba(219, 229, 223, 0.9);
87
+ background: rgba(247, 249, 247, 0.9);
88
+ backdrop-filter: blur(14px);
89
+ }
90
+
91
+ .site-nav-inner {
92
+ display: flex;
93
+ width: min(1160px, calc(100% - 2.5rem));
94
+ min-height: 4.25rem;
95
+ align-items: center;
96
+ justify-content: space-between;
97
+ gap: 1.5rem;
98
+ margin-inline: auto;
99
+ }
100
+
101
+ .site-nav-brand {
102
+ display: inline-flex;
103
+ align-items: center;
104
+ gap: 0.45rem;
105
+ color: var(--flexr-ink);
106
+ font-family: var(--sl-font-mono);
107
+ font-size: 1rem;
108
+ font-weight: 700;
109
+ letter-spacing: -0.08em;
110
+ text-decoration: none;
111
+ }
112
+
113
+ .site-nav-brand::before {
114
+ width: 0.45rem;
115
+ height: 0.45rem;
116
+ border-radius: 999px;
117
+ background: var(--flexr-green);
118
+ content: "";
119
+ }
120
+
121
+ .site-nav-brand span {
122
+ color: var(--flexr-green);
123
+ font-weight: 400;
124
+ }
125
+
126
+ .site-nav-links {
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 1.5rem;
130
+ margin-left: auto;
131
+ }
132
+
133
+ .site-nav-links a {
134
+ color: var(--flexr-muted);
135
+ font-size: 0.84rem;
136
+ text-decoration: none;
137
+ transition: color 160ms ease;
138
+ }
139
+
140
+ .site-nav-links a:hover,
141
+ .site-nav-links a[aria-current="page"] {
142
+ color: var(--flexr-ink);
143
+ }
144
+
145
+ .site-nav-cta {
146
+ display: inline-flex;
147
+ min-height: 2.45rem;
148
+ align-items: center;
149
+ gap: 0.45rem;
150
+ padding: 0.55rem 0.85rem;
151
+ color: #ffffff;
152
+ border-radius: 0.3rem;
153
+ background: var(--flexr-green-dark);
154
+ font-family: var(--sl-font-mono);
155
+ font-size: 0.72rem;
156
+ font-weight: 700;
157
+ text-decoration: none;
158
+ transition: background-color 160ms ease, transform 160ms ease;
159
+ }
160
+
161
+ .site-nav-cta:hover {
162
+ color: #ffffff;
163
+ background: var(--flexr-green);
164
+ transform: translateY(-1px);
165
+ }
166
+
167
+ .site-mark {
168
+ font-family: var(--sl-font-mono);
169
+ font-weight: 700;
170
+ letter-spacing: -0.07em;
171
+ }
172
+
173
+ .eyebrow {
174
+ color: var(--flexr-green);
175
+ font-family: var(--sl-font-mono);
176
+ font-size: 0.68rem;
177
+ font-weight: 700;
178
+ letter-spacing: 0.14em;
179
+ text-transform: uppercase;
180
+ }
181
+
182
+ .home-shell {
183
+ overflow: hidden;
184
+ background:
185
+ linear-gradient(rgba(23, 39, 31, 0.035) 1px, transparent 1px),
186
+ linear-gradient(90deg, rgba(23, 39, 31, 0.035) 1px, transparent 1px),
187
+ var(--flexr-canvas);
188
+ background-size: 4rem 4rem;
189
+ }
190
+
191
+ .home-hero,
192
+ .home-section,
193
+ .site-footer {
194
+ width: min(1160px, calc(100% - 2.5rem));
195
+ margin-inline: auto;
196
+ }
197
+
198
+ .home-hero {
199
+ display: grid;
200
+ grid-template-columns: minmax(0, 1.03fr) minmax(360px, 0.97fr);
201
+ align-items: center;
202
+ gap: 5.5rem;
203
+ padding: 7.25rem 0 6.5rem;
204
+ }
205
+
206
+ .home-hero h1 {
207
+ max-width: 10ch;
208
+ margin: 1.1rem 0 1.4rem;
209
+ color: var(--flexr-ink);
210
+ font-family: var(--sl-font);
211
+ font-size: clamp(3.1rem, 6vw, 5.5rem);
212
+ font-weight: 600;
213
+ line-height: 0.98;
214
+ letter-spacing: -0.075em;
215
+ }
216
+
217
+ .hero-copy {
218
+ max-width: 36rem;
219
+ color: var(--flexr-muted);
220
+ font-size: clamp(1.05rem, 1.7vw, 1.2rem);
221
+ line-height: 1.72;
222
+ }
223
+
224
+ .hero-actions,
225
+ .home-links {
226
+ display: flex;
227
+ flex-wrap: wrap;
228
+ gap: 0.7rem;
229
+ align-items: center;
230
+ margin-top: 2rem;
231
+ }
232
+
233
+ .button {
234
+ display: inline-flex;
235
+ min-height: 2.85rem;
236
+ align-items: center;
237
+ justify-content: center;
238
+ padding: 0.65rem 0.95rem;
239
+ border: 1px solid transparent;
240
+ border-radius: 0.3rem;
241
+ font-family: var(--sl-font-mono);
242
+ font-size: 0.72rem;
243
+ font-weight: 700;
244
+ text-decoration: none;
245
+ transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease, transform 160ms ease;
246
+ }
247
+
248
+ .button:hover {
249
+ transform: translateY(-1px);
250
+ }
251
+
252
+ .button-primary {
253
+ color: #ffffff;
254
+ background: var(--flexr-green-dark);
255
+ }
256
+
257
+ .button-primary:hover {
258
+ color: #ffffff;
259
+ background: var(--flexr-green);
260
+ }
261
+
262
+ .button-secondary {
263
+ color: var(--flexr-ink);
264
+ border-color: var(--flexr-line-strong);
265
+ background: rgba(255, 255, 255, 0.7);
266
+ }
267
+
268
+ .button-secondary:hover {
269
+ color: var(--flexr-green-dark);
270
+ border-color: #93b8aa;
271
+ background: var(--flexr-paper);
272
+ }
273
+
274
+ .demo-window {
275
+ overflow: hidden;
276
+ border: 1px solid #2b4037;
277
+ border-radius: 0.55rem;
278
+ background: var(--flexr-code);
279
+ box-shadow: 0 1.5rem 3rem rgba(23, 39, 31, 0.13);
280
+ }
281
+
282
+ .window-bar {
283
+ display: flex;
284
+ gap: 0.45rem;
285
+ align-items: center;
286
+ padding: 0.85rem 1rem;
287
+ border-bottom: 1px solid rgba(216, 235, 225, 0.14);
288
+ color: #94aaa0;
289
+ font-family: var(--sl-font-mono);
290
+ font-size: 0.68rem;
291
+ }
292
+
293
+ .window-dot {
294
+ width: 0.5rem;
295
+ height: 0.5rem;
296
+ border-radius: 999px;
297
+ background: #61746b;
298
+ }
299
+
300
+ .window-dot:first-child { background: #d38d7c; }
301
+ .window-dot:nth-child(2) { background: #c5ad6e; }
302
+ .window-dot:nth-child(3) { background: #69ad8e; }
303
+
304
+ .code-block {
305
+ margin: 0;
306
+ padding: 1.35rem;
307
+ overflow-x: auto;
308
+ color: #d8e9df;
309
+ font-family: var(--sl-font-mono);
310
+ font-size: 0.78rem;
311
+ line-height: 1.85;
312
+ }
313
+
314
+ .code-comment { color: #718a7d; }
315
+ .code-keyword { color: #c5a4d8; }
316
+ .code-string { color: #9ed3b0; }
317
+ .code-method { color: #90cbd0; }
318
+ .code-number { color: #dbc786; }
319
+
320
+ .decision-panel {
321
+ padding: 1rem 1.35rem 1.25rem;
322
+ border-top: 1px solid rgba(216, 235, 225, 0.14);
323
+ }
324
+
325
+ .decision-label {
326
+ display: flex;
327
+ justify-content: space-between;
328
+ margin-bottom: 0.7rem;
329
+ color: #8fa89b;
330
+ font-family: var(--sl-font-mono);
331
+ font-size: 0.66rem;
332
+ }
333
+
334
+ .decision-label strong { color: #d8e9df; font-weight: 500; }
335
+
336
+ .decision-row {
337
+ display: grid;
338
+ grid-template-columns: 1fr auto;
339
+ gap: 1rem;
340
+ padding: 0.58rem 0;
341
+ border-top: 1px solid rgba(216, 235, 225, 0.11);
342
+ font-family: var(--sl-font-mono);
343
+ font-size: 0.7rem;
344
+ }
345
+
346
+ .decision-row strong { color: #d8e9df; font-weight: 500; }
347
+ .decision-row span { color: #8fa89b; }
348
+ .decision-row.winner strong { color: #9ed3b0; }
349
+ .decision-row.winner span { color: #9ed3b0; }
350
+
351
+ .home-section {
352
+ padding: 6.25rem 0;
353
+ border-top: 1px solid var(--flexr-line);
354
+ }
355
+
356
+ .section-heading {
357
+ display: flex;
358
+ justify-content: space-between;
359
+ align-items: end;
360
+ gap: 3rem;
361
+ margin-bottom: 2.7rem;
362
+ }
363
+
364
+ .section-heading h2 {
365
+ max-width: 16ch;
366
+ margin: 0.7rem 0 0;
367
+ color: var(--flexr-ink);
368
+ font-family: var(--sl-font);
369
+ font-size: clamp(2rem, 4vw, 3.2rem);
370
+ font-weight: 600;
371
+ line-height: 1.05;
372
+ letter-spacing: -0.065em;
373
+ }
374
+
375
+ .section-heading p {
376
+ max-width: 31rem;
377
+ margin: 0;
378
+ color: var(--flexr-muted);
379
+ font-size: 0.98rem;
380
+ line-height: 1.75;
381
+ }
382
+
383
+ .card-grid {
384
+ display: grid;
385
+ grid-template-columns: repeat(2, minmax(0, 1fr));
386
+ gap: 0;
387
+ border-top: 1px solid var(--flexr-line-strong);
388
+ border-left: 1px solid var(--flexr-line-strong);
389
+ }
390
+
391
+ .site-card {
392
+ min-height: 10rem;
393
+ padding: 1.5rem 1.6rem;
394
+ border-right: 1px solid var(--flexr-line-strong);
395
+ border-bottom: 1px solid var(--flexr-line-strong);
396
+ background: rgba(255, 255, 255, 0.48);
397
+ }
398
+
399
+ .site-card:hover {
400
+ background: rgba(255, 255, 255, 0.88);
401
+ }
402
+
403
+ .site-card h3,
404
+ .workflow-step h3,
405
+ .trust-panel h3 {
406
+ margin: 0.8rem 0 0.55rem;
407
+ color: var(--flexr-ink);
408
+ font-size: 1rem;
409
+ font-weight: 600;
410
+ letter-spacing: -0.02em;
411
+ }
412
+
413
+ .site-card p,
414
+ .workflow-step p,
415
+ .trust-panel p {
416
+ max-width: 31rem;
417
+ margin: 0;
418
+ color: var(--flexr-muted);
419
+ font-size: 0.9rem;
420
+ line-height: 1.7;
421
+ }
422
+
423
+ .card-index {
424
+ color: var(--flexr-green);
425
+ font-family: var(--sl-font-mono);
426
+ font-size: 0.65rem;
427
+ font-weight: 700;
428
+ letter-spacing: 0.08em;
429
+ }
430
+
431
+ .workflow-grid {
432
+ display: grid;
433
+ grid-template-columns: repeat(4, minmax(0, 1fr));
434
+ gap: 1.25rem;
435
+ }
436
+
437
+ .workflow-step {
438
+ padding-top: 1.1rem;
439
+ border-top: 2px solid var(--flexr-line-strong);
440
+ }
441
+
442
+ .workflow-step:first-child { border-color: var(--flexr-green); }
443
+ .workflow-step h3 { font-family: var(--sl-font-mono); font-size: 0.82rem; }
444
+
445
+ .compare-panel {
446
+ display: grid;
447
+ grid-template-columns: 1fr 1fr;
448
+ gap: 1px;
449
+ overflow: hidden;
450
+ border: 1px solid var(--flexr-line-strong);
451
+ background: var(--flexr-line-strong);
452
+ }
453
+
454
+ .compare-panel > div {
455
+ min-height: 12rem;
456
+ padding: 1.7rem;
457
+ background: rgba(255, 255, 255, 0.72);
458
+ }
459
+
460
+ .compare-panel h3 {
461
+ margin: 0 0 1rem;
462
+ color: var(--flexr-green-dark);
463
+ font-family: var(--sl-font-mono);
464
+ font-size: 0.78rem;
465
+ }
466
+
467
+ .compare-panel p {
468
+ max-width: 30rem;
469
+ margin: 0;
470
+ color: var(--flexr-muted);
471
+ line-height: 1.75;
472
+ }
473
+
474
+ .compare-panel code {
475
+ padding: 0.12rem 0.3rem;
476
+ color: var(--flexr-green-dark);
477
+ background: var(--flexr-green-soft);
478
+ font-family: var(--sl-font-mono);
479
+ font-size: 0.8em;
480
+ }
481
+
482
+ .trust-grid {
483
+ display: grid;
484
+ grid-template-columns: repeat(4, minmax(0, 1fr));
485
+ gap: 2rem;
486
+ }
487
+
488
+ .trust-panel {
489
+ padding-top: 1.15rem;
490
+ border-top: 1px solid var(--flexr-line-strong);
491
+ }
492
+
493
+ .trust-panel h3 { font-size: 0.95rem; }
494
+ .trust-panel .home-links { margin-top: 1.1rem; }
495
+
496
+ .site-footer {
497
+ padding: 2rem 0 4rem;
498
+ border-top: 1px solid var(--flexr-line);
499
+ color: var(--flexr-muted);
500
+ font-size: 0.82rem;
501
+ }
502
+
503
+ .site-footer a { color: var(--flexr-green); }
504
+
505
+ .playground-shell {
506
+ min-height: calc(100vh - 4.25rem);
507
+ padding: 4.5rem max(1.25rem, calc((100vw - 1160px) / 2)) 6rem;
508
+ background:
509
+ linear-gradient(rgba(23, 39, 31, 0.035) 1px, transparent 1px),
510
+ linear-gradient(90deg, rgba(23, 39, 31, 0.035) 1px, transparent 1px),
511
+ var(--flexr-canvas);
512
+ background-size: 4rem 4rem;
513
+ }
514
+
515
+ .playground-header {
516
+ max-width: 47rem;
517
+ margin-bottom: 2.5rem;
518
+ }
519
+
520
+ .playground-header h1 {
521
+ margin: 0.8rem 0 1rem;
522
+ color: var(--flexr-ink);
523
+ font-family: var(--sl-font);
524
+ font-size: clamp(2.5rem, 5vw, 4.4rem);
525
+ font-weight: 600;
526
+ line-height: 1;
527
+ letter-spacing: -0.075em;
528
+ }
529
+
530
+ .playground-header p {
531
+ color: var(--flexr-muted);
532
+ font-size: 1rem;
533
+ line-height: 1.75;
534
+ }
535
+
536
+ .playground-notice {
537
+ margin-top: 1.5rem;
538
+ padding: 0.9rem 1rem;
539
+ border-left: 3px solid var(--flexr-yellow);
540
+ color: #76500d;
541
+ background: #fff8e5;
542
+ font-size: 0.88rem;
543
+ line-height: 1.65;
544
+ }
545
+
546
+ .playground-notice code {
547
+ font-family: var(--sl-font-mono);
548
+ font-size: 0.85em;
549
+ }
550
+
551
+ .workbench {
552
+ display: grid;
553
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
554
+ gap: 1rem;
555
+ }
556
+
557
+ .workbench-panel {
558
+ min-width: 0;
559
+ overflow: hidden;
560
+ border: 1px solid var(--flexr-line-strong);
561
+ background: var(--flexr-paper);
562
+ }
563
+
564
+ .workbench-panel.full { grid-column: 1 / -1; }
565
+
566
+ .panel-heading {
567
+ display: flex;
568
+ justify-content: space-between;
569
+ gap: 1rem;
570
+ padding: 0.85rem 1rem;
571
+ border-bottom: 1px solid var(--flexr-line);
572
+ color: var(--flexr-ink);
573
+ font-family: var(--sl-font-mono);
574
+ font-size: 0.7rem;
575
+ font-weight: 700;
576
+ }
577
+
578
+ .panel-heading small {
579
+ color: var(--flexr-faint);
580
+ font-weight: 400;
581
+ }
582
+
583
+ .workbench textarea,
584
+ .workbench select {
585
+ width: 100%;
586
+ box-sizing: border-box;
587
+ border: 0;
588
+ border-radius: 0;
589
+ color: #2b4439;
590
+ background: #fbfdfb;
591
+ font-family: var(--sl-font-mono);
592
+ font-size: 0.75rem;
593
+ line-height: 1.75;
594
+ }
595
+
596
+ .workbench textarea:focus,
597
+ .workbench select:focus {
598
+ outline: 2px solid #82b6a6;
599
+ outline-offset: -2px;
600
+ }
601
+
602
+ .workbench textarea {
603
+ min-height: 17rem;
604
+ padding: 1rem;
605
+ resize: vertical;
606
+ }
607
+
608
+ .workbench select {
609
+ min-height: 2.8rem;
610
+ padding: 0 1rem;
611
+ border-bottom: 1px solid var(--flexr-line);
612
+ }
613
+
614
+ .input-area { min-height: 8rem !important; }
615
+
616
+ .workbench-actions {
617
+ display: flex;
618
+ flex-wrap: wrap;
619
+ gap: 0.55rem;
620
+ padding: 0.8rem 1rem;
621
+ border-top: 1px solid var(--flexr-line);
622
+ }
623
+
624
+ .workbench-actions button { cursor: pointer; }
625
+ .workbench-output { min-height: 17rem; padding: 1rem; }
626
+ .output-empty { color: var(--flexr-muted); font-size: 0.88rem; line-height: 1.65; }
627
+
628
+ .token-list {
629
+ display: flex;
630
+ flex-wrap: wrap;
631
+ gap: 0.55rem;
632
+ margin: 0;
633
+ padding: 0;
634
+ list-style: none;
635
+ }
636
+
637
+ .token-chip {
638
+ padding: 0.55rem 0.7rem;
639
+ border: 1px solid #b8d3c7;
640
+ background: var(--flexr-green-soft);
641
+ font-family: var(--sl-font-mono);
642
+ font-size: 0.72rem;
643
+ }
644
+
645
+ .token-chip span {
646
+ display: block;
647
+ margin-bottom: 0.2rem;
648
+ color: var(--flexr-green-dark);
649
+ font-size: 0.62rem;
650
+ }
651
+
652
+ .result-table {
653
+ width: 100%;
654
+ border-collapse: collapse;
655
+ font-family: var(--sl-font-mono);
656
+ font-size: 0.69rem;
657
+ }
658
+
659
+ .result-table th,
660
+ .result-table td {
661
+ padding: 0.65rem 0.4rem;
662
+ border-bottom: 1px solid var(--flexr-line);
663
+ text-align: left;
664
+ vertical-align: top;
665
+ }
666
+
667
+ .result-table th { color: var(--flexr-faint); font-weight: 400; }
668
+ .result-table td { color: var(--flexr-ink); }
669
+ .winner-text { color: var(--flexr-green-dark) !important; }
670
+ .workbench-status { min-height: 1.4rem; margin: 0; color: var(--flexr-muted); font-size: 0.78rem; }
671
+ .back-link { display: inline-flex; margin-bottom: 1.5rem; color: var(--flexr-green); font-family: var(--sl-font-mono); font-size: 0.72rem; }
672
+
673
+ .sl-markdown-content a,
674
+ .sl-link-card a {
675
+ color: var(--flexr-green);
676
+ }
677
+
678
+ .sl-markdown-content code:not(:where(.expressive-code *)) {
679
+ color: var(--flexr-green-dark);
680
+ background: var(--flexr-green-soft);
681
+ }
682
+
683
+ @media (max-width: 920px) {
684
+ .home-hero { grid-template-columns: 1fr; gap: 3rem; padding-top: 5.5rem; }
685
+ .home-hero h1 { max-width: 12ch; }
686
+ .workflow-grid, .trust-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
687
+ }
688
+
689
+ @media (max-width: 700px) {
690
+ .site-nav-links { display: none; }
691
+ .site-nav-inner, .home-hero, .home-section, .site-footer { width: min(100% - 1.5rem, 1160px); }
692
+ .site-nav-inner { min-height: 3.9rem; }
693
+ .home-hero { padding: 4rem 0 4.5rem; }
694
+ .home-section { padding: 4.5rem 0; }
695
+ .section-heading { display: block; }
696
+ .section-heading p { margin-top: 1rem; }
697
+ .card-grid, .workflow-grid, .trust-grid, .compare-panel, .workbench { grid-template-columns: 1fr; }
698
+ .workbench-panel.full { grid-column: auto; }
699
+ .playground-shell { padding: 3rem 0.75rem 4rem; }
700
+ }
701
+
702
+ @media (prefers-reduced-motion: reduce) {
703
+ *, *::before, *::after {
704
+ scroll-behavior: auto !important;
705
+ transition-duration: 0.01ms !important;
706
+ animation-duration: 0.01ms !important;
707
+ }
708
+
709
+ .button:hover,
710
+ .site-nav-cta:hover { transform: none; }
711
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "include": [".astro/types.d.ts", "**/*"],
4
+ "exclude": ["dist"]
5
+ }