sonorance 0.1.0-beta.4.1

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 (43) hide show
  1. package/LICENSE +174 -0
  2. package/README.md +103 -0
  3. package/build/icon.png +0 -0
  4. package/package.json +86 -0
  5. package/skill/SKILL.md +41 -0
  6. package/skill/scripts/sonorance.mjs +31 -0
  7. package/src/azure-monitor.mjs +221 -0
  8. package/src/cli.mjs +315 -0
  9. package/src/comment-client.mjs +75 -0
  10. package/src/engine-default.mjs +136 -0
  11. package/src/feedback.mjs +151 -0
  12. package/src/gitignore.mjs +27 -0
  13. package/src/grammar.mjs +55 -0
  14. package/src/identity.mjs +126 -0
  15. package/src/otlp.mjs +166 -0
  16. package/src/plugins/deliberate/contribute.mjs +28 -0
  17. package/src/plugins/deliberate/domain.mjs +40 -0
  18. package/src/plugins/deliberate/frontmatter.mjs +85 -0
  19. package/src/plugins/deliberate/gitignore.mjs +21 -0
  20. package/src/plugins/deliberate/kinds.mjs +44 -0
  21. package/src/plugins/deliberate/markdown.mjs +42 -0
  22. package/src/plugins/deliberate/paths.mjs +245 -0
  23. package/src/plugins/deliberate/stages.mjs +27 -0
  24. package/src/plugins/deliberate/vault.mjs +1043 -0
  25. package/src/plugins.mjs +91 -0
  26. package/src/release-config.mjs +2 -0
  27. package/src/scrubber.mjs +64 -0
  28. package/src/server/index.mjs +993 -0
  29. package/src/sources.mjs +80 -0
  30. package/src/telemetry-schema.mjs +187 -0
  31. package/src/telemetry.mjs +390 -0
  32. package/src/ui/active-line.mjs +42 -0
  33. package/src/ui/app.js +3553 -0
  34. package/src/ui/at-mention.mjs +67 -0
  35. package/src/ui/comments-plugin.mjs +107 -0
  36. package/src/ui/diff-plugin.mjs +73 -0
  37. package/src/ui/editor.mjs +210 -0
  38. package/src/ui/index.html +1723 -0
  39. package/src/ui/md.mjs +233 -0
  40. package/src/ui/paste-md.mjs +54 -0
  41. package/src/ui/shell.html +1374 -0
  42. package/src/ui/slash.mjs +122 -0
  43. package/src/vault-registry.mjs +150 -0
@@ -0,0 +1,1374 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
6
+ <meta name="theme-color" content="#0a0e14">
7
+ <meta name="color-scheme" content="light dark">
8
+ <title>Sonorance</title>
9
+ <link rel="manifest" href="/manifest.webmanifest">
10
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f0995a'/%3E%3Cstop offset='1' stop-color='%23c85a12'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='url(%23g)'/%3E%3Crect x='10.5' y='10.5' width='11' height='11' rx='3.5' fill='none' stroke='white' stroke-opacity='.94' stroke-width='2'/%3E%3C/svg%3E">
11
+ <style>
12
+ /* ============================================================================
13
+ Deliberate — design tokens (see DESIGN.md). Light in :root; dark is the
14
+ signature theme, applied on OS preference (unless overridden) and via
15
+ [data-theme="dark"]. "Ember at dusk": one burnt-orange accent, warm graphite
16
+ neutrals, Apple-grade frosted glass over a soft ember ambient.
17
+ ========================================================================== */
18
+ :root{
19
+ /* Harbor — layered, analogous green→teal→blue→violet atmosphere. Three
20
+ partially-transparent gradient planes (bg1 far … bg3 near) each drift + parallax
21
+ at their own speed for a 3D depth effect; brightest + most saturated toward centre. */
22
+ --bgbase:#f1f6f6; --bg:#f1f6f6;
23
+ --bg1:
24
+ radial-gradient(1980px 1700px at 86% -18%, rgba(150,202,184,.13), transparent 78%),
25
+ radial-gradient(2060px 1780px at -16% 118%, rgba(160,168,224,.13), transparent 78%),
26
+ radial-gradient(1820px 1640px at 118% 114%, rgba(150,192,224,.12), transparent 76%),
27
+ radial-gradient(1720px 1560px at -12% -14%, rgba(158,210,214,.11), transparent 78%);
28
+ --bg2:
29
+ radial-gradient(1480px 1240px at 50% 36%, rgba(226,244,240,.34), transparent 72%),
30
+ radial-gradient(1180px 1040px at 66% 26%, rgba(168,220,220,.19), transparent 70%),
31
+ radial-gradient(1140px 1100px at 32% 60%, rgba(186,196,240,.18), transparent 70%),
32
+ radial-gradient(1080px 1000px at 78% 66%, rgba(172,206,236,.16), transparent 70%);
33
+ --bg3:
34
+ radial-gradient(1000px 880px at 46% 42%, rgba(242,250,249,.42), transparent 66%),
35
+ radial-gradient(820px 900px at 68% 54%, rgba(168,214,236,.19), transparent 68%),
36
+ radial-gradient(860px 800px at 30% 34%, rgba(198,192,240,.17), transparent 70%),
37
+ radial-gradient(760px 820px at 58% 24%, rgba(178,222,214,.15), transparent 70%);
38
+ --grain:.3; --grain-blend:soft-light;
39
+ --canvas:rgba(255,255,255,.6); --panel:rgba(255,255,255,.46); --tab-idle:rgba(20,38,56,.045);
40
+ --chrome:rgba(255,255,255,.64); --surface:rgba(246,250,253,.72); --raised:#f7fafc;
41
+ --glass:rgba(255,255,255,.58); --blur:saturate(150%) blur(20px);
42
+ --hairline:rgba(22,42,64,.12); --ink:#141b24; --ink-muted:#4a5563; --ink-faint:#5b6472;
43
+ --accent:#b05500; --accent-strong:#8f4400; --accent-hover:#b05500; --on-accent:#ffffff;
44
+ --btn-bg:#b05500; --btn-bg-hover:#8f4400;
45
+ --ok:#0a7d54; --warn:#8a6410; --bad:#96201f;
46
+ --accent-tint:rgba(176,85,0,.11); --ok-tint:rgba(10,125,84,.13);
47
+ --warn-tint:rgba(138,100,16,.14); --bad-tint:rgba(150,32,31,.15);
48
+ --hover:rgba(20,38,56,.055); --glass-brd:rgba(22,42,64,.16); --glass-top:rgba(255,255,255,.9);
49
+ --lit:radial-gradient(120% 110% at 18% -25%, rgba(255,255,255,.8), transparent 56%); --rimA:.95; --rimB:.5;
50
+ --sheen:linear-gradient(180deg,rgba(255,255,255,.5),rgba(255,255,255,0) 46%);
51
+ --shadow:0 30px 70px -30px rgba(24,40,60,.44), 0 3px 12px rgba(24,40,60,.15), inset 0 1px 0 rgba(255,255,255,.9), inset 0 0 0 1px var(--glass-brd);
52
+ --shadow-sm:0 12px 30px -18px rgba(24,40,60,.3), inset 0 1px 0 rgba(255,255,255,.85), inset 0 0 0 1px var(--glass-brd);
53
+ --shadow-md:0 18px 44px -22px rgba(24,40,60,.34), inset 0 1px 0 rgba(255,255,255,.85), inset 0 0 0 1px var(--glass-brd);
54
+ --shadow-lg:0 44px 96px -34px rgba(24,40,60,.48), inset 0 1px 0 rgba(255,255,255,.9), inset 0 0 0 1px var(--glass-brd);
55
+ --edge-shadow:rgba(19,45,89,.2);
56
+ --glow:0 0 0 1px rgba(176,85,0,.2), 0 14px 40px -16px rgba(176,85,0,.3);
57
+ --ring:rgba(176,85,0,.55); --sb-thumb:rgba(22,42,64,.22); --sb-thumb-h:rgba(22,42,64,.34); --guide-line:rgba(22,42,64,.16);
58
+ --center-glow:none; --grain-legacy:.022;
59
+ --ambient:none;
60
+ --paper-tex:none; --paper-blend:normal;
61
+ /* geometry */
62
+ --r-xs:6px; --r-sm:8px; --r-md:10px; --r-lg:14px; --r-xl:20px; --pill:999px;
63
+ --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px;
64
+ --sb:288px; --toc:250px; --sb-min:220px; --sb-max:560px;
65
+ --doc-w:127.2ch; --tbl-mincol:7.5rem;
66
+ --sans:ui-sans-serif,-apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Roboto,sans-serif;
67
+ --serif:"Iowan Old Style","Palatino Linotype","Book Antiqua",Palatino,Georgia,ui-serif,serif;
68
+ --mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,"JetBrains Mono",monospace;
69
+ --z-sticky:100; --z-dropdown:200; --z-scrim:300; --z-modal:400; --z-toast:500;
70
+ --dur:180ms; --ease:cubic-bezier(.22,1,.36,1);
71
+ }
72
+ @media (prefers-color-scheme:dark){ :root:not([data-theme="light"]){
73
+ --bgbase:#0a0e14; --bg:#0a0e14;
74
+ --bg1:
75
+ radial-gradient(1240px 1060px at 84% -6%, rgba(40,152,150,.32), transparent 58%),
76
+ radial-gradient(1320px 1140px at -4% 108%, rgba(98,98,204,.28), transparent 58%),
77
+ radial-gradient(1060px 960px at 108% 104%, rgba(60,142,192,.24), transparent 56%);
78
+ --bg2:
79
+ radial-gradient(1120px 920px at 50% 36%, rgba(92,202,182,.24), transparent 60%),
80
+ radial-gradient(840px 740px at 61% 28%, rgba(70,172,192,.21), transparent 60%),
81
+ radial-gradient(780px 740px at 39% 56%, rgba(112,122,212,.19), transparent 60%);
82
+ --bg3:
83
+ radial-gradient(560px 490px at 46% 42%, rgba(152,238,212,.17), transparent 58%),
84
+ radial-gradient(460px 540px at 66% 52%, rgba(92,182,222,.16), transparent 60%),
85
+ radial-gradient(500px 460px at 31% 36%, rgba(152,142,226,.14), transparent 60%);
86
+ --grain:.3; --grain-blend:soft-light;
87
+ --canvas:rgba(20,26,34,.5); --panel:rgba(15,20,28,.34); --tab-idle:rgba(0,0,0,.16);
88
+ --chrome:rgba(12,16,22,.5); --surface:rgba(28,34,44,.55); --raised:#171d26;
89
+ --glass:rgba(20,26,34,.44); --blur:saturate(160%) blur(22px);
90
+ --hairline:rgba(210,224,244,.09); --ink:#e9eef6; --ink-muted:#a4adba; --ink-faint:#8a93a0;
91
+ --accent:#f1944f; --accent-strong:#de742e; --accent-hover:#f6a566; --on-accent:#221812;
92
+ --btn-bg:#de742e; --btn-bg-hover:#cf6a26;
93
+ --ok:#65c89b; --warn:#e6c45a; --bad:#f28c8c;
94
+ --accent-tint:rgba(241,148,79,.15); --ok-tint:rgba(101,200,155,.14);
95
+ --warn-tint:rgba(230,196,90,.14); --bad-tint:rgba(234,106,106,.18);
96
+ --hover:rgba(210,224,244,.06); --glass-brd:rgba(210,224,244,.1); --glass-top:rgba(210,224,244,.1);
97
+ --lit:radial-gradient(120% 110% at 18% -25%, rgba(255,255,255,.14), transparent 58%); --rimA:.58; --rimB:.14;
98
+ --sheen:linear-gradient(180deg,rgba(224,226,245,.06),rgba(224,226,245,0) 46%);
99
+ --shadow:0 30px 70px -26px rgba(0,0,0,.72), 0 3px 12px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.1), inset 0 0 0 1px var(--glass-brd);
100
+ --shadow-sm:0 12px 30px -16px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.08), inset 0 0 0 1px var(--glass-brd);
101
+ --shadow-md:0 18px 44px -20px rgba(0,0,0,.64), inset 0 1px 0 rgba(255,255,255,.08), inset 0 0 0 1px var(--glass-brd);
102
+ --shadow-lg:0 44px 96px -30px rgba(0,0,0,.76), inset 0 1px 0 rgba(255,255,255,.1), inset 0 0 0 1px var(--glass-brd);
103
+ --edge-shadow:rgba(0,0,0,.62);
104
+ --glow:0 0 0 1px rgba(241,148,79,.18), 0 14px 40px -14px rgba(241,148,79,.38);
105
+ --ring:rgba(241,148,79,.62); --sb-thumb:rgba(210,224,244,.16); --sb-thumb-h:rgba(210,224,244,.28); --guide-line:rgba(210,224,244,.14);
106
+ --center-glow:none; --ambient:none;
107
+ --paper-tex:none; --paper-blend:normal;
108
+ }}
109
+ :root[data-theme="dark"]{
110
+ --bgbase:#0a0e14; --bg:#0a0e14;
111
+ --bg1:
112
+ radial-gradient(1240px 1060px at 84% -6%, rgba(40,152,150,.32), transparent 58%),
113
+ radial-gradient(1320px 1140px at -4% 108%, rgba(98,98,204,.28), transparent 58%),
114
+ radial-gradient(1060px 960px at 108% 104%, rgba(60,142,192,.24), transparent 56%);
115
+ --bg2:
116
+ radial-gradient(1120px 920px at 50% 36%, rgba(92,202,182,.24), transparent 60%),
117
+ radial-gradient(840px 740px at 61% 28%, rgba(70,172,192,.21), transparent 60%),
118
+ radial-gradient(780px 740px at 39% 56%, rgba(112,122,212,.19), transparent 60%);
119
+ --bg3:
120
+ radial-gradient(560px 490px at 46% 42%, rgba(152,238,212,.17), transparent 58%),
121
+ radial-gradient(460px 540px at 66% 52%, rgba(92,182,222,.16), transparent 60%),
122
+ radial-gradient(500px 460px at 31% 36%, rgba(152,142,226,.14), transparent 60%);
123
+ --grain:.3; --grain-blend:soft-light;
124
+ --canvas:rgba(20,26,34,.5); --panel:rgba(15,20,28,.34); --tab-idle:rgba(0,0,0,.16);
125
+ --chrome:rgba(12,16,22,.5); --surface:rgba(28,34,44,.55); --raised:#171d26;
126
+ --glass:rgba(20,26,34,.44); --blur:saturate(160%) blur(22px);
127
+ --hairline:rgba(210,224,244,.09); --ink:#e9eef6; --ink-muted:#a4adba; --ink-faint:#8a93a0;
128
+ --accent:#f1944f; --accent-strong:#de742e; --accent-hover:#f6a566; --on-accent:#221812;
129
+ --btn-bg:#de742e; --btn-bg-hover:#cf6a26;
130
+ --ok:#65c89b; --warn:#e6c45a; --bad:#f28c8c;
131
+ --accent-tint:rgba(241,148,79,.15); --ok-tint:rgba(101,200,155,.14);
132
+ --warn-tint:rgba(230,196,90,.14); --bad-tint:rgba(234,106,106,.18);
133
+ --hover:rgba(210,224,244,.06); --glass-brd:rgba(210,224,244,.1); --glass-top:rgba(210,224,244,.1);
134
+ --lit:radial-gradient(120% 110% at 18% -25%, rgba(255,255,255,.14), transparent 58%); --rimA:.58; --rimB:.14;
135
+ --sheen:linear-gradient(180deg,rgba(224,226,245,.06),rgba(224,226,245,0) 46%);
136
+ --shadow:0 30px 70px -26px rgba(0,0,0,.72), 0 3px 12px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.1), inset 0 0 0 1px var(--glass-brd);
137
+ --shadow-sm:0 12px 30px -16px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.08), inset 0 0 0 1px var(--glass-brd);
138
+ --shadow-md:0 18px 44px -20px rgba(0,0,0,.64), inset 0 1px 0 rgba(255,255,255,.08), inset 0 0 0 1px var(--glass-brd);
139
+ --shadow-lg:0 44px 96px -30px rgba(0,0,0,.76), inset 0 1px 0 rgba(255,255,255,.1), inset 0 0 0 1px var(--glass-brd);
140
+ --edge-shadow:rgba(0,0,0,.62);
141
+ --glow:0 0 0 1px rgba(241,148,79,.18), 0 14px 40px -14px rgba(241,148,79,.38);
142
+ --ring:rgba(241,148,79,.62); --sb-thumb:rgba(210,224,244,.16); --sb-thumb-h:rgba(210,224,244,.28); --guide-line:rgba(210,224,244,.14);
143
+ --center-glow:none; --ambient:none;
144
+ --paper-tex:none; --paper-blend:normal;
145
+ }
146
+
147
+ *{box-sizing:border-box}
148
+ html,body{height:100%}
149
+ body{
150
+ margin:0; background:var(--bgbase); color:var(--ink);
151
+ font-family:var(--sans); font-size:16px; line-height:1.62;
152
+ font-feature-settings:"kern","liga","calt"; font-optical-sizing:auto;
153
+ -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
154
+ overflow:hidden;
155
+ }
156
+ /* ── the living gradient: three fixed Harbor planes stacked over --bgbase. Each
157
+ plane (gl1 far → gl3 near) slowly drifts AND takes scroll parallax at its own
158
+ speed (--py set on #main scroll), so the atmosphere gains real 3D depth: the
159
+ near plane races the far one as you scroll. Brightest + most saturated toward
160
+ the centre, cooler/deeper at the edges, so the floating glass reads clearly. ── */
161
+ .living{ position:fixed; inset:-760px -60px; z-index:-2; pointer-events:none; background:var(--bgbase); }
162
+ .glayer{ content:""; position:absolute; inset:0; background-repeat:no-repeat; will-change:transform; }
163
+ .gl1{ background-image:var(--bg1); background-size:150% 150%; background-position:50% 42%;
164
+ transform:translate3d(0, calc(var(--py,0) * -.14px), 0); animation:drift1 66s ease-in-out infinite alternate; }
165
+ .gl2{ background-image:var(--bg2); background-size:150% 150%; background-position:50% 40%;
166
+ transform:translate3d(0, calc(var(--py,0) * -.34px), 0); animation:drift2 50s ease-in-out infinite alternate; }
167
+ .gl3{ background-image:var(--bg3); background-size:145% 145%; background-position:50% 40%;
168
+ transform:translate3d(0, calc(var(--py,0) * -.58px), 0); animation:drift3 40s ease-in-out infinite alternate; filter:saturate(108%); }
169
+ @keyframes drift1{ 0%{ background-position:42% 38%; } 100%{ background-position:58% 58%; } }
170
+ @keyframes drift2{ 0%{ background-position:60% 34%; } 50%{ background-position:40% 52%; } 100%{ background-position:56% 62%; } }
171
+ @keyframes drift3{ 0%{ background-position:38% 44%; } 50%{ background-position:60% 40%; } 100%{ background-position:44% 60%; } }
172
+ /* organic film grain so the glass reads as a real material over a real surface.
173
+ This is main's proven recipe: a plain grayscale fractalNoise (centred on mid-grey)
174
+ under soft-light, the SAME on both themes, at a restrained opacity. soft-light leaves
175
+ mid-grey a no-op, so grain textures the mid-tones WITHOUT washing anything toward grey.
176
+ Do NOT contrast-stretch the noise or raise the opacity to "make it pop": on the near-black
177
+ dark floor, soft-light can only lighten (darkening clips at 0), so strong grain there = a
178
+ lifted-black grey veil. Real film grain is quiet in deep shadow; let it live on the glass. */
179
+ .grain{ content:""; position:absolute; inset:0; opacity:var(--grain); mix-blend-mode:var(--grain-blend,overlay);
180
+ transform:translate3d(0, calc(var(--py,0) * -.20px), 0); will-change:transform;
181
+ background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
182
+ background-size:160px 160px; }
183
+ /* shared sharp Liquid-Glass rim: a 1px gradient ring (mask-composite trick) that
184
+ catches light along the top-left edge of any glass surface. */
185
+ .rim{ position:relative; }
186
+ .rim::before{ content:""; position:absolute; inset:0; border-radius:inherit; padding:1px; z-index:1;
187
+ background:linear-gradient(150deg, rgba(255,255,255,var(--rimA)) 0%, rgba(255,255,255,var(--rimB)) 26%, rgba(255,255,255,0) 60%);
188
+ -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
189
+ -webkit-mask-composite:xor; mask-composite:exclude; pointer-events:none; }
190
+ ::selection{ background:var(--accent-tint); }
191
+ :focus-visible{ outline:2px solid var(--ring); outline-offset:2px; border-radius:var(--r-xs); }
192
+ /* Themed scrollbars. Chrome/Safari/Electron get the always-visible `::-webkit-scrollbar` below;
193
+ they IGNORE those pseudo-elements if `scrollbar-width`/`scrollbar-color` is set on an element,
194
+ and on macOS the standard scrollbar auto-hides to a 0-width overlay — so a set-and-forget
195
+ `scrollbar-width:thin` made overflowing panes (the Explorer tree) show no scrollbar at all.
196
+ Restrict the standard properties to browsers WITHOUT webkit scrollbars (Firefox), so everyone
197
+ else falls through to the styled, persistent webkit bar. */
198
+ @supports not selector(::-webkit-scrollbar){
199
+ *{ scrollbar-width:thin; scrollbar-color:var(--sb-thumb) transparent; }
200
+ }
201
+ ::-webkit-scrollbar{ width:11px; height:11px; }
202
+ ::-webkit-scrollbar-track,::-webkit-scrollbar-corner{ background:transparent; }
203
+ ::-webkit-scrollbar-thumb{ background:var(--sb-thumb); border-radius:8px; border:3px solid transparent; background-clip:padding-box; min-height:36px; }
204
+ ::-webkit-scrollbar-thumb:hover{ background:var(--sb-thumb-h); background-clip:padding-box; }
205
+ /* While actively resizing OR scrolling, drop the expensive glass backdrop-blur so the
206
+ compositor stays smooth. Scrolling moves the Harbor atmosphere behind the container
207
+ (the scroll parallax), and re-blurring a MOVING backdrop every frame is what makes
208
+ scrolling stutter — so we pause the blur mid-scroll and restore it ~140ms after it
209
+ stops (see initAtmosphere). The parallax + drift keep animating; only the blur pauses. */
210
+ html.resizing *,html.resizing *::before,html.resizing *::after,
211
+ html.scrolling *,html.scrolling *::before,html.scrolling *::after{ backdrop-filter:none!important; -webkit-backdrop-filter:none!important; }
212
+ html.resizing .living .grain{ display:none; }
213
+
214
+ /* ---- app shell ------------------------------------------------------------ */
215
+ #app{ display:grid; grid-template-columns:var(--sb) 1fr; grid-template-rows:minmax(0,1fr);
216
+ grid-template-areas:"sidebar stage"; height:100vh; height:100dvh; position:relative; }
217
+ .sidebar{ grid-area:sidebar; }
218
+ /* Draggable divider between the sidebar and the stage — a wide invisible hit area that
219
+ sits ON the column boundary. Its affordance is a faint outward-chevron handle ‹ › at
220
+ rest (unmistakably "resize", never a scrollbar); on hover / focus / active drag the
221
+ chevrons brighten to ember and a faint ember hairline appears between them, completing
222
+ the ‹ | › splitter for precise edge feedback. */
223
+ .sb-resize{ position:absolute; top:0; bottom:0; left:var(--sb); width:12px; transform:translateX(-50%);
224
+ z-index:calc(var(--z-sticky) + 1); cursor:col-resize; background:transparent; border:0; padding:0;
225
+ touch-action:none; -webkit-user-select:none; user-select:none; }
226
+ /* full-height hairline — the centre "|", hidden at rest, an ember whisper on hover/drag */
227
+ .sb-resize::before{ content:""; position:absolute; top:0; bottom:0; left:50%; width:2px; transform:translateX(-50%);
228
+ border-radius:2px; background:transparent; transition:background var(--dur) var(--ease); }
229
+ .sb-resize:hover::before, .sb-resize:focus-visible::before, body.resizing-sb .sb-resize::before{
230
+ background:color-mix(in srgb, var(--accent) 50%, transparent); }
231
+ /* the ‹ › chevrons — faint + slightly drawn-in at rest, full + ember on interaction */
232
+ .sb-grip{ position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); display:flex; align-items:center; gap:3px;
233
+ color:var(--ink-faint); opacity:.42; pointer-events:none;
234
+ transition:opacity var(--dur) var(--ease), color var(--dur) var(--ease), gap var(--dur) var(--ease); }
235
+ .sb-grip .ico{ width:15px; height:15px; }
236
+ .sb-grip .ico:first-child{ margin-right:-2px; } .sb-grip .ico:last-child{ margin-left:-2px; }
237
+ .sb-resize:hover .sb-grip, .sb-resize:focus-visible .sb-grip, body.resizing-sb .sb-grip{
238
+ opacity:1; color:var(--accent); gap:6px; }
239
+ .sb-resize:focus-visible{ outline:none; }
240
+ @media (prefers-reduced-motion:reduce){ .sb-resize::before, .sb-grip{ transition:none; } }
241
+ body.resizing-sb{ cursor:col-resize; -webkit-user-select:none; user-select:none; }
242
+ body.resizing-sb *{ cursor:col-resize !important; }
243
+ .stage{ grid-area:stage; }
244
+
245
+ /* ── right column: the open-case tabs + the record body fused into ONE narrower,
246
+ partly-transparent floating glass shape, with a margin from each side. ── */
247
+ .stage{ min-width:0; min-height:0; display:flex; padding:14px 22px 18px; }
248
+ .container{ position:relative; flex:1 1 auto; min-width:0; min-height:0; width:100%; max-width:1120px; margin:0 auto;
249
+ display:flex; flex-direction:column; border-radius:var(--r-xl); overflow:hidden;
250
+ background:var(--panel); backdrop-filter:var(--blur); -webkit-backdrop-filter:var(--blur); box-shadow:var(--shadow); }
251
+ .container::after{ content:""; position:absolute; inset:0; pointer-events:none; background:var(--lit); z-index:0; border-radius:inherit; }
252
+
253
+ /* ---- top bar: open-case tabs + page controls = the top edge of the shape ---- */
254
+ .tabbar{ position:relative; z-index:2; display:flex; align-items:stretch; min-width:0; min-height:42px; }
255
+ .tabs{ display:flex; align-items:stretch; flex:0 1 auto; min-width:0; overflow-x:auto; scrollbar-width:none; scroll-behavior:smooth; }
256
+ .tabs::-webkit-scrollbar{ display:none; }
257
+ .tab{ position:relative; display:flex; align-items:center; gap:6px; max-width:200px; min-width:120px; padding:0 5px 0 11px;
258
+ cursor:pointer; color:var(--ink-muted); background:var(--tab-idle);
259
+ border-right:1px solid var(--hairline); border-bottom:1px solid var(--hairline);
260
+ transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
261
+ .tab:hover{ background:var(--hover); color:var(--ink); }
262
+ .tab.active{ background:transparent; color:var(--ink); border-bottom-color:transparent; }
263
+ .tab-ico{ display:inline-flex; }
264
+ .tab-ico:empty{ display:none; }
265
+ .tab-ico .ico{ width:14px; height:14px; color:var(--ink-faint); }
266
+ .tab.active .tab-ico .ico{ color:var(--accent); }
267
+ .tab-label{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:12.5px; font-weight:500; }
268
+ /* the active tab carries the one ember accent on its NAME so it reads as current at a glance
269
+ (the file icon was removed, so the label is the only identity the tab shows). */
270
+ .tab.active .tab-label{ color:var(--accent); font-weight:600; }
271
+ .tab-close{ position:relative; display:grid; place-items:center; width:20px; height:20px; flex:none; border:0; background:transparent; cursor:pointer;
272
+ color:var(--ink-faint); border-radius:6px; opacity:0; transition:opacity var(--dur) var(--ease), background var(--dur) var(--ease); }
273
+ .tab-close::before{ content:""; position:absolute; inset:-3px; } /* ≥24px hit target without inflating the visual */
274
+ .tab:hover .tab-close,.tab.active .tab-close{ opacity:1; }
275
+ .tab:focus-visible{ outline:none; box-shadow:inset 0 0 0 2px var(--ring); }
276
+ .tab:focus-visible .tab-close{ opacity:1; }
277
+ .tab-close:hover{ background:var(--hover); color:var(--ink); }
278
+ .tab-close .ico{ width:13px; height:13px; }
279
+ /* tab-strip scroll chevrons — shown only when the open tabs overflow the strip, so many tabs
280
+ stay legible (each keeps a min width) and are reachable without a system scrollbar. The arrow
281
+ at the current scroll end dims. */
282
+ .tabscroll{ flex:none; align-self:stretch; width:26px; border:0; border-bottom:1px solid var(--hairline); border-right:1px solid var(--hairline);
283
+ background:var(--tab-idle); cursor:pointer; color:var(--ink-muted); display:none; place-items:center;
284
+ transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
285
+ .tabscroll.show{ display:grid; }
286
+ .tabscroll:hover{ background:var(--hover); color:var(--ink); }
287
+ .tabscroll:disabled{ opacity:.32; cursor:default; background:var(--tab-idle); color:var(--ink-muted); }
288
+ .tabscroll .ico{ width:16px; height:16px; }
289
+ .tab-add{ flex:none; align-self:stretch; width:34px; border:0; border-right:1px solid var(--hairline); border-bottom:1px solid var(--hairline);
290
+ background:var(--tab-idle); cursor:pointer; color:var(--ink-muted); font-size:17px; font-weight:400; display:grid; place-items:center;
291
+ transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
292
+ .tab-add:hover{ background:var(--hover); color:var(--ink); }
293
+ /* flexible gap that pushes the page controls to the far right of the bar */
294
+ .tabspacer{ flex:1 1 auto; min-width:16px; align-self:stretch; background:var(--tab-idle); border-bottom:1px solid var(--hairline); }
295
+ /* the semi-translucent brand wordmark — anchored to the CENTRE of the whole tab bar (so it
296
+ never shifts as tabs change), sitting on the idle strip and never intercepting a click. JS
297
+ hides it (`.crowded`) when a tab / the + button or the page controls come within a margin, so
298
+ it recedes cleanly instead of crowding a neighbour; `.hide` suppresses it on the branded home. */
299
+ .tabmark{ position:absolute; left:50%; top:0; bottom:0; transform:translateX(-50%); z-index:0;
300
+ display:flex; align-items:center; max-width:38%; overflow:hidden; white-space:nowrap;
301
+ pointer-events:none; font-size:12px; font-weight:600; letter-spacing:.02em; color:var(--ink-faint); opacity:.5;
302
+ -webkit-user-select:none; user-select:none; }
303
+ .tabmark.hide, .tabmark.crowded{ display:none; }
304
+ /* page controls, right side of the same bar (view switch, save state, prototype) */
305
+ .topactions{ flex:none; display:flex; align-items:center; gap:var(--s3); padding:0 var(--s4) 0 var(--s3);
306
+ align-self:stretch; background:var(--tab-idle); border-bottom:1px solid var(--hairline); }
307
+ .topactions:empty{ display:none; }
308
+ #navbtn{ align-self:center; margin-left:6px; }
309
+
310
+ /* context menu (tab actions, project switcher) */
311
+ .menu{ position:fixed; z-index:var(--z-dropdown); min-width:180px; padding:5px; border-radius:var(--r-md);
312
+ background:var(--raised); border:1px solid var(--glass-brd); box-shadow:var(--shadow); animation:reveal .12s var(--ease) both; }
313
+ .menu button{ display:flex; align-items:center; gap:9px; width:100%; text-align:left; border:0; background:transparent; cursor:pointer;
314
+ font:inherit; font-size:13px; color:var(--ink); padding:7px 10px; border-radius:var(--r-sm); }
315
+ .menu button:hover{ background:var(--hover); }
316
+ .menu button .ico{ width:15px; height:15px; color:var(--ink-faint); }
317
+ .menu button.on .ico{ color:var(--accent); }
318
+ .menu .sep{ height:1px; margin:5px 4px; background:var(--hairline); }
319
+ /* A menu row with a secondary action (e.g. Remove a vault): the main button flexes, the trailing
320
+ button is invisible at rest and reveals only on hover or keyboard focus — never distracting.
321
+ The trailing button is itself a `.menu button`, so these selectors are qualified with `button` to
322
+ outrank the base `.menu button` rules (which would otherwise stretch it to full width). */
323
+ .menu-row{ display:flex; align-items:center; gap:2px; }
324
+ .menu-row > button:first-child{ flex:1 1 auto; min-width:0; width:auto; }
325
+ .menu button.menu-trail{ flex:0 0 auto; display:grid; place-items:center; width:26px; height:26px; padding:0; gap:0;
326
+ border:0; border-radius:var(--r-sm); background:transparent; color:var(--ink-faint); cursor:pointer;
327
+ opacity:0; transition:opacity .15s var(--ease), background .15s var(--ease), color .15s var(--ease); }
328
+ .menu button.menu-trail .ico{ width:15px; height:15px; }
329
+ .menu-row:hover button.menu-trail, .menu-row:focus-within button.menu-trail, button.menu-trail:focus-visible{ opacity:1; }
330
+ button.menu-trail:hover, button.menu-trail:focus-visible{ background:var(--hover); }
331
+ button.menu-trail.danger:hover, button.menu-trail.danger:focus-visible{ background:var(--bad-tint); color:var(--bad); }
332
+ @media (prefers-reduced-motion: reduce){ button.menu-trail{ transition:none; } }
333
+
334
+ /* ---- sidebar: suspended on the living gradient (transparent, no chrome) ----- */
335
+ .sidebar{
336
+ position:relative; z-index:var(--z-sticky);
337
+ display:flex; flex-direction:column; min-height:0;
338
+ background:transparent; padding:2px 4px 0;
339
+ }
340
+ .sb-head{ display:flex; align-items:center; gap:7px; padding:10px 8px 8px 10px; }
341
+ /* project switcher — the compact header row (the ember mark moved to the favicon) */
342
+ .projsel{ display:flex; align-items:center; gap:6px; flex:1 1 auto; min-width:0; padding:5px 5px 5px 8px; border-radius:var(--r-sm);
343
+ border:0; background:transparent; box-shadow:none; cursor:pointer; font:inherit; color:var(--ink); text-align:left;
344
+ transition:background var(--dur) var(--ease); }
345
+ .projsel:hover{ background:var(--hover); }
346
+ .projsel .pname{ min-width:0; flex:0 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:600; font-size:13px; }
347
+ .projsel .cv{ width:14px; height:14px; flex:none; margin-left:auto; color:var(--ink-faint); }
348
+ /* case search (item 9) */
349
+ .search{ position:relative; padding:0 var(--s3) var(--s3); }
350
+ .search .ico{ position:absolute; left:calc(var(--s3) + 9px); top:50%; transform:translateY(-50%); width:15px; height:15px; color:var(--ink-faint); pointer-events:none; }
351
+ .search input{ width:100%; height:34px; padding:0 26px 0 32px; border-radius:var(--r-md); border:0;
352
+ background:var(--glass); backdrop-filter:var(--blur); -webkit-backdrop-filter:var(--blur);
353
+ box-shadow:inset 0 0 0 1px var(--hairline); color:var(--ink); font:inherit; font-size:13px; outline:none;
354
+ transition:box-shadow var(--dur) var(--ease); }
355
+ .search input:focus{ outline:2px solid var(--ring); outline-offset:1px; box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--accent) 55%, var(--hairline)); }
356
+ .search input::placeholder{ color:var(--ink-faint); }
357
+ .search .clr{ position:absolute; right:calc(var(--s3) + 5px); top:50%; transform:translateY(-50%); width:18px; height:18px; border:0;
358
+ background:transparent; cursor:pointer; color:var(--ink-faint); border-radius:5px; display:none; place-items:center; }
359
+ .search .clr.show{ display:grid; }
360
+ .search .clr:hover{ background:var(--hover); color:var(--ink); }
361
+ /* top-bar global utility cluster (Inbox + Feedback + Settings) — right edge of the tab bar, continuing its
362
+ bottom hairline. Global chrome kept OUT of the sidebar so the sidebar is a pure nav column. */
363
+ .topglobal{ flex:none; display:flex; align-items:center; gap:1px; padding:0 25px 0 4px; align-self:stretch;
364
+ background:var(--tab-idle); border-bottom:1px solid var(--hairline); }
365
+ .gicon{ appearance:none; border:0; background:transparent; color:var(--ink-muted); cursor:pointer;
366
+ width:34px; height:34px; border-radius:var(--r-sm); display:grid; place-items:center;
367
+ transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
368
+ .gicon:hover{ background:var(--hover); color:var(--ink); }
369
+ .gicon:focus-visible{ outline:2px solid var(--ring); outline-offset:-2px; }
370
+ .gicon .ico{ width:19px; height:19px; }
371
+ .gicon.active{ color:var(--accent); }
372
+ .cap{ padding:var(--s4) var(--s4) var(--s2); font-size:12px; font-weight:560; color:var(--ink-muted); }
373
+ .caselist{ flex:1 1 auto; min-height:0; overflow-y:scroll; overflow-x:hidden; padding:0 6px var(--s4); display:flex; flex-direction:column; gap:2px;
374
+ /* Always show the tree's scrollbar when it overflows (see the global `@supports` note above:
375
+ the standard `scrollbar-width` is left OFF for webkit browsers so this custom bar renders and
376
+ never auto-hides). `overflow-y:scroll` + a stable gutter keep the layout steady. */
377
+ scrollbar-gutter:stable; }
378
+ .caselist::-webkit-scrollbar{ width:10px; }
379
+ .caselist::-webkit-scrollbar-thumb{ background:var(--sb-thumb); border-radius:8px; border:2px solid transparent; background-clip:padding-box; min-height:36px; }
380
+ .caselist::-webkit-scrollbar-thumb:hover{ background:var(--sb-thumb-h); background-clip:padding-box; }
381
+ .caseitem{
382
+ position:relative; display:block; text-decoration:none; color:inherit; cursor:pointer;
383
+ padding:9px 11px 9px 13px; border-radius:var(--r-sm); border:0; background:transparent; text-align:left; font:inherit; width:100%;
384
+ transition:background var(--dur) var(--ease);
385
+ }
386
+ /* The staggered entrance is a first-paint flourish only. Re-renders (e.g. clicking a
387
+ case re-runs renderSidebar) must NOT replay it — that reads as every row blinking and
388
+ sliding. The list carries `.intro` only on its first population; see fillCaseList. */
389
+ .caselist.intro .caseitem{ animation:casein .34s var(--ease) both; animation-delay:calc(var(--i,0)*18ms); }
390
+ .caseitem:hover{ background:var(--hover); }
391
+ .caseitem.active{ background:color-mix(in srgb, var(--accent) 7%, var(--hover)); }
392
+ .caseitem.active::before{ content:""; position:absolute; left:3px; top:9px; bottom:9px; width:2px; border-radius:2px; background:var(--accent); }
393
+ .caseitem .st{ font-weight:560; color:var(--ink); line-height:1.35;
394
+ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
395
+ .caseitem .sm{ display:flex; align-items:center; gap:8px; margin-top:3px; font-size:12px; color:var(--ink-muted); }
396
+ .caseitem.active .sm{ color:var(--ink); }
397
+
398
+ /* per-file Document ⇄ Diff toggle — mirrors the top-bar global icons (.gicon) exactly (same box,
399
+ size, hover) so it reads as one control vocabulary and lines up directly beneath them, with the
400
+ ember-tint "on" state borrowed from the sidebar's diff filter. A dot marks that THIS file has
401
+ uncommitted changes; pressed (ember) means its diff is showing; disabled when nothing to diff. */
402
+ .viewtoggle{ position:relative; appearance:none; border:0; background:transparent; cursor:pointer; color:var(--ink-muted);
403
+ width:34px; height:34px; border-radius:var(--r-sm); display:grid; place-items:center;
404
+ transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
405
+ .viewtoggle:hover{ background:var(--hover); color:var(--ink); }
406
+ .viewtoggle.on{ background:var(--accent-tint); color:var(--accent); }
407
+ .viewtoggle.on:hover{ background:color-mix(in srgb, var(--accent) 18%, transparent); }
408
+ .viewtoggle:disabled{ opacity:.42; cursor:default; background:transparent; color:var(--ink-faint); }
409
+ .viewtoggle:focus-visible{ outline:2px solid var(--ring); outline-offset:-2px; }
410
+ .viewtoggle .ico{ width:19px; height:19px; }
411
+ .viewtoggle .cdot{ position:absolute; top:5px; right:5px; width:6px; height:6px; border-radius:50%;
412
+ background:var(--warn); box-shadow:0 0 0 2px var(--canvas); }
413
+ /* The Document⇄Diff control floats at the top-right of the reading canvas — it acts on THIS
414
+ document and must stay reachable in BOTH the document and the diff view — so it lives with the
415
+ content, not the tab bar. Pinned (not scrolling), its right offset matches .topglobal's so the
416
+ toggle sits in the SAME column as the inbox/theme icons directly above it. */
417
+ /* A frosted background panel behind the command-bar controls so scrolling comment cards never show
418
+ THROUGH the gaps between the buttons (the bar reads as one solid cluster over the canvas). Roomy
419
+ padding keeps it from feeling crowded; the right offset + the topglobal padding are kept in step
420
+ (right = R, so the rightmost control lands in the SAME column as the tab-bar's rightmost icon —
421
+ see .topglobal). */
422
+ .docdiff{ position:absolute; top:46px; right:14px; z-index:var(--z-sticky);
423
+ display:flex; align-items:center; gap:var(--s2); padding:7px 10px; border-radius:var(--r-lg);
424
+ background:color-mix(in srgb, var(--bg) 88%, transparent); border:1px solid var(--glass-brd);
425
+ backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); box-shadow:var(--shadow-sm); }
426
+ .docdiff[hidden]{ display:none; }
427
+ @media (max-width:720px){ .docdiff{ right:var(--s2); } }
428
+ /* The corner cluster reads as one cohesive set of TOOL controls — not a marketing CTA floating
429
+ over the reading canvas. A shared 30px height and the workbench's recessed frosted vocabulary,
430
+ with ember kept as the ACCENT (the control icons, the pressed Diff toggle, the verdict pill),
431
+ so the Deliberate prototype actions match the rest of the portal's chrome instead of shouting. */
432
+ .docdiff .tbtn{ height:30px; padding:0 12px; font-size:12px; }
433
+ .docdiff .tbtn:not(.danger){ background:var(--surface); color:var(--ink); border:1px solid var(--hairline);
434
+ box-shadow:var(--shadow-sm); font-weight:600; }
435
+ .docdiff .tbtn:not(.danger):hover{ background:var(--surface); box-shadow:var(--shadow-sm); transform:translateY(-1px);
436
+ border-color:color-mix(in srgb, var(--accent) 42%, var(--hairline)); }
437
+ .docdiff .tbtn:not(.danger):active{ transform:translateY(0); box-shadow:var(--shadow-sm); }
438
+ .docdiff .tbtn:not(.danger) .ico{ color:var(--accent); }
439
+ .docdiff .viewtoggle{ width:30px; height:30px; }
440
+
441
+ /* verdict pill */
442
+ .pill{ display:inline-flex; align-items:center; gap:5px; height:20px; padding:0 9px; border-radius:var(--pill);
443
+ font-size:12px; font-weight:600; font-variant-numeric:tabular-nums; white-space:nowrap; }
444
+ .pill::before{ content:""; width:6px; height:6px; border-radius:50%; }
445
+ .pill.advance{ background:var(--ok-tint); color:var(--ok); } .pill.advance::before{ background:var(--ok); }
446
+ .pill.shelve{ background:var(--warn-tint); color:var(--warn); } .pill.shelve::before{ background:var(--warn); }
447
+ .pill.reject{ background:var(--bad-tint); color:var(--bad); } .pill.reject::before{ background:var(--bad); }
448
+ .pill.neutral{ background:rgba(128,128,140,.14); color:var(--ink-faint); } .pill.neutral::before{ background:var(--ink-faint); }
449
+ /* brief pill — a cool petrol signal (a Harbor atmosphere hue), theme-adaptive via the
450
+ ink mix so it stays legible on both light and dark without a second brand color. */
451
+ .pill.brief,.pill.readout{ background:color-mix(in srgb, #3a9eb8 15%, transparent); color:color-mix(in srgb, var(--ink) 46%, #2f8299); }
452
+ .pill.brief::before,.pill.readout::before{ background:color-mix(in srgb, var(--ink) 34%, #2f8299); }
453
+ /* matchup pill — a warm clay signal, set apart from the brief's cool petrol so a head-to-head
454
+ reads as a distinct kind at a glance; same ink-mix approach keeps it legible in both themes. */
455
+ .pill.matchup{ background:color-mix(in srgb, #c2703a 15%, transparent); color:color-mix(in srgb, var(--ink) 46%, #a8582a); }
456
+ .pill.matchup::before{ background:color-mix(in srgb, var(--ink) 34%, #a8582a); }
457
+ /* ---- Explorer: the sidebar's file view --------------------------------------
458
+ A header row ("EXPLORER" + a Flat/Tree toggle + Collapse-all), then either a flat
459
+ list of the meaningful documents (typed records) or the real markdown file
460
+ tree with disclosure carets. */
461
+ .expbar{ display:flex; align-items:center; gap:0; margin:var(--s3) 24px 4px var(--s4); }
462
+ .expbar .exptitle{ font-size:11px; font-weight:680; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-faint); margin-right:auto; }
463
+ .expseg{ display:inline-flex; padding:2px; gap:2px; background:rgba(128,128,140,.10); border-radius:var(--r-sm); }
464
+ .expseg button{ appearance:none; border:0; background:transparent; cursor:pointer; color:var(--ink-faint); padding:4px 6px; border-radius:5px;
465
+ display:inline-flex; align-items:center; transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
466
+ .expseg button:hover{ color:var(--ink); }
467
+ .expseg button[aria-pressed="true"]{ background:var(--raised); color:var(--ink); box-shadow:var(--shadow-sm); }
468
+ .expseg .ico{ width:15px; height:15px; }
469
+ /* The header action icons line up with the row's right-hand icon columns: each is a 24px square
470
+ (matching `.rowact`/`.gitslot`), and they are a contiguous, right-aligned run — so the rightmost
471
+ (Collapse-all) sits over the row's git-badge / hover-action column, and each icon to its left over
472
+ the next column. The bar's right margin (24px on `.expbar`, wider than a row's 8px padding) is what
473
+ pulls the run left onto those columns — matching the row content edge past the list's own gutter;
474
+ keep it in sync with `.trow`/`.gitslot` if the row's right padding or the list gutter changes. */
475
+ .expbar .collapseall{ appearance:none; border:0; background:transparent; cursor:pointer; color:var(--ink-faint); padding:0; min-width:24px; min-height:24px; border-radius:5px; display:inline-flex; align-items:center; justify-content:center; }
476
+ .expbar .collapseall:hover{ background:rgba(128,128,140,.10); color:var(--ink); }
477
+ .expbar .collapseall .ico{ width:15px; height:15px; }
478
+ .segcount{ font-size:11px; font-weight:640; color:var(--ink-faint); font-variant-numeric:tabular-nums; }
479
+ /* Explorer header action buttons: New file + the diff-mode toggle, sitting beside Collapse-all
480
+ (the diff filter moved up here from a full-width bar — see the branch on the project button). */
481
+ .expbar .expact{ position:relative; appearance:none; border:0; background:transparent; cursor:pointer; color:var(--ink-faint); padding:0; min-width:24px; min-height:24px; border-radius:5px; display:inline-flex; align-items:center; justify-content:center;
482
+ transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
483
+ .expbar .expact:hover{ background:rgba(128,128,140,.10); color:var(--ink); }
484
+ .expbar .expact .ico{ width:15px; height:15px; }
485
+ .expbar .expact.difftoggle.on{ background:var(--accent-tint); color:var(--accent); }
486
+ .expbar .expact.difftoggle.on:hover{ background:color-mix(in srgb, var(--accent) 18%, transparent); }
487
+ /* the comments-only filter toggle — amber when active, matching the comment channel */
488
+ .expbar .expact.cmttoggle.on{ background:var(--accent-tint); color:var(--accent); }
489
+ .expbar .expact.cmttoggle.on:hover{ background:color-mix(in srgb, var(--accent) 18%, transparent); }
490
+ .expbar .expact .cbadge{ position:absolute; top:-3px; right:-3px; min-width:14px; height:14px; padding:0 3px; box-sizing:border-box;
491
+ display:grid; place-items:center; border-radius:var(--pill); background:var(--accent); color:var(--on-accent,#fff);
492
+ font-size:9.5px; font-weight:680; line-height:1; font-variant-numeric:tabular-nums; }
493
+ .expbar .expact .cbadge.amber{ background:var(--accent); }
494
+ /* the per-file / per-folder comment marker — an amber comment glyph + count in its OWN slot,
495
+ so it sits beside (never on top of) the git A/M/D badge. Folders get just a dot. */
496
+ .trow .cmtb{ flex:none; display:inline-flex; align-items:center; gap:1px; height:16px; padding:0 3px; margin-left:1px; border-radius:5px;
497
+ color:var(--accent); background:color-mix(in srgb,var(--accent) 15%, transparent); font:700 10px/1 var(--mono); font-variant-numeric:tabular-nums; }
498
+ .trow .cmtb .ico{ width:12px; height:12px; }
499
+ .trow .cmtb.dot{ width:8px; height:8px; min-width:8px; padding:0; border-radius:50%; background:var(--accent); box-shadow:0 0 0 3px var(--accent-tint); }
500
+ /* The current branch, shown beside the project name on the project switcher (git repos only). */
501
+ .projsel .pbranch{ display:inline-flex; align-items:center; gap:4px; flex:0 1 auto; min-width:0; max-width:50%; color:var(--ink-faint); font-size:11.5px; font-weight:540; }
502
+ .projsel .pbranch .ico{ width:13px; height:13px; flex:none; }
503
+ .projsel .pbranch .pbn{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
504
+
505
+ /* A row in the explorer — shared by flat leaves and tree nodes. Space is at a premium, so
506
+ There are no file/folder icons — just the folder caret + the name — and
507
+ indentation is tight. `--depth` (the nesting level) drives both the left inset and the
508
+ vertical indent guides: a 1px hairline is tiled once per ancestor level via a clipped
509
+ repeating gradient, so multi-nested folders stay legible without a wrapper per level. */
510
+ .trow{ --ind-pad:7px; --ind-step:14px; --ind-caret:18px;
511
+ position:relative; display:flex; align-items:center; gap:5px; width:100%; box-sizing:border-box;
512
+ padding:3px 8px; padding-left:calc(var(--ind-pad) + var(--depth,0)*var(--ind-step) + var(--lead,0px));
513
+ border-radius:var(--r-sm); border:0; background:transparent; text-align:left; font:inherit;
514
+ color:var(--ink-muted); cursor:pointer; text-decoration:none; min-height:24px;
515
+ background-repeat:no-repeat;
516
+ background-image:repeating-linear-gradient(90deg, var(--guide-line) 0 1px, transparent 1px var(--ind-step));
517
+ background-position:calc(var(--ind-pad) + var(--ind-caret)/2) 0;
518
+ background-size:calc(var(--depth,0) * var(--ind-step)) 100%;
519
+ transition:background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
520
+ .trow:hover{ background-color:var(--hover); color:var(--ink); }
521
+ /* the selected row's highlight reads clean — the indent guides don't run through it */
522
+ .trow.active{ background-color:color-mix(in srgb, var(--accent) 8%, var(--hover)); background-image:none; color:var(--ink); }
523
+ .trow.active::before{ content:""; position:absolute; left:2px; top:6px; bottom:6px; width:2px; border-radius:2px; background:var(--accent); }
524
+ .trow .tw{ width:var(--ind-caret); height:18px; flex:none; display:grid; place-items:center; color:var(--ink-faint); }
525
+ .trow.dir .tw{ cursor:pointer; }
526
+ .trow .tw .ico{ width:16px; height:16px; transition:transform 140ms var(--ease); }
527
+ .trow.dir.open > .tw .ico{ transform:rotate(90deg); }
528
+ .trow .lbl{ min-width:0; flex:1 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px; font-weight:500; }
529
+ .trow.dir .lbl{ font-weight:560; color:var(--ink); }
530
+ /* Git-status name tint: a changed file or a folder containing changes colours its
531
+ NAME by the aggregate change. Colours reuse the git vocabulary (--ok add, --warn modified/mixed,
532
+ --bad deleted); all clear 4.5:1 on the sidebar. Deleted also gets a line-through so it never rests
533
+ on colour alone; files additionally keep their explicit A/M/D letter badge. */
534
+ .trow.gs-add .lbl{ color:var(--ok); }
535
+ .trow.gs-mod .lbl{ color:var(--warn); }
536
+ .trow.gs-del .lbl{ color:var(--bad); text-decoration:line-through; text-decoration-thickness:1px; text-decoration-color:color-mix(in srgb, var(--bad) 60%, transparent); }
537
+ .trow .meta{ flex:none; font-size:11px; color:var(--ink-faint); font-variant-numeric:tabular-nums; }
538
+ /* The row's right-hand icons are three even 24px columns — [action][action][status] — right
539
+ aligned. The status column (`.gitslot`: the git badge for files, empty for folders) is ALWAYS in
540
+ flow so the name width never changes. The two hover actions OVERLAY on top (absolutely positioned,
541
+ so revealing them never repositions the row — no layout shift); the name fades out beneath them. */
542
+ .trow .gitslot{ flex:none; width:24px; height:24px; display:grid; place-items:center; }
543
+ .trow .rowacts{ position:absolute; top:0; bottom:0; right:32px; display:inline-flex; align-items:center; gap:0;
544
+ opacity:0; pointer-events:none; transition:opacity var(--dur) var(--ease); }
545
+ .trow:hover .rowacts, .trow:focus-within .rowacts{ opacity:1; pointer-events:auto; }
546
+ /* On hover the name fades out BEFORE the icon zone (the two action buttons start 80px from the
547
+ row's right edge) with an 8px gap, so a long name is never legible under the icons and never
548
+ touches them. The fade spans ~16px for a soft, graceful cut-off. */
549
+ .trow:hover .lbl, .trow:focus-within .lbl{
550
+ -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 104px), transparent calc(100% - 88px));
551
+ mask-image:linear-gradient(to right, #000 calc(100% - 104px), transparent calc(100% - 88px)); }
552
+ .rowact{ appearance:none; border:0; background:transparent; cursor:pointer; color:var(--ink-faint); padding:0; border-radius:5px; display:inline-grid; place-items:center; min-width:24px; min-height:24px; }
553
+ .rowact:hover{ background:rgba(128,128,140,.16); color:var(--ink); }
554
+ .rowact .ico{ width:14px; height:14px; }
555
+ .rowact[data-del]:hover{ color:var(--bad); background:var(--bad-tint); }
556
+ @media (prefers-reduced-motion:reduce){ .trow .rowacts{ transition:none; } }
557
+ /* Drag-and-drop move: the dragged row dims; the folder you're about to drop into
558
+ gets a tinted fill + inset ring; dropping on empty space targets the vault root (whole list ring). */
559
+ .trow[draggable]{ -webkit-user-drag:element; }
560
+ .trow.dragging{ opacity:.45; }
561
+ .trow.dir.droptarget{ background:var(--accent-tint); box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--accent) 50%, transparent); color:var(--ink); }
562
+ .explorerlist.droproot{ box-shadow:inset 0 0 0 2px color-mix(in srgb, var(--accent) 42%, transparent); border-radius:8px; }
563
+ .tree, .flatlist{ display:flex; flex-direction:column; gap:0; }
564
+ /* staggered first-paint flourish (matches the old list); never replays on re-render */
565
+ .explorerlist.intro .trow{ animation:casein .3s var(--ease) both; animation-delay:calc(var(--i,0)*16ms); }
566
+
567
+ /* ---- main: the transparent scroll body inside the glass container ---------- */
568
+ .main{ position:relative; z-index:2; flex:1 1 auto; min-height:0; min-width:0; overflow:auto; overscroll-behavior:contain;
569
+ display:flex; flex-direction:column; background:transparent; }
570
+ /* control bits (now living in the top bar's .topactions) */
571
+ .tb-title{ font-weight:600; letter-spacing:-.01em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
572
+ .tb-spacer{ flex:1 1 auto; }
573
+ .savedot{ display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--ink-faint); }
574
+ .savedot i{ width:7px; height:7px; border-radius:50%; background:transparent; transition:background var(--dur) var(--ease); }
575
+ .savedot.dirty i{ background:var(--warn); }
576
+ .savedot.saving i{ background:var(--accent); animation:pulse 1s var(--ease) infinite; }
577
+ @keyframes pulse{ 50%{ opacity:.3; } }
578
+ @keyframes reveal{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; transform:none; } }
579
+ @keyframes casein{ from{ opacity:0; transform:translateX(-6px); } to{ opacity:1; transform:none; } }
580
+ .iconbtn{ appearance:none; border:0; background:transparent; color:var(--ink-muted); cursor:pointer; padding:6px; border-radius:var(--r-sm);
581
+ display:none; align-items:center; }
582
+ .iconbtn:hover{ background:rgba(128,128,140,.10); color:var(--ink); }
583
+ .iconbtn .ico{ width:20px; height:20px; }
584
+
585
+ /* reading / editing canvas */
586
+ .canvaswrap{ flex:1 1 auto; min-width:0; display:flex; justify-content:center; position:relative; }
587
+ /* padding-left carries an extra ~14px "comment gutter" so the hover + handle has room to sit clear
588
+ of the text instead of rendering over it. */
589
+ .canvas{ width:100%; max-width:min(var(--doc-w),100%); padding:var(--s7) var(--s6) 22vh calc(var(--s6) + 14px); animation:reveal .42s var(--ease) both; }
590
+ /* long unbreakable tokens (URLs, IDs) wrap instead of forcing a horizontal scroll on narrow screens */
591
+ .canvas :is(p,li,h1,h2,h3,h4,h5,h6,blockquote,td,th){ overflow-wrap:anywhere; }
592
+ .banner{ display:flex; gap:10px; align-items:flex-start; margin-bottom:var(--s5); padding:12px 14px;
593
+ background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-lg); color:var(--ink-muted); font-size:14px; }
594
+ .banner .ico{ width:18px; height:18px; flex:none; color:var(--accent); margin-top:1px; }
595
+
596
+ /* rendered markdown */
597
+ /* .blk = one live-preview block wrapper (rendered by default; its raw source is
598
+ revealed in place when the caret enters it — see .blk.editing). The wrapper is
599
+ the STABLE element across the rendered↔source swap so entering edit never swaps
600
+ a node or reflows the block. */
601
+ .blk{ border-radius:var(--r-sm); margin:0 -6px; padding:1px 6px; cursor:text; transition:background 120ms var(--ease); }
602
+ .blk:hover:not(.editing):not(.active){ background:rgba(128,128,140,.045); }
603
+ /* active-line highlight: the block holding the caret (a collapsed selection). Cleared as
604
+ soon as text is selected or the caret leaves, so nothing lingers. Subtle, no border. */
605
+ .blk.active{ background:color-mix(in srgb, var(--accent) 7%, transparent); }
606
+ .blk > :is(h1,h2,h3,h4,h5,h6,p,ul,ol,blockquote,pre,table,hr):first-child{ margin-top:0; }
607
+ .blk.empty::before{ content:attr(data-placeholder); color:var(--ink-faint); }
608
+ .blk-cmt{ color:var(--ink-faint); font-family:var(--mono); font-size:12px; opacity:.6; }
609
+ .md-h{ letter-spacing:-.014em; text-wrap:pretty; line-height:1.25; margin:0; }
610
+ .md-h1{ font-family:var(--serif); font-size:2rem; font-weight:600; letter-spacing:-.012em; line-height:1.14; }
611
+ .md-h2{ font-size:1.32rem; font-weight:640; }
612
+ .md-h3{ font-size:1.08rem; font-weight:640; }
613
+ .md-h4{ font-size:.94rem; font-weight:660; color:var(--ink-muted); letter-spacing:.005em; }
614
+ .md-h5,.md-h6{ font-size:.88rem; font-weight:640; color:var(--ink-muted); }
615
+ .md-p{ margin:0; text-wrap:pretty; }
616
+ .md-list{ margin:0; padding-left:1.4em; }
617
+ .md-list li{ margin:.15em 0; }
618
+ .md-list .md-list{ margin:.15em 0; }
619
+ /* GitHub-style task list: no bullet, a real checkbox aligned to the first text line.
620
+ The box carries the app accent and is clickable even inside the contenteditable canvas
621
+ (it's a contenteditable=false island); a done item's label reads dimmed + struck. */
622
+ .md-list li.md-task{ list-style:none; margin-left:-1.4em; padding-left:0; display:flex; align-items:flex-start; gap:.5em; }
623
+ .md-taskbox{ appearance:auto; -webkit-appearance:auto; accent-color:var(--accent); width:1em; height:1em;
624
+ margin:.25em 0 0; flex:0 0 auto; cursor:pointer; }
625
+ .md-task > .md-tasktxt{ flex:1 1 auto; }
626
+ .md-task > .md-taskbox:checked + .md-tasktxt{ color:var(--ink-faint); text-decoration:line-through; }
627
+ .md-list li.md-task .md-list{ flex-basis:100%; margin-left:1.4em; }
628
+ .md-bq{ margin:0; padding:2px 0 2px 16px; border-left:2px solid var(--hairline); color:var(--ink-muted); }
629
+ .md-hr{ border:0; border-top:1px solid var(--hairline); margin:0; }
630
+ .md-img{ max-width:100%; height:auto; border-radius:8px; display:block; margin:.2em 0; }
631
+ .md-tbl{ border-collapse:collapse; width:100%; font-size:14.5px; }
632
+ .md-tbl th,.md-tbl td{ text-align:left; padding:7px 12px; border-bottom:1px solid var(--hairline); vertical-align:top; min-width:var(--tbl-mincol); }
633
+ .md-tbl thead th{ background:var(--hover); font-weight:660; color:var(--ink); font-size:12px; letter-spacing:.02em;
634
+ border-bottom:1.5px solid color-mix(in srgb, var(--ink-faint) 55%, transparent); }
635
+ .md-tbl thead th:first-child{ border-top-left-radius:var(--r-xs); }
636
+ .md-tbl thead th:last-child{ border-top-right-radius:var(--r-xs); }
637
+ .md-tbl tbody tr:nth-child(even) td{ background:color-mix(in srgb, var(--hover) 45%, transparent); }
638
+ .md-tbl tbody tr:last-child td{ border-bottom:0; }
639
+ /* A wide table scrolls horizontally on ITS OWN (the wrapper is the only scroll
640
+ container) instead of pushing the record wider than its column. Columns hold a
641
+ minimum width (--tbl-mincol) so prose never crushes to one word per line; once the
642
+ min widths exceed the container, only this wrapper scrolls — the prose around it
643
+ stays put. In the live editor the equivalent scroll container is `.blk.t-table`. */
644
+ .md-tblwrap{ overflow-x:auto; overscroll-behavior-x:contain; max-width:100%; margin:0; }
645
+ .blk.t-table{ overflow-x:auto; overscroll-behavior-x:contain; }
646
+ /* Table shape controls (edit mode only): add/delete a row or column. Rendered as a floating
647
+ overlay pinned just under the grid's bottom-right so ENTERING edit mode never reflows the
648
+ document (the table stays put). The editing block drops its own overflow clip
649
+ and delegates horizontal scroll to an inner wrapper, so the overlay is never clipped. */
650
+ .blk.editing.t-table{ position:relative; overflow:visible; }
651
+ .ed-tblwrap{ overflow-x:auto; overscroll-behavior-x:contain; max-width:100%; }
652
+ .tbl-tools{ position:absolute; top:100%; right:0; margin-top:5px; z-index:2; display:flex; align-items:center; gap:4px; user-select:none;
653
+ flex-wrap:wrap; padding:3px; border-radius:calc(var(--r-sm) + 3px); background:color-mix(in srgb, var(--surface) 88%, transparent);
654
+ backdrop-filter:blur(6px); box-shadow:var(--shadow); animation:ed-tools-in .16s var(--ease) both; }
655
+ @keyframes ed-tools-in{ from{ opacity:0; transform:translateY(-4px); } to{ opacity:1; transform:none; } }
656
+ @media (prefers-reduced-motion:reduce){ .tbl-tools{ animation:none; } }
657
+ .tbl-tools button{ appearance:none; border:1px solid var(--hairline); background:var(--surface); color:var(--ink-muted); cursor:pointer;
658
+ font:inherit; font-size:11.5px; font-weight:600; padding:3px 9px; border-radius:var(--r-sm); display:inline-flex; align-items:center; gap:4px;
659
+ transition:background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease); }
660
+ .tbl-tools button .tt-sign{ font-size:12px; line-height:1; color:var(--ink-faint); }
661
+ .tbl-tools button:hover{ background:var(--hover); color:var(--ink); border-color:color-mix(in srgb, var(--accent) 40%, var(--hairline)); }
662
+ .tbl-tools button:hover .tt-sign{ color:var(--accent); }
663
+ .tbl-tools button[data-tbl^="del"]:hover{ color:var(--bad); border-color:color-mix(in srgb, var(--bad) 40%, var(--hairline)); }
664
+ .tbl-tools button[data-tbl^="del"]:hover .tt-sign{ color:var(--bad); }
665
+ .tbl-tools-sep{ width:1px; height:15px; background:var(--hairline); margin:0 3px; }
666
+ .md-pre{ margin:0; padding:12px 14px; background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-md);
667
+ overflow:auto; font-family:var(--mono); font-size:13px; line-height:1.55; }
668
+ .md-p code,.md-list code,.md-tbl code,.md-bq code{ font-family:var(--mono); font-size:.86em; background:rgba(128,128,140,.14);
669
+ padding:.1em .34em; border-radius:5px; }
670
+ .canvas a{ color:var(--accent); text-decoration:none; cursor:pointer; border-bottom:1px solid color-mix(in srgb,var(--accent) 35%, transparent); }
671
+ .canvas a:hover{ border-bottom-color:var(--accent); }
672
+
673
+ /* the score pill in the top strip doubles as a jump to the score reasoning when
674
+ the record has one. It stays a chip (its verdict colour is the score, not an
675
+ action) — the hover is a NEUTRAL liquid-glass lift (a soft top highlight + a
676
+ quiet shadow), never a coloured ring, so clicking never reads as destructive.
677
+ A small arrow reveals on hover to signal "jump". Plain span when nowhere to go. */
678
+ button.pill.jump{ position:relative; border:0; cursor:pointer; font:inherit; -webkit-appearance:none; appearance:none;
679
+ padding-right:8px; -webkit-backdrop-filter:var(--blur); backdrop-filter:var(--blur);
680
+ transition:box-shadow .18s var(--ease), transform .18s var(--ease); }
681
+ button.pill.jump::after{ content:"→"; margin-left:1px; font-weight:700; opacity:0; max-width:0; overflow:hidden;
682
+ transition:opacity .18s var(--ease), max-width .18s var(--ease), transform .18s var(--ease); }
683
+ button.pill.jump:hover, button.pill.jump:focus-visible{
684
+ transform:translateY(-.5px);
685
+ box-shadow:0 2px 6px -2px rgba(18,24,30,.22), inset 0 1px 0 rgba(255,255,255,.5); }
686
+ button.pill.jump:hover::after, button.pill.jump:focus-visible::after{ opacity:.75; max-width:1.1em; transform:translateX(1px); }
687
+ button.pill.jump:active{ transform:translateY(0); box-shadow:inset 0 1px 2px rgba(18,24,30,.16); }
688
+ button.pill.jump:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
689
+ /* the combined live-preview document: one contiguous rendered article that is itself the
690
+ single contenteditable surface. Vertical rhythm lives on the .blk wrappers (stable across
691
+ the rendered↔source swap), so entering/leaving edit never shifts spacing. No focus ring on
692
+ the surface — the caret is the cue, not a box around the whole record. */
693
+ .doc{ outline:none; }
694
+ .doc:focus, .doc:focus-visible{ outline:none; }
695
+ .doc > :is(.blk, .diffdel) + :is(.blk, .diffdel){ margin-top:.72em; }
696
+ .doc > :is(.blk, .diffdel).t-h2{ margin-top:1.5em; }
697
+ .doc > :is(.blk, .diffdel).t-h1{ margin-top:0; }
698
+ .doc > :is(.blk, .diffdel).t-h3{ margin-top:1.1em; }
699
+ .doc > :is(.blk, .diffdel):first-child{ margin-top:0; }
700
+ /* Read-only rendered markdown (reports, matchups, plain files) is a flat sequence of .md-* elements
701
+ directly under .doc — it isn't wrapped in .blk blocks, so it carries its own vertical
702
+ rhythm here (the case editor gets its spacing from the .blk wrappers above instead).
703
+ `:not(:first-child)` keeps the leading title flush to the top. */
704
+ .doc > :is(.md-p,.md-list,.md-bq,.md-pre,.md-hr,.md-tblwrap):not(:first-child){ margin-top:.72em; }
705
+ .doc > .md-h1:not(:first-child){ margin-top:1.4em; }
706
+ .doc > .md-h2:not(:first-child){ margin-top:1.5em; }
707
+ .doc > .md-h3:not(:first-child){ margin-top:1.15em; }
708
+ .doc > :is(.md-h4,.md-h5,.md-h6):not(:first-child){ margin-top:1.05em; }
709
+ .doc :is(h1,h2,h3,h4,h5,h6){ scroll-margin-top:18px; }
710
+ /* a span the reader has left an open comment on — a quiet, legible ember underline */
711
+ .doc .cmark{ background:var(--accent-tint); border-radius:3px; box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 20%, transparent) inset; cursor:pointer; padding:0 1px;
712
+ transition:box-shadow var(--dur) var(--ease); }
713
+ .doc .cmark:hover{ box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 45%, transparent); }
714
+ .doc .cmark.hot{ background:var(--accent-tint); box-shadow:0 0 0 3px var(--accent-tint); }
715
+ /* the anchor whose card is currently active — a stronger, persistent amber fill + ring so it's
716
+ unmistakable which span the open comment applies to (cleared when another card is focused). */
717
+ .doc .cmark.on{ background:color-mix(in srgb,var(--accent) 30%, transparent);
718
+ box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 60%, transparent); }
719
+ /* block-level anchor: a whole-block / multi-element comment (a bullet list, table, or several
720
+ paragraphs the quote can't be wrapped inline). The block gets a soft amber wash + a rounded ring
721
+ so it still reads as "commented" and lights up when its card is clicked. */
722
+ .doc .cmark-blk{ cursor:pointer; border-radius:var(--r-sm); box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 22%, transparent) inset;
723
+ background:color-mix(in srgb,var(--accent-tint) 40%, transparent); transition:box-shadow var(--dur) var(--ease), background var(--dur) var(--ease); }
724
+ .doc .cmark-blk:hover{ box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 40%, transparent); }
725
+ .doc .cmark-blk.hot{ background:var(--accent-tint); }
726
+ .doc .cmark-blk.on{ background:color-mix(in srgb,var(--accent) 20%, transparent); box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 55%, transparent); }
727
+
728
+ /* markdown syntax highlight, shared by the revealed block source */
729
+ .ed-mk{ color:var(--ink-faint); opacity:.62; } /* markdown punctuation, dimmed */
730
+ .ed-cmt{ color:var(--ink-faint); opacity:.6; font-style:italic; }
731
+ .ed-b{ font-weight:700; }
732
+ .ed-i{ font-style:italic; }
733
+ .ed-code{ font-family:var(--mono); font-size:.88em; color:var(--accent); }
734
+ .ed-link{ color:var(--accent); }
735
+ /* Inline reveal: while editing a block, an inline token's markdown
736
+ markers (** * _ ` [ ]( ) ) are hidden — so **bold** simply reads as bold — and only
737
+ reappear when the caret is inside that token (.ed-tok.on). The markers stay in the DOM
738
+ (display:none, so still in textContent) → the block source round-trips losslessly. */
739
+ .blk.editing .ed-tok .ed-mk{ display:none; }
740
+ .blk.editing .ed-tok.on .ed-mk{ display:inline; }
741
+ /* .blk.editing = the block whose raw source is revealed. white-space:pre-wrap keeps literal
742
+ newlines/spaces (so the block source round-trips losslessly); a .t-* type class matches the
743
+ block's rendered type (size/weight/serif) so revealing the source never resizes or reflows
744
+ the block. No box/border: the revealed markers + native caret are
745
+ the only "you're here" cue, so nothing lingers when the caret leaves or a selection spans. */
746
+ .blk.editing{ white-space:pre-wrap; word-break:break-word; overflow-wrap:anywhere; outline:none;
747
+ caret-color:var(--accent); }
748
+ .blk.editing.t-h1{ font-family:var(--serif); font-size:2rem; font-weight:600; letter-spacing:-.012em; line-height:1.14; }
749
+ .blk.editing.t-h2{ font-size:1.32rem; font-weight:640; letter-spacing:-.014em; line-height:1.25; }
750
+ .blk.editing.t-h3{ font-size:1.08rem; font-weight:640; letter-spacing:-.014em; line-height:1.25; }
751
+ .blk.editing.t-h4{ font-size:.94rem; font-weight:660; color:var(--ink-muted); letter-spacing:.005em; }
752
+ .blk.editing.t-h5,.blk.editing.t-h6{ font-size:.88rem; font-weight:640; color:var(--ink-muted); }
753
+ .blk.editing.t-code{ font-family:var(--mono); font-size:13px; line-height:1.55; }
754
+ .blk.editing.t-table{ font-family:var(--mono); font-size:13px; line-height:1.55; }
755
+ /* table + list widgets edit as real cells/items, not pre-wrapped source */
756
+ .blk.editing.t-table, .blk.editing.t-list{ white-space:normal; }
757
+ .blk.editing.t-list{ font-family:var(--sans); }
758
+ .md-tbl .ed-cell{ outline:none; cursor:text; min-width:2ch; border-radius:4px; }
759
+ .md-tbl .ed-cell:focus{ box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--accent) 45%, transparent); }
760
+ .ed-list{ outline:none; }
761
+ .ed-list li{ cursor:text; }
762
+
763
+ /* the prototype button. Default = a solid ember pill (primary action, white/on-accent
764
+ text clears 4.5:1 on both themes). For a REJECTED case it downshifts to `.quiet` — a
765
+ neutral ghost link — so the build path never shouts louder than a "don't build" verdict. */
766
+ .tbtn{ display:inline-flex; align-items:center; gap:7px; height:34px; padding:0 15px; border-radius:var(--pill);
767
+ border:0; background:var(--accent); color:var(--on-accent);
768
+ box-shadow:var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,.22);
769
+ font:inherit; font-size:12.5px; font-weight:640; cursor:pointer; text-decoration:none;
770
+ transition:background var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease); }
771
+ .tbtn:hover{ background:var(--accent-hover); box-shadow:var(--glow), inset 0 1px 0 rgba(255,255,255,.3); transform:translateY(-1px); }
772
+ .tbtn:active{ background:var(--accent-strong); box-shadow:var(--shadow-sm), inset 0 1px 2px rgba(0,0,0,.14); transform:translateY(0); }
773
+ .tbtn .ico{ width:14px; height:14px; color:var(--on-accent); }
774
+ /* Secondary (frosted) top-bar button — Build-prototype hint; the workbench default weight. */
775
+ .tbtn.ghost{ background:var(--surface); color:var(--ink); border:1px solid var(--hairline);
776
+ box-shadow:var(--shadow-sm); font-weight:600; }
777
+ .tbtn.ghost:hover{ background:var(--surface); border-color:color-mix(in srgb,var(--accent) 45%, var(--hairline)); box-shadow:var(--glow); transform:translateY(-1px); }
778
+ .tbtn.ghost:active{ transform:translateY(0); box-shadow:var(--shadow-sm); }
779
+ .tbtn.ghost .ico{ width:15px; height:15px; color:var(--ink-muted); }
780
+ /* Read-only indicator (a case being generated by the agent). */
781
+ .rochip{ display:inline-flex; align-items:center; gap:6px; height:26px; padding:0 11px; border-radius:var(--pill);
782
+ background:color-mix(in srgb,var(--accent) 12%, transparent); color:var(--accent);
783
+ font-size:12px; font-weight:600; white-space:nowrap; }
784
+ .rochip .ico{ width:14px; height:14px; }
785
+ /* A read-only record surface: normal text cursor, no editing chrome. */
786
+ .doc.ro{ cursor:default; }
787
+ .doc.ro .blk{ cursor:default; }
788
+ /* The "being generated" banner — accent-tinted, distinct from the neutral empty banner. */
789
+ .banner.gen{ border-color:color-mix(in srgb,var(--accent) 30%, var(--hairline)); background:color-mix(in srgb,var(--accent) 8%, var(--surface)); }
790
+ .banner.gen .ico{ color:var(--accent); }
791
+ /* Command popover (Build-prototype hint) — an opaque raised tier over the atmosphere. */
792
+ .cmdpop{ position:fixed; z-index:var(--z-dropdown); width:min(360px,calc(100vw - 16px)); padding:14px; border-radius:var(--r-lg);
793
+ background:var(--raised); border:1px solid var(--glass-brd); box-shadow:var(--shadow);
794
+ animation:reveal .14s var(--ease) both; }
795
+ .cmdpop-t{ font-weight:640; font-size:13.5px; margin-bottom:4px; }
796
+ .cmdpop-d{ margin:0 0 10px; font-size:12.5px; color:var(--ink-muted); line-height:1.45; }
797
+ .cmdpop-row{ display:flex; gap:8px; align-items:stretch; }
798
+ .cmdpop-c{ flex:1 1 auto; min-width:0; display:flex; align-items:center; padding:0 10px; border-radius:var(--r-sm);
799
+ background:var(--surface); border:1px solid var(--hairline); font:500 12.5px/1 var(--mono); color:var(--ink);
800
+ overflow:auto; white-space:nowrap; }
801
+ .cmdpop-copy{ flex:none; display:inline-flex; align-items:center; gap:6px; padding:8px 12px; border-radius:var(--r-sm);
802
+ border:0; background:var(--accent); color:var(--on-accent); font:inherit; font-size:12.5px; font-weight:640; cursor:pointer;
803
+ transition:background var(--dur) var(--ease); }
804
+ .cmdpop-copy:hover{ background:var(--accent-hover); }
805
+ .cmdpop-copy .ico{ width:14px; height:14px; color:var(--on-accent); }
806
+ /* Danger top-bar button (Discard changes) — quiet by default, fills on hover. */
807
+ .tbtn.danger{ background:transparent; color:var(--bad); border:1px solid color-mix(in srgb,var(--bad) 34%, var(--hairline)); box-shadow:none; font-weight:600; }
808
+ .tbtn.danger:hover{ background:color-mix(in srgb,var(--bad) 14%, transparent); border-color:var(--bad); box-shadow:none; transform:translateY(-1px); }
809
+ .tbtn.danger .ico{ width:15px; height:15px; color:var(--bad); }
810
+ /* Confirmation dialog (destructive actions) — an opaque raised sheet over a dimmed scrim. */
811
+ dialog.confirm{ border:0; padding:0; background:transparent; color:inherit; max-width:min(440px,calc(100vw - 32px)); }
812
+ dialog.confirm::backdrop{ background:rgba(6,9,14,.52); backdrop-filter:blur(2px); }
813
+ .confirm-in{ background:var(--raised); border:1px solid var(--glass-brd); border-radius:var(--r-xl); box-shadow:var(--shadow-lg);
814
+ padding:22px 22px 18px; animation:reveal .16s var(--ease) both; }
815
+ .confirm-in h2{ margin:0 0 8px; font-size:16px; font-weight:660; letter-spacing:-.01em; }
816
+ .confirm-b{ margin:0 0 20px; font-size:13.5px; line-height:1.55; color:var(--ink-muted); }
817
+ .confirm-b b{ color:var(--ink); font-weight:600; }
818
+ .confirm-actions{ display:flex; justify-content:flex-end; gap:10px; }
819
+ .cbtn{ appearance:none; font:inherit; font-size:13px; font-weight:600; padding:9px 16px; border-radius:var(--r-sm); cursor:pointer;
820
+ border:1px solid transparent; transition:background var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease); }
821
+ .cbtn:active{ transform:translateY(1px); }
822
+ .cbtn.ghost{ background:transparent; color:var(--ink-muted); border-color:var(--hairline); }
823
+ .cbtn.ghost:hover{ background:rgba(128,128,140,.10); color:var(--ink); }
824
+ .cbtn.primary{ background:var(--accent); color:var(--on-accent); }
825
+ .cbtn.primary:hover{ background:var(--accent-hover); }
826
+ .cbtn.danger{ background:var(--bad); color:#fff; }
827
+ .cbtn.danger:hover{ filter:brightness(1.08); }
828
+ .cbtn:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
829
+ /* the New-file / New-folder prompt: a labelled single-line input inside the same dialog shell.
830
+ The destination folder is shown in full ABOVE the input (wrapping, never truncated) so a
831
+ deeply-nested path stays legible; the dialog is a touch wider to give it room. */
832
+ .confirm.prompt .confirm-in{ gap:0; }
833
+ dialog.confirm.prompt{ max-width:min(540px, calc(100vw - 32px)); width:min(468px, calc(100vw - 32px)); }
834
+ .prompt-path{ display:flex; align-items:flex-start; gap:8px; margin:0 0 16px; min-width:0; }
835
+ .prompt-path-cap{ flex:none; margin-top:6px; font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-faint); }
836
+ .prompt-path-val{ flex:1 1 auto; min-width:0; font-family:var(--mono); font-size:12.5px; line-height:1.55; color:var(--ink-muted);
837
+ background:var(--glass); border:1px solid var(--hairline); border-radius:var(--r-sm); padding:5px 9px; word-break:break-all; }
838
+ .prompt-l{ display:block; margin:0 0 20px; font-size:12px; font-weight:600; letter-spacing:.02em; color:var(--ink-muted); }
839
+ .prompt-row{ display:flex; align-items:stretch; margin-top:8px; border:1px solid var(--hairline); border-radius:var(--r-sm); overflow:hidden;
840
+ background:var(--glass); transition:border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
841
+ .prompt-row:focus-within{ border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-tint); }
842
+ .prompt-i{ flex:1 1 auto; min-width:0; appearance:none; border:0; background:transparent; color:var(--ink); font:inherit; font-size:13.5px; padding:10px 12px; }
843
+ .prompt-i:focus{ outline:none; }
844
+
845
+ /* ---- folder picker (Open folder…): a server-side directory browser ---------
846
+ A browser can't return an absolute path from a native dialog, so the local daemon lists
847
+ directories and the user drills in. Frosted-glass modal, ember accent. */
848
+ dialog.folderpick{ max-width:min(560px, calc(100vw - 32px)); width:560px; }
849
+ .fp{ gap:0; padding:0; overflow:hidden; }
850
+ .fp-head{ padding:20px 22px 12px; border-bottom:1px solid var(--hairline); }
851
+ .fp-head h2{ margin:0 0 12px; }
852
+ .fp-crumbs{ display:flex; align-items:center; flex-wrap:wrap; gap:1px; font-size:12.5px; min-height:22px; }
853
+ .fp-crumb{ appearance:none; border:0; background:transparent; color:var(--ink-muted); font:inherit; font-size:12.5px; font-weight:560;
854
+ padding:3px 7px; border-radius:var(--r-xs); cursor:pointer; max-width:200px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
855
+ .fp-crumb:hover{ background:var(--hover); color:var(--ink); }
856
+ .fp-crumb:last-child{ color:var(--ink); cursor:default; }
857
+ .fp-crumb:last-child:hover{ background:transparent; }
858
+ .fp-crumb:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }
859
+ .fp-crumb .ico{ width:14px; height:14px; }
860
+ .fp-sep{ color:var(--ink-faint); flex:none; opacity:.6; display:inline-flex; }
861
+ .fp-sep .ico{ width:13px; height:13px; }
862
+ .fp-body{ height:min(46vh, 360px); overflow-y:auto; overscroll-behavior:contain; padding:6px; }
863
+ .fp-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:1px; transition:opacity .12s ease; }
864
+ .fp-loading .fp-list{ opacity:.4; }
865
+ .fp-row{ display:flex; align-items:center; gap:10px; width:100%; text-align:left; appearance:none; border:0; background:transparent;
866
+ color:var(--ink); font:inherit; font-size:14px; padding:9px 11px; border-radius:var(--r-sm); cursor:pointer; }
867
+ .fp-row:hover{ background:var(--hover); }
868
+ .fp-row.sel{ background:var(--accent-tint); }
869
+ .fp-row:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }
870
+ .fp-row .fp-fico{ width:17px; height:17px; flex:none; color:var(--accent); opacity:.9; }
871
+ .fp-row .fp-fico .ico{ width:17px; height:17px; }
872
+ .fp-row .fp-name{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
873
+ .fp-row .fp-go{ width:15px; height:15px; flex:none; color:var(--ink-faint); opacity:0; transition:opacity .12s ease; }
874
+ .fp-row .fp-go .ico{ width:15px; height:15px; }
875
+ .fp-row:hover .fp-go, .fp-row.sel .fp-go, .fp-row:focus-visible .fp-go{ opacity:1; }
876
+ .fp-empty{ padding:26px 14px; text-align:center; color:var(--ink-faint); font-size:13px; }
877
+ .fp-foot{ display:flex; align-items:center; justify-content:flex-end; padding:14px 18px 16px; border-top:1px solid var(--hairline); }
878
+ .fp-foot .confirm-actions{ margin:0; }
879
+ .fp-foot .confirm-actions{ flex:none; margin:0; }
880
+ @media (prefers-reduced-motion: reduce){ .fp-list, .fp-row .fp-go{ transition:none; } }
881
+
882
+ /* case layout: reading canvas + a sticky rail (the table of contents).
883
+ flex:1 0 auto (grow, never shrink) makes .casebody grow to its content height inside
884
+ the flex-column .main — so the sticky rail's containing block spans the whole record
885
+ and the rail stays pinned for the ENTIRE scroll (not just the first viewport). */
886
+ .casebody{ flex:1 0 auto; display:grid; grid-template-columns:minmax(0,1fr) var(--toc); align-items:start; }
887
+ .casebody.wide{ grid-template-columns:minmax(0,1fr); }
888
+ .rail{ position:sticky; top:var(--s5); align-self:start; max-height:calc(100dvh - 40px); overflow:auto;
889
+ padding:6px var(--s5) var(--s6) 6px; display:flex; flex-direction:column; gap:var(--s5); }
890
+ .toc-h{ font-size:11.5px; font-weight:600; letter-spacing:0; color:var(--ink-muted);
891
+ margin:0 0 10px 13px; display:flex; align-items:center; gap:8px; }
892
+ .toc a{ display:block; text-decoration:none; color:var(--ink-muted); padding:5px 10px 5px 13px; border-left:2px solid transparent;
893
+ line-height:1.4; font-size:13.5px; transition:color var(--dur) var(--ease), border-color var(--dur) var(--ease), background var(--dur) var(--ease);
894
+ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
895
+ .toc a:hover{ color:var(--ink); background:var(--hover); }
896
+ .toc a.lvl3{ padding-left:27px; font-size:12.5px; color:var(--ink-faint); }
897
+ .toc a.active{ color:var(--accent); border-left-color:var(--accent); font-weight:560; }
898
+ .toc a.active.lvl3{ color:var(--accent); }
899
+
900
+ /* ToC: a section with open comments carries a small ember dot */
901
+ .toc a{ position:relative; }
902
+ .toc a .tdot{ position:absolute; right:8px; top:50%; transform:translateY(-50%); width:6px; height:6px; border-radius:50%;
903
+ background:var(--accent); box-shadow:0 0 0 3px var(--accent-tint); }
904
+
905
+ /* ---- in-record comments (amber, always-visible) ----------------------------
906
+ Comments are a THIRD decoration colour: amber = "a human wants a change", deliberately distinct
907
+ from the diff's green (added) / red (removed) — they can co-exist on screen. The anchored span
908
+ gets a soft amber highlight; the comment BODY is always visible as an amber card, either inline
909
+ in the document flow ('inline' mode) or in the right rail ('rail' mode) — never hidden behind a
910
+ click-to-open bubble. The single amber comment affordance appears on block-hover OR by a text
911
+ selection, never both. */
912
+ .cgutter{ position:absolute; inset:0; pointer-events:none; z-index:5; }
913
+ .cgutter > *{ pointer-events:auto; }
914
+ /* the ONE comment affordance — identical amber comment button whether in the gutter (hover) or
915
+ floating by a selection, so they always match. */
916
+ .cadd, .askbtn{ width:26px; height:26px; padding:0; align-items:center; justify-content:center; box-sizing:border-box;
917
+ border-radius:var(--r-sm); border:1px solid color-mix(in srgb,var(--accent) 32%, var(--glass-brd)); background:var(--raised); color:var(--accent);
918
+ cursor:pointer; box-shadow:var(--shadow-sm); transition:background var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease); }
919
+ .cadd:hover, .askbtn:hover{ background:var(--accent-tint); border-color:color-mix(in srgb,var(--accent) 58%, var(--glass-brd)); }
920
+ .cadd .ico, .askbtn .ico{ width:15px; height:15px; }
921
+ .cadd{ position:absolute; display:none; opacity:0; }
922
+ .cadd.show{ display:inline-flex; opacity:1; animation:cfade .14s var(--ease) both; }
923
+ .askbtn{ position:fixed; z-index:var(--z-dropdown); display:inline-flex; }
924
+ @keyframes cfade{ from{ opacity:0; transform:scale(.88); } to{ opacity:1; transform:none; } }
925
+ @media (prefers-reduced-motion:reduce){ .cadd.show{ animation:none; } }
926
+
927
+ /* the comment card — amber fill + border, always visible (inline island or rail column) */
928
+ .cnote{ display:block; width:100%; box-sizing:border-box; text-align:left; cursor:pointer;
929
+ border:1px solid color-mix(in srgb,var(--accent) 26%, var(--glass-brd)); border-radius:var(--r-md);
930
+ background:color-mix(in srgb,var(--accent-tint) 55%, var(--raised)); padding:9px 11px;
931
+ transition:border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
932
+ .cnote + .cnote{ margin-top:8px; }
933
+ .cnote:hover{ border-color:color-mix(in srgb,var(--accent) 46%, var(--glass-brd)); }
934
+ .cnote.active{ border-color:color-mix(in srgb,var(--accent) 60%, var(--glass-brd)); box-shadow:0 0 0 3px var(--accent-tint); }
935
+ .cnote-q{ font-size:11.5px; color:var(--ink-faint); border-left:2px solid var(--accent); padding-left:8px; margin-bottom:7px;
936
+ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
937
+ .cnote-b{ font-size:13px; line-height:1.5; color:var(--ink); white-space:pre-wrap; }
938
+ .cnote-f{ display:flex; align-items:center; gap:6px; margin-top:8px; font-size:11px; color:var(--ink-faint); }
939
+ .cnote-who{ font-weight:640; color:var(--ink-muted); }
940
+ .cnote-sp{ flex:1 1 auto; }
941
+ .cnote-act{ appearance:none; border:0; background:transparent; color:var(--ink-faint); cursor:pointer; width:22px; height:22px;
942
+ border-radius:5px; display:grid; place-items:center; transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
943
+ .cnote-act:hover{ background:var(--accent-tint); color:var(--accent); }
944
+ .cnote-del:hover{ background:color-mix(in srgb,var(--bad) 16%, transparent); color:var(--bad); }
945
+ .cnote-act .ico{ width:14px; height:14px; }
946
+ /* inline edit: the card body becomes a textarea + Save / Cancel */
947
+ .cnote.editing{ cursor:default; }
948
+ .cnote.editing .cnote-act{ display:none; } /* editing already offers Save/Cancel — hide the row's edit/delete */
949
+ .cnote-edit{ width:100%; box-sizing:border-box; resize:vertical; min-height:52px; font:inherit; font-size:13px; line-height:1.5;
950
+ color:var(--ink); background:var(--raised); border:1px solid color-mix(in srgb,var(--accent) 34%, var(--glass-brd)); border-radius:var(--r-sm);
951
+ padding:7px 9px; }
952
+ .cnote-edit:focus{ outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-tint); }
953
+ .cnote-editrow{ display:flex; justify-content:flex-end; gap:6px; margin-top:7px; }
954
+ .cnote-editrow .cbtn{ appearance:none; cursor:pointer; font:inherit; font-size:12px; font-weight:600; padding:4px 12px; border-radius:var(--r-sm);
955
+ border:1px solid var(--hairline); background:var(--surface); color:var(--ink-muted); transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
956
+ .cnote-editrow .cbtn.ghost:hover{ background:var(--hover); color:var(--ink); }
957
+ .cnote-editrow .cbtn.primary{ border-color:transparent; background:var(--accent); color:var(--on-accent); }
958
+ .cnote-editrow .cbtn.primary:hover{ filter:brightness(1.04); }
959
+ /* inline island in the doc flow ('inline' mode): a non-editable amber card under its block.
960
+ white-space:normal because #doc is contenteditable (pre-wrap) and would otherwise render the
961
+ card template's inter-element whitespace as blank lines, inflating the card height. */
962
+ .cnote-island{ margin:6px 0 12px; user-select:none; white-space:normal; }
963
+ .doc > .cnote-island{ animation:cfade .16s var(--ease) both; }
964
+ @media (prefers-reduced-motion:reduce){ .doc > .cnote-island{ animation:none; } }
965
+
966
+ /* the rail comments column ('rail' mode) + its non-sticky hint (the count/nav live in the command bar) */
967
+ .comments{ display:flex; flex-direction:column; gap:8px; padding:0 4px 4px 13px; }
968
+ /* Aligned rail (Google-Docs margin notes): the rail drops its sticky/scroll behaviour and spans
969
+ the full document height so each card can be absolutely placed beside its anchor line. The hint
970
+ box and the cards share ONE horizontal geometry (--crail-l / --crail-r) so the hint lines up with
971
+ the cards. Rail horizontal padding is zeroed here so the hint's content box and the cards'
972
+ containing block (the padding box) have identical edges. Nothing here is sticky — the hint and
973
+ cards scroll together, below the fixed command bar (which reserveRailForControls pads clear of). */
974
+ .rail.cm-aligned{ --crail-l:14px; --crail-r:6px; position:relative; max-height:none; overflow:visible; align-self:stretch;
975
+ padding-left:0; padding-right:0; }
976
+ .rail.cm-aligned .crail-head{ margin:0; padding:0 var(--crail-r) 12px var(--crail-l); border:0; background:none; }
977
+ .comments.aligned{ position:absolute; inset:0; display:block; padding:0; }
978
+ /* width:auto so the absolute left/right define the card box (the base .cnote width:100% would
979
+ otherwise win and overflow the rail). */
980
+ .comments.aligned .cnote{ position:absolute; left:var(--crail-l); right:var(--crail-r); width:auto; margin:0;
981
+ overflow-wrap:anywhere; word-break:break-word; transition:top var(--dur) var(--ease); }
982
+ .comments.aligned .cnote .cnote-b{ overflow-wrap:anywhere; word-break:break-word; }
983
+ @media (prefers-reduced-motion:reduce){ .comments.aligned .cnote{ transition:none; } }
984
+ .crail-head{ display:block; }
985
+ /* the comment navigator in the canvas command bar (#docdiff): glyph + open count + prev/next. Kept
986
+ INLINE in the frosted bar — no box of its own (a box-in-box looked heavy). */
987
+ .cnavcmd{ display:inline-flex; align-items:center; gap:1px; }
988
+ .cnavcmd-count{ display:inline-flex; align-items:center; gap:5px; font-size:12.5px; font-weight:640; color:var(--ink); padding:0 5px; white-space:nowrap; }
989
+ .cnavcmd-count .ico{ width:14px; height:14px; color:var(--accent); }
990
+ .cnavcmd-step{ appearance:none; border:0; background:transparent; color:var(--ink-muted); cursor:pointer; width:22px; height:24px;
991
+ border-radius:var(--r-xs); display:inline-flex; align-items:center; justify-content:center; font-size:15px; line-height:1;
992
+ transition:background var(--dur) var(--ease), color var(--dur) var(--ease); }
993
+ .cnavcmd-step:hover{ background:var(--hover); color:var(--ink); }
994
+ /* the single global hint — prominent (not fine print) with a one-click copy of the command; sized
995
+ to match the comment cards (same --crail-l / --crail-r via the header padding). */
996
+ .crail-hint{ display:flex; align-items:flex-start; gap:8px; margin:0; font-size:12px; line-height:1.5; color:var(--ink-muted);
997
+ background:var(--accent-tint); border:1px solid color-mix(in srgb,var(--accent) 22%, var(--glass-brd)); border-radius:var(--r-sm); padding:8px 10px; }
998
+ .crail-hint-t{ flex:1 1 auto; min-width:0; }
999
+ .crail-hint code{ font-family:var(--mono); font-size:.9em; font-weight:600; color:var(--ink); background:color-mix(in srgb,var(--accent) 16%, transparent); padding:.1em .34em; border-radius:5px; white-space:nowrap; }
1000
+ .crail-copy{ appearance:none; border:0; background:transparent; color:var(--accent); cursor:pointer; width:22px; height:22px; flex:0 0 auto;
1001
+ border-radius:5px; display:grid; place-items:center; transition:background var(--dur) var(--ease); }
1002
+ .crail-copy:hover{ background:color-mix(in srgb,var(--accent) 16%, transparent); }
1003
+ .crail-copy .ico{ width:14px; height:14px; }
1004
+ /* inline mode: the hint rides at the top of the document as its own island */
1005
+ .cnote-hint-island{ margin:0 0 14px; }
1006
+
1007
+ /* transient "agent is addressing your comments" toast */
1008
+ .caddr{ position:fixed; z-index:70; left:50%; bottom:18px; transform:translateX(-50%); display:flex; align-items:center; gap:9px;
1009
+ padding:9px 14px; border-radius:var(--pill); background:var(--raised); border:1px solid color-mix(in srgb,var(--accent) 35%, var(--glass-brd));
1010
+ box-shadow:var(--shadow-lg); color:var(--ink); font-size:12.5px; font-weight:560; animation:reveal .2s var(--ease) both; }
1011
+ .caddr .spin{ width:12px; height:12px; border-radius:50%; border:2px solid var(--accent-tint); border-top-color:var(--accent); animation:cspin .7s linear infinite; }
1012
+ @keyframes cspin{ to{ transform:rotate(360deg); } }
1013
+ @media (prefers-reduced-motion: reduce){ .caddr .spin{ animation:none; } }
1014
+
1015
+ /* stale-server (version-skew) banner: new UI served by an out-of-date `serve` process */
1016
+ .stalebar{ position:fixed; z-index:80; left:50%; bottom:18px; transform:translateX(-50%); max-width:min(560px,92vw);
1017
+ display:flex; align-items:center; gap:12px; padding:10px 12px 10px 16px; border-radius:var(--r-lg);
1018
+ background:var(--raised); border:1px solid color-mix(in srgb,var(--warn) 45%, var(--glass-brd));
1019
+ box-shadow:var(--shadow-lg); color:var(--ink); font-size:13px; line-height:1.45; animation:reveal .2s var(--ease) both; }
1020
+ .stalebar code{ font-family:var(--mono); font-size:.86em; background:rgba(128,128,140,.16); padding:.1em .34em; border-radius:5px; }
1021
+ .stalebar .x{ appearance:none; border:0; background:transparent; color:var(--ink-muted); cursor:pointer; padding:4px; flex:none; border-radius:var(--r-sm); display:inline-flex; }
1022
+ .stalebar .x:hover{ background:rgba(128,128,140,.12); color:var(--ink); }
1023
+ .stalebar .x .ico{ width:16px; height:16px; }
1024
+
1025
+ /* the composer popover (position:fixed → never clipped). The selection button (.askbtn) shares
1026
+ the amber comment-button style above so it matches the gutter handle. */
1027
+ .askpop{ position:fixed; z-index:var(--z-dropdown); width:min(340px,86vw); background:var(--raised); border:1px solid var(--glass-brd);
1028
+ border-radius:var(--r-lg); box-shadow:var(--shadow-lg); padding:12px; animation:reveal .14s var(--ease) both; }
1029
+ /* Slash-command menu (WYSIWYG): "/" at the start of an empty block opens this block-inserter. */
1030
+ .slashmenu{ position:fixed; z-index:var(--z-dropdown); display:none; min-width:200px; max-height:min(320px,60vh); overflow:auto;
1031
+ background:var(--raised); border:1px solid var(--glass-brd); border-radius:var(--r-lg); box-shadow:var(--shadow-lg);
1032
+ padding:6px; animation:reveal .12s var(--ease) both; }
1033
+ .slashmenu.show{ display:block; }
1034
+ .slashmenu .slashitem{ display:block; width:100%; text-align:left; padding:7px 10px; border:0; border-radius:var(--r-sm);
1035
+ background:transparent; color:var(--ink); font:inherit; font-size:13px; cursor:pointer; }
1036
+ .slashmenu .slashitem:hover, .slashmenu .slashitem.active{ background:var(--accent-tint); color:var(--ink); }
1037
+ /* Selection formatting bubble (WYSIWYG edit mode): bold / italic / link / H2 + Comment. Fixed →
1038
+ never clipped. Carries the Comment button so the standalone .askbtn never doubles up in edit mode. */
1039
+ .bubblebar{ position:fixed; z-index:var(--z-dropdown); display:none; align-items:center; gap:2px; padding:4px;
1040
+ background:var(--raised); border:1px solid var(--glass-brd); border-radius:var(--r-md); box-shadow:var(--shadow-lg);
1041
+ animation:reveal .12s var(--ease) both; }
1042
+ .bubblebar.show{ display:inline-flex; }
1043
+ .bubblebar .bbtn{ appearance:none; border:0; background:transparent; cursor:pointer; color:var(--ink-muted);
1044
+ width:28px; height:28px; padding:0; border-radius:var(--r-sm); display:grid; place-items:center; }
1045
+ .bubblebar .bbtn:hover{ background:var(--hover); color:var(--ink); }
1046
+ .bubblebar .bbtn.on{ background:var(--accent-tint); color:var(--accent); }
1047
+ .bubblebar .bbtn .ico{ width:16px; height:16px; }
1048
+ .bubblebar .bbsep{ width:1px; align-self:stretch; margin:3px 3px; background:var(--hairline); }
1049
+ /* In-table toolbar (WYSIWYG edit mode): add/remove rows & columns, drop the table. */
1050
+ .tabletools{ position:fixed; z-index:var(--z-dropdown); display:none; align-items:center; gap:2px; padding:4px;
1051
+ background:var(--raised); border:1px solid var(--glass-brd); border-radius:var(--r-md); box-shadow:var(--shadow-lg);
1052
+ animation:reveal .12s var(--ease) both; }
1053
+ .tabletools.show{ display:inline-flex; }
1054
+ .tabletools .ttbtn{ appearance:none; border:0; background:transparent; cursor:pointer; color:var(--ink-muted);
1055
+ height:28px; padding:0 8px; border-radius:var(--r-sm); font:inherit; font-size:12px; font-weight:560; display:inline-flex; align-items:center; gap:4px; }
1056
+ .tabletools .ttbtn:hover{ background:var(--hover); color:var(--ink); }
1057
+ .tabletools .ttbtn.danger{ width:30px; padding:0; justify-content:center; }
1058
+ .tabletools .ttbtn.danger:hover{ background:var(--bad-tint); color:var(--bad); }
1059
+ .tabletools .ttbtn .ico{ width:15px; height:15px; }
1060
+ .tabletools .ttsep{ width:1px; align-self:stretch; margin:3px 3px; background:var(--hairline); }
1061
+ /* the composer header — states plainly that this comment is a request for the agent */
1062
+ .askpop .askhd{ display:flex; align-items:center; gap:7px; font-size:12.5px; font-weight:640; color:var(--ink); margin-bottom:9px; }
1063
+ .askpop .askhd .ico{ width:15px; height:15px; color:var(--accent); flex:0 0 auto; }
1064
+ .askpop .aq{ font-size:11.5px; color:var(--ink-faint); border-left:2px solid var(--accent); padding-left:8px; margin-bottom:9px;
1065
+ display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
1066
+ .askpop textarea{ width:100%; box-sizing:border-box; resize:vertical; min-height:58px; font:inherit; font-size:13px;
1067
+ color:var(--ink); background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-sm); padding:8px 10px; outline:none; }
1068
+ .askpop textarea:focus{ border-color:color-mix(in srgb,var(--accent) 50%, var(--hairline)); }
1069
+ .askpop .arow{ display:flex; align-items:center; gap:8px; margin-top:9px; }
1070
+ .askpop .arow .sp{ flex:1 1 auto; }
1071
+ .askpop .mic,.askpop .send,.askpop .ghost{ appearance:none; border:1px solid var(--glass-brd); background:rgba(120,124,132,.06);
1072
+ color:var(--ink); font:inherit; font-size:12.5px; font-weight:560; border-radius:var(--r-sm); cursor:pointer;
1073
+ display:inline-flex; align-items:center; gap:6px; height:30px; padding:0 12px; }
1074
+ .askpop .send{ background:var(--accent-strong); color:#fff; border-color:transparent; }
1075
+ .askpop .send:hover{ filter:brightness(1.06); }
1076
+ .askpop .ghost{ background:transparent; color:var(--ink-muted); }
1077
+ .askpop .mic{ width:34px; padding:0; justify-content:center; }
1078
+ .askpop .mic.rec{ color:var(--bad); border-color:color-mix(in srgb,var(--bad) 50%, var(--glass-brd)); animation:pulse 1s var(--ease) infinite; }
1079
+ .askpop .mic .ico,.askpop .send .ico{ width:15px; height:15px; }
1080
+ .askhint{ font-size:11px; color:var(--ink-faint); margin-top:8px; line-height:1.5; }
1081
+ .askhint code{ font-family:var(--mono); font-size:.9em; background:rgba(128,128,140,.16); padding:.1em .32em; border-radius:5px; }
1082
+ @media (max-width:1080px){ .casebody{ grid-template-columns:minmax(0,1fr); } .rail{ display:none; } .cadd{ display:none!important; } }
1083
+
1084
+ /* mobile section spine — when the rail (ToC) is hidden, a floating "Sections"
1085
+ control reopens the record's outline as a sheet, so long records stay navigable. */
1086
+ .secfab{ position:fixed; right:16px; bottom:calc(16px + env(safe-area-inset-bottom,0)); z-index:var(--z-sticky);
1087
+ display:none; align-items:center; gap:8px; height:44px; padding:0 16px; border:0; cursor:pointer;
1088
+ font:inherit; font-size:13px; font-weight:600; color:var(--on-accent); background:var(--btn-bg);
1089
+ border-radius:var(--pill); box-shadow:var(--shadow-md), var(--glow); }
1090
+ .secfab .ico{ width:16px; height:16px; }
1091
+ .secsheet{ position:fixed; right:16px; bottom:calc(70px + env(safe-area-inset-bottom,0)); z-index:var(--z-dropdown);
1092
+ display:none; flex-direction:column; gap:1px; width:min(74vw,320px); max-height:60vh; overflow:auto;
1093
+ padding:8px; border-radius:var(--r-lg); background:var(--glass); backdrop-filter:var(--blur); -webkit-backdrop-filter:var(--blur);
1094
+ box-shadow:var(--shadow-lg); animation:reveal .14s var(--ease) both; }
1095
+ .secsheet a{ display:block; padding:10px 12px; border-radius:var(--r-sm); text-decoration:none; color:var(--ink-muted); font-size:14px; }
1096
+ .secsheet a.lvl3{ padding-left:26px; font-size:13px; color:var(--ink-faint); }
1097
+ .secsheet a:hover,.secsheet a:focus-visible{ background:var(--hover); color:var(--ink); }
1098
+ @media (max-width:1080px){
1099
+ body.has-sections .secfab{ display:inline-flex; }
1100
+ body.has-sections.sec-open .secsheet{ display:flex; }
1101
+ }
1102
+ @media (min-width:1081px){ .secfab,.secsheet{ display:none !important; } }
1103
+
1104
+
1105
+ /* Diff mode is the LIVE EDITOR with change decorations layered on — the SAME document, typography,
1106
+ layout, spacing and functionality (editing, checkboxes, comments, ToC), just washed with colour.
1107
+ ADDED blocks get a green highlighter wash; MODIFIED blocks the same green with their previous form
1108
+ shown above; REMOVED blocks appear as read-only red `.diffdel` islands inline. Crucially, the
1109
+ decorations use the SAME box model as `.blk` (margin/padding) and share the document's vertical
1110
+ rhythm rules (`.doc > :is(.blk,.diffdel) …` above, keyed by `t-<type>`), so spacing between
1111
+ headers/paragraphs/sections is identical to the reading view. No borders; a +/− gutter sign backs
1112
+ the colour (a11y); removed text is dimmed so the surviving version reads as the current one. */
1113
+ /* Read-only records render their Markdown directly into #doc — the top-level elements
1114
+ are `.md-p` / `.md-h*`, not `.blk` — so the diff wash must also target #doc's direct children,
1115
+ or a brand-new report would carry the `.diff-add` class with no visible highlight. */
1116
+ .blk.diff-add, .blk.diff-mod, #doc > .diff-add, #doc > .diff-mod{ position:relative; background:var(--ok-tint); }
1117
+ .diffdel{ position:relative; margin:0 -6px; padding:1px 6px; border-radius:var(--r-sm); background:var(--bad-tint); }
1118
+ .diffdel > *{ opacity:.7; }
1119
+ .diffdel > :is(h1,h2,h3,h4,h5,h6,p,ul,ol,blockquote,pre,table,hr):first-child{ margin-top:0; }
1120
+ .diffdel > :is(h1,h2,h3,h4,h5,h6,p,ul,ol,blockquote,pre,table,hr):last-child{ margin-bottom:0; }
1121
+ .diffdel .dempty{ color:var(--ink-faint); font-style:italic; }
1122
+ .blk.diff-add::before, .blk.diff-mod::before, #doc > .diff-add::before, #doc > .diff-mod::before, .diffdel::before{ position:absolute; left:calc(-1 * var(--s5)); font:700 12px/1.55 var(--mono); }
1123
+ .blk.diff-add::before, .blk.diff-mod::before, #doc > .diff-add::before, #doc > .diff-mod::before{ content:"+"; color:var(--ok); top:1px; }
1124
+ .diffdel::before{ content:"\2212"; color:var(--bad); top:1px; }
1125
+ .diffdel.diffanim{ animation:reveal .32s var(--ease) both; } /* only on the Document→Diff switch; edit refreshes don't re-animate */
1126
+ @media (prefers-reduced-motion:reduce){ .diffdel.diffanim{ animation:none; } }
1127
+ /* WYSIWYG (Tiptap) diff: ProseMirror renders node/widget decorations INSIDE `#doc .ProseMirror`,
1128
+ so the top-level blocks are `.ProseMirror > .diff-add|.diff-mod` and removed-block islands are
1129
+ `.ProseMirror > .diffdel-wrap > .diffdel` — mirror the reading-view wash + − gutter for them. */
1130
+ #doc .ProseMirror > .diff-add, #doc .ProseMirror > .diff-mod{ position:relative; background:var(--ok-tint); }
1131
+ #doc .ProseMirror > .diff-add::before, #doc .ProseMirror > .diff-mod::before{ content:"+"; color:var(--ok); position:absolute; left:calc(-1 * var(--s5)); top:1px; font:700 12px/1.55 var(--mono); }
1132
+ #doc .ProseMirror > .diffdel-wrap{ margin:0; }
1133
+ /* The Tiptap surface is a child contenteditable of `.doc`; the global :focus-visible outline
1134
+ would otherwise draw a 2px frame around the WHOLE record on focus. Suppress it — the caret and
1135
+ the amber active line (below) are the "you are here" cue, not a box around the file. */
1136
+ #doc .ProseMirror{ outline:none; }
1137
+ #doc .ProseMirror:focus, #doc .ProseMirror:focus-visible{ outline:none; }
1138
+ /* Active line: the block holding the caret gets a warm amber wash while the editor is focused
1139
+ (see active-line.mjs). Bleeds into the same -6px gutter the reading blocks use so it reads as a
1140
+ full-width line, and clears the instant focus leaves. */
1141
+ #doc .ProseMirror .pm-activeline{ background:var(--warn-tint); border-radius:var(--r-sm); box-shadow:0 0 0 6px var(--warn-tint); }
1142
+ /* Per-block placeholder (Tiptap Placeholder extension): the empty block with the caret shows its
1143
+ data-placeholder hint (advertising "/" for blocks and "@" to ask the agent). Shown only on the
1144
+ current empty block (showOnlyCurrent), so it never litters every blank line. */
1145
+ #doc .ProseMirror .is-empty::before{ content:attr(data-placeholder); float:left; height:0; pointer-events:none;
1146
+ color:var(--ink-faint); opacity:.7; }
1147
+ /* Live-editor tables (Tiptap TableKit). Unlike the reading renderer's `.md-tbl`, the editor emits a
1148
+ `.tableWrapper > table > tbody > tr > th|td` shape (no <thead> — the header row is the first <tr>
1149
+ of <th> cells) plus a <colgroup> for resizable columns. Give it the same visual language as the
1150
+ reading view — collapsed grid, a shaded header row, hairline cell borders — so it reads as a table
1151
+ while editing. Cells are `position:relative` so the resize handle and selection overlay anchor to
1152
+ them. */
1153
+ #doc .ProseMirror .tableWrapper{ overflow-x:auto; overscroll-behavior-x:contain; max-width:100%; margin:.2em 0; }
1154
+ #doc .ProseMirror table{ border-collapse:collapse; table-layout:fixed; width:100%; font-size:14.5px; margin:0; overflow:hidden; }
1155
+ #doc .ProseMirror table td, #doc .ProseMirror table th{ position:relative; vertical-align:top; text-align:left; box-sizing:border-box;
1156
+ min-width:var(--tbl-mincol); padding:7px 12px; border:1px solid var(--hairline); }
1157
+ #doc .ProseMirror table th{ background:var(--hover); font-weight:660; color:var(--ink);
1158
+ border-bottom:1.5px solid color-mix(in srgb, var(--ink-faint) 55%, transparent); }
1159
+ #doc .ProseMirror table td > p, #doc .ProseMirror table th > p{ margin:0; }
1160
+ /* Cell selection (the CellSelection plugin marks selected cells with `.selectedCell`): a soft accent
1161
+ wash drawn as an overlay so it never shifts text. */
1162
+ #doc .ProseMirror table .selectedCell::after{ content:""; position:absolute; inset:0; pointer-events:none; z-index:2;
1163
+ background:color-mix(in srgb, var(--accent) 16%, transparent); }
1164
+ /* Column resize handle + cursor (resizable:true). */
1165
+ #doc .ProseMirror table .column-resize-handle{ position:absolute; right:-2px; top:0; bottom:0; width:4px; z-index:3;
1166
+ background:var(--accent); pointer-events:none; }
1167
+ #doc .ProseMirror.resize-cursor{ cursor:col-resize; }
1168
+ @media (max-width:720px){ #doc .ProseMirror > .diff-add::before, #doc .ProseMirror > .diff-mod::before{ display:none; } }
1169
+ @media (max-width:720px){ .blk.diff-add::before, .blk.diff-mod::before, #doc > .diff-add::before, #doc > .diff-mod::before, .diffdel::before{ display:none; } }
1170
+ /* Per-line diff: a one-line change in a list/table tints only the changed item/row (green add /
1171
+ modified, red + struck for removed), never the whole block. Colour is paired with a strike on
1172
+ removed rows so it doesn't rest on hue alone. */
1173
+ .md-list > li.diff-add, .md-list > li.diff-mod{ background:var(--ok-tint); border-radius:var(--r-xs); }
1174
+ .md-list > li.d-lidel{ list-style:none; background:var(--bad-tint); border-radius:var(--r-xs); opacity:.72; text-decoration:line-through; text-decoration-color:color-mix(in srgb, var(--bad) 55%, transparent); }
1175
+ .md-tbl tbody tr.diff-add > td, .md-tbl tbody tr.diff-mod > td{ background:var(--ok-tint); }
1176
+ .md-tbl tbody tr.d-trdel > td{ background:var(--bad-tint); opacity:.72; text-decoration:line-through; text-decoration-color:color-mix(in srgb, var(--bad) 55%, transparent); font-family:var(--mono); font-size:12.5px; }
1177
+ .md-list > li.diffanim, .md-tbl tbody tr.diffanim{ animation:reveal .3s var(--ease) both; }
1178
+ @media (prefers-reduced-motion:reduce){ .md-list > li.diffanim, .md-tbl tbody tr.diffanim{ animation:none; } }
1179
+
1180
+ /* queue */
1181
+ .qwrap{ flex:1 1 auto; display:flex; justify-content:center; }
1182
+ .queue{ width:100%; max-width:760px; padding:var(--s7) var(--s5) 20vh; animation:reveal .42s var(--ease) both; }
1183
+ .queue h1{ font-size:1.5rem; font-weight:640; letter-spacing:-.015em; margin:0 0 4px; }
1184
+ .queue .sub{ color:var(--ink-muted); margin:0 0 var(--s5); }
1185
+ .qrow{ display:flex; align-items:center; gap:var(--s4); width:100%; text-align:left; cursor:pointer;
1186
+ padding:14px 16px; border:1px solid var(--hairline); border-radius:var(--r-lg); background:var(--surface);
1187
+ color:inherit; font:inherit; margin-bottom:10px; transition:border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease); }
1188
+ .qrow:hover{ border-color:color-mix(in srgb,var(--accent) 40%, var(--hairline)); box-shadow:var(--glow); transform:translateY(-1px); }
1189
+ .qrow .qmain{ min-width:0; flex:1 1 auto; display:flex; flex-direction:column; gap:2px; }
1190
+ .qrow .qt{ display:block; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
1191
+ .qrow .qr{ display:block; font-size:13px; color:var(--ink-muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
1192
+ .qrow .go{ width:18px; height:18px; color:var(--ink-faint); flex:none; transition:transform var(--dur) var(--ease); }
1193
+ .qrow:hover .go{ transform:translateX(2px); color:var(--accent); }
1194
+ .qrow .qtime{ font-size:12.5px; color:var(--ink-faint); flex:none; white-space:nowrap; font-variant-numeric:tabular-nums; }
1195
+ .queue .qsec{ font-size:12.5px; font-weight:560; color:var(--ink-muted); letter-spacing:.01em; margin:var(--s6) 0 12px; }
1196
+ .queue h1 + .sub + .qsec{ margin-top:var(--s5); }
1197
+ /* settings */
1198
+ .settings-wrap{ flex:1 1 auto; display:flex; justify-content:center; overflow:auto; }
1199
+ .settings-page{ width:100%; max-width:780px; padding:var(--s7) var(--s5) 18vh; animation:reveal .35s var(--ease) both; }
1200
+ .settings-head{ margin-bottom:var(--s6); }
1201
+ .settings-head h1{ margin:0 0 5px; font-size:1.5rem; font-weight:640; letter-spacing:-.015em; }
1202
+ .settings-head p,.settings-copy p,.settings-note{ margin:0; color:var(--ink-muted); }
1203
+ .settings-section{ padding:var(--s5) 0; border-top:1px solid var(--hairline); }
1204
+ .settings-copy{ margin-bottom:var(--s4); }
1205
+ .settings-copy h2{ margin:0 0 4px; font-size:14px; font-weight:650; }
1206
+ .settings-copy p,.settings-note{ font-size:12.5px; line-height:1.5; }
1207
+ .theme-options{ display:grid; grid-template-columns:repeat(3,1fr); gap:10px; }
1208
+ .theme-option{ appearance:none; min-width:0; display:flex; align-items:center; gap:11px; padding:13px; text-align:left; color:var(--ink);
1209
+ font:inherit; border:1px solid var(--hairline); border-radius:var(--r-md); background:var(--surface); cursor:pointer;
1210
+ transition:border-color var(--dur) var(--ease), background var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
1211
+ .theme-option:hover{ border-color:color-mix(in srgb,var(--accent) 38%,var(--hairline)); }
1212
+ .theme-option.selected{ border-color:color-mix(in srgb,var(--accent) 62%,var(--hairline)); background:var(--accent-tint); box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 16%,transparent); }
1213
+ .theme-option:focus-visible,.settings-toggle input:focus-visible + i{ outline:2px solid var(--ring); outline-offset:2px; }
1214
+ .theme-icon{ width:32px; height:32px; flex:none; display:grid; place-items:center; border-radius:9px; color:var(--ink-muted); background:var(--hover); }
1215
+ .theme-icon .ico{ width:18px; height:18px; }
1216
+ .theme-option span:last-child{ min-width:0; display:flex; flex-direction:column; gap:2px; }
1217
+ .theme-option strong,.settings-toggle strong{ font-size:13px; font-weight:620; }
1218
+ .theme-option small,.settings-toggle small{ color:var(--ink-muted); font-size:11.5px; line-height:1.4; }
1219
+ .settings-toggle{ display:flex; align-items:flex-start; gap:var(--s5); padding:15px 16px; border:1px solid var(--hairline); border-radius:var(--r-md); background:var(--surface); cursor:pointer; }
1220
+ .settings-toggle > span{ min-width:0; flex:1; display:flex; flex-direction:column; gap:4px; }
1221
+ .settings-toggle input{ position:absolute; opacity:0; pointer-events:none; }
1222
+ .settings-toggle i{ position:relative; flex:none; width:38px; height:22px; margin-top:1px; border-radius:var(--pill); background:var(--sb-thumb-h); transition:background var(--dur) var(--ease); }
1223
+ .settings-toggle i::after{ content:""; position:absolute; width:16px; height:16px; left:3px; top:3px; border-radius:50%; background:white; box-shadow:0 1px 4px rgba(0,0,0,.25); transition:transform var(--dur) var(--ease); }
1224
+ .settings-toggle input:checked + i{ background:var(--accent); }
1225
+ .settings-toggle input:checked + i::after{ transform:translateX(16px); }
1226
+ .settings-toggle input:disabled + i{ opacity:.55; }
1227
+ .settings-note{ margin-top:9px; padding-left:2px; }
1228
+ /* Explorer git-change badge (files) — letter + tint, never colour alone; centred in its column. */
1229
+ .gitb{ flex:none; font:600 10px/1 var(--mono); width:16px; height:16px; display:inline-grid; place-items:center; border-radius:5px; }
1230
+ .gitb.mod{ color:var(--warn); background:color-mix(in srgb,var(--warn) 18%, transparent); }
1231
+ .gitb.add{ color:var(--ok); background:color-mix(in srgb,var(--ok) 18%, transparent); }
1232
+ .gitb.del{ color:var(--bad); background:color-mix(in srgb,var(--bad) 18%, transparent); }
1233
+
1234
+ /* empty states */
1235
+ .empty{ margin:auto; text-align:center; max-width:420px; padding:var(--s7) var(--s5); }
1236
+ .empty h2{ font-size:1.5rem; font-weight:600; letter-spacing:-.01em; margin:0 0 8px; }
1237
+ .empty p{ color:var(--ink-muted); margin:0; }
1238
+ .empty code{ font-family:var(--mono); font-size:.85em; background:rgba(128,128,140,.14); padding:.12em .4em; border-radius:5px; }
1239
+ /* branded home surface (empty state) — the ember mark + injected product name as the hero.
1240
+ The tab-bar wordmark is suppressed while this shows, so the brand is never doubled. */
1241
+ .hero{ margin:auto; display:flex; flex-direction:column; align-items:center; text-align:center; max-width:460px; padding:var(--s7) var(--s5);
1242
+ animation:reveal .5s var(--ease) both; }
1243
+ .hero-mark{ width:52px; height:52px; border-radius:14px; flex:none; margin:0 0 18px;
1244
+ background:linear-gradient(140deg,#f4a566,#c85a12); box-shadow:var(--shadow-md), inset 0 1px 0 rgba(255,255,255,.4); display:grid; place-items:center; }
1245
+ .hero-mark i{ width:21px; height:21px; border:2.5px solid rgba(255,255,255,.92); border-radius:6px; }
1246
+ .hero-name{ font-size:1.9rem; font-weight:640; letter-spacing:-.02em; margin:0 0 6px; }
1247
+ .hero-sub{ font-size:1.05rem; color:var(--ink-muted); margin:0 0 16px; }
1248
+ .hero-actions{ display:flex; flex-wrap:wrap; gap:var(--s2); justify-content:center; margin:0 0 16px; }
1249
+ .hero-actions .tbtn code{ font-family:var(--mono); font-size:.92em; }
1250
+ .hero-hint{ font-size:.9rem; color:var(--ink-faint); margin:0; }
1251
+ .hero-hint code{ font-family:var(--mono); font-size:.9em; background:rgba(128,128,140,.14); padding:.12em .4em; border-radius:5px; }
1252
+ @media (prefers-reduced-motion:reduce){ .hero{ animation:none; } }
1253
+
1254
+ /* scrim for mobile drawer */
1255
+ .scrim{ display:none; position:fixed; inset:0; z-index:calc(var(--z-sticky) - 1); background:rgba(6,7,10,.5); backdrop-filter:blur(2px); }
1256
+
1257
+ /* responsive: collapse the sidebar to a slide-over */
1258
+ @media (max-width:820px){
1259
+ #app{ grid-template-columns:1fr; grid-template-areas:"stage"; }
1260
+ .stage{ padding:10px 10px 12px; }
1261
+ .container{ max-width:100%; }
1262
+ .sidebar{ position:fixed; z-index:var(--z-modal); top:0; bottom:0; left:0; width:min(84vw,var(--sb));
1263
+ padding:10px 12px; background:var(--glass); backdrop-filter:var(--blur); -webkit-backdrop-filter:var(--blur);
1264
+ box-shadow:var(--shadow); transform:translateX(-102%); transition:transform var(--dur) var(--ease); }
1265
+ body.nav-open .sidebar{ transform:none; }
1266
+ body.nav-open .scrim{ display:block; }
1267
+ .sb-resize{ display:none; }
1268
+ .iconbtn{ display:inline-flex; }
1269
+ .canvas,.queue{ padding-left:var(--s4); padding-right:var(--s4); }
1270
+ .settings-page{ padding-left:var(--s4); padding-right:var(--s4); }
1271
+ .feedback-page{ padding-left:var(--s4); padding-right:var(--s4); }
1272
+ .theme-options{ grid-template-columns:1fr; }
1273
+ .feedback-type-options{ grid-template-columns:1fr; }
1274
+ .tab{ min-width:112px; }
1275
+ .tab.active{ min-width:148px; }
1276
+ }
1277
+
1278
+ /* touch devices: give the small chrome controls a comfortable ≥40px tap target
1279
+ without inflating them on the desktop pointer layout. */
1280
+ @media (pointer:coarse){
1281
+ .iconbtn{ min-width:44px; min-height:44px; }
1282
+ .tab-close{ width:40px; height:40px; opacity:1; }
1283
+ .viewtoggle{ min-height:40px; min-width:40px; }
1284
+ .tbtn{ height:40px; }
1285
+ .search input{ height:44px; }
1286
+ .projsel{ min-height:40px; }
1287
+ .navq{ min-height:44px; }
1288
+ .gicon{ min-width:40px; min-height:40px; }
1289
+ .trow{ min-height:38px; padding-top:6px; padding-bottom:6px; }
1290
+ .menu button{ min-height:44px; }
1291
+ }
1292
+
1293
+ @media (prefers-reduced-motion:reduce){
1294
+ *{ animation-duration:.001ms !important; animation-iteration-count:1 !important; transition-duration:.001ms !important; }
1295
+ .glayer,.grain{ transform:none !important; animation:none !important; }
1296
+ }
1297
+
1298
+ /* ---- feedback tab + toast ---- */
1299
+ .feedback-wrap{ flex:1 1 auto; display:flex; justify-content:center; overflow:auto; }
1300
+ .feedback-page{ width:100%; max-width:780px; padding:var(--s7) var(--s5) 16vh; animation:reveal .35s var(--ease) both; }
1301
+ .feedback-head{ margin-bottom:var(--s6); }
1302
+ .feedback-head h1{ margin:0 0 5px; font-size:1.5rem; font-weight:640; letter-spacing:-.015em; }
1303
+ .feedback-head p{ max-width:62ch; margin:0; color:var(--ink-muted); line-height:1.5; text-wrap:pretty; }
1304
+ .feedback-form{ padding-top:var(--s5); border-top:1px solid var(--hairline); }
1305
+ .feedback-type{ margin:0 0 20px; padding:0; border:0; }
1306
+ .feedback-type legend,.feedback-sentiment legend{ margin:0 0 8px; color:var(--ink); font-size:13px; font-weight:600; }
1307
+ .feedback-type-options{ display:grid; grid-template-columns:repeat(3,1fr); gap:9px; }
1308
+ .feedback-type-option{ appearance:none; min-width:0; display:flex; flex-direction:column; gap:3px; padding:12px 13px; color:var(--ink); text-align:left; font:inherit; border:1px solid var(--hairline); border-radius:var(--r-md); background:var(--surface); cursor:pointer; transition:border-color var(--dur) var(--ease), background var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
1309
+ .feedback-type-option:hover{ border-color:color-mix(in srgb,var(--accent) 36%,var(--hairline)); }
1310
+ .feedback-type-option:focus-visible{ outline:2px solid var(--ring); outline-offset:2px; }
1311
+ .feedback-type-option.selected{ border-color:color-mix(in srgb,var(--accent) 64%,var(--hairline)); background:var(--accent-tint); box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 14%,transparent); }
1312
+ .feedback-type-option strong{ overflow:hidden; text-overflow:ellipsis; font-size:13px; font-weight:620; }
1313
+ .feedback-type-option small{ color:var(--ink-muted); font-size:11.5px; font-weight:450; line-height:1.4; }
1314
+ .feedback-field{ display:flex; flex-direction:column; gap:7px; margin:0 0 18px; color:var(--ink); font-size:13px; font-weight:600; }
1315
+ .feedback-field.compact{ max-width:360px; }
1316
+ .feedback-field span em{ margin-left:5px; color:var(--ink-faint); font-style:normal; font-size:11.5px; font-weight:450; }
1317
+ .feedback-field textarea,.feedback-field input{ width:100%; min-width:0; color:var(--ink); background:var(--surface); border:1px solid var(--hairline); border-radius:var(--r-sm); font:400 13.5px/1.5 var(--sans); padding:10px 11px; transition:border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), background var(--dur) var(--ease); }
1318
+ .feedback-field textarea{ min-height:74px; resize:vertical; }
1319
+ .feedback-field textarea::placeholder,.feedback-field input::placeholder{ color:var(--ink-muted); opacity:1; }
1320
+ .feedback-field textarea:hover,.feedback-field input:hover{ border-color:color-mix(in srgb,var(--ink-muted) 42%,var(--hairline)); }
1321
+ .feedback-field textarea:focus,.feedback-field input:focus{ outline:0; border-color:var(--accent); box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 16%,transparent); background:var(--canvas); }
1322
+ .feedback-field small{ color:var(--ink-muted); font-size:11.5px; font-weight:450; line-height:1.45; }
1323
+ .feedback-sentiment{ margin:0 0 20px; padding:0; border:0; }
1324
+ .feedback-sentiment legend span{ color:var(--ink-faint); font-size:11.5px; font-weight:450; }
1325
+ .feedback-sentiment-options{ display:flex; flex-wrap:wrap; gap:7px; }
1326
+ .feedback-sentiment-option{ appearance:none; display:inline-flex; align-items:center; gap:7px; min-height:36px; padding:7px 11px; border:1px solid var(--hairline); border-radius:var(--pill); color:var(--ink-muted); background:var(--surface); font:500 12px/1 var(--sans); cursor:pointer; transition:color var(--dur) var(--ease), border-color var(--dur) var(--ease), background var(--dur) var(--ease), transform var(--dur) var(--ease); }
1327
+ .feedback-sentiment-option .ico{ width:16px; height:16px; }
1328
+ .feedback-sentiment-option:hover{ color:var(--ink); border-color:color-mix(in srgb,var(--accent) 34%,var(--hairline)); transform:translateY(-1px); }
1329
+ .feedback-sentiment-option:focus-visible{ outline:2px solid var(--ring); outline-offset:2px; }
1330
+ .feedback-sentiment-option.selected{ color:var(--accent); border-color:color-mix(in srgb,var(--accent) 58%,var(--hairline)); background:var(--accent-tint); }
1331
+ .feedback-trust{ display:flex; align-items:flex-start; gap:8px; margin:3px 0 var(--s4); color:var(--ink-muted); font-size:11.5px; line-height:1.45; }
1332
+ .feedback-trust .ico{ width:15px; height:15px; flex:none; margin-top:1px; color:var(--ok); }
1333
+ .fb-note{ min-height:18px; margin:0 0 8px; color:var(--ink-muted); font-size:12px; }
1334
+ .fb-note.warn{ color:var(--warn); }
1335
+ .fb-actions{ display:flex; justify-content:flex-end; padding-top:var(--s3); }
1336
+ .fb-send:disabled{ opacity:.6; cursor:default; }
1337
+ .dlb-toast{ position:fixed; left:50%; bottom:24px; transform:translate(-50%,10px); z-index:130; padding:9px 16px; font-size:13px; color:var(--fg,#e6ecf1); background:var(--panel,#12151c); border:1px solid var(--line,#232a36); border-radius:10px; box-shadow:0 12px 40px rgba(0,0,0,.45); opacity:0; transition:opacity .25s, transform .25s; pointer-events:none; }
1338
+ .dlb-toast.show{ opacity:1; transform:translate(-50%,0); }
1339
+ </style>
1340
+ </head>
1341
+ <body>
1342
+ <div class="living" id="living" aria-hidden="true"><div class="glayer gl1"></div><div class="glayer gl2"></div><div class="glayer gl3"></div><div class="grain"></div></div>
1343
+ <div id="app">
1344
+ <aside class="sidebar" id="sidebar"></aside>
1345
+ <div class="sb-resize" id="sbresize" role="separator" aria-orientation="vertical" aria-label="Resize sidebar" tabindex="0"><span class="sb-grip" aria-hidden="true"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M13.5 8l-4 4 4 4"/></svg><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M10.5 8l4 4-4 4"/></svg></span></div>
1346
+ <section class="stage">
1347
+ <div class="container rim">
1348
+ <div class="tabbar" id="tabbar">
1349
+ <span class="tabmark" id="tabmark" aria-hidden="true"></span>
1350
+ <button class="iconbtn" id="navbtn" aria-label="Menu"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"/></svg></button>
1351
+ <button class="tabscroll" id="tabscrollL" type="button" aria-label="Scroll tabs left"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 6l-6 6 6 6"/></svg></button>
1352
+ <div class="tabs" id="tabs" role="tablist" aria-label="Open records"></div>
1353
+ <button class="tabscroll" id="tabscrollR" type="button" aria-label="Scroll tabs right"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6l6 6-6 6"/></svg></button>
1354
+ <button class="tab-add" id="tabadd" title="New tab" aria-label="New tab">+</button>
1355
+ <div class="tabspacer"></div>
1356
+ <div class="topactions" id="topactions"></div>
1357
+ <div class="topglobal" id="topglobal">
1358
+ <button class="gicon" id="inboxbtn" type="button" title="Inbox" aria-label="Inbox"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 13v5a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-5"/><path d="M4 13l2.4-6.7A2 2 0 0 1 8.3 5h7.4a2 2 0 0 1 1.9 1.3L20 13"/><path d="M4 13h4l1.2 2.2a1 1 0 0 0 .9.5h3.8a1 1 0 0 0 .9-.5L16 13h4"/></svg></button>
1359
+ <button class="gicon" id="feedbackbtn" type="button" title="Send feedback" aria-label="Send feedback"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 11v2a2 2 0 0 0 2 2h2l3 5h3l-2.5-5H12l8 3V6l-8 3H5a2 2 0 0 0-2 2z"/><path d="M7 9v6M20 10l2-1M20 14l2 1"/></svg></button>
1360
+ <button class="gicon" id="settingsbtn" type="button" title="Settings" aria-label="Settings"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.83 2.83-.06-.06a1.7 1.7 0 0 0-1.88-.34 1.7 1.7 0 0 0-1.03 1.56V21h-4v-.08A1.7 1.7 0 0 0 9 19.37a1.7 1.7 0 0 0-1.88.34l-.06.06-2.83-2.83.06-.06A1.7 1.7 0 0 0 4.63 15 1.7 1.7 0 0 0 3.08 14H3v-4h.08A1.7 1.7 0 0 0 4.63 9a1.7 1.7 0 0 0-.34-1.88l-.06-.06 2.83-2.83.06.06A1.7 1.7 0 0 0 9 4.63 1.7 1.7 0 0 0 10 3.08V3h4v.08A1.7 1.7 0 0 0 15 4.63a1.7 1.7 0 0 0 1.88-.34l.06-.06 2.83 2.83-.06.06A1.7 1.7 0 0 0 19.37 9 1.7 1.7 0 0 0 20.92 10H21v4h-.08A1.7 1.7 0 0 0 19.4 15z"/></svg></button>
1361
+ </div>
1362
+ </div>
1363
+ <main class="main" id="main" role="tabpanel" tabindex="-1" aria-label="Open record"></main>
1364
+ <div class="docdiff" id="docdiff" hidden></div>
1365
+ </div>
1366
+ </section>
1367
+ <div class="scrim" id="scrim"></div>
1368
+ </div>
1369
+
1370
+ <script>
1371
+ /*__APP_BUNDLE__*/
1372
+ </script>
1373
+ </body>
1374
+ </html>