stack-site-builder 1.10.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 (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/dev/local-samples-client.js +284 -0
  4. package/dev/local-samples.mjs +594 -0
  5. package/index.d.ts +12 -0
  6. package/index.mjs +124 -0
  7. package/markdown.mjs +394 -0
  8. package/package.json +52 -0
  9. package/src/components/ArrowUpRight.astro +24 -0
  10. package/src/components/ArticleCard.astro +56 -0
  11. package/src/components/ArticleDetail.astro +100 -0
  12. package/src/components/ArticleLink.astro +45 -0
  13. package/src/components/BackToTop.astro +47 -0
  14. package/src/components/BlogIndex.astro +137 -0
  15. package/src/components/Breadcrumb.astro +37 -0
  16. package/src/components/CategoryIndex.astro +115 -0
  17. package/src/components/ChevronDown.astro +23 -0
  18. package/src/components/CodeSamples.astro +141 -0
  19. package/src/components/CollapsibleGrid.astro +50 -0
  20. package/src/components/ConceptCard.astro +59 -0
  21. package/src/components/ConceptDetail.astro +102 -0
  22. package/src/components/ConceptIndex.astro +143 -0
  23. package/src/components/ConceptLink.astro +43 -0
  24. package/src/components/ConceptTools.astro +49 -0
  25. package/src/components/DeckView.astro +772 -0
  26. package/src/components/DetailTabs.astro +219 -0
  27. package/src/components/FilterControls.astro +407 -0
  28. package/src/components/Glossary.astro +264 -0
  29. package/src/components/Home.astro +90 -0
  30. package/src/components/LanguageSwitcher.astro +86 -0
  31. package/src/components/ListControls.astro +182 -0
  32. package/src/components/MermaidLoader.astro +96 -0
  33. package/src/components/PricingSection.astro +100 -0
  34. package/src/components/ProjectFileList.astro +68 -0
  35. package/src/components/ProjectFileTree.astro +95 -0
  36. package/src/components/ProjectRelatedTools.astro +70 -0
  37. package/src/components/ProjectViewer.astro +431 -0
  38. package/src/components/RelatedArticles.astro +52 -0
  39. package/src/components/RelatedConcepts.astro +60 -0
  40. package/src/components/RelatedTools.astro +55 -0
  41. package/src/components/SampleIndex.astro +464 -0
  42. package/src/components/SamplePage.astro +95 -0
  43. package/src/components/SampleProject.astro +56 -0
  44. package/src/components/Slide.astro +36 -0
  45. package/src/components/SlidesIndex.astro +60 -0
  46. package/src/components/StackCard.astro +183 -0
  47. package/src/components/StackDetail.astro +507 -0
  48. package/src/components/StarIcon.astro +21 -0
  49. package/src/components/TagIndex.astro +51 -0
  50. package/src/components/ThemeToggle.astro +106 -0
  51. package/src/components/Toc.astro +39 -0
  52. package/src/components/TocRail.astro +74 -0
  53. package/src/components/ToolAvatar.astro +133 -0
  54. package/src/components/ToolMeta.astro +95 -0
  55. package/src/components/VendorIndex.astro +51 -0
  56. package/src/content.ts +228 -0
  57. package/src/i18n/ui.ts +337 -0
  58. package/src/layouts/BaseLayout.astro +287 -0
  59. package/src/lib/articles.ts +27 -0
  60. package/src/lib/assets.ts +19 -0
  61. package/src/lib/category-tree.ts +64 -0
  62. package/src/lib/concepts.ts +35 -0
  63. package/src/lib/dates.ts +21 -0
  64. package/src/lib/facets.ts +57 -0
  65. package/src/lib/github.ts +186 -0
  66. package/src/lib/inline-md.ts +30 -0
  67. package/src/lib/pricing.ts +22 -0
  68. package/src/lib/project.ts +309 -0
  69. package/src/lib/samples.ts +88 -0
  70. package/src/lib/slides.ts +25 -0
  71. package/src/lib/stacks.ts +129 -0
  72. package/src/lib/toc-rail-client.ts +79 -0
  73. package/src/pages/article/[...id].astro +24 -0
  74. package/src/pages/article/category/[id].astro +17 -0
  75. package/src/pages/article/index.astro +12 -0
  76. package/src/pages/categories/[id].astro +25 -0
  77. package/src/pages/concept/[...id].astro +24 -0
  78. package/src/pages/concept/category/[id].astro +17 -0
  79. package/src/pages/concept/index.astro +12 -0
  80. package/src/pages/glossary.astro +12 -0
  81. package/src/pages/index.astro +9 -0
  82. package/src/pages/ko/article/[...id].astro +24 -0
  83. package/src/pages/ko/article/category/[id].astro +17 -0
  84. package/src/pages/ko/article/index.astro +12 -0
  85. package/src/pages/ko/categories/[id].astro +24 -0
  86. package/src/pages/ko/concept/[...id].astro +24 -0
  87. package/src/pages/ko/concept/category/[id].astro +17 -0
  88. package/src/pages/ko/concept/index.astro +12 -0
  89. package/src/pages/ko/glossary.astro +12 -0
  90. package/src/pages/ko/index.astro +9 -0
  91. package/src/pages/ko/sample/[folder].astro +16 -0
  92. package/src/pages/ko/sample/index.astro +12 -0
  93. package/src/pages/ko/slides/[deck].astro +14 -0
  94. package/src/pages/ko/slides/index.astro +12 -0
  95. package/src/pages/ko/stack/[...id].astro +51 -0
  96. package/src/pages/ko/tags/[tag].astro +18 -0
  97. package/src/pages/ko/vendors/[vendor].astro +18 -0
  98. package/src/pages/sample/[folder].astro +16 -0
  99. package/src/pages/sample/index.astro +12 -0
  100. package/src/pages/slides/[deck].astro +14 -0
  101. package/src/pages/slides/index.astro +12 -0
  102. package/src/pages/stack/[...id].astro +51 -0
  103. package/src/pages/tags/[tag].astro +18 -0
  104. package/src/pages/vendors/[vendor].astro +18 -0
  105. package/src/styles/global.css +2350 -0
@@ -0,0 +1,2350 @@
1
+ @import "tailwindcss";
2
+
3
+ /* This stylesheet ships inside the stack-site-builder package. Tailwind
4
+ only auto-scans the consuming site's own sources, so point it at the
5
+ theme's sources too — relative to this file, i.e. packages/stack-site-builder/src. */
6
+ @source "../";
7
+
8
+ /* MaruBuri (Naver) — a Korean serif that also covers Latin. Used for the
9
+ concept/article reading body (a Medium-style serif column). Remote woff2,
10
+ font-display: swap so text shows immediately in the fallback serif. */
11
+ @font-face {
12
+ font-family: 'MaruBuri';
13
+ src: url(https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-ExtraLight.woff2) format('woff2');
14
+ font-weight: 200;
15
+ font-display: swap;
16
+ }
17
+ @font-face {
18
+ font-family: 'MaruBuri';
19
+ src: url(https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Light.woff2) format('woff2');
20
+ font-weight: 300;
21
+ font-display: swap;
22
+ }
23
+ @font-face {
24
+ font-family: 'MaruBuri';
25
+ src: url(https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2) format('woff2');
26
+ font-weight: 400;
27
+ font-display: swap;
28
+ }
29
+ @font-face {
30
+ font-family: 'MaruBuri';
31
+ src: url(https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2) format('woff2');
32
+ font-weight: 600;
33
+ font-display: swap;
34
+ }
35
+ @font-face {
36
+ font-family: 'MaruBuri';
37
+ src: url(https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Bold.woff2) format('woff2');
38
+ font-weight: 700;
39
+ font-display: swap;
40
+ }
41
+
42
+ /* Pretendard — the site's default gothic (sans) for Korean + Latin. */
43
+ @font-face {
44
+ font-family: 'Pretendard';
45
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Thin.woff2') format('woff2');
46
+ font-weight: 100;
47
+ font-display: swap;
48
+ }
49
+ @font-face {
50
+ font-family: 'Pretendard';
51
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-ExtraLight.woff2') format('woff2');
52
+ font-weight: 200;
53
+ font-display: swap;
54
+ }
55
+ @font-face {
56
+ font-family: 'Pretendard';
57
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Light.woff2') format('woff2');
58
+ font-weight: 300;
59
+ font-display: swap;
60
+ }
61
+ @font-face {
62
+ font-family: 'Pretendard';
63
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Regular.woff2') format('woff2');
64
+ font-weight: 400;
65
+ font-display: swap;
66
+ }
67
+ @font-face {
68
+ font-family: 'Pretendard';
69
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Medium.woff2') format('woff2');
70
+ font-weight: 500;
71
+ font-display: swap;
72
+ }
73
+ @font-face {
74
+ font-family: 'Pretendard';
75
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-SemiBold.woff2') format('woff2');
76
+ font-weight: 600;
77
+ font-display: swap;
78
+ }
79
+ @font-face {
80
+ font-family: 'Pretendard';
81
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Bold.woff2') format('woff2');
82
+ font-weight: 700;
83
+ font-display: swap;
84
+ }
85
+ @font-face {
86
+ font-family: 'Pretendard';
87
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-ExtraBold.woff2') format('woff2');
88
+ font-weight: 800;
89
+ font-display: swap;
90
+ }
91
+ @font-face {
92
+ font-family: 'Pretendard';
93
+ src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Black.woff2') format('woff2');
94
+ font-weight: 900;
95
+ font-display: swap;
96
+ }
97
+
98
+ /* Light is the default; `data-theme="dark"` (set by the inline script in
99
+ BaseLayout) switches to dark. The @media block covers no-JS users whose
100
+ system prefers dark before any explicit choice is stored. */
101
+ :root {
102
+ color-scheme: light;
103
+ --aas-bg: #fafafa;
104
+ --aas-panel: #fdfdfd;
105
+ --aas-border: #f4f4f4;
106
+ --aas-text: #2b2b31;
107
+ --aas-muted: #76767e;
108
+ --aas-accent: #5f63c4;
109
+ --aas-shadow: 0 8px 24px -10px rgb(20 20 40 / 0.22);
110
+ /* Interactive-surface tint (hover/active). Text-derived so it has real
111
+ contrast on any background even when --aas-panel is near-white. Defined
112
+ once; the var(--aas-text) inside resolves per theme. */
113
+ --aas-tint: color-mix(in srgb, var(--aas-text) 7%, transparent);
114
+ /* Default gothic (sans) stack — Pretendard first, system fallbacks after.
115
+ Applied site-wide via the `body` rule below; the concept/article reading
116
+ body opts into MaruBuri serif. */
117
+ --aas-sans: 'Pretendard', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
118
+ /* Mermaid cluster ("zone") highlight colours. `defaultzone` is the accent
119
+ nudged toward teal (a calm blue); `aaszone` is a warm amber that stands out
120
+ as the emphasized region (e.g. the harness we're describing). */
121
+ --aas-diagram-zone: color-mix(in srgb, var(--aas-accent) 65%, #2dd4bf);
122
+ --aas-diagram-zone-warm: #f59e0b;
123
+ /* Diagram role colours: the model, the evaluator/checker, the tracing store,
124
+ and the reference it checks against (a source DB or an execution env). */
125
+ --aas-role-model: #3b82f6;
126
+ --aas-role-eval: #22c55e;
127
+ --aas-role-trace: #f59e0b;
128
+ --aas-role-source: #14b8a6;
129
+ --aas-role-tool: #8b5cf6;
130
+ --aas-role-guard: #f43f5e;
131
+ /* "You are here" highlight — orange, used to spotlight one node when a
132
+ section walks through a single piece of a larger diagram. */
133
+ --aas-role-focus: #f97316;
134
+ }
135
+
136
+ :root[data-theme="dark"] {
137
+ color-scheme: dark;
138
+ --aas-bg: #0c0d11;
139
+ --aas-panel: #1a1b21;
140
+ --aas-border: #25262e;
141
+ --aas-text: #e2e3e8;
142
+ --aas-muted: #989aa4;
143
+ --aas-accent: #9398f0;
144
+ --aas-shadow: 0 8px 24px -8px rgb(0 0 0 / 0.6);
145
+ }
146
+
147
+ @media (prefers-color-scheme: dark) {
148
+ :root:not([data-theme]) {
149
+ color-scheme: dark;
150
+ --aas-bg: #0c0d11;
151
+ --aas-panel: #1a1b21;
152
+ --aas-border: #25262e;
153
+ --aas-text: #e2e3e8;
154
+ --aas-muted: #989aa4;
155
+ --aas-accent: #9398f0;
156
+ }
157
+ }
158
+
159
+ html {
160
+ /* No global smooth scroll: it makes the browser animate scroll *restoration*
161
+ on back/forward, which is disorienting when returning to the list. The
162
+ deliberate smooth scrolls (back-to-top, file selector) pass behavior:
163
+ 'smooth' explicitly in JS, so they still animate. */
164
+ background: var(--aas-bg);
165
+ color: var(--aas-text);
166
+ }
167
+
168
+ /* Neutral chip surface — a subtle tint that reads on any background (page or
169
+ the panel-colored cards), so no outline is needed. */
170
+ .aas-chip {
171
+ background: color-mix(in srgb, var(--aas-text) 5%, transparent);
172
+ color: var(--aas-muted);
173
+ }
174
+ /* Deprecated / maintenance-only badge — amber warning surface. */
175
+ .aas-deprecated {
176
+ background: color-mix(in srgb, #d97706 16%, transparent);
177
+ color: #b45309;
178
+ }
179
+ :root[data-theme='dark'] .aas-deprecated {
180
+ color: #fbbf24;
181
+ }
182
+ /* "No updates in 1+ year" — same amber warning weight as deprecated. */
183
+ .aas-stale {
184
+ background: color-mix(in srgb, #d97706 16%, transparent);
185
+ color: #b45309;
186
+ }
187
+ :root[data-theme='dark'] .aas-stale {
188
+ color: #fbbf24;
189
+ }
190
+ /* "No updates in 6+ months" — a milder, lower-contrast caution (softer yellow). */
191
+ .aas-aging {
192
+ background: color-mix(in srgb, #ca8a04 12%, transparent);
193
+ color: #a16207;
194
+ }
195
+ :root[data-theme='dark'] .aas-aging {
196
+ color: #facc15;
197
+ }
198
+ /* Pricing chip: a soft accent-tinted surface with a faint accent outline. */
199
+ .aas-pricing {
200
+ background: color-mix(in srgb, var(--aas-accent) 13%, transparent);
201
+ color: var(--aas-accent);
202
+ }
203
+ /* "Completely free" pricing chip — a positive green to stand out from paid tiers. */
204
+ .aas-free {
205
+ background: color-mix(in srgb, #16a34a 15%, transparent);
206
+ color: #15803d;
207
+ }
208
+ :root[data-theme='dark'] .aas-free {
209
+ color: #4ade80;
210
+ }
211
+ /* Theme-aware logos: show only the variant matching the active theme
212
+ (ToolAvatar emits both when a logoDark is set). */
213
+ .aas-logo-dark {
214
+ display: none;
215
+ }
216
+ :root[data-theme='dark'] .aas-logo-light {
217
+ display: none;
218
+ }
219
+ :root[data-theme='dark'] .aas-logo-dark {
220
+ display: revert;
221
+ }
222
+
223
+ /* Subtle "lift + shadow" on hover for cards and button-like elements. Gated to
224
+ devices with real hover, so the effect never "sticks" after a tap on touch. */
225
+ .aas-lift {
226
+ transition:
227
+ transform 0.16s ease,
228
+ box-shadow 0.16s ease,
229
+ border-color 0.16s ease;
230
+ }
231
+ @media (hover: hover) {
232
+ .aas-lift:hover {
233
+ transform: translateY(-2px);
234
+ box-shadow: var(--aas-shadow);
235
+ }
236
+ }
237
+ @media (hover: hover) and (prefers-reduced-motion: reduce) {
238
+ .aas-lift:hover {
239
+ transform: none;
240
+ }
241
+ }
242
+
243
+ /* Floating "back to top" button: only below the TOC-rail breakpoint (the rail
244
+ handles navigation on wide screens), and only once scrolled down a bit
245
+ (`data-visible`, toggled in BackToTop.astro). */
246
+ .aas-totop {
247
+ display: none;
248
+ }
249
+ @media (max-width: 1199.98px) {
250
+ .aas-totop[data-visible] {
251
+ display: flex;
252
+ }
253
+ }
254
+
255
+ /* Right-rail TOC: an in-flow sticky column beside the content, shown once the
256
+ viewport is wide enough to hold both (≥1200px). Below that it hides and the
257
+ content spans the full column. A flex column so a long TOC scrolls internally
258
+ while the back-to-top button stays pinned at the bottom. */
259
+ .aas-toc-rail {
260
+ display: none;
261
+ }
262
+ @media (min-width: 1200px) {
263
+ .aas-toc-rail {
264
+ display: flex;
265
+ flex-direction: column;
266
+ /* Sit on the LEFT of the content (source order stays content-first for a11y;
267
+ order pulls the rail visually ahead). */
268
+ order: -1;
269
+ position: sticky;
270
+ top: 7rem;
271
+ align-self: flex-start;
272
+ flex: none;
273
+ width: 13rem;
274
+ max-height: calc(100vh - 9rem);
275
+ }
276
+ /* Embedded article sample: at the TOC-rail breakpoint the rail already lists
277
+ the files + related tools, so drop the in-body sidebar and let the code pane
278
+ span full width. Below 1200px (no rail) the inline sidebar stays. */
279
+ .aas-embed-proj {
280
+ display: block;
281
+ }
282
+ .aas-embed-sidebar {
283
+ display: none;
284
+ }
285
+ }
286
+ .aas-toc-scroll {
287
+ min-height: 0; /* allow this flex child to shrink so it can scroll */
288
+ overflow-y: auto;
289
+ }
290
+ .aas-toc-rail a[aria-current='true'] {
291
+ background: var(--aas-tint);
292
+ color: var(--aas-text);
293
+ }
294
+
295
+ /* "Back to top" pinned at the bottom of the TOC rail (surface row, no divider). */
296
+ /* Pinned foot of the TOC rail: back-to-top on the left, collapse/expand-all on
297
+ the right. */
298
+ .aas-toc-foot {
299
+ flex-shrink: 0;
300
+ margin-top: 0.5rem;
301
+ display: flex;
302
+ align-items: center;
303
+ justify-content: space-between;
304
+ gap: 0.5rem;
305
+ }
306
+ .aas-toc-top {
307
+ padding: 0.4rem 0.625rem;
308
+ border-radius: 0.375rem;
309
+ text-align: left;
310
+ font-size: 0.8rem;
311
+ color: var(--aas-muted);
312
+ background: none;
313
+ cursor: pointer;
314
+ transition:
315
+ color 0.15s,
316
+ background-color 0.15s;
317
+ }
318
+ .aas-toc-allbtn {
319
+ flex-shrink: 0;
320
+ padding: 0.4rem 0.5rem;
321
+ border-radius: 0.375rem;
322
+ font-size: 0.72rem;
323
+ color: var(--aas-muted);
324
+ background: none;
325
+ cursor: pointer;
326
+ transition:
327
+ color 0.15s,
328
+ background-color 0.15s;
329
+ }
330
+ .aas-toc-top:hover,
331
+ .aas-toc-allbtn:hover {
332
+ background: var(--aas-tint);
333
+ color: var(--aas-text);
334
+ }
335
+
336
+ /* Medium-style reading body: a larger MaruBuri serif column for the concept,
337
+ article, and stack prose (anything tagged .aas-reading). Headings stay sans
338
+ for contrast, inline tool cards stay sans, and code stays monospace — only
339
+ the running text becomes serif. Concept/article/stack share the same width
340
+ and the .aas-toc-rail TOC, so there is no separate doc layout. */
341
+ .aas-reading.aas-reading {
342
+ font-family: 'MaruBuri', ui-serif, Georgia, serif;
343
+ font-size: 1.2rem;
344
+ line-height: 1.85;
345
+ }
346
+ .aas-reading.aas-reading :where(p, ul, ol, blockquote, td, th) {
347
+ line-height: 1.85;
348
+ }
349
+ .aas-reading.aas-reading :where(h2) {
350
+ font-size: 1.7rem;
351
+ }
352
+ .aas-reading.aas-reading :where(h3) {
353
+ font-size: 1.35rem;
354
+ }
355
+ .aas-reading.aas-reading :where(h1, h2, h3, h4, h5, h6),
356
+ .aas-reading.aas-reading :where(table),
357
+ .aas-reading.aas-reading a.aas-lift,
358
+ .aas-reading.aas-reading .aas-sample-picker {
359
+ font-family: var(--aas-sans);
360
+ }
361
+ .aas-reading.aas-reading :where(code, pre, kbd, samp) {
362
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
363
+ }
364
+ /* Korean line breaking: wrap at word (eojeol) boundaries — i.e. at spaces —
365
+ instead of between arbitrary characters, so a word like "처음부터" never
366
+ splits mid-word across two lines. `word-break` inherits, so setting it on the
367
+ reading column and the description covers all their descendants. Scoped to
368
+ Korean pages via <html lang="ko">; Latin scripts already wrap on spaces. */
369
+ html[lang='ko'] .aas-reading,
370
+ html[lang='ko'] .aas-md,
371
+ html[lang='ko'] h1 {
372
+ word-break: keep-all;
373
+ }
374
+ /* Doc tables: a touch larger, centered (shrink-to-content), roomier cells, and
375
+ a full-row hover highlight. `fit-content` + `margin-inline: auto` centers the
376
+ table block while `max-width`/overflow (from the base .prose rule) keep it
377
+ scrollable if it ever outgrows the column. */
378
+ .aas-reading.aas-reading :where(table) {
379
+ display: table;
380
+ width: fit-content;
381
+ max-width: 100%;
382
+ margin-inline: auto;
383
+ font-size: 1.05rem;
384
+ /* Rounded outer corners need separated borders + clipping (border-collapse
385
+ can't round). The table draws the outer frame; cells draw the inner grid. */
386
+ border-collapse: separate;
387
+ border-spacing: 0;
388
+ border: 1px solid var(--aas-border);
389
+ border-radius: 0.75rem;
390
+ overflow: hidden;
391
+ }
392
+ .aas-reading.aas-reading :where(th, td) {
393
+ padding: 0.7rem 1.25rem;
394
+ border: 0;
395
+ border-right: 1px solid var(--aas-border);
396
+ border-bottom: 1px solid var(--aas-border);
397
+ }
398
+ .aas-reading.aas-reading :where(tr > :last-child) {
399
+ border-right: 0;
400
+ }
401
+ .aas-reading.aas-reading :where(tr:last-child > *) {
402
+ border-bottom: 0;
403
+ }
404
+ .aas-reading.aas-reading :where(tbody tr) {
405
+ transition: background-color 0.15s;
406
+ }
407
+ .aas-reading.aas-reading tbody tr:hover {
408
+ background: var(--aas-tint);
409
+ }
410
+
411
+ /* "#" copy-link anchor prepended to headings (h2/h3). Faint by default, fully
412
+ shown on hover/focus. `a.aas-anchor` outranks `.prose :where(a)` so it keeps
413
+ no underline / muted color. Click copies the section URL (see BaseLayout). */
414
+ a.aas-anchor {
415
+ position: relative;
416
+ margin-right: 0.4rem;
417
+ font-weight: 400;
418
+ color: var(--aas-muted);
419
+ text-decoration: none;
420
+ opacity: 0.25;
421
+ transition:
422
+ opacity 0.15s,
423
+ color 0.15s;
424
+ }
425
+ /* The "#" count mirrors the heading depth: h2 → #, h3 → ##, h4 → ###. */
426
+ :where(h2) > a.aas-anchor::before {
427
+ content: '#';
428
+ }
429
+ :where(h3) > a.aas-anchor::before {
430
+ content: '##';
431
+ }
432
+ :where(h4) > a.aas-anchor::before {
433
+ content: '###';
434
+ }
435
+ :where(h2, h3, h4):hover > a.aas-anchor,
436
+ a.aas-anchor:hover,
437
+ a.aas-anchor:focus {
438
+ opacity: 1;
439
+ color: var(--aas-accent);
440
+ }
441
+ a.aas-anchor.copied {
442
+ opacity: 1;
443
+ }
444
+ a.aas-anchor.copied::after {
445
+ content: 'Copied';
446
+ position: absolute;
447
+ bottom: 100%;
448
+ left: 0;
449
+ margin-bottom: 0.3rem;
450
+ padding: 0.05rem 0.4rem;
451
+ white-space: nowrap;
452
+ font-size: 0.7rem;
453
+ font-weight: 400;
454
+ color: var(--aas-accent);
455
+ border: 1px solid var(--aas-border);
456
+ border-radius: 0.3rem;
457
+ background: var(--aas-panel);
458
+ }
459
+
460
+ body {
461
+ font-family: var(--aas-sans);
462
+ }
463
+
464
+ /* Tailwind v4 no longer sets a pointer cursor on buttons; restore it (and for
465
+ <summary>, used by the custom dropdowns). */
466
+ button:not(:disabled),
467
+ summary {
468
+ cursor: pointer;
469
+ }
470
+
471
+ /* Make the `hidden` attribute win over display:flex/grid (used by the homepage
472
+ search/filter to hide cards and sections). */
473
+ [hidden] {
474
+ display: none !important;
475
+ }
476
+
477
+ /* Generic <details> dropdown used by the homepage filter/sort controls. */
478
+ .aas-dd {
479
+ position: relative;
480
+ }
481
+ .aas-dd > summary {
482
+ list-style: none;
483
+ }
484
+ .aas-dd > summary::-webkit-details-marker {
485
+ display: none;
486
+ }
487
+ .aas-dd [data-sort][aria-current='true'] [data-check] {
488
+ opacity: 1;
489
+ }
490
+
491
+ /* Custom checkbox (filter facets). */
492
+ .aas-check {
493
+ appearance: none;
494
+ -webkit-appearance: none;
495
+ position: relative;
496
+ width: 1.1rem;
497
+ height: 1.1rem;
498
+ flex-shrink: 0;
499
+ border: 1.5px solid var(--aas-border);
500
+ border-radius: 0.35rem;
501
+ background: var(--aas-panel);
502
+ cursor: pointer;
503
+ transition:
504
+ background-color 0.12s,
505
+ border-color 0.12s;
506
+ }
507
+ .aas-check:hover {
508
+ border-color: var(--aas-accent);
509
+ }
510
+ .aas-check:checked {
511
+ background: var(--aas-accent);
512
+ border-color: var(--aas-accent);
513
+ }
514
+ .aas-check::after {
515
+ content: '';
516
+ position: absolute;
517
+ left: 0.34rem;
518
+ top: 0.14rem;
519
+ width: 0.3rem;
520
+ height: 0.55rem;
521
+ border: solid #fff;
522
+ border-width: 0 2px 2px 0;
523
+ transform: rotate(45deg) scale(0);
524
+ transition: transform 0.12s;
525
+ }
526
+ .aas-check:checked::after {
527
+ transform: rotate(45deg) scale(1);
528
+ }
529
+
530
+ /* Readable defaults for rendered Markdown bodies. */
531
+ .prose :where(h1) {
532
+ font-size: 1.6rem;
533
+ font-weight: 700;
534
+ margin: 1.5rem 0 0.75rem;
535
+ scroll-margin-top: 1.5rem;
536
+ }
537
+ .prose :where(h1):first-child {
538
+ margin-top: 0;
539
+ }
540
+ .prose :where(h2) {
541
+ font-size: 1.5rem;
542
+ font-weight: 700;
543
+ margin: 2rem 0 0.75rem;
544
+ scroll-margin-top: 1.5rem; /* so TOC anchor jumps don't clip the heading */
545
+ }
546
+ .prose :where(h3) {
547
+ font-size: 1.2rem;
548
+ font-weight: 600;
549
+ margin: 1.5rem 0 0.5rem;
550
+ scroll-margin-top: 1.5rem;
551
+ }
552
+ .prose :where(h4) {
553
+ font-size: 1.05rem;
554
+ font-weight: 600;
555
+ margin: 1.5rem 0 0.5rem;
556
+ scroll-margin-top: 1.5rem;
557
+ }
558
+ .prose :where(p, ul, ol) {
559
+ margin: 0.75rem 0;
560
+ line-height: 1.7;
561
+ color: var(--aas-text);
562
+ }
563
+ .prose :where(ul) {
564
+ list-style: disc;
565
+ padding-left: 1.25rem;
566
+ }
567
+ .prose :where(ol) {
568
+ list-style: decimal;
569
+ padding-left: 1.25rem;
570
+ }
571
+ .prose :where(a) {
572
+ color: var(--aas-accent);
573
+ text-decoration: none;
574
+ }
575
+ .prose :where(a):hover {
576
+ text-decoration: underline;
577
+ }
578
+ /* Inline code chips in running prose. Chips inside card-style UI links
579
+ (a.aas-lift — e.g. the project viewer's GitHub button) style themselves
580
+ with utilities and must not pick this up. */
581
+ .prose :where(code):not(pre code, .aas-lift code) {
582
+ background: var(--aas-panel);
583
+ border: 1px solid var(--aas-border);
584
+ border-radius: 0.3rem;
585
+ padding: 0.1rem 0.35rem;
586
+ font-size: 0.85em;
587
+ }
588
+ /* Tiny inline Markdown (`code`/bold/italic) rendered into description fields
589
+ shown outside .prose — detail headers, cards, and related-item links. */
590
+ .aas-md code {
591
+ font-family: var(--aas-mono, ui-monospace, monospace);
592
+ background: var(--aas-panel);
593
+ border: 1px solid var(--aas-border);
594
+ border-radius: 0.3rem;
595
+ padding: 0.05rem 0.3rem;
596
+ font-size: 0.85em;
597
+ }
598
+ /* Code blocks — but not Mermaid diagrams, which carry their own background. */
599
+ .prose :where(pre:not(.mermaid)) {
600
+ background: #0e1117 !important;
601
+ border: 1px solid var(--aas-border);
602
+ border-radius: 0.6rem;
603
+ padding: 1rem 1.1rem;
604
+ overflow-x: auto;
605
+ margin: 1rem 0;
606
+ }
607
+ /* Hybrid wrapping: soft-wrap shell commands and example output (which are
608
+ prose-like and run long on mobile) while leaving real source code blocks
609
+ horizontally scrollable so their indentation/alignment stays intact. The
610
+ data-lang attribute is set by src/lib/project.ts when rendering READMEs. */
611
+ .prose :where(pre[data-lang='bash'], pre[data-lang='sh'], pre[data-lang='shell'], pre[data-lang='console'], pre[data-lang='text']) {
612
+ white-space: pre-wrap;
613
+ overflow-wrap: anywhere;
614
+ }
615
+
616
+ /* Copy-to-clipboard button mounted on code blocks (see BaseLayout script).
617
+ The host is a non-scrolling wrapper so the button stays pinned top-right
618
+ instead of scrolling away with long code. */
619
+ .aas-copyhost {
620
+ position: relative;
621
+ }
622
+ .aas-copy-btn {
623
+ position: absolute;
624
+ top: 0.5rem;
625
+ right: 0.5rem;
626
+ z-index: 2;
627
+ display: inline-flex;
628
+ align-items: center;
629
+ justify-content: center;
630
+ width: 1.9rem;
631
+ height: 1.9rem;
632
+ padding: 0;
633
+ border: 1px solid var(--aas-border);
634
+ border-radius: 0.4rem;
635
+ background: color-mix(in srgb, var(--aas-panel) 80%, transparent);
636
+ color: var(--aas-muted);
637
+ cursor: pointer;
638
+ opacity: 0;
639
+ transition:
640
+ opacity 0.15s ease,
641
+ color 0.15s ease,
642
+ border-color 0.15s ease;
643
+ }
644
+ .aas-copyhost:hover .aas-copy-btn,
645
+ .aas-copy-btn:focus-visible {
646
+ opacity: 1;
647
+ }
648
+ /* Touch devices have no hover — keep the button discoverable. */
649
+ @media (hover: none) {
650
+ .aas-copy-btn {
651
+ opacity: 0.65;
652
+ }
653
+ }
654
+ .aas-copy-btn:hover {
655
+ color: var(--aas-text);
656
+ border-color: var(--aas-text);
657
+ }
658
+ .aas-copy-btn.copied {
659
+ color: #16a34a;
660
+ border-color: #16a34a;
661
+ opacity: 1;
662
+ }
663
+ .aas-copy-btn svg {
664
+ width: 0.95rem;
665
+ height: 0.95rem;
666
+ }
667
+ .aas-copy-btn .aas-ico-check {
668
+ display: none;
669
+ }
670
+ .aas-copy-btn.copied .aas-ico-copy {
671
+ display: none;
672
+ }
673
+ .aas-copy-btn.copied .aas-ico-check {
674
+ display: inline;
675
+ }
676
+
677
+ /* Mermaid diagram container. The styled wrapper is a div we own, so the
678
+ border/radius survive Mermaid replacing the inner <pre> with an <svg>. */
679
+ .aas-diagram {
680
+ margin: 1rem 0;
681
+ padding: 1rem;
682
+ background: transparent;
683
+ border: 1px solid var(--aas-border);
684
+ border-radius: 0.6rem;
685
+ overflow: hidden;
686
+ }
687
+ .aas-diagram .mermaid {
688
+ margin: 0;
689
+ background: transparent;
690
+ text-align: center;
691
+ line-height: 0; /* avoid extra space from the raw source before render */
692
+ }
693
+ /* Mermaid renders a block <svg>; center it horizontally. */
694
+ .aas-diagram .mermaid svg {
695
+ display: block;
696
+ margin-inline: auto;
697
+ }
698
+
699
+ /* Mermaid theme: rounded corners + soft pastel fills, derived from the site
700
+ accent so it adapts to light/dark. `!important` overrides Mermaid's own
701
+ id-scoped <style> block. Applies wherever a diagram renders (prose + samples). */
702
+ .mermaid g.node rect,
703
+ .mermaid g.node polygon {
704
+ rx: 12px;
705
+ ry: 12px;
706
+ }
707
+ .mermaid g.node rect,
708
+ .mermaid g.node polygon,
709
+ .mermaid g.node path {
710
+ fill: color-mix(in srgb, var(--aas-accent) 9%, var(--aas-panel)) !important;
711
+ stroke: color-mix(in srgb, var(--aas-accent) 45%, transparent) !important;
712
+ stroke-width: 1.5px !important;
713
+ }
714
+ /* Role-coloured nodes — opt in per diagram with `class <node> role…`. The model,
715
+ the evaluator/checker, and the tracing store each read as a distinct colour. */
716
+ .mermaid g.node.roleModel :is(rect, polygon, path) {
717
+ fill: color-mix(in srgb, var(--aas-role-model) 14%, var(--aas-panel)) !important;
718
+ stroke: color-mix(in srgb, var(--aas-role-model) 55%, transparent) !important;
719
+ }
720
+ .mermaid g.node.roleEval :is(rect, polygon, path) {
721
+ fill: color-mix(in srgb, var(--aas-role-eval) 15%, var(--aas-panel)) !important;
722
+ stroke: color-mix(in srgb, var(--aas-role-eval) 55%, transparent) !important;
723
+ }
724
+ .mermaid g.node.roleTrace :is(rect, polygon, path) {
725
+ fill: color-mix(in srgb, var(--aas-role-trace) 15%, var(--aas-panel)) !important;
726
+ stroke: color-mix(in srgb, var(--aas-role-trace) 55%, transparent) !important;
727
+ }
728
+ .mermaid g.node.roleSource :is(rect, polygon, path) {
729
+ fill: color-mix(in srgb, var(--aas-role-source) 15%, var(--aas-panel)) !important;
730
+ stroke: color-mix(in srgb, var(--aas-role-source) 55%, transparent) !important;
731
+ }
732
+ .mermaid g.node.roleTool :is(rect, polygon, path) {
733
+ fill: color-mix(in srgb, var(--aas-role-tool) 14%, var(--aas-panel)) !important;
734
+ stroke: color-mix(in srgb, var(--aas-role-tool) 55%, transparent) !important;
735
+ }
736
+ .mermaid g.node.roleGuard :is(rect, polygon, path) {
737
+ fill: color-mix(in srgb, var(--aas-role-guard) 14%, var(--aas-panel)) !important;
738
+ stroke: color-mix(in srgb, var(--aas-role-guard) 55%, transparent) !important;
739
+ }
740
+ /* Spotlight (orange) — stronger fill/stroke than the role tints so the node the
741
+ surrounding prose is explaining pops out. Declared last so it wins over a
742
+ node's ordinary role class. */
743
+ .mermaid g.node.roleFocus :is(rect, polygon, path) {
744
+ fill: color-mix(in srgb, var(--aas-role-focus) 22%, var(--aas-panel)) !important;
745
+ stroke: color-mix(in srgb, var(--aas-role-focus) 75%, transparent) !important;
746
+ stroke-width: 1.5px !important;
747
+ }
748
+ .mermaid g.cluster rect {
749
+ rx: 18px;
750
+ ry: 18px;
751
+ /* Default zone colour — a calm blue for any cluster (overrides Mermaid's
752
+ transparent clusterBkg). A subgraph opts into another zone per-diagram via
753
+ `class <id> <zone>`. */
754
+ fill: color-mix(in srgb, var(--aas-diagram-zone) 13%, transparent) !important;
755
+ stroke: color-mix(in srgb, var(--aas-diagram-zone) 42%, transparent) !important;
756
+ stroke-width: 1.5px !important;
757
+ }
758
+ /* Emphasized region — warm amber. Opt in with `class <id> aaszone` (e.g. the
759
+ harness we're describing). */
760
+ .mermaid g.cluster.aaszone rect {
761
+ fill: color-mix(in srgb, var(--aas-diagram-zone-warm) 14%, transparent) !important;
762
+ stroke: color-mix(in srgb, var(--aas-diagram-zone-warm) 48%, transparent) !important;
763
+ }
764
+
765
+
766
+ /* Markdown tables — without these, columns collapse together. */
767
+ .prose :where(table) {
768
+ width: 100%;
769
+ border-collapse: collapse;
770
+ margin: 1.25rem 0;
771
+ font-size: 0.92rem;
772
+ display: block;
773
+ overflow-x: auto;
774
+ }
775
+ .prose :where(th, td) {
776
+ border: 1px solid var(--aas-border);
777
+ padding: 0.55rem 0.85rem;
778
+ text-align: left;
779
+ vertical-align: top;
780
+ }
781
+ .prose :where(thead th) {
782
+ background: var(--aas-panel);
783
+ font-weight: 600;
784
+ }
785
+ .prose :where(tbody tr:nth-child(even)) {
786
+ background: color-mix(in srgb, var(--aas-panel) 45%, transparent);
787
+ }
788
+
789
+ /* ── Listing density: gallery (compact mini-cards) ───────────────────────────
790
+ Toggled by [data-view='gallery'] on <html> (persisted in localStorage). A
791
+ denser grid plus a stripped-down card — logo, name, stars, pricing, and a
792
+ short clamped description; the meta row and keyword tags are hidden. */
793
+ :root[data-view='gallery'] [data-grid] {
794
+ grid-template-columns: repeat(2, minmax(0, 1fr));
795
+ gap: 0.6rem;
796
+ }
797
+ @media (min-width: 640px) {
798
+ :root[data-view='gallery'] [data-grid] {
799
+ grid-template-columns: repeat(3, minmax(0, 1fr));
800
+ }
801
+ }
802
+ @media (min-width: 1024px) {
803
+ :root[data-view='gallery'] [data-grid] {
804
+ grid-template-columns: repeat(4, minmax(0, 1fr));
805
+ }
806
+ }
807
+ /* A taller, refined portrait tile: an enlarged, blurred copy of the logo as an
808
+ ambient backdrop behind the header, then a short description and the stars +
809
+ open-source chip below. */
810
+ :root[data-view='gallery'] [data-tool] {
811
+ flex-direction: column;
812
+ gap: 0;
813
+ padding: 1rem;
814
+ /* radius / overflow / isolation come from the base card (rounded-3xl). */
815
+ }
816
+ :root[data-view='gallery'] [data-card-head] {
817
+ flex-direction: column;
818
+ align-items: flex-start;
819
+ gap: 0.6rem;
820
+ }
821
+ /* Give the name wrapper the full tile width so the h3's `truncate` can ellipsize
822
+ a long title instead of overflowing the card. */
823
+ :root[data-view='gallery'] [data-card-head] > div {
824
+ width: 100%;
825
+ }
826
+ :root[data-view='gallery'] [data-card-head] h3 {
827
+ font-size: 1.0625rem;
828
+ font-weight: 700;
829
+ /* Up to two lines, ellipsised beyond (overrides the single-line truncate).
830
+ The title takes its natural height; the description fills what's left. */
831
+ white-space: normal;
832
+ display: -webkit-box;
833
+ -webkit-line-clamp: 2;
834
+ line-clamp: 2;
835
+ -webkit-box-orient: vertical;
836
+ overflow: hidden;
837
+ line-height: 1.3;
838
+ }
839
+ :root[data-view='gallery'] [data-card-meta],
840
+ :root[data-view='gallery'] [data-card-tags] {
841
+ display: none;
842
+ }
843
+ :root[data-view='gallery'] [data-card-desc] {
844
+ margin-top: 0.55rem;
845
+ /* Up to 3 lines, ellipsised; reserve 3 lines via `lh` so every card is the
846
+ same height. Using `lh` (not a fixed rem) keeps it correct if the font or
847
+ line-height changes — line-clamp always truncates at a line boundary. */
848
+ display: -webkit-box;
849
+ -webkit-line-clamp: 3;
850
+ line-clamp: 3;
851
+ -webkit-box-orient: vertical;
852
+ overflow: hidden;
853
+ font-size: 0.78rem;
854
+ line-height: 1.2rem;
855
+ min-height: 3lh;
856
+ /* Fade via colour, not opacity — opacity would create a stacking context and
857
+ lift the description above the card's full-card link overlay. */
858
+ color: color-mix(in srgb, var(--aas-muted) 78%, transparent);
859
+ }
860
+ /* Footer: stars + open-source chip, just spaced below the content. The chips
861
+ drop their borders for a soft, tinted-pill look in this view. */
862
+ :root[data-view='gallery'] [data-card-foot] {
863
+ flex-direction: row;
864
+ align-items: center;
865
+ margin-top: 0.85rem;
866
+ }
867
+ :root[data-view='gallery'] [data-card-foot] > span {
868
+ border-color: transparent;
869
+ background: color-mix(in srgb, var(--aas-text) 2%, transparent);
870
+ }
871
+ /* The open-source chip becomes an icon, pinned to the right edge; its text label
872
+ (which wraps awkwardly in a narrow tile) is swapped out. */
873
+ .aas-oss-icon {
874
+ display: none;
875
+ }
876
+ :root[data-view='gallery'] [data-card-foot] [data-pricing-tag='open-source'] {
877
+ margin-left: auto;
878
+ display: inline-flex;
879
+ align-items: center;
880
+ }
881
+ :root[data-view='gallery'] [data-card-foot] [data-pricing-tag='open-source'] [data-chip-label] {
882
+ display: none;
883
+ }
884
+ :root[data-view='gallery'] [data-card-foot] [data-pricing-tag='open-source'] .aas-oss-icon {
885
+ display: block;
886
+ color: var(--aas-text); /* match the star glyph, not the accent */
887
+ }
888
+ /* The blurred-logo backdrop. Shown on every card, kept at z-index:-1 (behind the
889
+ static content) so the name's full-card link overlay still covers it — a click
890
+ anywhere on the card navigates. Subtle by default; a touch stronger and taller
891
+ in the gallery view. */
892
+ .aas-gallery-bg {
893
+ position: absolute;
894
+ inset: 0 0 auto 0;
895
+ height: 50%;
896
+ z-index: -1;
897
+ background-position: center 35%;
898
+ background-repeat: no-repeat;
899
+ background-size: cover;
900
+ filter: blur(24px) saturate(1.2);
901
+ transform: scale(1.6);
902
+ opacity: 0.09;
903
+ pointer-events: none;
904
+ -webkit-mask-image: linear-gradient(to bottom, #000 45%, transparent);
905
+ mask-image: linear-gradient(to bottom, #000 45%, transparent);
906
+ animation: aas-bg-drift 22s ease-in-out infinite;
907
+ }
908
+ :root[data-view='gallery'] .aas-gallery-bg {
909
+ height: 60%;
910
+ opacity: 0.15;
911
+ filter: blur(22px) saturate(1.25);
912
+ -webkit-mask-image: linear-gradient(to bottom, #000 50%, transparent);
913
+ mask-image: linear-gradient(to bottom, #000 50%, transparent);
914
+ }
915
+ /* A gentle, slow drift so the backdrop feels alive without distracting. */
916
+ @keyframes aas-bg-drift {
917
+ 0%,
918
+ 100% {
919
+ transform: scale(1.55) translate(-1.5%, -1%);
920
+ }
921
+ 50% {
922
+ transform: scale(1.72) translate(1.5%, 1%);
923
+ }
924
+ }
925
+ /* Stagger neighbouring cards so they don't all drift in lockstep. */
926
+ :root[data-view='gallery'] [data-tool]:nth-child(3n + 2) .aas-gallery-bg {
927
+ animation-delay: -7s;
928
+ }
929
+ :root[data-view='gallery'] [data-tool]:nth-child(3n + 3) .aas-gallery-bg {
930
+ animation-delay: -14s;
931
+ }
932
+ @media (prefers-reduced-motion: reduce) {
933
+ :root[data-view='gallery'] .aas-gallery-bg {
934
+ animation: none;
935
+ }
936
+ }
937
+
938
+ /* Density toggle buttons (standard / gallery). */
939
+ .aas-viewbtn:hover {
940
+ color: var(--aas-text);
941
+ }
942
+ .aas-viewbtn[aria-current='true'] {
943
+ background: var(--aas-tint);
944
+ color: var(--aas-text);
945
+ }
946
+
947
+ /* Gallery: among the pricing chips, keep only the open-source badge. */
948
+ :root[data-view='gallery'] [data-tool] [data-pricing-tag]:not([data-pricing-tag='open-source']) {
949
+ display: none;
950
+ }
951
+
952
+ /* Markdown horizontal rules — a light hairline, not the heavy UA default. */
953
+ .prose :where(hr) {
954
+ border: 0;
955
+ border-top: 1px solid var(--aas-border);
956
+ margin: 1.75rem 0;
957
+ }
958
+
959
+ /* Collapsible <details> used in prose (e.g. "구체적인 예시" reveals). A soft
960
+ panel with a clickable summary; the disclosure marker stays. UI dropdowns
961
+ (.aas-vdd) and the file tree (.aas-tree-dir) — e.g. an article-embedded
962
+ project viewer — carry their own chrome and must not pick this up. */
963
+ .prose :where(details):not(:where(.aas-vdd, .aas-tree-dir)) {
964
+ margin: 0.9rem 0;
965
+ border: 1px solid var(--aas-border);
966
+ border-radius: 0.6rem;
967
+ background: var(--aas-panel);
968
+ padding: 0.1rem 0.95rem;
969
+ }
970
+ .prose :where(summary):not(:where(.aas-vdd, .aas-tree-dir) summary) {
971
+ cursor: pointer;
972
+ padding: 0.55rem 0;
973
+ font-weight: 600;
974
+ color: var(--aas-muted);
975
+ }
976
+ .prose :where(details[open]):not(:where(.aas-vdd, .aas-tree-dir)) {
977
+ padding-bottom: 0.55rem;
978
+ }
979
+
980
+ /* Card-style links rendered inside prose (e.g. a concept's inline <Tools/>
981
+ cards, the project viewer's GitHub button) must not pick up the prose link
982
+ accent color or the underline on hover. */
983
+ .prose a.aas-lift,
984
+ .prose a.aas-lift:hover {
985
+ color: var(--aas-text);
986
+ text-decoration: none;
987
+ }
988
+
989
+ /* Collapsible card grids (related tools / related concepts): show the first 6
990
+ cards, hide the rest until the toggle adds [data-expanded]. Keep the 6-item
991
+ cutoff in sync with LIMIT in CollapsibleGrid.astro. */
992
+ [data-collapse-grid]:not([data-expanded]) [data-collapse-items] > *:nth-child(n + 7) {
993
+ display: none;
994
+ }
995
+ [data-collapse-toggle] {
996
+ cursor: pointer;
997
+ }
998
+ [data-collapse-toggle]:hover {
999
+ text-decoration: underline;
1000
+ }
1001
+
1002
+ /* Hero card backdrop on the tool detail page: a large, blurred copy of the logo
1003
+ as an ambient tint. Static and subtle (independent of the listing view
1004
+ toggle, unlike .aas-gallery-bg). Sits behind the card content (z-index:-1
1005
+ within the card's `isolate`). */
1006
+ .aas-hero-bg {
1007
+ position: absolute;
1008
+ inset: 0 0 auto 0;
1009
+ height: 60%;
1010
+ z-index: -1;
1011
+ background-position: center 28%;
1012
+ background-repeat: no-repeat;
1013
+ background-size: cover;
1014
+ filter: blur(44px) saturate(1.25);
1015
+ transform: scale(1.6);
1016
+ opacity: 0.1;
1017
+ pointer-events: none;
1018
+ -webkit-mask-image: linear-gradient(to bottom, #000 40%, transparent);
1019
+ mask-image: linear-gradient(to bottom, #000 40%, transparent);
1020
+ }
1021
+
1022
+ /* Avoid a flash of unfiltered catalog: when arriving with a filter/sort in the
1023
+ URL, a pre-paint inline script sets [data-prefilter] on <html>; that hides the
1024
+ controls bar + result grids until FilterControls restores and applies the
1025
+ state, then removes the marker to reveal the final view in one paint. The
1026
+ marker is only ever set by JS, so no-JS users still see the content. */
1027
+ [data-prefilter] [data-controls],
1028
+ [data-prefilter] [data-section] {
1029
+ visibility: hidden;
1030
+ }
1031
+
1032
+ /* Tab bar and the sample/project viewer are UI chrome (file tree, selectors,
1033
+ buttons) — keep them gothic even when they sit inside the reading (serif)
1034
+ column. Setting font-family directly on these elements overrides the serif
1035
+ they'd otherwise inherit from the surrounding .aas-reading wrapper. */
1036
+ [role='tablist'],
1037
+ [data-projects] {
1038
+ font-family: var(--aas-sans);
1039
+ }
1040
+ /* …but the README *body* inside the viewer is running prose, not chrome, so in a
1041
+ reading column give it the same serif as the article body. Its headings and
1042
+ code keep the .aas-reading sans/mono rules — only the inherited gothic from
1043
+ [data-projects] is undone for this text. */
1044
+ .aas-reading [data-readme-body] {
1045
+ font-family: 'MaruBuri', ui-serif, Georgia, serif;
1046
+ }
1047
+
1048
+ /* ==========================================================================
1049
+ Slides / decks
1050
+ Native presentation decks rendered at /slides/<name>/. A deck is a horizontal
1051
+ scroll-snap track of full-viewport <section.aas-slide> panels (one per
1052
+ <Slide> in the deck MDX). Content is plain MDX, so it reuses Shiki code
1053
+ highlighting, the Mermaid loader, and the site's --aas-* theme (light/dark).
1054
+ ========================================================================== */
1055
+ .aas-deck-body {
1056
+ margin: 0;
1057
+ overflow: hidden;
1058
+ background: var(--aas-bg);
1059
+ color: var(--aas-text);
1060
+ font-family: var(--aas-sans);
1061
+ /* Content aspect ratio (width tracks ratio × height). Set via data-aspect. */
1062
+ --aas-ratio: 1.7778; /* 16:9 */
1063
+ }
1064
+ .aas-deck-body[data-aspect='16:10'] {
1065
+ --aas-ratio: 1.6;
1066
+ }
1067
+ .aas-deck-body[data-aspect='4:3'] {
1068
+ --aas-ratio: 1.3333;
1069
+ }
1070
+ .aas-deck-body[data-aspect='fill'] {
1071
+ --aas-ratio: 999;
1072
+ }
1073
+
1074
+ .aas-deck {
1075
+ position: fixed;
1076
+ inset: 0;
1077
+ display: flex;
1078
+ overflow-x: auto;
1079
+ overflow-y: hidden;
1080
+ scroll-snap-type: x mandatory;
1081
+ /* Programmatic slide changes are tweened in JS (DeckView), which sets
1082
+ scrollLeft directly; keep scroll-behavior auto so it isn't double-animated.
1083
+ The `transition` / reduced-motion signals are honored there too. */
1084
+ scroll-behavior: auto;
1085
+ scrollbar-width: none; /* the counter + progress bar are the position cue */
1086
+ }
1087
+ .aas-deck::-webkit-scrollbar {
1088
+ display: none;
1089
+ }
1090
+
1091
+ .aas-slide {
1092
+ position: relative; /* anchor the bottom-right source line */
1093
+ flex: 0 0 100%;
1094
+ width: 100vw;
1095
+ height: 100dvh;
1096
+ scroll-snap-align: center;
1097
+ scroll-snap-stop: always;
1098
+ display: flex;
1099
+ /* Pin content to the top so every slide's title sits at the same height (like
1100
+ real slides). Cover / center slides opt back into vertical centering. */
1101
+ align-items: flex-start;
1102
+ justify-content: center;
1103
+ padding: clamp(2.5rem, 8vh, 6rem) clamp(1.5rem, 4vw, 4rem);
1104
+ overflow-y: auto; /* a slide taller than the viewport scrolls internally */
1105
+ }
1106
+ .aas-slide.cover,
1107
+ .aas-slide.center,
1108
+ .aas-slide.quote {
1109
+ align-items: center;
1110
+ }
1111
+
1112
+ .aas-slide-inner {
1113
+ width: 100%;
1114
+ /* Content width tracks the deck's aspect ratio (× height): 16:9/16:10/4:3 give
1115
+ a proportioned, wider column; 'fill' (--aas-ratio 999) uses the full width. */
1116
+ max-width: min(96rem, calc((100dvh - 11vh) * var(--aas-ratio, 1.7778)));
1117
+ margin: 0 auto;
1118
+ /* Wrap Korean/CJK at spaces (word boundaries), not mid-word. */
1119
+ word-break: keep-all;
1120
+ overflow-wrap: break-word;
1121
+ }
1122
+
1123
+ /* Cover / closing slides: centered, roomier. Add class="cover" on the <Slide>. */
1124
+ .aas-slide.cover .aas-slide-inner,
1125
+ .aas-slide.center .aas-slide-inner {
1126
+ text-align: center;
1127
+ }
1128
+
1129
+ /* --- Slide typography (styles the plain MDX inside a slide) --------------- */
1130
+ /* The site adds a "#" copy-link anchor to headings; it's noise in a deck. */
1131
+ .aas-slide-inner .aas-anchor {
1132
+ display: none;
1133
+ }
1134
+ .aas-slide-inner > :first-child {
1135
+ margin-top: 0;
1136
+ }
1137
+ .aas-slide-inner > :last-child {
1138
+ margin-bottom: 0;
1139
+ }
1140
+ .aas-slide-inner h1 {
1141
+ font-size: clamp(2rem, 5.5vw, 3.75rem);
1142
+ font-weight: 800;
1143
+ line-height: 1.1;
1144
+ letter-spacing: -0.02em;
1145
+ margin: 0 0 0.6em;
1146
+ }
1147
+ .aas-slide-inner h2 {
1148
+ font-size: clamp(1.9rem, 4.2vw, 3rem);
1149
+ font-weight: 700;
1150
+ line-height: 1.15;
1151
+ letter-spacing: -0.015em;
1152
+ margin: 0 0 0.7em;
1153
+ color: var(--aas-accent);
1154
+ }
1155
+ /* A slide's leading heading is its title — style ### / #### the same as ## when
1156
+ it opens the slide, so heading levels can express deck/TOC hierarchy without
1157
+ shrinking the on-slide title. (Nested ### inside :::cols keep their small size.) */
1158
+ .aas-slide-inner > h3:first-child,
1159
+ .aas-slide-inner > h4:first-child {
1160
+ font-size: clamp(1.9rem, 4.2vw, 3rem);
1161
+ font-weight: 700;
1162
+ line-height: 1.15;
1163
+ letter-spacing: -0.015em;
1164
+ margin: 0 0 0.7em;
1165
+ color: var(--aas-accent);
1166
+ }
1167
+
1168
+ /* Subtitle (`::sub[…]`): a small, muted line tucked under the slide title. */
1169
+ .aas-slide-inner .aas-subtitle {
1170
+ margin: -0.3em 0 0.9em;
1171
+ font-size: clamp(1.1rem, 2vw, 1.55rem);
1172
+ font-weight: 400;
1173
+ line-height: 1.4;
1174
+ color: var(--aas-muted);
1175
+ }
1176
+ .aas-slide.cover .aas-subtitle,
1177
+ .aas-slide.center .aas-subtitle {
1178
+ margin-top: -0.2em;
1179
+ }
1180
+ .aas-slide-inner h3 {
1181
+ font-size: clamp(1.3rem, 2.5vw, 1.85rem);
1182
+ font-weight: 700;
1183
+ margin: 1.2em 0 0.4em;
1184
+ }
1185
+ .aas-slide-inner p,
1186
+ .aas-slide-inner li {
1187
+ font-size: clamp(1.2rem, 2vw, 1.65rem);
1188
+ line-height: 1.55;
1189
+ }
1190
+ .aas-slide-inner p {
1191
+ margin: 0 0 0.7em;
1192
+ color: var(--aas-text);
1193
+ }
1194
+ .aas-slide-inner strong {
1195
+ font-weight: 700;
1196
+ }
1197
+ .aas-slide-inner em {
1198
+ color: var(--aas-muted);
1199
+ font-style: normal;
1200
+ }
1201
+ /* Restore list markers (the site's CSS reset strips them). */
1202
+ .aas-slide-inner ul,
1203
+ .aas-slide-inner ol {
1204
+ margin: 0.4em 0 0.8em;
1205
+ padding-left: 1.5em;
1206
+ }
1207
+ .aas-slide-inner ul {
1208
+ list-style: disc;
1209
+ }
1210
+ .aas-slide-inner ul ul {
1211
+ list-style: circle;
1212
+ }
1213
+ .aas-slide-inner ol {
1214
+ list-style: decimal;
1215
+ }
1216
+ .aas-slide-inner ol ol {
1217
+ list-style: lower-alpha;
1218
+ }
1219
+ .aas-slide-inner li {
1220
+ margin: 0.35em 0;
1221
+ padding-left: 0.2em;
1222
+ }
1223
+ .aas-slide-inner li::marker {
1224
+ color: var(--aas-accent);
1225
+ }
1226
+ .aas-slide-inner a {
1227
+ color: var(--aas-accent);
1228
+ }
1229
+ .aas-slide-inner hr {
1230
+ border: none;
1231
+ border-top: 1px solid var(--aas-border);
1232
+ margin: 1.2em 0;
1233
+ }
1234
+ /* Cover title: bigger, muted subtitle */
1235
+ .aas-slide.cover h1 {
1236
+ font-size: clamp(2.5rem, 8vw, 5.5rem);
1237
+ }
1238
+ .aas-slide.cover p {
1239
+ font-size: clamp(1.1rem, 2.4vw, 1.7rem);
1240
+ color: var(--aas-muted);
1241
+ }
1242
+
1243
+ /* Tables */
1244
+ .aas-slide-inner table {
1245
+ width: 100%;
1246
+ border-collapse: collapse;
1247
+ font-size: clamp(0.9rem, 1.5vw, 1.25rem);
1248
+ margin: 0.5em 0;
1249
+ }
1250
+ .aas-slide-inner th,
1251
+ .aas-slide-inner td {
1252
+ text-align: left;
1253
+ padding: 0.5em 0.9em;
1254
+ border-bottom: 1px solid var(--aas-border);
1255
+ vertical-align: top;
1256
+ }
1257
+ .aas-slide-inner th {
1258
+ font-weight: 700;
1259
+ color: var(--aas-muted);
1260
+ }
1261
+
1262
+ /* Code (Shiki) + Mermaid inside slides */
1263
+ .aas-slide-inner pre {
1264
+ border-radius: 0.9rem;
1265
+ padding: 1.1em 1.3em;
1266
+ overflow: auto;
1267
+ font-size: clamp(0.95rem, 1.5vw, 1.3rem);
1268
+ max-height: 62vh;
1269
+ }
1270
+ /* Code in a proper monospace stack (a visual theme may override --aas-code-font). */
1271
+ .aas-slide-inner pre:not(.mermaid),
1272
+ .aas-slide-inner pre:not(.mermaid) code,
1273
+ .aas-slide-inner :not(pre) > code {
1274
+ font-family: var(
1275
+ --aas-code-font,
1276
+ ui-monospace,
1277
+ 'SF Mono',
1278
+ 'JetBrains Mono',
1279
+ 'Cascadia Code',
1280
+ 'Fira Code',
1281
+ Menlo,
1282
+ Consolas,
1283
+ monospace
1284
+ );
1285
+ }
1286
+ .aas-slide-inner :not(pre) > code {
1287
+ font-size: 0.9em;
1288
+ padding: 0.1em 0.35em;
1289
+ border-radius: 0.35rem;
1290
+ background: var(--aas-tint);
1291
+ }
1292
+ /* Language badge on code blocks (Shiki sets data-language). The code box is
1293
+ always dark, so a light, quiet label reads on any site theme. */
1294
+ .aas-slide-inner pre.astro-code {
1295
+ position: relative;
1296
+ }
1297
+ .aas-slide-inner pre.astro-code[data-language]::before {
1298
+ content: attr(data-language);
1299
+ position: absolute;
1300
+ top: 0;
1301
+ right: 0;
1302
+ padding: 0.3em 0.7em;
1303
+ font-family: var(--aas-code-font, ui-monospace, monospace);
1304
+ font-size: 0.72em;
1305
+ letter-spacing: 0.04em;
1306
+ text-transform: uppercase;
1307
+ color: rgba(255, 255, 255, 0.45);
1308
+ pointer-events: none;
1309
+ }
1310
+ .aas-slide-inner pre.mermaid {
1311
+ background: transparent;
1312
+ padding: 0;
1313
+ display: flex;
1314
+ justify-content: center;
1315
+ }
1316
+ .aas-slide-inner pre.mermaid svg {
1317
+ /* Mermaid ships each svg with width="100%" + an inline max-width equal to the
1318
+ diagram's natural width. Respect that (no !important) so it renders at its
1319
+ true size — capped to the container by width:100%, never blown up past its
1320
+ natural size. Forcing max-width here would upscale a small diagram (e.g. a
1321
+ narrow vertical flowchart) to fill the column and balloon the labels. */
1322
+ max-width: 100%;
1323
+ max-height: 52vh;
1324
+ height: auto;
1325
+ }
1326
+
1327
+ /* Multi-column helper: <div class="cols"> in a slide splits into columns. */
1328
+ .aas-slide-inner .cols {
1329
+ display: grid;
1330
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
1331
+ gap: clamp(1rem, 3vw, 2.5rem);
1332
+ text-align: left;
1333
+ }
1334
+
1335
+ /* --- Images in slides ---------------------------------------------------- */
1336
+ /* Full-bleed background (<Slide bg={img}>): cover-fit image + a dark scrim so
1337
+ the centered text stays legible on any photo. */
1338
+ .aas-slide.has-bg {
1339
+ position: relative;
1340
+ background-size: cover;
1341
+ background-position: center;
1342
+ }
1343
+ .aas-slide.has-bg::before {
1344
+ content: '';
1345
+ position: absolute;
1346
+ inset: 0;
1347
+ background: rgba(0, 0, 0, 0.45);
1348
+ }
1349
+ .aas-slide.has-bg .aas-slide-inner {
1350
+ position: relative;
1351
+ z-index: 1;
1352
+ }
1353
+ .aas-slide.has-bg .aas-slide-inner,
1354
+ .aas-slide.has-bg h1,
1355
+ .aas-slide.has-bg h2,
1356
+ .aas-slide.has-bg h3,
1357
+ .aas-slide.has-bg p {
1358
+ color: #fff;
1359
+ }
1360
+
1361
+ /* Media on top, text below. For an image, add class="aas-media-top"; a diagram
1362
+ or code block just stacks above the text on its own (no class needed). */
1363
+ .aas-slide-inner .aas-media-top {
1364
+ display: block;
1365
+ width: 100%;
1366
+ max-height: 55vh;
1367
+ object-fit: cover;
1368
+ border-radius: 0.9rem;
1369
+ margin: 0 0 1rem;
1370
+ }
1371
+
1372
+ /* Media + text side by side. The media child can be an image, a diagram
1373
+ (`pre.mermaid`), or a code block (`pre`). Order the media and the text block
1374
+ as you want them to appear; pick the split ratio with a modifier class. */
1375
+ .aas-slide-inner .aas-split {
1376
+ display: grid;
1377
+ grid-template-columns: 1fr 1fr; /* r-50-50 (default) */
1378
+ gap: clamp(1rem, 3vw, 2.5rem);
1379
+ align-items: center;
1380
+ text-align: left;
1381
+ }
1382
+ /* Let code / diagrams shrink to their column instead of blowing the grid out
1383
+ (a <pre> with long lines otherwise refuses to go below its content width). */
1384
+ .aas-slide-inner .aas-split > * {
1385
+ min-width: 0;
1386
+ }
1387
+ .aas-slide-inner .aas-split.r-30-70 {
1388
+ grid-template-columns: 3fr 7fr;
1389
+ }
1390
+ .aas-slide-inner .aas-split.r-70-30 {
1391
+ grid-template-columns: 7fr 3fr;
1392
+ }
1393
+ /* Image media. */
1394
+ .aas-slide-inner .aas-split img {
1395
+ width: 100%;
1396
+ max-height: 46vh;
1397
+ object-fit: cover;
1398
+ border-radius: 0.9rem;
1399
+ }
1400
+ /* Code / diagram media: drop the outer margin and cap the height so the column
1401
+ stays inside the slide (code scrolls; the diagram scales down). */
1402
+ .aas-slide-inner .aas-split > pre {
1403
+ width: 100%;
1404
+ margin: 0;
1405
+ max-height: 56vh;
1406
+ }
1407
+ .aas-slide-inner .aas-split > pre:not(.mermaid) {
1408
+ font-size: clamp(0.85rem, 1.2vw, 1.15rem);
1409
+ }
1410
+ /* Text column of a split stays top-aligned (the media may center/stretch, but
1411
+ the prose reads from the top) — for image, diagram, and code splits alike. */
1412
+ .aas-slide-inner .aas-split > div:not(.aas-diagram) {
1413
+ align-self: start;
1414
+ }
1415
+ /* Media vertical alignment within the split (default: centered). Pair with
1416
+ `tall` (below) so top / center / bottom are measured against a full-height
1417
+ slide, not just the text column. Works for images, diagrams, and code.
1418
+ (Images get wrapped in .aas-zoomable-img at runtime, so target that too.) */
1419
+ .aas-slide-inner .aas-split.img-top > :is(img, .aas-zoomable-img, pre) {
1420
+ align-self: start;
1421
+ }
1422
+ .aas-slide-inner .aas-split.img-center > :is(img, .aas-zoomable-img, pre) {
1423
+ align-self: center;
1424
+ }
1425
+ .aas-slide-inner .aas-split.img-bottom > :is(img, .aas-zoomable-img, pre) {
1426
+ align-self: end;
1427
+ }
1428
+ /* Tall: the split fills the slide vertically, so image alignment is relative to
1429
+ a vertically-filled slide (this is what "center" means). */
1430
+ .aas-slide-inner .aas-split.tall {
1431
+ min-height: min(66vh, 42rem);
1432
+ }
1433
+ /* Fill mode: a tall image cropped to `cover`, filling the slide vertically.
1434
+ Add `fill` to the split. */
1435
+ .aas-slide-inner .aas-split.fill > :is(img, .aas-zoomable-img) {
1436
+ height: min(64vh, 42rem);
1437
+ max-height: min(64vh, 42rem);
1438
+ align-self: stretch;
1439
+ }
1440
+ .aas-slide-inner .aas-split.fill .aas-zoomable-img img {
1441
+ height: 100%;
1442
+ max-height: none;
1443
+ }
1444
+ .aas-slide-inner .aas-split > div > :first-child {
1445
+ margin-top: 0;
1446
+ }
1447
+
1448
+ /* --- Code / diagram slides fill the slide vertically --------------------- */
1449
+ /* A diagram (wrapped in .aas-diagram) or code block (a bare <pre>) — in a split
1450
+ or stacked on its own — grows to fill the slide height instead of being
1451
+ stranded at the top. Diagrams center in the filled box; code stays pinned to
1452
+ the top. Images are untouched (they keep their own `tall` / `fill` above). */
1453
+
1454
+ /* 1) The slide body becomes a full-height column whenever it holds a diagram or
1455
+ code block (directly, or as one side of a split — but not an image split).
1456
+ A slide marked `compact` opts out: the media keeps its natural size (padded)
1457
+ and the text flows normally, leaving room for longer copy. */
1458
+ .aas-slide:not(.compact) .aas-slide-inner:has(> .aas-diagram),
1459
+ .aas-slide:not(.compact) .aas-slide-inner:has(> pre),
1460
+ .aas-slide:not(.compact) .aas-slide-inner:has(> .aas-split:not(.scroll) > .aas-diagram),
1461
+ .aas-slide:not(.compact) .aas-slide-inner:has(> .aas-split:not(.scroll) > pre) {
1462
+ display: flex;
1463
+ flex-direction: column;
1464
+ min-height: 100%;
1465
+ }
1466
+ /* 2) The media region (a standalone diagram/pre, or the split holding one) takes
1467
+ the leftover height; drop the height cap so flex-grow can actually fill. */
1468
+ .aas-slide:not(.compact) .aas-slide-inner > .aas-diagram,
1469
+ .aas-slide:not(.compact) .aas-slide-inner > pre,
1470
+ .aas-slide:not(.compact) .aas-slide-inner > .aas-split:not(.scroll):has(> .aas-diagram),
1471
+ .aas-slide:not(.compact) .aas-slide-inner > .aas-split:not(.scroll):has(> pre) {
1472
+ flex: 1 1 auto;
1473
+ min-height: 0;
1474
+ }
1475
+ .aas-slide:not(.compact) .aas-slide-inner > pre {
1476
+ max-height: none;
1477
+ }
1478
+ /* 3) In a split, the media cell stretches to the full height (the box fills);
1479
+ the text column stays vertically centered (.aas-split align-items: center). */
1480
+ .aas-slide:not(.compact) .aas-slide-inner > .aas-split:not(.scroll):has(> .aas-diagram) > .aas-diagram,
1481
+ .aas-slide:not(.compact) .aas-slide-inner > .aas-split:not(.scroll):has(> pre) > pre {
1482
+ align-self: stretch;
1483
+ height: 100%;
1484
+ }
1485
+ .aas-slide:not(.compact) .aas-slide-inner > .aas-split:not(.scroll):has(> pre) > pre {
1486
+ max-height: none;
1487
+ }
1488
+ /* 3a) Diagram: the bordered box becomes a centering column, so the rendered
1489
+ diagram sits in the vertical middle of the filled box. (The diagram keeps
1490
+ its natural size — see the svg rule, which no longer forces width:auto —
1491
+ so filling the box no longer balloons the labels.) */
1492
+ .aas-slide-inner .aas-diagram {
1493
+ display: flex;
1494
+ flex-direction: column;
1495
+ justify-content: center;
1496
+ }
1497
+ /* 3b) Code stays pinned to the top of its filled box — that's the default block
1498
+ flow of a tall <pre>, so nothing extra is needed. */
1499
+
1500
+ /* Scrollable diagram (`<div class="aas-split scroll">`): the diagram column is a
1501
+ fixed-height scroll box (opted out of the fill rules above) and the diagram
1502
+ fits the column width, so a tall/complex flowchart scrolls vertically next to
1503
+ its explanation instead of shrinking to fit. Click the diagram to enlarge. */
1504
+ .aas-slide-inner .aas-split.scroll > .aas-diagram {
1505
+ display: block;
1506
+ align-self: start;
1507
+ height: min(64vh, 34rem);
1508
+ overflow: auto;
1509
+ overscroll-behavior: contain;
1510
+ }
1511
+ /* The inner <pre.mermaid> otherwise caps itself at 62vh and scrolls on its own,
1512
+ creating a nested scroll box; unclamp it so .aas-diagram is the sole scroller. */
1513
+ .aas-slide-inner .aas-split.scroll > .aas-diagram .mermaid {
1514
+ display: block;
1515
+ max-height: none;
1516
+ overflow: visible;
1517
+ }
1518
+ .aas-slide-inner .aas-split.scroll > .aas-diagram svg {
1519
+ width: 100%;
1520
+ max-width: 100%;
1521
+ max-height: none;
1522
+ height: auto;
1523
+ }
1524
+ /* Code in a scroll split: a fixed-height scroll box (opted out of the fill
1525
+ rules), so a long listing scrolls next to its walkthrough steps. */
1526
+ .aas-slide-inner .aas-split.scroll > pre {
1527
+ align-self: start;
1528
+ height: min(60vh, 30rem);
1529
+ max-height: min(60vh, 30rem);
1530
+ overflow: auto;
1531
+ }
1532
+
1533
+ /* Horizontally-scrolling diagram (`<Slide class="scroll-x">`, usually paired with
1534
+ `compact`): a wide flowchart keeps its natural width and the box scrolls
1535
+ sideways instead of squeezing every node to fit. */
1536
+ .aas-slide.scroll-x .aas-slide-inner .aas-diagram {
1537
+ display: block;
1538
+ overflow-x: auto;
1539
+ overflow-y: hidden;
1540
+ overscroll-behavior-x: contain;
1541
+ }
1542
+ .aas-slide.scroll-x .aas-slide-inner .aas-diagram .mermaid {
1543
+ display: block;
1544
+ max-height: none;
1545
+ overflow: visible;
1546
+ }
1547
+ .aas-slide.scroll-x .aas-slide-inner .aas-diagram svg {
1548
+ /* Mermaid keeps an inline `max-width: <natural>px`; a huge width is clamped by
1549
+ it to the diagram's natural width, so a wide flowchart overflows and scrolls
1550
+ (max-content mis-resolves to the container width here). */
1551
+ width: 99999px;
1552
+ max-height: none;
1553
+ height: auto;
1554
+ }
1555
+
1556
+ /* Generic scroll box (`<div class="aas-scrollbox">`): a bounded, scrollable frame
1557
+ for any media — used for the image walkthrough, where a full-width image is
1558
+ taller than the box and scrolls vertically. */
1559
+ .aas-slide-inner .aas-scrollbox {
1560
+ max-height: min(46vh, 22rem);
1561
+ overflow: auto;
1562
+ overscroll-behavior: contain;
1563
+ border: 1px solid var(--aas-border);
1564
+ border-radius: 0.9rem;
1565
+ margin: 0.3rem 0 1rem;
1566
+ }
1567
+ .aas-slide-inner .aas-scrollbox img {
1568
+ display: block;
1569
+ width: 100%;
1570
+ height: auto;
1571
+ max-height: none;
1572
+ border-radius: 0.9rem;
1573
+ }
1574
+
1575
+ /* Code walkthrough line highlight: the active step's rows get a full-width band
1576
+ (whole line, not just coloured text) and the others dim, so the eye lands on
1577
+ the lines being explained. The code box is always dark (github theme). */
1578
+ .aas-slide-inner pre.astro-code .line {
1579
+ transition:
1580
+ opacity 0.25s ease,
1581
+ background 0.2s ease;
1582
+ }
1583
+ .aas-slide-inner pre.astro-code .line.is-line-hl {
1584
+ display: inline-block;
1585
+ width: 100%;
1586
+ background: color-mix(in srgb, var(--aas-accent) 34%, transparent);
1587
+ box-shadow: inset 3px 0 0 var(--aas-accent);
1588
+ }
1589
+ .aas-slide-inner pre.astro-code.has-hl .line:not(.is-line-hl) {
1590
+ opacity: 0.4;
1591
+ }
1592
+
1593
+ /* --- Per-slide source / citation (bottom-right, ≤2 lines) ---------------- */
1594
+ .aas-slide-source {
1595
+ position: absolute;
1596
+ right: clamp(1.5rem, 4vw, 4rem);
1597
+ bottom: clamp(0.9rem, 3vh, 2rem);
1598
+ max-width: min(46%, 30rem);
1599
+ margin: 0;
1600
+ text-align: right;
1601
+ font-size: clamp(0.68rem, 0.95vw, 0.82rem);
1602
+ line-height: 1.35;
1603
+ color: var(--aas-muted);
1604
+ opacity: 0.8;
1605
+ display: -webkit-box;
1606
+ -webkit-line-clamp: 2;
1607
+ -webkit-box-orient: vertical;
1608
+ overflow: hidden;
1609
+ pointer-events: none;
1610
+ }
1611
+ .aas-slide.has-bg .aas-slide-source {
1612
+ color: rgba(255, 255, 255, 0.82);
1613
+ }
1614
+
1615
+ /* --- Zoom / lightbox: enlarge a diagram or image ------------------------- */
1616
+ /* The media becomes clickable; a small button sits in its top-right corner. */
1617
+ .aas-slide-inner .aas-zoomable {
1618
+ position: relative;
1619
+ cursor: zoom-in;
1620
+ }
1621
+ .aas-slide-inner .aas-zoomable-img {
1622
+ display: block;
1623
+ min-width: 0;
1624
+ }
1625
+ .aas-zoom-btn {
1626
+ position: absolute;
1627
+ top: 0.5rem;
1628
+ right: 0.5rem;
1629
+ z-index: 2;
1630
+ display: flex;
1631
+ align-items: center;
1632
+ justify-content: center;
1633
+ width: 2rem;
1634
+ height: 2rem;
1635
+ padding: 0;
1636
+ border: 1px solid var(--aas-border);
1637
+ border-radius: 0.55rem;
1638
+ background: color-mix(in srgb, var(--aas-panel) 82%, transparent);
1639
+ color: var(--aas-text);
1640
+ cursor: zoom-in;
1641
+ opacity: 0;
1642
+ transition: opacity 0.15s ease;
1643
+ }
1644
+ .aas-zoomable:hover .aas-zoom-btn,
1645
+ .aas-zoom-btn:focus-visible {
1646
+ opacity: 0.9;
1647
+ }
1648
+ .aas-zoom-btn:hover {
1649
+ opacity: 1;
1650
+ }
1651
+ /* The zoom button is a UI affordance — never in print/PDF. */
1652
+ @media print {
1653
+ .aas-zoom-btn {
1654
+ display: none;
1655
+ }
1656
+ }
1657
+ /* Overlay. A dark scrim (regardless of site theme) so the enlarged media pops. */
1658
+ .aas-zoom {
1659
+ position: fixed;
1660
+ inset: 0;
1661
+ z-index: 50;
1662
+ display: flex;
1663
+ align-items: center;
1664
+ justify-content: center;
1665
+ padding: clamp(1.5rem, 5vh, 4rem);
1666
+ /* Match the deck: light backdrop in light mode, dark in dark mode (a diagram's
1667
+ pastel nodes read the same as they do on the slide). Opaque so the slide
1668
+ behind — its own diagram/text — doesn't ghost through under the enlarged one. */
1669
+ background: var(--aas-bg);
1670
+ opacity: 0;
1671
+ transition: opacity 0.2s ease;
1672
+ cursor: zoom-out;
1673
+ }
1674
+ .aas-zoom.is-open {
1675
+ opacity: 1;
1676
+ }
1677
+ .aas-zoom[hidden] {
1678
+ display: none;
1679
+ }
1680
+ /* A large stage. The media is sized by the script (svg) or fit (img); `margin:
1681
+ auto` centres it when it's smaller than the stage and lets the stage scroll
1682
+ when it's bigger — so a complex diagram can be panned. */
1683
+ .aas-zoom-stage {
1684
+ width: 92vw;
1685
+ height: 86vh;
1686
+ overflow: auto;
1687
+ overscroll-behavior: contain;
1688
+ cursor: default;
1689
+ /* Same text context as .aas-slide-inner, where Mermaid measured its labels.
1690
+ Without keep-all, Korean labels re-wrap per syllable inside their measured
1691
+ boxes and drift to the top-left of each node. */
1692
+ word-break: keep-all;
1693
+ overflow-wrap: break-word;
1694
+ }
1695
+ /* Centres content that fits; grows with (and scrolls) content that doesn't. */
1696
+ .aas-zoom-fit {
1697
+ display: flex;
1698
+ align-items: center;
1699
+ justify-content: center;
1700
+ min-width: 100%;
1701
+ min-height: 100%;
1702
+ }
1703
+ /* Holds the transform-scaled diagram at its scaled size (so scroll/centre work). */
1704
+ .aas-zoom-scalebox {
1705
+ flex: none;
1706
+ }
1707
+ .aas-zoom-stage svg {
1708
+ max-width: none !important;
1709
+ flex: none;
1710
+ display: block;
1711
+ }
1712
+ .aas-zoom-stage img {
1713
+ max-width: 100%;
1714
+ max-height: 100%;
1715
+ width: auto;
1716
+ height: auto;
1717
+ object-fit: contain;
1718
+ border-radius: 0.6rem;
1719
+ }
1720
+ .aas-zoom-close {
1721
+ position: fixed;
1722
+ top: clamp(1rem, 3vh, 2rem);
1723
+ right: clamp(1rem, 3vw, 2rem);
1724
+ display: flex;
1725
+ align-items: center;
1726
+ justify-content: center;
1727
+ width: 2.6rem;
1728
+ height: 2.6rem;
1729
+ border: 1px solid var(--aas-border);
1730
+ border-radius: 0.7rem;
1731
+ background: var(--aas-panel);
1732
+ color: var(--aas-text);
1733
+ cursor: pointer;
1734
+ }
1735
+
1736
+ /* --- Overview grid (press `o`) ------------------------------------------- */
1737
+ .aas-ov {
1738
+ position: fixed;
1739
+ inset: 0;
1740
+ z-index: 40;
1741
+ overflow-y: auto;
1742
+ padding: clamp(1rem, 4vh, 3rem);
1743
+ display: grid;
1744
+ grid-template-columns: repeat(auto-fill, 260px);
1745
+ justify-content: center;
1746
+ align-content: start;
1747
+ gap: clamp(0.8rem, 2vw, 1.6rem);
1748
+ background: color-mix(in srgb, var(--aas-bg) 93%, #000);
1749
+ opacity: 0;
1750
+ transition: opacity 0.18s ease;
1751
+ }
1752
+ .aas-ov.is-open {
1753
+ opacity: 1;
1754
+ }
1755
+ .aas-ov[hidden] {
1756
+ display: none;
1757
+ }
1758
+ .aas-ov-cell {
1759
+ position: relative;
1760
+ padding: 0;
1761
+ border: 2px solid var(--aas-border);
1762
+ border-radius: 0.6rem;
1763
+ background: var(--aas-panel);
1764
+ overflow: hidden;
1765
+ cursor: pointer;
1766
+ transition: border-color 0.15s ease;
1767
+ }
1768
+ .aas-ov-cell:hover,
1769
+ .aas-ov-cell:focus-visible {
1770
+ border-color: var(--aas-accent);
1771
+ }
1772
+ .aas-ov-cell.is-current {
1773
+ border-color: var(--aas-accent);
1774
+ box-shadow: 0 0 0 2px var(--aas-accent);
1775
+ }
1776
+ .aas-ov-thumb {
1777
+ position: relative;
1778
+ width: 260px;
1779
+ overflow: hidden;
1780
+ background: var(--aas-bg);
1781
+ pointer-events: none;
1782
+ }
1783
+ .aas-ov-num {
1784
+ position: absolute;
1785
+ left: 0.4rem;
1786
+ bottom: 0.4rem;
1787
+ padding: 0.05rem 0.45rem;
1788
+ border-radius: 0.4rem;
1789
+ background: rgba(0, 0, 0, 0.62);
1790
+ color: #fff;
1791
+ font-size: 0.75rem;
1792
+ font-variant-numeric: tabular-nums;
1793
+ }
1794
+ /* Overview + lightbox are screen-only affordances. */
1795
+ @media print {
1796
+ .aas-ov {
1797
+ display: none !important;
1798
+ }
1799
+ }
1800
+
1801
+ /* Stack image + text on narrow screens (screen only — print keeps them side by
1802
+ side regardless of the page width). */
1803
+ @media screen and (max-width: 48rem) {
1804
+ .aas-slide-inner .aas-split,
1805
+ .aas-slide-inner .aas-split.r-30-70,
1806
+ .aas-slide-inner .aas-split.r-70-30 {
1807
+ grid-template-columns: 1fr;
1808
+ }
1809
+ }
1810
+
1811
+ /* --- Callout boxes (:::note / :::tip / :::warning / :::info) -------------- */
1812
+ .aas-slide-inner .aas-callout {
1813
+ --cal: var(--aas-accent);
1814
+ margin: 0.9em 0;
1815
+ padding: 0.9em 1.2em;
1816
+ border-left: 4px solid var(--cal);
1817
+ border-radius: 0.6rem;
1818
+ background: color-mix(in srgb, var(--cal) 12%, transparent);
1819
+ }
1820
+ .aas-slide-inner .aas-callout > :first-child {
1821
+ margin-top: 0;
1822
+ }
1823
+ .aas-slide-inner .aas-callout > :last-child {
1824
+ margin-bottom: 0;
1825
+ }
1826
+ .aas-slide-inner .aas-callout strong:first-child {
1827
+ color: var(--cal);
1828
+ }
1829
+ /* Scoped to match the base rule's specificity so the colour actually wins. */
1830
+ .aas-slide-inner .aas-callout-tip {
1831
+ --cal: #16a34a;
1832
+ }
1833
+ .aas-slide-inner .aas-callout-warning {
1834
+ --cal: #d97706;
1835
+ }
1836
+ .aas-slide-inner .aas-callout-info {
1837
+ --cal: #0284c7;
1838
+ }
1839
+
1840
+ /* --- Stats / KPI (:::stats, cards split by ---) -------------------------- */
1841
+ .aas-slide-inner .aas-stats {
1842
+ display: grid;
1843
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
1844
+ gap: clamp(1rem, 3vw, 2rem);
1845
+ margin: 1.1em 0;
1846
+ text-align: center;
1847
+ }
1848
+ .aas-slide-inner .aas-stat {
1849
+ padding: 1.2em 1em;
1850
+ border: 1px solid var(--aas-border);
1851
+ border-radius: 0.9rem;
1852
+ background: var(--aas-tint);
1853
+ }
1854
+ .aas-slide-inner .aas-stat > :first-child {
1855
+ margin: 0 0 0.15em;
1856
+ font-size: clamp(2.2rem, 5.5vw, 3.8rem);
1857
+ font-weight: 800;
1858
+ line-height: 1;
1859
+ letter-spacing: -0.02em;
1860
+ color: var(--aas-accent);
1861
+ }
1862
+ .aas-slide-inner .aas-stat > :first-child + * {
1863
+ margin-top: 0;
1864
+ }
1865
+ .aas-slide-inner .aas-stat p {
1866
+ margin: 0;
1867
+ color: var(--aas-muted);
1868
+ }
1869
+
1870
+ /* --- Comparison (:::compare, cards split by ---) ------------------------- */
1871
+ .aas-slide-inner .aas-compare {
1872
+ display: grid;
1873
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
1874
+ gap: clamp(1rem, 3vw, 2rem);
1875
+ margin: 1.1em 0;
1876
+ align-items: start;
1877
+ }
1878
+ .aas-slide-inner .aas-compare-col {
1879
+ padding: 1.1em 1.3em 1.2em;
1880
+ border: 1px solid var(--aas-border);
1881
+ border-radius: 0.9rem;
1882
+ background: var(--aas-tint);
1883
+ }
1884
+ .aas-slide-inner .aas-compare-col > :first-child {
1885
+ margin-top: 0;
1886
+ }
1887
+ .aas-slide-inner .aas-compare-col > :last-child {
1888
+ margin-bottom: 0;
1889
+ }
1890
+ .aas-slide-inner .aas-compare-col > :is(h2, h3, h4):first-child {
1891
+ font-size: clamp(1.2rem, 2.4vw, 1.7rem);
1892
+ margin: 0 0 0.5em;
1893
+ color: var(--aas-text);
1894
+ }
1895
+
1896
+ /* --- Hero quote (<Slide class="quote">) ---------------------------------- */
1897
+ .aas-slide.quote .aas-slide-inner {
1898
+ text-align: center;
1899
+ max-width: min(60rem, 90%);
1900
+ }
1901
+ .aas-slide.quote blockquote {
1902
+ position: relative;
1903
+ margin: 0;
1904
+ padding: 0;
1905
+ border: none;
1906
+ font-size: clamp(1.8rem, 4.4vw, 3.1rem);
1907
+ font-weight: 600;
1908
+ line-height: 1.28;
1909
+ letter-spacing: -0.01em;
1910
+ color: var(--aas-text);
1911
+ }
1912
+ .aas-slide.quote blockquote p {
1913
+ margin: 0;
1914
+ font-size: inherit;
1915
+ }
1916
+ .aas-slide.quote blockquote::before {
1917
+ content: '\201C';
1918
+ display: block;
1919
+ font-size: 2.2em;
1920
+ line-height: 0.7;
1921
+ margin-bottom: 0.1em;
1922
+ color: var(--aas-accent);
1923
+ opacity: 0.55;
1924
+ }
1925
+ .aas-slide.quote .aas-subtitle {
1926
+ margin: 1.1em 0 0;
1927
+ font-size: clamp(1rem, 1.9vw, 1.45rem);
1928
+ color: var(--aas-muted);
1929
+ }
1930
+
1931
+ /* --- Step reveal (:::steps) — fragments shown one at a time -------------- */
1932
+ /* Progressive enhancement: hidden state is added by the deck script, so with no
1933
+ JS (and in print) every step is visible. */
1934
+ .aas-slide-inner .aas-step {
1935
+ transition:
1936
+ opacity 0.3s ease,
1937
+ transform 0.3s ease;
1938
+ }
1939
+ .aas-slide-inner .aas-step.is-step-hidden {
1940
+ opacity: 0;
1941
+ transform: translateY(0.4em);
1942
+ }
1943
+
1944
+ /* Walkthrough steps (`:::step{scroll=…}`): the block-level step gets some room,
1945
+ and while stepping the non-active ones dim so the current one leads the eye
1946
+ (paired with the diagram scrolling to the step's spot). */
1947
+ .aas-slide-inner .aas-step[data-scroll] {
1948
+ margin: 0 0 0.9em;
1949
+ padding-left: 0.9em;
1950
+ border-left: 3px solid transparent;
1951
+ }
1952
+ .aas-slide-inner .aas-step[data-scroll] > :first-child {
1953
+ margin-top: 0;
1954
+ }
1955
+ .aas-slide-inner .aas-step[data-scroll]:not(.is-step-hidden):not(.is-step-active) {
1956
+ opacity: 0.4;
1957
+ }
1958
+ .aas-slide-inner .aas-step[data-scroll].is-step-active {
1959
+ border-left-color: var(--aas-accent);
1960
+ }
1961
+
1962
+ /* --- Visual themes ------------------------------------------------------- */
1963
+ /* Picked per deck via `theme:` in frontmatter (adds .aas-theme-<name> to the
1964
+ deck). They restyle slide typography only; light/dark still follows the site.
1965
+ `default` is the base look above — the classes below are the alternatives. */
1966
+
1967
+ /* simple — quiet and monochrome: headings in the text colour (no accent pop),
1968
+ markers and table headers muted. Reads like clean notes. */
1969
+ .aas-theme-simple .aas-slide-inner h2 {
1970
+ color: var(--aas-text);
1971
+ font-weight: 600;
1972
+ }
1973
+ .aas-theme-simple .aas-slide-inner li::marker {
1974
+ color: var(--aas-muted);
1975
+ }
1976
+
1977
+ /* calm — serif headings, softened accent, roomier lines. A relaxed, editorial
1978
+ feel. Uses the site's reading serif (MaruBuri) for headings. */
1979
+ .aas-theme-calm .aas-slide-inner h1,
1980
+ .aas-theme-calm .aas-slide-inner h2,
1981
+ .aas-theme-calm .aas-slide-inner h3 {
1982
+ font-family: 'MaruBuri', ui-serif, Georgia, serif;
1983
+ font-weight: 600;
1984
+ letter-spacing: 0;
1985
+ }
1986
+ .aas-theme-calm .aas-slide-inner h2 {
1987
+ color: color-mix(in srgb, var(--aas-accent) 55%, var(--aas-text));
1988
+ }
1989
+ .aas-theme-calm .aas-slide-inner p,
1990
+ .aas-theme-calm .aas-slide-inner li {
1991
+ line-height: 1.75;
1992
+ }
1993
+ .aas-theme-calm .aas-slide-inner p {
1994
+ margin-bottom: 0.9em;
1995
+ }
1996
+
1997
+ /* bold — big, heavy, high-contrast headings; accent-coloured emphasis. */
1998
+ .aas-theme-bold .aas-slide-inner h1 {
1999
+ font-weight: 800;
2000
+ letter-spacing: -0.03em;
2001
+ }
2002
+ .aas-theme-bold .aas-slide-inner h2 {
2003
+ font-weight: 800;
2004
+ letter-spacing: -0.025em;
2005
+ font-size: clamp(1.7rem, 4.2vw, 3rem);
2006
+ }
2007
+ .aas-theme-bold .aas-slide-inner strong {
2008
+ color: var(--aas-accent);
2009
+ }
2010
+ .aas-theme-bold .aas-slide.cover h1 {
2011
+ font-size: clamp(3rem, 9vw, 6.5rem);
2012
+ font-weight: 800;
2013
+ letter-spacing: -0.04em;
2014
+ }
2015
+
2016
+ /* --- Deck chrome (progress bar, back link, prev/next + counter) ---------- */
2017
+ .aas-deck-progress {
2018
+ position: fixed;
2019
+ top: 0;
2020
+ left: 0;
2021
+ right: 0;
2022
+ height: 3px;
2023
+ background: var(--aas-border);
2024
+ z-index: 10;
2025
+ }
2026
+ .aas-deck-progress-fill {
2027
+ height: 100%;
2028
+ width: 0;
2029
+ background: var(--aas-accent);
2030
+ transition: width 0.25s ease;
2031
+ }
2032
+
2033
+ .aas-deck-back {
2034
+ position: fixed;
2035
+ top: 1rem;
2036
+ left: 1rem;
2037
+ z-index: 10;
2038
+ display: inline-flex;
2039
+ align-items: center;
2040
+ gap: 0.3rem;
2041
+ padding: 0.4rem 0.75rem 0.4rem 0.55rem;
2042
+ border-radius: 999px;
2043
+ font-size: 0.85rem;
2044
+ color: var(--aas-muted);
2045
+ background: color-mix(in srgb, var(--aas-panel) 85%, transparent);
2046
+ border: 1px solid var(--aas-border);
2047
+ text-decoration: none;
2048
+ opacity: 0.5;
2049
+ transition: opacity 0.2s ease, color 0.2s ease;
2050
+ }
2051
+ .aas-deck-back:hover {
2052
+ opacity: 1;
2053
+ color: var(--aas-text);
2054
+ }
2055
+
2056
+ /* Actions drawer (left edge, vertically centered): a slim tab pokes out; when
2057
+ opened, the panel slides in from the left and the tab's arrow flips. */
2058
+ .aas-deck-menu {
2059
+ --menu-w: 12rem;
2060
+ position: fixed;
2061
+ top: 50%;
2062
+ left: 0;
2063
+ z-index: 11;
2064
+ display: flex;
2065
+ align-items: center;
2066
+ /* Closed: the panel is parked off-screen left, only the tab shows at the edge. */
2067
+ transform: translate(calc(-1 * var(--menu-w)), -50%);
2068
+ transition: transform 0.28s ease;
2069
+ }
2070
+ .aas-deck-menu.is-open {
2071
+ transform: translate(0, -50%);
2072
+ }
2073
+ .aas-deck-menu-panel {
2074
+ width: var(--menu-w);
2075
+ padding: 0.4rem;
2076
+ border: 1px solid var(--aas-border);
2077
+ border-left: none;
2078
+ border-radius: 0 0.9rem 0.9rem 0;
2079
+ background: var(--aas-panel);
2080
+ box-shadow: var(--aas-shadow);
2081
+ }
2082
+ .aas-deck-menu-panel button {
2083
+ display: flex;
2084
+ align-items: center;
2085
+ gap: 0.55rem;
2086
+ width: 100%;
2087
+ text-align: left;
2088
+ border: none;
2089
+ background: transparent;
2090
+ color: var(--aas-text);
2091
+ font-size: 0.9rem;
2092
+ padding: 0.45rem 0.6rem;
2093
+ border-radius: 0.5rem;
2094
+ cursor: pointer;
2095
+ }
2096
+ .aas-deck-menu-panel button:hover {
2097
+ background: var(--aas-tint);
2098
+ }
2099
+ .aas-deck-menu-panel button svg {
2100
+ color: var(--aas-muted);
2101
+ flex: none;
2102
+ }
2103
+ /* Divider between the actions and the theme options. */
2104
+ .aas-deck-menu-sep {
2105
+ height: 1px;
2106
+ margin: 0.35rem 0.3rem;
2107
+ background: var(--aas-border);
2108
+ }
2109
+ /* Theme options: a single row of icon buttons (system · light · dark). */
2110
+ .aas-deck-menu-themes {
2111
+ display: flex;
2112
+ gap: 0.25rem;
2113
+ padding: 0.15rem 0.2rem 0.05rem;
2114
+ }
2115
+ .aas-deck-menu-themes button {
2116
+ flex: 1;
2117
+ justify-content: center;
2118
+ padding: 0.4rem 0;
2119
+ color: var(--aas-muted);
2120
+ }
2121
+ .aas-deck-menu-themes button svg {
2122
+ color: inherit;
2123
+ }
2124
+ .aas-deck-menu-themes button[aria-pressed='true'] {
2125
+ background: var(--aas-tint);
2126
+ color: var(--aas-accent, var(--aas-text));
2127
+ }
2128
+ /* The handle/tab — attached to the panel's right edge. */
2129
+ .aas-deck-menu-tab {
2130
+ display: flex;
2131
+ align-items: center;
2132
+ justify-content: center;
2133
+ width: 1.5rem;
2134
+ height: 3.25rem;
2135
+ padding: 0;
2136
+ border: 1px solid var(--aas-border);
2137
+ border-left: none;
2138
+ border-radius: 0 0.6rem 0.6rem 0;
2139
+ background: color-mix(in srgb, var(--aas-panel) 88%, transparent);
2140
+ color: var(--aas-muted);
2141
+ cursor: pointer;
2142
+ opacity: 0.55;
2143
+ transition: opacity 0.2s ease, color 0.2s ease;
2144
+ }
2145
+ .aas-deck-menu-tab:hover,
2146
+ .aas-deck-menu.is-open .aas-deck-menu-tab {
2147
+ opacity: 1;
2148
+ color: var(--aas-text);
2149
+ }
2150
+ .aas-deck-menu-arrow {
2151
+ transition: transform 0.28s ease;
2152
+ }
2153
+ .aas-deck-menu.is-open .aas-deck-menu-arrow {
2154
+ transform: rotate(180deg); /* ▶ (open) → ◀ (close) */
2155
+ }
2156
+
2157
+ /* Footer (bottom-left): presenter · date. */
2158
+ .aas-deck-footer {
2159
+ position: fixed;
2160
+ bottom: 1.1rem;
2161
+ left: 1rem;
2162
+ z-index: 10;
2163
+ display: inline-flex;
2164
+ align-items: center;
2165
+ gap: 0.4rem;
2166
+ font-size: 0.8rem;
2167
+ color: var(--aas-muted);
2168
+ opacity: 0.7;
2169
+ }
2170
+ .aas-deck-footer-sep {
2171
+ opacity: 0.5;
2172
+ }
2173
+
2174
+ /* Print / PDF export (menu → Print/PDF, or the browser print dialog). Each slide
2175
+ becomes exactly one 16:9 page — a fixed physical size so it can't overflow onto
2176
+ a stray extra page, and wide enough that image splits never stack. Enable
2177
+ "Background graphics" in the print dialog for themed/image backgrounds. */
2178
+ @media print {
2179
+ /* PowerPoint-style 16:9 page (13.333in × 7.5in). */
2180
+ @page {
2181
+ size: 13.333in 7.5in;
2182
+ margin: 0;
2183
+ }
2184
+ .aas-deck-body {
2185
+ overflow: visible;
2186
+ -webkit-print-color-adjust: exact;
2187
+ print-color-adjust: exact;
2188
+ }
2189
+ .aas-deck-back,
2190
+ .aas-deck-menu,
2191
+ .aas-deck-toc,
2192
+ .aas-deck-nav,
2193
+ .aas-deck-progress,
2194
+ .aas-deck-footer,
2195
+ .mermaidTooltip {
2196
+ /* the last one is Mermaid's stray hover-tooltip div — its few px otherwise
2197
+ overflow into a trailing blank page. */
2198
+ display: none !important;
2199
+ }
2200
+ .aas-deck {
2201
+ position: static;
2202
+ display: block;
2203
+ overflow: visible;
2204
+ height: auto;
2205
+ scroll-snap-type: none;
2206
+ }
2207
+ .aas-slide {
2208
+ width: 13.333in;
2209
+ height: 7.5in;
2210
+ overflow: hidden;
2211
+ scroll-snap-align: none;
2212
+ break-inside: avoid;
2213
+ }
2214
+ .aas-slide:not(:last-child) {
2215
+ break-after: page;
2216
+ }
2217
+ /* Fixed page size, so don't derive the content width from viewport units. */
2218
+ .aas-slide-inner {
2219
+ max-width: 92%;
2220
+ }
2221
+ }
2222
+
2223
+ /* Table of contents — a top-right toggle that opens a jump-list of headings. */
2224
+ .aas-deck-toc {
2225
+ position: fixed;
2226
+ top: 1rem;
2227
+ right: 1rem;
2228
+ z-index: 11;
2229
+ }
2230
+ .aas-deck-toc-btn {
2231
+ display: inline-flex;
2232
+ align-items: center;
2233
+ justify-content: center;
2234
+ width: 2.1rem;
2235
+ height: 2.1rem;
2236
+ border-radius: 999px;
2237
+ color: var(--aas-muted);
2238
+ background: color-mix(in srgb, var(--aas-panel) 85%, transparent);
2239
+ border: 1px solid var(--aas-border);
2240
+ cursor: pointer;
2241
+ opacity: 0.5;
2242
+ transition: opacity 0.2s ease, color 0.2s ease;
2243
+ }
2244
+ .aas-deck-toc-btn:hover,
2245
+ .aas-deck-toc-btn[aria-expanded='true'] {
2246
+ opacity: 1;
2247
+ color: var(--aas-text);
2248
+ }
2249
+ .aas-deck-toc-panel {
2250
+ position: absolute;
2251
+ top: calc(100% + 0.4rem);
2252
+ right: 0;
2253
+ min-width: 15rem;
2254
+ max-width: min(22rem, 80vw);
2255
+ max-height: 70vh;
2256
+ overflow-y: auto;
2257
+ padding: 0.4rem;
2258
+ border-radius: 0.9rem;
2259
+ background: var(--aas-panel);
2260
+ border: 1px solid var(--aas-border);
2261
+ box-shadow: var(--aas-shadow);
2262
+ }
2263
+ .aas-deck-toc-panel ul {
2264
+ list-style: none;
2265
+ margin: 0;
2266
+ padding: 0;
2267
+ }
2268
+ .aas-deck-toc-panel li {
2269
+ margin: 0;
2270
+ }
2271
+ .aas-deck-toc-panel button {
2272
+ display: block;
2273
+ width: 100%;
2274
+ text-align: left;
2275
+ border: none;
2276
+ background: transparent;
2277
+ color: var(--aas-text);
2278
+ font-size: 0.9rem;
2279
+ line-height: 1.35;
2280
+ padding: 0.4rem 0.6rem;
2281
+ border-radius: 0.5rem;
2282
+ cursor: pointer;
2283
+ }
2284
+ .aas-deck-toc-panel button:hover {
2285
+ background: var(--aas-tint);
2286
+ }
2287
+ .aas-deck-toc-panel button.active {
2288
+ color: var(--aas-accent);
2289
+ font-weight: 600;
2290
+ background: color-mix(in srgb, var(--aas-accent) 10%, transparent);
2291
+ }
2292
+ /* ### / #### sub-entries (toc_level 3-4) sit indented and a touch smaller. */
2293
+ .aas-deck-toc-panel .aas-toc-l3 button,
2294
+ .aas-deck-toc-panel .aas-toc-l4 button {
2295
+ font-size: 0.82rem;
2296
+ color: var(--aas-muted);
2297
+ }
2298
+ .aas-deck-toc-panel .aas-toc-l3 button {
2299
+ padding-left: 1.4rem;
2300
+ }
2301
+ .aas-deck-toc-panel .aas-toc-l4 button {
2302
+ padding-left: 2.4rem;
2303
+ }
2304
+ .aas-deck-toc-panel .aas-toc-l3 button.active,
2305
+ .aas-deck-toc-panel .aas-toc-l4 button.active {
2306
+ color: var(--aas-accent);
2307
+ }
2308
+
2309
+ .aas-deck-nav {
2310
+ position: fixed;
2311
+ bottom: 1.1rem;
2312
+ left: 50%;
2313
+ transform: translateX(-50%);
2314
+ z-index: 10;
2315
+ display: inline-flex;
2316
+ align-items: center;
2317
+ gap: 0.35rem;
2318
+ padding: 0.3rem 0.4rem;
2319
+ border-radius: 999px;
2320
+ background: color-mix(in srgb, var(--aas-panel) 85%, transparent);
2321
+ border: 1px solid var(--aas-border);
2322
+ backdrop-filter: blur(6px);
2323
+ box-shadow: var(--aas-shadow);
2324
+ }
2325
+ .aas-deck-btn {
2326
+ display: inline-flex;
2327
+ align-items: center;
2328
+ justify-content: center;
2329
+ width: 2rem;
2330
+ height: 2rem;
2331
+ border: none;
2332
+ border-radius: 999px;
2333
+ background: transparent;
2334
+ color: var(--aas-text);
2335
+ cursor: pointer;
2336
+ }
2337
+ .aas-deck-btn:hover:not(:disabled) {
2338
+ background: var(--aas-tint);
2339
+ }
2340
+ .aas-deck-btn:disabled {
2341
+ opacity: 0.3;
2342
+ cursor: default;
2343
+ }
2344
+ .aas-deck-count {
2345
+ font-size: 0.85rem;
2346
+ color: var(--aas-muted);
2347
+ font-variant-numeric: tabular-nums;
2348
+ min-width: 3.5ch;
2349
+ text-align: center;
2350
+ }