reviewzy 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,244 +1,340 @@
1
1
  /* ============================================================
2
2
  reviewzy — dashboard page styles
3
+ The page layer: the sidebar shell, the list's own rhythm, and the
4
+ htmx behavior hooks. Every component surface is a shipped class;
5
+ nothing here repaints one.
3
6
  ============================================================ */
4
7
 
5
- /* The one override that retunes the column: the navbar's card and inner
6
- column, the page width and this shell all read it. */
7
- :root { --page-w: 1400px; }
8
+ /* ---- The shell ----
9
+ The sidebar is a grid track, so the page column follows the animated
10
+ column width. A fixed 252px track would leave a 186px gap around the
11
+ 66px rail. */
12
+ .layout { display: grid; grid-template-columns: auto minmax(0, 1fr); min-height: 100vh; }
8
13
 
9
- /* ---- Page shell (navbar shell) ---- */
10
- .app-shell { min-height: 100vh; display: flex; flex-direction: column; }
11
- .main { padding: var(--space-10) var(--space-8); max-width: var(--page-w); margin: 0 auto; width: 100%; }
14
+ /* The page column takes the main track's width: the design drops the cap beside a sidebar,
15
+ because a capped column leaves a dead strip beside the content once the track outgrows
16
+ --page-w (268px on each side at 1920). The track is pinned rather than auto-placed: under
17
+ 768 the sidebar is the fixed drawer and leaves the grid, so auto-placement would land the
18
+ page in the `auto` track, which sizes to the content's own intrinsic width — zero inside a
19
+ `container-type: inline-size` wrapper, collapsing the editor page to its padding. min-width
20
+ keeps the grid item from growing to its widest table's min-content width, which would widen
21
+ the document at 390. */
22
+ .layout > .page { grid-column: 2; min-width: 0; }
12
23
 
13
- /* ---- Page header: the eyebrow / title / lede triplet (login/editor pages) ---- */
14
- .page-eyebrow { color: var(--accent-text); margin-bottom: var(--space-2); }
15
- .page-lede { font-size: var(--t-3); color: var(--text-3); max-width: 540px; }
24
+ /* One spacing ladder: the page opens at --space-8 and closes at --space-16. */
25
+ .page { padding-block: var(--space-8) var(--space-16); }
16
26
 
17
- /* ---- Filter form ---- */
18
- .filters { display: grid; grid-template-columns: minmax(0, 2fr) 200px 200px; gap: var(--space-3); align-items: end; margin-bottom: var(--space-8); }
19
- @media (max-width: 768px) { .filters { grid-template-columns: 1fr; } }
27
+ /* The rail's footer holds one control: the theme toggle keeps the slot, and
28
+ sign-out returns with the expanded column. */
29
+ @media (min-width: 769px) {
30
+ .sidebar.rail .sidebar-user form { display: none; }
31
+ }
32
+
33
+ /* ---- List page rhythm: header, banner, tabs, then the queue section ---- */
34
+ .page-header { margin-bottom: var(--space-8); }
35
+
36
+ /* The banner is the component's card form here, not the bleed form: it sits on
37
+ the page column under the header. */
38
+ #page-banner > .banner { margin-bottom: var(--space-4); }
39
+
40
+ /* The status strip is a row of submit buttons inside the filters form; the count
41
+ line rides the toolbar's end edge and the filter-bar's own block margin closes
42
+ the pair. Five tabs with counts never fit a 390px column, so the strip scrolls
43
+ in its own box the way the navbar's link band does; the row keeps its hairline
44
+ across the scrolled content. */
45
+ .status-scroll { margin-bottom: var(--space-4); overflow-x: auto; }
46
+ .status-scroll .tabs,
47
+ .style-guide-projects .tabs { width: max-content; min-width: 100%; }
48
+ .list-meta {
49
+ margin-left: auto;
50
+ display: flex;
51
+ align-items: center;
52
+ gap: var(--space-3);
53
+ }
20
54
 
21
55
  /* ---- Entries region ---- */
22
56
  /* The loading strip is hidden until htmx marks the request in flight (hx-indicator adds the
23
57
  .htmx-request class); it sits beside the swap target, so a swap never removes it. */
24
58
  #entries-loading { display: none; align-items: center; gap: var(--space-2); font-size: var(--t-2); color: var(--text-3); padding: var(--space-2) 0; }
25
59
  #entries-loading.htmx-request { display: flex; }
60
+ /* A control mid-request ignores further activations: htmx marks the requesting element with
61
+ .htmx-request while the request is in flight, and pointer-events drops the duplicate click a
62
+ double-click would otherwise fire. */
63
+ .htmx-request { pointer-events: none; }
26
64
 
27
- /* ---- Partial state ---- */
28
- .partial-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-4); }
29
- .partial-count { font-size: var(--t-2); color: var(--text-3); }
65
+ /* ---- Bulk approve ----
66
+ The pinned batch action is the shipped section-sticky bar: transparent in flow so the sky
67
+ shows through, toned and hairlined only while it is stuck. Only its spacing is the page's. */
68
+ .batch-bar { margin-bottom: var(--space-4); }
30
69
 
31
- /* ---- Project groups ---- */
32
- .project-group { margin-bottom: var(--space-10); }
33
- .project-header { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
34
- .project-name { font: 500 var(--t-5)/1.2 var(--font-mono); color: var(--text); letter-spacing: -0.01em; }
35
- .batch { margin-bottom: var(--space-6); }
36
- .batch-header { display: flex; align-items: baseline; gap: var(--space-3); margin-bottom: var(--space-2); }
37
- .batch-meta { font: 400 var(--t-2)/1 var(--font-sans); color: var(--text-4); }
70
+ /* ---- Project sections: one heading and one table each ----
71
+ Batches stay a data concept behind the row selection and the one approve
72
+ action; nothing on the page discloses or groups by them. */
73
+ .project-section { margin-bottom: var(--space-8); }
74
+ .project-head {
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: flex-start;
78
+ gap: var(--space-1);
79
+ margin-bottom: var(--space-3);
80
+ }
81
+ .project-name {
82
+ font: 500 var(--t-5)/1.2 var(--font-sans);
83
+ letter-spacing: -0.01em;
84
+ color: var(--text);
85
+ margin: 0;
86
+ min-width: 0;
87
+ overflow-wrap: anywhere;
88
+ }
89
+ /* The project's count line: the drafts and entries of the rows shown in this group. */
90
+ .project-meta {
91
+ margin-top: 0;
92
+ font-size: var(--t-2);
93
+ max-width: none;
94
+ }
38
95
 
39
96
  /* ---- Entry table cells ----
40
- The table lays out fixed with a colgroup so the title column takes the remainder and the row
41
- never overflows; select, status, file, and actions are the fixed columns. The status pill has
42
- its own column, so the title truncates at one consistent point and the pill stays tight. */
43
- .col-select { width: 48px; }
44
- .col-status { width: 106px; }
45
- .col-file { width: 200px; }
97
+ The table lays out with a fixed colgroup so the entry column takes the remainder and a long
98
+ title or file path truncates instead of widening the table; select, status, filed and actions
99
+ are the fixed columns. The table never compresses below 560px: the fixed columns sum to 422
100
+ and the entry column needs room to read, so a narrower wrap scrolls instead of collapsing it.
101
+ Truncation itself is the shipped .truncate utility. */
102
+ #entries-list table { table-layout: fixed; min-width: 560px; }
103
+ .col-select { width: 44px; }
104
+ .col-status { width: 108px; }
105
+ .col-filed { width: 150px; }
46
106
  .col-actions { width: 120px; }
47
107
  .entry-head {
48
108
  display: flex;
49
- align-items: center;
50
- gap: var(--space-2);
51
- }
52
- .entry-title {
53
- flex: 1 1 auto;
109
+ flex-direction: column;
110
+ gap: var(--space-1);
54
111
  min-width: 0;
112
+ }
113
+ /* The title is the row's link into the editor; the hover underline is the affordance. */
114
+ .entry-link {
115
+ display: block;
55
116
  font: 400 var(--t-3)/1.4 var(--font-sans);
56
117
  color: var(--text);
57
- white-space: nowrap;
58
- overflow: hidden;
59
- text-overflow: ellipsis;
118
+ text-decoration: none;
60
119
  }
61
- .entry-filer {
62
- flex-shrink: 0;
120
+ .entry-link:hover { text-decoration: underline; }
121
+ .entry-file {
122
+ display: block;
63
123
  font-size: var(--t-2);
64
124
  color: var(--text-4);
65
- white-space: nowrap;
66
125
  }
67
- .cell-file {
68
- font-family: var(--font-mono);
126
+ .cell-filed {
69
127
  font-size: var(--t-2);
70
128
  color: var(--text-4);
71
129
  white-space: nowrap;
72
- overflow: hidden;
73
- text-overflow: ellipsis;
74
130
  }
75
-
76
- /* ---- Row actions and batch approve ----
77
- A control mid-request ignores further activations: htmx marks the requesting element with
78
- .htmx-request while the request is in flight, and pointer-events drops the duplicate click a
79
- double-click would otherwise fire. */
80
- .htmx-request { pointer-events: none; }
81
131
  .cell-actions { white-space: nowrap; display: inline-flex; align-items: center; gap: var(--space-1); }
82
132
  .table-wrap tbody td.cell-actions { padding-left: var(--space-1); padding-right: var(--space-1); }
83
- .batch-bar {
133
+ /* The row's three icon actions sit at the small control step, so the 120px column holds them
134
+ without a 4px spill; the coarse floor is restated at the override's specificity, or a touch
135
+ row's tap targets would shrink with them. */
136
+ .cell-actions .btn-icon { width: var(--ctl-sm); min-width: var(--ctl-sm); height: var(--ctl-sm); min-height: var(--ctl-sm); }
137
+ @media (pointer: coarse) {
138
+ .cell-actions .btn-icon { width: var(--ctl-lg); min-width: var(--ctl-lg); height: var(--ctl-lg); min-height: var(--ctl-lg); }
139
+ }
140
+ /* The semantic icon actions: the shipped .btn-icon paints the label colour, these give the
141
+ approve and reject glyphs their status colours in every state a disabled slot sits in. */
142
+ .action-approve, .action-approve:hover { color: var(--success); }
143
+ .action-reject, .action-reject:hover { color: var(--danger); }
144
+
145
+ /* Under 900px the filed stamp (metadata, not a decision) leaves the row so the rest of the
146
+ table keeps its columns inside the wrap instead of scrolling wider. The column's width is
147
+ zeroed and its cells are dropped (Chromium ignores `visibility: collapse` on a col), and the
148
+ table's floor steps down with the column it lost. The wrap stays the named scroller for what
149
+ remains. */
150
+ @media (max-width: 900px) {
151
+ #entries-list table { min-width: 440px; }
152
+ #entries-list col.col-filed { width: 0; }
153
+ #entries-list .cell-filed { display: none; }
154
+ }
155
+
156
+ /* ---- Notices ----
157
+ The refusal list rides inside the page banner's body, in the banner text's voice. */
158
+ .notice-list {
159
+ margin: var(--space-2) 0 0;
160
+ padding-left: var(--space-5);
84
161
  display: flex;
85
- align-items: center;
86
- margin-bottom: var(--space-4);
87
- /* Sticks just below the navbar so the approve button stays reachable while rows scroll
88
- under it; the background and z-index stop table rows from showing through the bar. */
89
- position: sticky;
90
- top: var(--navbar-h);
91
- z-index: 10;
92
- padding: var(--space-2) 0;
93
- background: var(--bg);
94
- }
95
- .batch-header .checkbox-label { align-self: center; }
96
- /* ---- Collapsible project sections and batches ----
97
- The chevron points down expanded and turns when collapsed; the state is applied by dashboard.js
98
- from localStorage, so the server ships everything open and the viewer's choice re-applies. */
99
- .collapse-toggle { align-self: center; }
100
- .collapse-toggle svg { transition: transform var(--dur-tint) var(--ease); }
101
- .collapse-toggle[aria-expanded="false"] svg { transform: rotate(-90deg); }
102
- .project-header .collapse-toggle, .batch-header .collapse-toggle { margin-left: auto; }
103
- .notice-list { margin: 0; padding-left: var(--space-5); display: flex; flex-direction: column; gap: var(--space-1); }
104
-
105
- /* ---- Diff view: the anchored snippet vs the entry's text, mono on the well, anchor distinct ---- */
106
- .diff-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); margin-bottom: var(--space-8); }
107
- @media (max-width: 900px) { .diff-grid { grid-template-columns: 1fr; } }
162
+ flex-direction: column;
163
+ gap: var(--space-1);
164
+ font-size: var(--t-3);
165
+ color: var(--text-2);
166
+ }
167
+
168
+ /* ---- Empty-state actions: this page's filter-miss state holds two ways onward ---- */
169
+ .empty-actions { display: flex; gap: var(--space-2); justify-content: center; }
170
+
171
+ /* ---- The change: the anchored snippet as code on the well ----
172
+ The shipped pre owns the surface; the diff wraps instead of scrolling, which is the one
173
+ deviation this view adds. The anchor's annotation bar is the shipped .annot-mark. */
108
174
  .diff-code {
109
- font: 400 var(--t-2)/1.6 var(--font-mono);
110
- background: var(--well);
111
- border: 1px solid var(--line);
112
- border-radius: var(--r-1);
113
- padding: var(--space-4);
175
+ margin: 0;
114
176
  white-space: pre-wrap;
115
177
  word-break: break-word;
116
- color: var(--text-2);
117
- margin: 0;
118
178
  }
119
179
  .diff-line { display: block; }
120
- .diff-anchor {
121
- background: var(--accent-quiet);
122
- border-left: 2px solid var(--accent);
123
- color: var(--text);
124
- padding-left: var(--space-2);
180
+ /* A change with both removed and added words renders two rows: the file text with its removals,
181
+ then the proposal with its additions. The rule between them keeps each sentence whole. */
182
+ .diff-rows .diff-proposed {
183
+ margin-top: var(--space-2);
184
+ padding-top: var(--space-2);
185
+ border-top: 1px solid var(--line-soft);
125
186
  }
126
187
 
127
- /* ---- Empty state actions: the shipped .empty-state lays out one button, this row holds two ---- */
128
- .empty-actions { display: flex; gap: var(--space-2); justify-content: center; }
129
-
130
- /* ---- Error box (cloned from #error-box by dashboard.js) ---- */
131
- .callout-content { min-width: 0; }
132
- .callout-actions { margin-top: var(--space-2); }
188
+ /* ---- Entry editor: one full-track workspace ----
189
+ The workspace, its header and its support card take the page column's whole track: a cap beside
190
+ the sidebar centres a column away from the nav it belongs to. The page is the query container,
191
+ so the title step, the support card's columns and the pair's order follow the track width, not
192
+ the viewport. Container queries sit inside the browser floor the readme states, so the title step
193
+ and the pair's order keep the query rather than an intrinsic fallback. Each card sizes to its own
194
+ content and its pane scrolls past the cap; one support card spans the workspace below. */
195
+ .editor-page { container-type: inline-size; }
196
+ /* The entry title runs one line on the 1920 and 1440 tracks; a narrow page column steps it down
197
+ so a long machine title does not wrap mid-hyphen. */
198
+ .editor-title { font-size: var(--t-7); }
199
+ @container (max-width: 800px) {
200
+ .editor-title { font-size: var(--t-6); }
201
+ }
202
+ .editor-grid {
203
+ display: flex;
204
+ flex-wrap: wrap;
205
+ gap: var(--space-4);
206
+ /* The pair's cards share one height: the taller card sets the row and the shorter one keeps its
207
+ own content at the top. The inner boxes stay content-sized; the slack is card surface. */
208
+ align-items: stretch;
209
+ }
210
+ /* Each card asks for its own readable measure and the pair wraps when the track cannot hold two,
211
+ so the collapse follows the track's own width instead of a breakpoint: it needs no container
212
+ query and holds in an engine without them. A terminal entry has no change card, so the settled
213
+ pane asks for the whole row, and one support card spans it below the pair. */
214
+ .editor-grid > * { flex: 1 1 320px; min-width: 0; }
215
+ #editor-region.editor-region-wide,
216
+ .editor-support { flex: 1 0 100%; }
217
+ .editor-pane { max-height: 60vh; overflow: auto; }
218
+ /* The proposal box sizes to its text through dashboard.js's autosizer, so a one-line entry gets a
219
+ one-line box and a long one grows to the viewport cap instead of scrolling from the start. */
220
+ /* One support card, two balanced columns: the sections keep their DOM order down each column and
221
+ each takes its own height, so a short Details never stretches to a taller Notes. The section
222
+ labels head their sections at the next type step; the nested "More" keeps the shipped size. The
223
+ 320px floor is the same measure the pair asks for, so an engine without container queries still
224
+ meets one column on a phone instead of two slivers. */
225
+ .editor-support-card .card-content {
226
+ padding: var(--space-2);
227
+ columns: 320px 2;
228
+ column-gap: var(--space-5);
229
+ }
230
+ .editor-support-card .support-section {
231
+ break-inside: avoid;
232
+ margin-bottom: var(--space-3);
233
+ }
234
+ .editor-support-card .support-section > summary { font-size: var(--t-4); }
235
+ @container (max-width: 900px) {
236
+ .editor-support-card .card-content { columns: 1; }
237
+ }
238
+ /* The shipped empty state's block padding is card-scale; one line of it inside a section leaves
239
+ a tall blank band. It also centres its one line across the full card, which reads as a hole;
240
+ inside a section it lines up with the section's rows instead. */
241
+ .support-section .empty-state {
242
+ align-items: flex-start;
243
+ text-align: left;
244
+ padding-block: var(--space-2);
245
+ }
246
+ #editor-notice:empty { display: none; }
247
+ #editor-notice { margin-bottom: var(--space-4); }
248
+ /* The one part of the narrow layout the pair's own measure cannot carry: once it has wrapped, the
249
+ proposal leads, so the reader meets the words before the file's own text. An engine without
250
+ container queries keeps the file's text first. */
251
+ @container (max-width: 640px) {
252
+ #editor-region { order: -1; }
253
+ }
133
254
 
134
- /* ---- Entry editor ---- */
135
- /* The one live region, kept out of sight by the shipped .sr-only utility. */
136
- .editor-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-8); align-items: start; }
137
- .editor-main { min-width: 0; }
138
- .editor-rail { display: flex; flex-direction: column; gap: var(--space-6); position: sticky; top: var(--space-8); }
139
- @media (max-width: 900px) {
140
- .editor-grid { grid-template-columns: 1fr; }
141
- .editor-rail { position: static; }
142
- }
143
- /* The editor's title card header: the title falls back to the anchor text, which can be one long
144
- unbroken string, so it wraps inside the card instead of overflowing or shoving the status pill. */
145
- .editor-title { min-width: 0; overflow-wrap: anywhere; }
146
- /* The entry text ships verbatim, often carrying markdown backticks and significant whitespace;
147
- mono gives every glyph, space included, one full cell so both stay countable while editing. */
255
+ /* ---- Textareas: no manual resize ----
256
+ The shipped .input pins a control height and the shipped textarea rule adds a resize
257
+ handle; both fight the page's sizing. The editor box holds a viewport-clamped height and the
258
+ notes box a fixed one; the style-guide fields autosize to their content. */
259
+ main.page textarea.input { height: auto; resize: none; }
148
260
  #editor-text { font-family: var(--font-mono); }
261
+ #editor-notes { height: 120px; min-height: 120px; max-height: 120px; overflow-y: auto; }
262
+ #style-guide-markdown { min-height: 200px; }
263
+ #style-guide-glossary { min-height: 140px; }
149
264
 
150
- /* ---- Editor prev/next rails ----
151
- One full-height fixed strip per side, below the navbar, so the previous and next entries are
152
- a single click away while reading. The strip sits inside the page's inline padding, clear of content. */
153
- .editor-nav-btn {
154
- position: fixed;
155
- top: var(--navbar-h);
156
- bottom: 0;
157
- width: 44px;
265
+ /* The live metrics row under the proposal's textarea: length, placeholders, tone. */
266
+ .constraint-row {
158
267
  display: flex;
159
268
  align-items: center;
160
- justify-content: center;
269
+ flex-wrap: wrap;
270
+ gap: var(--space-2) var(--space-4);
271
+ margin-top: var(--space-3);
272
+ font-size: var(--t-2);
161
273
  color: var(--text-3);
162
- text-decoration: none;
163
- z-index: 40;
164
- transition: background var(--dur-tint) var(--ease), color var(--dur-tint) var(--ease);
165
- }
166
- .editor-nav-prev { left: 0; }
167
- .editor-nav-next { right: 0; }
168
- .editor-nav-btn:hover { background: var(--surface-2); color: var(--text); }
169
- .editor-nav-btn.is-disabled { color: var(--text-4); opacity: 0.4; pointer-events: none; }
170
- .editor-nav-btn svg { width: 18px; height: 18px; }
171
- @media (max-width: 900px) { .editor-nav { display: none; } }
172
- #editor-region > .callout { margin-bottom: var(--space-4); }
173
- #editor-region > .btn { margin-top: var(--space-4); }
174
- .editor-actions { margin-top: var(--space-4); display: flex; align-items: center; gap: var(--space-2); }
175
- /* The in-flight indicator for an approve or reject from the editor: htmx's own rules fade it in. */
176
- .editor-actions .htmx-indicator { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--t-2); color: var(--text-3); }
177
-
178
- /* The notes card: the agent's row reuses the constraint row shape; the human's textarea and its
179
- save button sit below it. */
180
- .notes-field { margin-top: var(--space-2); }
181
- .notes-actions { margin-top: var(--space-2); display: flex; justify-content: flex-end; }
182
-
183
- /* Constraint panel: live count and placeholder checklist; the server owns the refusal. */
184
- .constraint-row { display: flex; gap: var(--space-3); padding: 6px 0; }
185
- .constraint-row + .constraint-row { border-top: 1px solid var(--line); }
186
- .constraint-label { flex-shrink: 0; font: 500 11px/1.4 var(--font-sans); letter-spacing: var(--caps); text-transform: uppercase; color: var(--text-3); width: 96px; padding-top: 2px; }
187
- .constraint-value { min-width: 0; color: var(--text-2); font-size: var(--t-2); }
188
- .char-count { color: var(--text-2); font-size: var(--t-2); }
189
- .char-count.over { color: var(--danger); }
190
- .placeholder-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
274
+ }
275
+ .placeholder-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-3); }
191
276
  .placeholder-item { display: flex; align-items: center; gap: var(--space-2); }
192
277
  .placeholder-state { font-size: var(--t-2); color: var(--text-3); min-width: 52px; }
193
278
  .placeholder-state.is-present { color: var(--success); }
279
+ .char-count.over { color: var(--danger); }
194
280
 
195
- /* The context card's secondary block: the provenance rows (hash, filer, stale note) collapsed
196
- behind a summary, so the anchor and the agent-supplied context rows lead the card. */
281
+ .editor-actions { margin-top: var(--space-4); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
282
+ /* The in-flight indicators for a reject from the editor and a style-guide save: htmx
283
+ fades the span in via .htmx-request, and display:none until then keeps the laid-out span from
284
+ widening the actions row past the viewport on a narrow screen. */
285
+ .editor-actions .htmx-indicator { display: none; }
286
+ .editor-actions .htmx-indicator.htmx-request { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--t-2); color: var(--text-3); }
287
+
288
+ /* ---- Header prev/next ----
289
+ The neighbour pair lives in the page header's toolbar as secondary buttons; a boundary
290
+ keeps the slot with a disabled button so the pair never shifts. The meta row under the
291
+ title truncates the file path instead of letting a long one widen the page. */
292
+ .page-meta { min-width: 0; }
293
+ .page-meta-item { min-width: 0; }
294
+ .page-file { min-width: 0; max-width: 100%; }
295
+
296
+ /* ---- Notes ----
297
+ The notes card: the agent's row is the info-table readout; the human's textarea and its
298
+ save button sit below it. */
299
+ .notes-field { margin-top: var(--space-2); }
300
+ .notes-actions { margin-top: var(--space-2); display: flex; justify-content: flex-end; }
301
+
302
+ /* The details card's secondary block: the provenance rows (hash, filer, stale note) collapsed
303
+ behind a disclosure, so the agent-supplied context rows lead the card. */
197
304
  .context-secondary { margin-top: var(--space-3); }
198
- .context-secondary summary { cursor: pointer; font-size: var(--t-2); color: var(--text-3); }
199
305
  .context-secondary .info-table { margin-top: var(--space-2); }
200
306
 
201
307
  /* Repo row link: the brand icon beside the shortened owner/repo path, linking out to the host. */
202
- .repo-link { display: inline-flex; align-items: center; gap: 6px; color: var(--text); text-decoration: none; }
203
- .repo-link:hover { color: var(--accent-text); }
308
+ .repo-link { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--text); text-decoration: none; }
309
+ .repo-link:hover { color: var(--accent-text); text-decoration: none; }
204
310
  .repo-icon { width: 14px; height: 14px; flex-shrink: 0; fill: currentColor; opacity: var(--icon-dim); }
205
- .placeholder-token { font: 400 var(--t-2)/1 var(--font-mono); color: var(--text); background: var(--well); border: 1px solid var(--line); border-radius: var(--r-1); padding: 2px 6px; }
206
311
 
207
- /* Entry images: one image per row at the card's width, a remove form below each, and the
208
- one-file upload form closing the card. The file input flexes to whatever width remains. */
209
- .image-list { list-style: none; margin: 0 0 var(--space-3); padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
312
+ /* Entry images: the dropzone is the upload control; the arrived images sit under it as the
313
+ shipped dropzone list, and the submit ends the block as its action row. */
314
+ .image-list { gap: var(--space-3); }
210
315
  .entry-image { display: block; max-width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--r-1); }
211
316
  .image-skip { margin: 0; font-size: var(--t-2); color: var(--text-3); }
212
317
  .image-remove { margin-top: var(--space-1); }
213
- .image-upload { display: flex; gap: var(--space-2); align-items: center; }
214
- .image-upload .input { flex: 1; min-width: 0; }
215
-
216
- /* Revision history: newest first; the text clamps like the list's entry text. */
217
- .history-empty { font-size: var(--t-2); color: var(--text-4); padding: var(--space-2) 0; }
218
- .revision-list { list-style: none; margin: 0; padding: 0; }
219
- .revision { padding: var(--space-3) 0; }
220
- .revision + .revision { border-top: 1px solid var(--line); }
221
- .revision-head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
222
- .revision-time { font-size: var(--t-2); color: var(--text-4); }
223
- .revision-head .btn { margin-left: auto; }
224
- .revision-text {
225
- display: -webkit-box;
226
- -webkit-line-clamp: 2;
227
- -webkit-box-orient: vertical;
228
- overflow: hidden;
229
- font-size: var(--t-2);
230
- color: var(--text-2);
231
- }
318
+ .image-upload { display: flex; justify-content: flex-end; }
232
319
 
233
- /* ---- Login page: the navbar shell, then one centered card ---- */
234
- .login-main { display: flex; flex-direction: column; align-items: center; }
235
- .login-main .page-header, .login-card { width: 100%; max-width: 400px; }
236
- .login-card .card-content { display: flex; flex-direction: column; gap: var(--space-4); }
320
+ /* ---- Login: the page-state card's content column ---- */
321
+ .login-content { display: flex; flex-direction: column; gap: var(--space-4); }
237
322
  .login-actions { display: flex; justify-content: flex-end; }
238
323
 
239
- /* ---- Style guide editor: the section selector, then the one card ---- */
240
- .style-guide-projects { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-8); }
241
- #style-guide-region > .callout { margin-bottom: var(--space-4); }
324
+ /* ---- Style guide editor: the section tabs, then the one card ---- */
325
+ /* The project strip can hold every project's tab, so it scrolls in its own box like the status
326
+ strip does; the document never widens for it. */
327
+ .style-guide-projects { margin-bottom: var(--space-4); overflow-x: auto; }
328
+ /* The document default underlines every link on hover; the tab costume is a button's, so the
329
+ reset rides the anchor here. */
330
+ .style-guide-projects a.tab,
331
+ .style-guide-projects a.tab:hover { text-decoration: none; }
332
+ /* The settled notices sit on the same measure as the card below them; a full-width callout
333
+ over a half-width form read as two unrelated blocks. */
334
+ #style-guide-region > .callout { max-width: var(--page-narrow); margin-bottom: var(--space-4); }
242
335
  #style-guide-region > .btn { margin-top: var(--space-4); }
243
- .style-guide-card .card-content { display: flex; flex-direction: column; gap: var(--space-5); }
336
+ /* The fields stack in one column on the narrow measure, so the guide's long inputs never
337
+ stretch the whole page column. */
338
+ .style-guide-card { max-width: var(--page-narrow); }
339
+ .style-guide-fields { display: flex; flex-direction: column; gap: var(--space-4); }
244
340
  .style-guide-card .editor-actions { margin-top: 0; }