1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/* Docket's layout on top of Gloss (turn 9c). Signal Teal is the project
accent: it marks the Do badge and the Comment button, nothing else. */
:root { --gl-color-accent: var(--gl-accent-teal); }
html, body { height: 100%; }
body { font: var(--gl-step--1)/1.5 var(--gl-font-text); }
h1, h2, p { margin: 0; }
button { font: inherit; color: inherit; }
.grow { flex: 1; }
.empty { color: var(--gl-color-text-tertiary); }
.count { font-size: var(--gl-step--2); color: var(--gl-color-text-secondary); }
/* Three panes on the fill: bare mailboxes, the list on a card, the thread
as a stack of cards. dvh over vh where it exists: mobile URL bars
shouldn't shrink the panes twice. */
.app { height: 100vh; height: 100dvh; display: grid; grid-template-columns: 200px 420px minmax(0, 1fr); gap: var(--gl-space-2xs); padding: var(--gl-space-2xs) var(--gl-space-2xs) var(--gl-space-s); background: var(--gl-color-surface-fill); }
/* Gloss Input: a field on a rule. */
.field { box-sizing: border-box; width: 100%; border: none; border-bottom: var(--gl-border-w) solid var(--gl-color-border-default); border-radius: 0; background: transparent; padding: 8px 2px; font: var(--gl-step--1) var(--gl-font-text); color: var(--gl-color-text-primary); outline: none; }
.field::placeholder { color: var(--gl-color-text-tertiary); }
.field:focus { border-bottom-color: var(--gl-color-accent); }
/* Gloss Button, primary/sm: the one accent stamp in a view. */
.btn { display: inline-flex; align-items: center; justify-content: center; padding: 7px 13px; border: none; border-radius: var(--gl-radius); font: 500 var(--gl-step--2)/1.15 var(--gl-font-mono); letter-spacing: .01em; white-space: nowrap; cursor: pointer; }
.btn.primary { background: var(--gl-color-accent); color: var(--gl-color-accent-fg); }
.btn.primary:hover { filter: brightness(.94); }
/* Gloss Badge: an ink square and a mono label, no fill. */
.badge { display: inline-flex; align-items: center; gap: 6px; font: 500 var(--gl-step--2)/1 var(--gl-font-mono); text-transform: uppercase; letter-spacing: .09em; white-space: nowrap; color: var(--gl-color-text-secondary); }
.badge .mark { width: 6px; height: 6px; flex-shrink: 0; background: currentColor; }
.badge.accent { color: var(--gl-color-accent-ink); }
.badge.success { color: var(--gl-color-success); }
/* ── mailboxes ── */
.side { padding: var(--gl-space-m) var(--gl-space-s) var(--gl-space-2xs); display: flex; flex-direction: column; gap: var(--gl-space-m); min-height: 0; overflow-y: auto; }
.brand-row { display: flex; justify-content: space-between; align-items: center; }
/* Centered in the strip .app leaves free at the bottom right. */
.version { position: fixed; right: var(--gl-space-2xs); bottom: calc((var(--gl-space-s) - 1em) / 2); text-transform: none; letter-spacing: 0; color: var(--gl-color-text-tertiary); }
.search-btn { all: unset; display: flex; cursor: pointer; color: var(--gl-color-text-tertiary); transition: color var(--gl-dur-fast) var(--gl-ease); }
.search-btn:hover { color: var(--gl-color-text-primary); }
.search-btn:focus-visible { outline: 2px solid var(--gl-color-accent); outline-offset: 2px; border-radius: 2px; }
.brand { font: 600 var(--gl-step-0)/1.2 var(--gl-font-text); letter-spacing: -.01em; padding: 0 2px; }
.nav { display: flex; flex-direction: column; gap: 4px; }
.nav-item { display: flex; justify-content: space-between; align-items: baseline; padding: 7px 8px; margin: 0 -8px; border-radius: var(--gl-radius); color: inherit; text-decoration: none; transition: background var(--gl-dur-fast) var(--gl-ease); }
.nav-item:hover { background: var(--gl-color-surface-fill-2); color: inherit; }
.nav-item.on { background: var(--gl-color-surface-card); font-weight: 600; }
.who { display: flex; flex-direction: column; gap: 3px; }
.who-btn { all: unset; cursor: pointer; display: flex; flex-direction: column; gap: 3px; padding: 7px var(--gl-space-s); margin: 0 calc(-1 * var(--gl-space-s)) calc(-1 * var(--gl-space-2xs)); border-radius: var(--gl-radius); }
.who-btn:hover { background: var(--gl-color-surface-fill-2); }
.who-name { font-weight: 600; }
.who-login { text-transform: none; letter-spacing: 0; color: var(--gl-color-text-tertiary); }
/* ── the list: one card. A one-message thread is a single row; a longer one
heads a row per message. Unread rows carry full ink; read ones
step back. ── */
.list { background: var(--gl-color-surface-card); border-radius: 6px; padding: var(--gl-space-m) var(--gl-space-m) 0; display: flex; flex-direction: column; gap: var(--gl-space-m); min-height: 0; overflow-y: auto; }
.list-head { display: flex; justify-content: space-between; align-items: baseline; }
.list-head h1, .thread-head h2 { font: 600 var(--gl-step-0)/1.2 var(--gl-font-text); letter-spacing: -.01em; }
.section-head { color: var(--gl-color-text-tertiary); }
.groups { display: flex; flex-direction: column; gap: var(--gl-space-s); padding: var(--gl-space-2xs) 0 var(--gl-space-m); }
.group { display: flex; flex-direction: column; gap: var(--gl-space-3xs); }
.subj { min-width: 0; font-weight: 600; color: var(--gl-color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.group:not(.unread) > .subj, .row.solo:not(.unread) .subj { font-weight: 500; color: var(--gl-color-text-secondary); }
.age { font-size: var(--gl-step--2); color: var(--gl-color-text-tertiary); white-space: nowrap; text-align: right; }
.rail { display: flex; flex-direction: column; gap: 2px; padding-left: 6px; }
.row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 2px 10px; padding: 6px 8px 8px 10px; margin-right: -8px; border-radius: var(--gl-radius); color: var(--gl-color-text-secondary); text-decoration: none; transition: background var(--gl-dur-fast) var(--gl-ease); }
.row.solo { margin-left: -10px; }
.row:hover { background: var(--gl-color-surface-fill); color: var(--gl-color-text-secondary); }
.row.sel { background: var(--gl-color-surface-fill); }
.row .from, .row .line { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row.unread .from { font-weight: 500; color: var(--gl-color-text-primary); }
.row .line { font-size: var(--gl-step--2); }
.row .snip { font-size: var(--gl-step--2); color: var(--gl-color-text-tertiary); }
.rail .row .snip { grid-column: 1 / -1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ── the thread: each email its own card, comments bare between them ── */
.reader { min-height: 0; overflow-y: auto; }
.reader > .empty { padding: var(--gl-space-m) var(--gl-space-l); }
.thread { padding: var(--gl-space-m) var(--gl-space-s) var(--gl-space-m) var(--gl-space-l); display: flex; flex-direction: column; gap: var(--gl-space-s); }
.thread-head { display: flex; align-items: baseline; gap: var(--gl-space-2xs); }
.thread-head .type-label { color: var(--gl-color-text-tertiary); }
.chain { display: flex; flex-direction: column; gap: var(--gl-space-xs); }
.item { display: grid; grid-template-columns: minmax(0, 1fr) 112px; gap: var(--gl-space-m); padding: var(--gl-space-s); border-radius: 6px; }
.item.card { background: var(--gl-color-surface-card); }
.item.card.sel { box-shadow: 0 0 0 var(--gl-border-w) var(--gl-color-border-default), var(--gl-shadow-float); }
.item.comment { gap: var(--gl-space-s); }
.item .body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.item .author { display: flex; gap: 10px; align-items: baseline; }
.item .from { cursor: pointer; }
.item .addr, .item .copies { color: var(--gl-color-text-tertiary); }
.item .copies { font-size: var(--gl-step--2); }
.item .text { line-height: 1.6; white-space: pre-line; overflow-wrap: anywhere; }
/* A reply's quoted history: folded behind a quiet toggle, faint when open. */
.quote-btn, .images-btn { all: unset; box-sizing: border-box; display: inline-flex; align-items: center; align-self: flex-start; cursor: pointer; padding: 4px 6px; margin-left: -6px; border-radius: var(--gl-radius); color: var(--gl-color-text-tertiary); font: var(--gl-step--2) var(--gl-font-mono); transition: background var(--gl-dur-fast) var(--gl-ease); }
.quote-btn:hover, .images-btn:hover { background: var(--gl-color-surface-fill); color: var(--gl-color-text-primary); }
.quote-btn:focus-visible, .images-btn:focus-visible { outline: 2px solid var(--gl-color-accent); outline-offset: 1px; }
.item .text.quoted { color: var(--gl-color-text-tertiary); }
/* Body links stay in the text's ink — teal is spoken for — and say
they're links with the underline alone. */
.item .text a { color: inherit; }
.item .text a:hover { color: var(--gl-color-text-primary); }
.item .text.closed { cursor: pointer; color: var(--gl-color-text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* HTML mail keeps its own design on the white ground senders assume;
the frame can't be measured from outside, so it takes a tall default
and can be dragged taller. */
.html-mail { display: flex; flex-direction: column; gap: 6px; }
.html-body { width: 100%; height: 70vh; border: var(--gl-border-w) solid var(--gl-color-border-default); border-radius: var(--gl-radius); background: #fff; resize: vertical; overflow: auto; }
/* The gutter: date, then the email's values, right-aligned. */
.gutter { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; text-align: right; }
.comment .gutter { justify-content: flex-start; }
/* History: a quiet line between rows, not a card. */
.item.event { padding-top: 0; padding-bottom: 0; }
.event-text { font-size: var(--gl-step--2); color: var(--gl-color-text-tertiary); }
.when { display: flex; align-items: center; gap: 2px; }
.date { font-size: var(--gl-step--2); color: var(--gl-color-text-secondary); }
.icon-btn { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: var(--gl-radius); color: var(--gl-color-text-secondary); transition: background var(--gl-dur-fast) var(--gl-ease); }
.icon-btn:hover { background: var(--gl-color-surface-fill); color: var(--gl-color-text-primary); }
.sent { color: var(--gl-color-text-tertiary); }
/* A value is its own control: click it to open its menu. */
.ctrl { position: relative; }
.ctrl-btn { all: unset; box-sizing: border-box; display: flex; padding: 4px 6px; border-radius: var(--gl-radius); cursor: pointer; transition: background var(--gl-dur-fast) var(--gl-ease); }
.ctrl-btn:hover, .ctrl-btn.on { background: var(--gl-color-surface-fill); }
.ctrl-btn:focus-visible, .opt:focus-visible, .who-btn:focus-visible, .icon-btn:focus-visible { outline: 2px solid var(--gl-color-accent); outline-offset: 1px; }
.value { color: var(--gl-color-text-primary); }
.value.unset { color: var(--gl-color-text-tertiary); }
.value.static { padding: 4px 6px; }
/* The open menu is the one floating thing in the view. */
.menu { position: absolute; right: -12px; width: 176px; z-index: 2; background: var(--gl-color-surface-card); border-radius: var(--gl-radius); box-shadow: var(--gl-shadow-float); padding: 6px; display: flex; flex-direction: column; text-align: left; }
.menu.up { bottom: calc(100% + 2px); }
.menu.down { top: calc(100% + 2px); }
.who .menu.up { left: 0; right: auto; }
.opt { all: unset; box-sizing: border-box; cursor: pointer; display: flex; justify-content: space-between; align-items: center; padding: 9px 10px; border-radius: var(--gl-radius); }
.opt:hover, .opt.cur { background: var(--gl-color-surface-fill); }
.check { font: 500 var(--gl-step--2)/1 var(--gl-font-mono); }
/* The mobile back link; the desktop two-pane never shows it. */
.back { display: none; color: var(--gl-color-text-secondary); }
/* Deleting is the one destructive value: quiet until hovered. */
.del-btn { all: unset; box-sizing: border-box; cursor: pointer; padding: 4px 6px; border-radius: var(--gl-radius); color: var(--gl-color-text-tertiary); font: var(--gl-step--2) var(--gl-font-mono); transition: background var(--gl-dur-fast) var(--gl-ease); }
.del-btn:hover { background: var(--gl-color-surface-fill); color: var(--gl-color-text-primary); }
.del-btn:focus-visible { outline: 2px solid var(--gl-color-accent); outline-offset: 1px; }
/* The trash confirm: a small centered modal over a dimmed page. */
dialog { margin: auto; border: none; border-radius: var(--gl-radius); padding: var(--gl-space-m); background: var(--gl-color-surface-card); color: var(--gl-color-text-primary); box-shadow: var(--gl-shadow-float); max-width: min(380px, calc(100vw - 2 * var(--gl-space-m))); }
dialog::backdrop { background: rgb(0 0 0 / .4); }
dialog h3 { font: 500 var(--gl-step-0)/1.3 var(--gl-font-text); margin: 0 0 var(--gl-space-s) 0; }
dialog form { display: flex; justify-content: flex-end; gap: var(--gl-space-xs); }
.btn.danger { background: var(--gl-color-danger); color: #fff; }
.comment-box { display: flex; align-items: flex-end; gap: var(--gl-space-xs); }
/* Gloss toast: an ink slab, mono, no border. */
.toast { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); display: flex; align-items: center; gap: var(--gl-space-s); padding: 12px var(--gl-space-s); min-width: 320px; background: var(--gl-color-text-primary); color: var(--gl-color-surface-page); border-radius: var(--gl-radius); box-shadow: var(--gl-shadow-float); font: var(--gl-step--2) var(--gl-font-mono); z-index: 3; }
.toast form { display: contents; }
.toast-act { all: unset; cursor: pointer; text-transform: uppercase; letter-spacing: .09em; text-decoration: underline; text-underline-offset: 3px; }
.toast-x { all: unset; cursor: pointer; opacity: .6; font-size: 16px; line-height: 1; }
/* ── phones: one pane at a time ──
The shell drops to a top bar over either the list or the thread —
which one rides on .app.sel, the same URL state the desktop two-pane
reads. Opening a thread hides the list; the back link returns. */
@media (max-width: 760px) {
.app { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); padding: var(--gl-space-3xs) var(--gl-space-3xs) var(--gl-space-2xs); }
.side { flex-direction: row; align-items: center; flex-wrap: wrap; gap: var(--gl-space-3xs) var(--gl-space-xs); padding: 0 var(--gl-space-3xs); overflow-y: visible; }
.brand-row { gap: var(--gl-space-xs); }
.nav { flex-direction: row; flex-wrap: wrap; gap: 2px; order: 3; width: 100%; }
.nav-item { margin: 0; padding: 8px 10px; }
.grow { display: none; }
.who { order: 2; margin-left: auto; }
.who-btn { margin: 0; padding: 8px 10px; }
.who-login { display: none; }
.version { display: none; }
.list { padding: var(--gl-space-xs) var(--gl-space-2xs) 0; }
.thread { padding: var(--gl-space-xs) var(--gl-space-2xs) var(--gl-space-m); }
.thread-head { gap: var(--gl-space-3xs); }
.thread-head .back { display: inline-flex; align-items: center; padding: 6px; margin-left: -6px; border-radius: var(--gl-radius); color: var(--gl-color-text-secondary); }
.thread-head .back:hover { background: var(--gl-color-surface-fill); }
.app.sel .list { display: none; }
.app:not(.sel) .reader { display: none; }
.item { grid-template-columns: minmax(0, 1fr); gap: var(--gl-space-2xs); }
.item .gutter { flex-direction: row; align-items: center; flex-wrap: wrap; gap: var(--gl-space-3xs) var(--gl-space-2xs); text-align: left; }
.comment .gutter { justify-content: flex-start; }
.toast { left: var(--gl-space-2xs); right: var(--gl-space-2xs); transform: none; min-width: 0; }
}
/* ── touch: fingers, not cursors ──
Every control grows to a 44px hit area, and fields stay at 16px or
more, below which iOS Safari zooms the page on focus. */
@media (pointer: coarse) {
.field { font-size: max(16px, var(--gl-step--1)); }
.ctrl-btn, .del-btn, .quote-btn, .images-btn, .opt, .nav-item, .who-btn { box-sizing: border-box; min-height: 44px; align-items: center; }
.who-btn { align-items: stretch; justify-content: center; }
.icon-btn, .back, .search-btn { min-width: 44px; min-height: 44px; justify-content: center; align-items: center; }
.thread-head .back { margin-left: -14px; }
.search-btn { margin: -14px 0; }
.toast-act, .toast-x { padding: 12px 8px; margin: -12px 0; }
}