@fasl-work/caos-app-shell 0.2.0 → 0.5.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.
package/styles.css CHANGED
@@ -1,221 +1,303 @@
1
- @import "katex/dist/katex.min.css";
1
+ @import "katex/dist/katex.min.css";
2
+
3
+ /* @fasl-work/caos-app-shell — shared design system (ADR-0016 + visual-standards palette).
4
+ * Theming via CSS custom properties keyed on data-theme on <html> (set pre-paint by an inline
5
+ * script in index.html, kept in sync by the shell theme store). Import once per app:
6
+ * import "@fasl-work/caos-app-shell/styles.css"; */
7
+
8
+ :root,
9
+ [data-theme="dark"] {
10
+ --color-bg: #0d1117;
11
+ --color-surface: #161b22;
12
+ --color-surface-2: #1c2230;
13
+ --color-border: #30363d;
14
+ --color-fg: #c9d1d9;
15
+ --color-fg-subtle: #9aa6b2;
16
+ --color-fg-faint: #6c7785;
17
+ --color-accent: #58a6ff;
18
+ --color-accent-fg: #0d1117;
19
+ --color-accent-soft: #132036;
20
+ --color-accent-2: #3fb1c8;
21
+ --color-magenta: #f778ba;
22
+ --color-good: #3fb950;
23
+ --color-warn: #d29922;
24
+ --color-bad: #f85149;
25
+ --color-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 16px rgba(0, 0, 0, 0.45);
26
+ --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
27
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
28
+ --maxw: 1200px; /* reading width, for PROSE pages */
29
+ --maxw-wide: 100%; /* instrument width, for workbench pages */
30
+ }
31
+
32
+ [data-theme="light"] {
33
+ --color-bg: #f6f8fa;
34
+ --color-surface: #ffffff;
35
+ --color-surface-2: #f0f3f6;
36
+ --color-border: #d0d7de;
37
+ --color-fg: #1f2328;
38
+ --color-fg-subtle: #57606a;
39
+ --color-fg-faint: #6e7781;
40
+ --color-accent: #0969da;
41
+ --color-accent-fg: #ffffff;
42
+ --color-accent-soft: #ddf4ff;
43
+ --color-accent-2: #0a7c8a;
44
+ --color-magenta: #bf3989;
45
+ --color-good: #1a7f37;
46
+ --color-warn: #9a6700;
47
+ --color-bad: #cf222e;
48
+ --color-shadow: 0 1px 2px rgba(31, 35, 40, 0.06), 0 4px 10px rgba(31, 35, 40, 0.04);
49
+ }
50
+
51
+ * { box-sizing: border-box; }
52
+ html, body, #root { height: 100%; }
53
+ body {
54
+ margin: 0;
55
+ font-family: var(--font-sans);
56
+ background-color: var(--color-bg);
57
+ color: var(--color-fg);
58
+ -webkit-font-smoothing: antialiased;
59
+ -moz-osx-font-smoothing: grayscale;
60
+ line-height: 1.55;
61
+ }
62
+ a { color: var(--color-accent); text-decoration: none; }
63
+ a:hover { text-decoration: underline; }
64
+ button { font: inherit; cursor: pointer; color: inherit; }
65
+ h1, h2, h3, h4 { line-height: 1.2; font-weight: 650; letter-spacing: -0.01em; }
66
+ code, kbd { font-family: var(--font-mono); }
67
+
68
+ /* ── Layout primitives ── */
69
+ .app-shell { min-height: 100vh; display: flex; flex-direction: column; background: var(--color-bg); color: var(--color-fg); }
70
+ .container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: 1.5rem; }
71
+ .page { flex: 1 1 auto; width: 100%; }
72
+ .page-body { width: 100%; max-width: var(--maxw); margin-inline: auto; padding: 2.25rem 1.5rem 3rem; }
73
+ /* A WORKBENCH is not prose. Capping an instrument at a 1200px reading measure wastes every pixel beyond
74
+ it, which is why the App surfaces across the line render their visualization as a thumbnail on a wide
75
+ display. Prose pages keep the reading width; the App route opts into the full viewport. */
76
+ .page-body.wide { max-width: var(--maxw-wide); padding-inline: 1.25rem; }
77
+ .muted { color: var(--color-fg-subtle); }
78
+ .faint { color: var(--color-fg-faint); }
79
+ .mono { font-family: var(--font-mono); font-size: 0.9em; }
80
+ .small { font-size: 0.84rem; }
81
+ .card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 10px; padding: 1.25rem 1.5rem; box-shadow: var(--color-shadow); }
82
+ .badge { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.78rem; padding: 0.15rem 0.55rem; border-radius: 999px; border: 1px solid var(--color-border); background: var(--color-surface-2); color: var(--color-fg-subtle); }
83
+ .badge.live { color: var(--color-good); border-color: color-mix(in oklab, var(--color-good) 45%, transparent); }
84
+ .badge.precomputed, .badge.accent { color: var(--color-accent); border-color: color-mix(in oklab, var(--color-accent) 45%, transparent); }
85
+ .badge.warn { color: var(--color-warn); border-color: color-mix(in oklab, var(--color-warn) 45%, transparent); }
86
+
87
+ /* ── Header ── */
88
+ .site-header { position: sticky; top: 0; z-index: 50; border-bottom: 1px solid var(--color-border); background: color-mix(in oklab, var(--color-bg) 82%, transparent); backdrop-filter: blur(8px); }
89
+ .header-inner { max-width: var(--maxw); margin-inline: auto; height: 56px; padding-inline: 1.5rem; display: flex; align-items: center; gap: 1rem; }
90
+ .brand { display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 700; letter-spacing: -0.02em; color: var(--color-fg); }
91
+ .brand:hover { text-decoration: none; }
92
+ .brand .brand-mark { color: var(--color-accent); display: inline-flex; flex: none; }
93
+ .main-nav { display: flex; align-items: center; gap: 0.15rem; }
94
+ .nav-link { padding: 0.4rem 0.7rem; border-radius: 6px; font-size: 0.9rem; color: var(--color-fg-subtle); transition: color 0.15s, background-color 0.15s; }
95
+ .nav-link:hover { text-decoration: none; color: var(--color-fg); }
96
+ .nav-link.active { color: var(--color-accent); background: var(--color-accent-soft); font-weight: 600; }
97
+ .header-actions { margin-left: auto; display: inline-flex; align-items: center; gap: 0.15rem; }
98
+ .icon-btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.3rem; padding: 0.4rem 0.5rem; border-radius: 6px; background: transparent; border: none; color: var(--color-fg-subtle); opacity: 0.85; transition: opacity 0.15s, color 0.15s; }
99
+ .icon-btn:hover { opacity: 1; color: var(--color-fg); text-decoration: none; }
100
+ .icon-btn svg { display: block; flex: none; }
101
+ .lang-code { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600; }
102
+ .header-sep { width: 1px; height: 20px; margin-inline: 0.4rem; background: var(--color-border); }
103
+
104
+ /* ── Footer ── */
105
+ .site-footer { border-top: 1px solid var(--color-border); margin-top: 3rem; background: var(--color-surface); }
106
+ .footer-inner { max-width: var(--maxw); margin-inline: auto; padding: 1.5rem; display: flex; flex-direction: column; gap: 0.9rem; font-size: 0.85rem; color: var(--color-fg-faint); }
107
+ .footer-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; }
108
+ .footer-build { margin-left: auto; font-family: var(--font-mono); font-size: 0.72rem; color: var(--color-fg-faint); display: inline-flex; gap: 0.6rem; flex-wrap: wrap; }
109
+
110
+ /* ── Misc ── */
111
+ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
112
+ :where(a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])):focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 4px; }
113
+ @media (max-width: 760px) { .main-nav { display: none; } }
114
+ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }
115
+
116
+ /* ── Prose / content pages ── (fill the page-body width with grids/figures/sub-tab rails, not a narrow column) */
117
+ .prose { font-size: 0.98rem; }
118
+ .prose section { margin-block: 1.75rem; }
119
+ .measure { max-width: 70ch; }
120
+ .fig-row { display: grid; grid-template-columns: 1.1fr 1fr; gap: 1.5rem; align-items: center; margin-block: 1.5rem; }
121
+ .fig-row.rev { grid-template-columns: 1fr 1.1fr; }
122
+ @media (max-width: 820px) { .fig-row, .fig-row.rev { grid-template-columns: 1fr; } }
123
+ .def-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; margin-block: 1.25rem; }
124
+ .def { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 10px; padding: 0.9rem 1.1rem; }
125
+ .def h4 { margin: 0 0 0.3rem; color: var(--color-accent); font-size: 0.95rem; }
126
+ .def p { margin: 0; font-size: 0.9rem; color: var(--color-fg-subtle); }
127
+ .assume { margin-block: 1rem; padding: 0.85rem 1rem; background: var(--color-surface-2); border: 1px solid var(--color-border); border-radius: 8px; }
128
+ .assume-title { margin: 0 0 0.4rem; font-size: 0.76rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-fg-faint); }
129
+ .assume ul { margin: 0; padding-left: 1.2rem; color: var(--color-fg-subtle); }
130
+ .assume li { margin-block: 0.3rem; font-size: 0.92rem; }
131
+ .references { margin-top: 2.5rem; border-top: 1px solid var(--color-border); padding-top: 1rem; }
132
+ .reference-list { font-size: 0.85rem; color: var(--color-fg-subtle); line-height: 1.5; padding-left: 1.4rem; }
133
+ .reference-list li { margin-block: 0.5rem; }
134
+ .cite-inline { font-style: normal; font-size: 0.92em; color: var(--color-fg-subtle); }
135
+ .th-refs { margin-top: 1.25rem; padding-top: 0.6rem; border-top: 1px dashed var(--color-border); font-size: 0.85rem; color: var(--color-fg-subtle); }
136
+ .th-refs-label { font-weight: 650; color: var(--color-fg-faint); }
137
+ .prose h2 { font-size: 1.3rem; margin-block: 1.4rem 0.6rem; }
138
+ .prose h3 { font-size: 1.06rem; margin-block: 1.1rem 0.4rem; }
139
+ .prose p { margin-block: 0.6rem; }
140
+ .prose ul, .prose ol { margin-block: 0.5rem; padding-left: 1.3rem; }
141
+ .prose li { margin-block: 0.35rem; }
142
+ .page-head { border-bottom: 1px solid var(--color-border); padding-bottom: 1rem; margin-bottom: 1.25rem; }
143
+ .page-head h1 { font-size: 1.7rem; margin: 0 0 0.4rem; }
144
+ .lede { color: var(--color-fg-subtle); font-size: 1.08rem; margin: 0; max-width: 70ch; }
145
+ .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; margin-block: 1.5rem; }
146
+ @media (max-width: 760px) { .two-col { grid-template-columns: 1fr; } }
147
+ .tick-list, .cross-list { list-style: none; padding-left: 0; margin: 0.4rem 0 0; }
148
+ .tick-list li, .cross-list li { position: relative; padding-left: 1.5rem; margin-block: 0.5rem; }
149
+ .tick-list li::before { content: "✓"; position: absolute; left: 0; color: var(--color-good); font-weight: 700; }
150
+ .cross-list li::before { content: "✕"; position: absolute; left: 0; color: var(--color-fg-faint); font-weight: 700; }
151
+
152
+ /* ── Callouts ── */
153
+ .callout { margin-block: 1rem; padding: 0.8rem 1.1rem; border-left: 3px solid var(--color-border); background: var(--color-surface-2); border-radius: 0 8px 8px 0; }
154
+ .callout-honest { border-left-color: var(--color-warn); }
155
+ .callout-strong { border-left-color: var(--color-accent); background: var(--color-accent-soft); }
156
+ .callout-note { border-left-color: var(--color-fg-faint); }
157
+ .callout-title { font-weight: 650; margin: 0 0 0.4rem; color: var(--color-fg); }
158
+ .callout-body { color: var(--color-fg-subtle); }
159
+ .callout-body p:first-child { margin-top: 0; }
160
+ .callout-body p:last-child { margin-bottom: 0; }
161
+
162
+ /* ── Tabs ── */
163
+ .tablist { display: flex; gap: 0.35rem; padding-bottom: 0.6rem; margin-bottom: 1.5rem; border-bottom: 1px solid var(--color-border); flex-wrap: nowrap; }
164
+ /* SINGLE-LINE TAB BAR.
165
+ `flex-wrap: wrap` lets a 14-to-18 tab bar spill onto a second and third row, and every extra row is
166
+ vertical space taken from the instrument below it, permanently, on every render. A tab bar is
167
+ navigation chrome: it gets ONE row and scrolls horizontally when it does not fit.
168
+ Scrollbar is hidden but the row stays scrollable by wheel, trackpad, drag and keyboard. */
169
+ .tablist { flex-wrap: nowrap !important; overflow-x: auto; overflow-y: hidden;
170
+ scrollbar-width: none; -ms-overflow-style: none; padding-bottom: 0.4rem; margin-bottom: 0.9rem;
171
+ scroll-behavior: smooth; }
172
+ .tablist::-webkit-scrollbar { display: none; }
173
+ .tablist .tab, .tablist button { flex: 0 0 auto; white-space: nowrap; }
174
+
175
+ .tab { padding: 0.5rem 1rem; border: 1px solid transparent; border-radius: 8px; background: transparent; color: var(--color-fg-subtle); font-size: 0.92rem; font-weight: 550; transition: color 0.15s, background-color 0.15s, border-color 0.15s; }
176
+ .tab:hover { color: var(--color-fg); background: var(--color-surface-2); }
177
+ .tab.active { color: var(--color-accent); background: var(--color-accent-soft); border-color: color-mix(in oklab, var(--color-accent) 35%, transparent); font-weight: 650; }
178
+ .tabpanel:focus, .subtabpanel:focus { outline: none; }
179
+ .subtabs { margin-block: 1rem; }
180
+ .subtablist { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
181
+ .subtab { padding: 0.32rem 0.8rem; border: 1px solid var(--color-border); border-radius: 999px; background: var(--color-surface); color: var(--color-fg-subtle); font-size: 0.84rem; font-weight: 500; transition: color 0.15s, background-color 0.15s, border-color 0.15s; }
182
+ .subtab:hover { color: var(--color-fg); border-color: var(--color-fg-faint); }
183
+ .subtab.active { color: var(--color-accent); background: var(--color-accent-soft); border-color: color-mix(in oklab, var(--color-accent) 40%, transparent); font-weight: 600; }
184
+ .subtabs-vertical { display: grid; grid-template-columns: minmax(180px, 240px) 1fr; gap: 1.5rem; align-items: start; }
185
+ .subtabs-vertical .subtablist { flex-direction: column; margin-bottom: 0; }
186
+ .subtabs-vertical .subtab { text-align: left; border-radius: 8px; }
187
+ @media (max-width: 760px) { .subtabs-vertical { grid-template-columns: 1fr; } .subtabs-vertical .subtablist { flex-direction: row; } }
188
+
189
+ /* ── Equations / figures ── */
190
+ .equation { margin: 1rem 0; padding: 0.9rem 1rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 8px; overflow-x: auto; }
191
+ .equation-caption { margin-top: 0.5rem; font-size: 0.82rem; color: var(--color-fg-faint); text-align: center; }
192
+ .katex { color: var(--color-fg); }
193
+ .figure { margin: 1.5rem 0; }
194
+ .figure-caption, .fig-cap { text-align: center; font-size: 0.84rem; color: var(--color-fg-faint); margin-top: 0.6rem; font-style: italic; }
195
+
196
+ /* ── Theme-aware inline SVG figures (teaching diagrams) ── */
197
+ .fig-svg { width: 100%; max-width: 560px; height: auto; display: block; margin-inline: auto; }
198
+ .fig-svg.wide { max-width: 760px; }
199
+ .dg-axis { stroke: var(--color-fg-faint); stroke-width: 1.3; }
200
+ .dg-axis-label { fill: var(--color-fg-subtle); font: 12px var(--font-sans); }
201
+ .dg-tick { fill: var(--color-fg-faint); font: 11px var(--font-mono); }
202
+ .dg-grid { stroke: var(--color-border); stroke-width: 0.5; opacity: 0.6; }
203
+ .dg-curve { fill: none; stroke: var(--color-accent); stroke-width: 2.2; stroke-linejoin: round; }
204
+ .dg-curve-2 { fill: none; stroke: var(--color-magenta); stroke-width: 2; }
205
+ .dg-curve-faint { fill: none; stroke: var(--color-fg-faint); stroke-width: 1.4; stroke-dasharray: 4 3; }
206
+ .dg-asymptote { stroke: var(--color-bad); stroke-width: 1.4; stroke-dasharray: 4 4; }
207
+ .dg-marker { stroke: var(--color-warn); stroke-width: 1.2; stroke-dasharray: 3 3; }
208
+ .dg-marker-label { fill: var(--color-warn); font: 600 11px var(--font-mono); }
209
+ .dg-note { fill: var(--color-fg-faint); font: italic 11px var(--font-sans); }
210
+ .dg-fill-accent { fill: color-mix(in oklab, var(--color-accent) 16%, transparent); stroke: var(--color-accent); stroke-width: 1; }
211
+ .dg-fill-warn { fill: color-mix(in oklab, var(--color-warn) 16%, transparent); stroke: var(--color-warn); stroke-width: 1; }
212
+ .dg-box { fill: var(--color-surface); stroke: var(--color-border); stroke-width: 1.3; }
213
+ .dg-box.accent { stroke: var(--color-accent); fill: var(--color-accent-soft); }
214
+ .dg-box.good { stroke: var(--color-good); }
215
+ .dg-box-title { fill: var(--color-fg); font: 650 12px var(--font-sans); }
216
+ .dg-box-title.accent { fill: var(--color-accent); }
217
+ .dg-box-sub { fill: var(--color-fg-subtle); font: 10.5px var(--font-mono); }
218
+ .dg-node { fill: var(--color-surface); stroke: var(--color-accent); stroke-width: 1.4; }
219
+ .dg-node-label { fill: var(--color-fg); font: 600 12px var(--font-mono); }
220
+ .dg-edge { stroke: var(--color-fg-faint); stroke-width: 1.4; fill: none; }
221
+ .dg-edge-label { fill: var(--color-fg-subtle); font: 11px var(--font-mono); }
222
+ .dg-arrowhead { fill: var(--color-fg-faint); }
223
+ .dg-bar { fill: var(--color-accent); }
224
+ .dg-bar-2 { fill: var(--color-magenta); }
225
+ .dg-bar-label { fill: var(--color-fg-subtle); font: 11px var(--font-sans); }
226
+
227
+ /* ── Generic controls ── */
228
+ .btn { border: 1px solid var(--color-border); background: var(--color-surface-2); color: var(--color-fg); padding: 0.4rem 0.9rem; border-radius: 8px; font-weight: 600; font-size: 0.88rem; display: inline-flex; align-items: center; gap: 0.35rem; }
229
+ .btn:hover { border-color: var(--color-fg-faint); }
230
+ .btn.primary { background: var(--color-accent); color: var(--color-accent-fg); border-color: var(--color-accent); }
231
+ .btn:disabled { opacity: 0.5; cursor: default; }
232
+ .select { background: var(--color-surface-2); color: var(--color-fg); border: 1px solid var(--color-border); border-radius: 8px; padding: 0.35rem 0.5rem; font-size: 0.85rem; }
233
+ .hint { font-size: 0.8rem; color: var(--color-fg-faint); margin: 0; }
234
+ .banner { padding: 0.6rem 0.9rem; border-radius: 8px; font-size: 0.85rem; margin-block: 0.6rem; border-left: 3px solid var(--color-border); background: var(--color-surface-2); }
235
+ .banner.warn { border-left-color: var(--color-warn); color: var(--color-warn); }
236
+ .banner.error { border-left-color: var(--color-bad); color: var(--color-bad); }
237
+ .range { width: 100%; accent-color: var(--color-accent); }
238
+
239
+ /* ── CaseSelector v2 (shared source + case picker) ── */
240
+ .cs { display: flex; flex-direction: column; gap: 0.7rem; }
241
+ .cs-source { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
242
+ .cs-source-label { font-size: 0.78rem; font-weight: 600; color: var(--color-fg-subtle); text-transform: uppercase; letter-spacing: 0.03em; }
243
+ .cs-source-tabs { display: inline-flex; gap: 0.35rem; flex-wrap: wrap; }
244
+ .cs-locked { margin: 0; border-left-color: var(--color-accent); }
245
+ .cs-group { display: flex; flex-direction: column; gap: 0.35rem; }
246
+ .cs-group-label { font-size: 0.76rem; font-weight: 600; color: var(--color-fg-subtle); text-transform: uppercase; letter-spacing: 0.03em; }
247
+ .cs-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
248
+ .cs-chip { display: inline-flex; align-items: baseline; gap: 0.4rem; padding: 0.32rem 0.6rem; border: 1px solid var(--color-border); border-radius: 999px; background: var(--color-surface); color: var(--color-fg-subtle); font-size: 0.84rem; font-weight: 500; transition: color 0.15s, background-color 0.15s, border-color 0.15s; }
249
+ .cs-chip:hover:not(:disabled) { color: var(--color-fg); background: var(--color-surface-2); }
250
+ .cs-chip.on { color: var(--color-accent); background: var(--color-accent-soft); border-color: color-mix(in oklab, var(--color-accent) 40%, transparent); font-weight: 600; }
251
+ .cs-chip:disabled { opacity: 0.45; cursor: default; }
252
+ .cs-chip-id { font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.76rem; font-weight: 600; opacity: 0.85; }
253
+ .cs-chip-name { }
254
+ .cs-kind { font-size: 0.62rem; font-weight: 700; line-height: 1; padding: 0.12rem 0.28rem; border-radius: 4px; border: 1px solid var(--color-border); color: var(--color-fg-faint); background: var(--color-surface-2); align-self: center; }
255
+ .cs-kind-real { color: var(--color-accent-2); border-color: color-mix(in oklab, var(--color-accent-2) 40%, transparent); }
256
+ .cs-kind-uploaded { color: var(--color-warn); border-color: color-mix(in oklab, var(--color-warn) 40%, transparent); }
257
+ .cs-modified { display: flex; align-items: center; gap: 0.5rem; }
258
+ .cs-reset { border: none; background: transparent; color: var(--color-accent); font-size: 0.8rem; font-weight: 600; padding: 0.1rem 0.2rem; text-decoration: underline; text-underline-offset: 2px; }
2
259
 
3
- /* @fasl-work/caos-app-shell — shared design system (ADR-0016 + visual-standards palette).
4
- * Theming via CSS custom properties keyed on data-theme on <html> (set pre-paint by an inline
5
- * script in index.html, kept in sync by the shell theme store). Import once per app:
6
- * import "@fasl-work/caos-app-shell/styles.css"; */
260
+ /* Authenticated workbench frame. Product styles may refine tokens and controls, but the package owns
261
+ the responsive navigation/layout contract and keeps route/content landmarks consistent. */
262
+ .caos-workbench-shell { min-height: 100vh; display: grid; grid-template-columns: 238px minmax(0, 1fr); }
263
+ .caos-workbench-sidebar { position: fixed; inset: 0 auto 0 0; z-index: 20; width: 238px; display: flex; flex-direction: column; padding: 1.25rem 0.875rem; border-right: 1px solid var(--color-border); background: var(--color-surface); }
264
+ .caos-workbench-sidebar nav { display: grid; gap: 0.25rem; }
265
+ .caos-workbench-sidebar nav a { display: flex; align-items: center; gap: 0.75rem; padding: 0.625rem 0.7rem; border: 1px solid transparent; border-radius: 0.7rem; color: var(--color-fg-subtle); font-size: 0.9rem; font-weight: 650; }
266
+ .caos-workbench-sidebar nav a:hover { color: var(--color-fg); background: var(--color-surface-2); text-decoration: none; }
267
+ .caos-workbench-sidebar nav a.active { color: var(--color-accent); border-color: color-mix(in oklab, var(--color-accent) 24%, transparent); background: var(--color-accent-soft); }
268
+ .caos-workbench-main { grid-column: 2; min-width: 0; }
269
+ .caos-workbench-header { position: sticky; top: 0; z-index: 15; min-height: 64px; display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0 2rem; border-bottom: 1px solid var(--color-border); background: color-mix(in oklab, var(--color-bg) 88%, transparent); backdrop-filter: blur(12px); }
270
+ .caos-workbench-content { width: min(1540px, 100%); margin: 0 auto; padding: 2.25rem 2rem 4rem; }
7
271
 
8
- :root,
9
- [data-theme="dark"] {
10
- --color-bg: #0d1117;
11
- --color-surface: #161b22;
12
- --color-surface-2: #1c2230;
13
- --color-border: #30363d;
14
- --color-fg: #c9d1d9;
15
- --color-fg-subtle: #9aa6b2;
16
- --color-fg-faint: #6c7785;
17
- --color-accent: #58a6ff;
18
- --color-accent-fg: #0d1117;
19
- --color-accent-soft: #132036;
20
- --color-accent-2: #3fb1c8;
21
- --color-magenta: #f778ba;
22
- --color-good: #3fb950;
23
- --color-warn: #d29922;
24
- --color-bad: #f85149;
25
- --color-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 16px rgba(0, 0, 0, 0.45);
26
- --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
27
- --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
28
- --maxw: 1200px;
272
+ @media (max-width: 760px) {
273
+ .caos-workbench-shell { display: block; padding-bottom: 66px; }
274
+ .caos-workbench-sidebar { inset: auto 0 0; width: auto; height: 62px; display: block; border: 0; border-top: 1px solid var(--color-border); padding: 5px 8px; }
275
+ .caos-workbench-sidebar > :not(nav) { display: none; }
276
+ .caos-workbench-sidebar nav { grid-template-columns: repeat(var(--caos-workbench-route-count, 5), minmax(0, 1fr)); }
277
+ .caos-workbench-sidebar nav a { display: grid; justify-items: center; gap: 2px; overflow: hidden; padding: 5px; font-size: 0.7rem; text-overflow: ellipsis; white-space: nowrap; }
278
+ .caos-workbench-main { min-width: 0; }
279
+ .caos-workbench-header { min-height: 58px; padding: 0 1rem; }
280
+ .caos-workbench-content { padding: 1.5rem 1rem 2.5rem; }
29
281
  }
30
-
31
- [data-theme="light"] {
32
- --color-bg: #f6f8fa;
33
- --color-surface: #ffffff;
34
- --color-surface-2: #f0f3f6;
35
- --color-border: #d0d7de;
36
- --color-fg: #1f2328;
37
- --color-fg-subtle: #57606a;
38
- --color-fg-faint: #6e7781;
39
- --color-accent: #0969da;
40
- --color-accent-fg: #ffffff;
41
- --color-accent-soft: #ddf4ff;
42
- --color-accent-2: #0a7c8a;
43
- --color-magenta: #bf3989;
44
- --color-good: #1a7f37;
45
- --color-warn: #9a6700;
46
- --color-bad: #cf222e;
47
- --color-shadow: 0 1px 2px rgba(31, 35, 40, 0.06), 0 4px 10px rgba(31, 35, 40, 0.04);
48
- }
49
-
50
- * { box-sizing: border-box; }
51
- html, body, #root { height: 100%; }
52
- body {
53
- margin: 0;
54
- font-family: var(--font-sans);
55
- background-color: var(--color-bg);
56
- color: var(--color-fg);
57
- -webkit-font-smoothing: antialiased;
58
- -moz-osx-font-smoothing: grayscale;
59
- line-height: 1.55;
60
- }
61
- a { color: var(--color-accent); text-decoration: none; }
62
- a:hover { text-decoration: underline; }
63
- button { font: inherit; cursor: pointer; color: inherit; }
64
- h1, h2, h3, h4 { line-height: 1.2; font-weight: 650; letter-spacing: -0.01em; }
65
- code, kbd { font-family: var(--font-mono); }
66
-
67
- /* ── Layout primitives ── */
68
- .app-shell { min-height: 100vh; display: flex; flex-direction: column; background: var(--color-bg); color: var(--color-fg); }
69
- .container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: 1.5rem; }
70
- .page { flex: 1 1 auto; width: 100%; }
71
- .page-body { width: 100%; max-width: var(--maxw); margin-inline: auto; padding: 2.25rem 1.5rem 3rem; }
72
- .muted { color: var(--color-fg-subtle); }
73
- .faint { color: var(--color-fg-faint); }
74
- .mono { font-family: var(--font-mono); font-size: 0.9em; }
75
- .small { font-size: 0.84rem; }
76
- .card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 10px; padding: 1.25rem 1.5rem; box-shadow: var(--color-shadow); }
77
- .badge { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.78rem; padding: 0.15rem 0.55rem; border-radius: 999px; border: 1px solid var(--color-border); background: var(--color-surface-2); color: var(--color-fg-subtle); }
78
- .badge.live { color: var(--color-good); border-color: color-mix(in oklab, var(--color-good) 45%, transparent); }
79
- .badge.precomputed, .badge.accent { color: var(--color-accent); border-color: color-mix(in oklab, var(--color-accent) 45%, transparent); }
80
- .badge.warn { color: var(--color-warn); border-color: color-mix(in oklab, var(--color-warn) 45%, transparent); }
81
-
82
- /* ── Header ── */
83
- .site-header { position: sticky; top: 0; z-index: 50; border-bottom: 1px solid var(--color-border); background: color-mix(in oklab, var(--color-bg) 82%, transparent); backdrop-filter: blur(8px); }
84
- .header-inner { max-width: var(--maxw); margin-inline: auto; height: 56px; padding-inline: 1.5rem; display: flex; align-items: center; gap: 1rem; }
85
- .brand { display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 700; letter-spacing: -0.02em; color: var(--color-fg); }
86
- .brand:hover { text-decoration: none; }
87
- .brand .brand-mark { color: var(--color-accent); display: inline-flex; flex: none; }
88
- .main-nav { display: flex; align-items: center; gap: 0.15rem; }
89
- .nav-link { padding: 0.4rem 0.7rem; border-radius: 6px; font-size: 0.9rem; color: var(--color-fg-subtle); transition: color 0.15s, background-color 0.15s; }
90
- .nav-link:hover { text-decoration: none; color: var(--color-fg); }
91
- .nav-link.active { color: var(--color-accent); background: var(--color-accent-soft); font-weight: 600; }
92
- .header-actions { margin-left: auto; display: inline-flex; align-items: center; gap: 0.15rem; }
93
- .icon-btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.3rem; padding: 0.4rem 0.5rem; border-radius: 6px; background: transparent; border: none; color: var(--color-fg-subtle); opacity: 0.85; transition: opacity 0.15s, color 0.15s; }
94
- .icon-btn:hover { opacity: 1; color: var(--color-fg); text-decoration: none; }
95
- .icon-btn svg { display: block; flex: none; }
96
- .lang-code { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600; }
97
- .header-sep { width: 1px; height: 20px; margin-inline: 0.4rem; background: var(--color-border); }
98
-
99
- /* ── Footer ── */
100
- .site-footer { border-top: 1px solid var(--color-border); margin-top: 3rem; background: var(--color-surface); }
101
- .footer-inner { max-width: var(--maxw); margin-inline: auto; padding: 1.5rem; display: flex; flex-direction: column; gap: 0.9rem; font-size: 0.85rem; color: var(--color-fg-faint); }
102
- .footer-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; }
103
- .footer-build { margin-left: auto; font-family: var(--font-mono); font-size: 0.72rem; color: var(--color-fg-faint); display: inline-flex; gap: 0.6rem; flex-wrap: wrap; }
104
-
105
- /* ── Misc ── */
106
- .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
107
- :where(a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])):focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 4px; }
108
- @media (max-width: 760px) { .main-nav { display: none; } }
109
- @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }
110
-
111
- /* ── Prose / content pages ── (fill the page-body width with grids/figures/sub-tab rails, not a narrow column) */
112
- .prose { font-size: 0.98rem; }
113
- .prose section { margin-block: 1.75rem; }
114
- .measure { max-width: 70ch; }
115
- .fig-row { display: grid; grid-template-columns: 1.1fr 1fr; gap: 1.5rem; align-items: center; margin-block: 1.5rem; }
116
- .fig-row.rev { grid-template-columns: 1fr 1.1fr; }
117
- @media (max-width: 820px) { .fig-row, .fig-row.rev { grid-template-columns: 1fr; } }
118
- .def-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; margin-block: 1.25rem; }
119
- .def { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 10px; padding: 0.9rem 1.1rem; }
120
- .def h4 { margin: 0 0 0.3rem; color: var(--color-accent); font-size: 0.95rem; }
121
- .def p { margin: 0; font-size: 0.9rem; color: var(--color-fg-subtle); }
122
- .assume { margin-block: 1rem; padding: 0.85rem 1rem; background: var(--color-surface-2); border: 1px solid var(--color-border); border-radius: 8px; }
123
- .assume-title { margin: 0 0 0.4rem; font-size: 0.76rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-fg-faint); }
124
- .assume ul { margin: 0; padding-left: 1.2rem; color: var(--color-fg-subtle); }
125
- .assume li { margin-block: 0.3rem; font-size: 0.92rem; }
126
- .references { margin-top: 2.5rem; border-top: 1px solid var(--color-border); padding-top: 1rem; }
127
- .reference-list { font-size: 0.85rem; color: var(--color-fg-subtle); line-height: 1.5; padding-left: 1.4rem; }
128
- .reference-list li { margin-block: 0.5rem; }
129
- .cite-inline { font-style: normal; font-size: 0.92em; color: var(--color-fg-subtle); }
130
- .th-refs { margin-top: 1.25rem; padding-top: 0.6rem; border-top: 1px dashed var(--color-border); font-size: 0.85rem; color: var(--color-fg-subtle); }
131
- .th-refs-label { font-weight: 650; color: var(--color-fg-faint); }
132
- .prose h2 { font-size: 1.3rem; margin-block: 1.4rem 0.6rem; }
133
- .prose h3 { font-size: 1.06rem; margin-block: 1.1rem 0.4rem; }
134
- .prose p { margin-block: 0.6rem; }
135
- .prose ul, .prose ol { margin-block: 0.5rem; padding-left: 1.3rem; }
136
- .prose li { margin-block: 0.35rem; }
137
- .page-head { border-bottom: 1px solid var(--color-border); padding-bottom: 1rem; margin-bottom: 1.25rem; }
138
- .page-head h1 { font-size: 1.7rem; margin: 0 0 0.4rem; }
139
- .lede { color: var(--color-fg-subtle); font-size: 1.08rem; margin: 0; max-width: 70ch; }
140
- .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; margin-block: 1.5rem; }
141
- @media (max-width: 760px) { .two-col { grid-template-columns: 1fr; } }
142
- .tick-list, .cross-list { list-style: none; padding-left: 0; margin: 0.4rem 0 0; }
143
- .tick-list li, .cross-list li { position: relative; padding-left: 1.5rem; margin-block: 0.5rem; }
144
- .tick-list li::before { content: "✓"; position: absolute; left: 0; color: var(--color-good); font-weight: 700; }
145
- .cross-list li::before { content: "✕"; position: absolute; left: 0; color: var(--color-fg-faint); font-weight: 700; }
146
-
147
- /* ── Callouts ── */
148
- .callout { margin-block: 1rem; padding: 0.8rem 1.1rem; border-left: 3px solid var(--color-border); background: var(--color-surface-2); border-radius: 0 8px 8px 0; }
149
- .callout-honest { border-left-color: var(--color-warn); }
150
- .callout-strong { border-left-color: var(--color-accent); background: var(--color-accent-soft); }
151
- .callout-note { border-left-color: var(--color-fg-faint); }
152
- .callout-title { font-weight: 650; margin: 0 0 0.4rem; color: var(--color-fg); }
153
- .callout-body { color: var(--color-fg-subtle); }
154
- .callout-body p:first-child { margin-top: 0; }
155
- .callout-body p:last-child { margin-bottom: 0; }
156
-
157
- /* ── Tabs ── */
158
- .tablist { display: flex; gap: 0.35rem; padding-bottom: 0.6rem; margin-bottom: 1.5rem; border-bottom: 1px solid var(--color-border); flex-wrap: wrap; }
159
- .tab { padding: 0.5rem 1rem; border: 1px solid transparent; border-radius: 8px; background: transparent; color: var(--color-fg-subtle); font-size: 0.92rem; font-weight: 550; transition: color 0.15s, background-color 0.15s, border-color 0.15s; }
160
- .tab:hover { color: var(--color-fg); background: var(--color-surface-2); }
161
- .tab.active { color: var(--color-accent); background: var(--color-accent-soft); border-color: color-mix(in oklab, var(--color-accent) 35%, transparent); font-weight: 650; }
162
- .tabpanel:focus, .subtabpanel:focus { outline: none; }
163
- .subtabs { margin-block: 1rem; }
164
- .subtablist { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
165
- .subtab { padding: 0.32rem 0.8rem; border: 1px solid var(--color-border); border-radius: 999px; background: var(--color-surface); color: var(--color-fg-subtle); font-size: 0.84rem; font-weight: 500; transition: color 0.15s, background-color 0.15s, border-color 0.15s; }
166
- .subtab:hover { color: var(--color-fg); border-color: var(--color-fg-faint); }
167
- .subtab.active { color: var(--color-accent); background: var(--color-accent-soft); border-color: color-mix(in oklab, var(--color-accent) 40%, transparent); font-weight: 600; }
168
- .subtabs-vertical { display: grid; grid-template-columns: minmax(180px, 240px) 1fr; gap: 1.5rem; align-items: start; }
169
- .subtabs-vertical .subtablist { flex-direction: column; margin-bottom: 0; }
170
- .subtabs-vertical .subtab { text-align: left; border-radius: 8px; }
171
- @media (max-width: 760px) { .subtabs-vertical { grid-template-columns: 1fr; } .subtabs-vertical .subtablist { flex-direction: row; } }
172
-
173
- /* ── Equations / figures ── */
174
- .equation { margin: 1rem 0; padding: 0.9rem 1rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 8px; overflow-x: auto; }
175
- .equation-caption { margin-top: 0.5rem; font-size: 0.82rem; color: var(--color-fg-faint); text-align: center; }
176
- .katex { color: var(--color-fg); }
177
- .figure { margin: 1.5rem 0; }
178
- .figure-caption, .fig-cap { text-align: center; font-size: 0.84rem; color: var(--color-fg-faint); margin-top: 0.6rem; font-style: italic; }
179
-
180
- /* ── Theme-aware inline SVG figures (teaching diagrams) ── */
181
- .fig-svg { width: 100%; max-width: 560px; height: auto; display: block; margin-inline: auto; }
182
- .fig-svg.wide { max-width: 760px; }
183
- .dg-axis { stroke: var(--color-fg-faint); stroke-width: 1.3; }
184
- .dg-axis-label { fill: var(--color-fg-subtle); font: 12px var(--font-sans); }
185
- .dg-tick { fill: var(--color-fg-faint); font: 11px var(--font-mono); }
186
- .dg-grid { stroke: var(--color-border); stroke-width: 0.5; opacity: 0.6; }
187
- .dg-curve { fill: none; stroke: var(--color-accent); stroke-width: 2.2; stroke-linejoin: round; }
188
- .dg-curve-2 { fill: none; stroke: var(--color-magenta); stroke-width: 2; }
189
- .dg-curve-faint { fill: none; stroke: var(--color-fg-faint); stroke-width: 1.4; stroke-dasharray: 4 3; }
190
- .dg-asymptote { stroke: var(--color-bad); stroke-width: 1.4; stroke-dasharray: 4 4; }
191
- .dg-marker { stroke: var(--color-warn); stroke-width: 1.2; stroke-dasharray: 3 3; }
192
- .dg-marker-label { fill: var(--color-warn); font: 600 11px var(--font-mono); }
193
- .dg-note { fill: var(--color-fg-faint); font: italic 11px var(--font-sans); }
194
- .dg-fill-accent { fill: color-mix(in oklab, var(--color-accent) 16%, transparent); stroke: var(--color-accent); stroke-width: 1; }
195
- .dg-fill-warn { fill: color-mix(in oklab, var(--color-warn) 16%, transparent); stroke: var(--color-warn); stroke-width: 1; }
196
- .dg-box { fill: var(--color-surface); stroke: var(--color-border); stroke-width: 1.3; }
197
- .dg-box.accent { stroke: var(--color-accent); fill: var(--color-accent-soft); }
198
- .dg-box.good { stroke: var(--color-good); }
199
- .dg-box-title { fill: var(--color-fg); font: 650 12px var(--font-sans); }
200
- .dg-box-title.accent { fill: var(--color-accent); }
201
- .dg-box-sub { fill: var(--color-fg-subtle); font: 10.5px var(--font-mono); }
202
- .dg-node { fill: var(--color-surface); stroke: var(--color-accent); stroke-width: 1.4; }
203
- .dg-node-label { fill: var(--color-fg); font: 600 12px var(--font-mono); }
204
- .dg-edge { stroke: var(--color-fg-faint); stroke-width: 1.4; fill: none; }
205
- .dg-edge-label { fill: var(--color-fg-subtle); font: 11px var(--font-mono); }
206
- .dg-arrowhead { fill: var(--color-fg-faint); }
207
- .dg-bar { fill: var(--color-accent); }
208
- .dg-bar-2 { fill: var(--color-magenta); }
209
- .dg-bar-label { fill: var(--color-fg-subtle); font: 11px var(--font-sans); }
210
-
211
- /* ── Generic controls ── */
212
- .btn { border: 1px solid var(--color-border); background: var(--color-surface-2); color: var(--color-fg); padding: 0.4rem 0.9rem; border-radius: 8px; font-weight: 600; font-size: 0.88rem; display: inline-flex; align-items: center; gap: 0.35rem; }
213
- .btn:hover { border-color: var(--color-fg-faint); }
214
- .btn.primary { background: var(--color-accent); color: var(--color-accent-fg); border-color: var(--color-accent); }
215
- .btn:disabled { opacity: 0.5; cursor: default; }
216
- .select { background: var(--color-surface-2); color: var(--color-fg); border: 1px solid var(--color-border); border-radius: 8px; padding: 0.35rem 0.5rem; font-size: 0.85rem; }
217
- .hint { font-size: 0.8rem; color: var(--color-fg-faint); margin: 0; }
218
- .banner { padding: 0.6rem 0.9rem; border-radius: 8px; font-size: 0.85rem; margin-block: 0.6rem; border-left: 3px solid var(--color-border); background: var(--color-surface-2); }
219
- .banner.warn { border-left-color: var(--color-warn); color: var(--color-warn); }
220
- .banner.error { border-left-color: var(--color-bad); color: var(--color-bad); }
221
- .range { width: 100%; accent-color: var(--color-accent); }
282
+
283
+ /* CONTAINED LAYOUT. A `nowrap` tab bar sizes its container to its intrinsic width unless every
284
+ ancestor is allowed to shrink: a flex/grid item defaults to `min-width: auto`. Without this, making
285
+ the tab bar a single scrollable row pushes the whole page wider than the viewport and the user has to
286
+ drag sideways to reach the right edge. Measured on ChargeCascade: scrollWidth 1817 on a 1600 viewport. */
287
+ html, body { overflow-x: hidden; }
288
+ .page-body { max-width: 100%; }
289
+ .tabs, .tabpanel { min-width: 0; max-width: 100%; }
290
+ .tablist { min-width: 0; max-width: 100%; width: 100%; }
291
+
292
+ /* NO MAGIC CHROME NUMBER. An app surface must not compute its height from a hardcoded guess at the
293
+ header and footer: the guess drifts the moment either changes, leaving either a dead gap above the
294
+ footer or a few pixels of scroll. Measured on ChargeCascade: guess 150px, real 175px (header 57 +
295
+ footer 70 + 48px body padding), so 25px of scroll and a 48px gap. Apps that fill the viewport add
296
+ `.app-shell.fixed` and let flex distribute the height. */
297
+ .app-shell.fixed { height: 100dvh; min-height: 100dvh; overflow: hidden; }
298
+ .app-shell.fixed > .page { flex: 1 1 auto; min-height: 0; display: flex; }
299
+ .app-shell.fixed .page-body.wide { flex: 1 1 auto; min-height: 0; padding-top: 1rem; padding-bottom: 1rem; }
300
+
301
+ /* A viewport-filling app must not carry the prose footer margin: it becomes dead space between the
302
+ content and the chrome. Measured on ChargeCascade as exactly 48px (3rem). */
303
+ .app-shell.fixed .site-footer { margin-top: 0; }