@kurtbruns/kestrel 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dev.vars.example +17 -24
- package/CHANGELOG.md +75 -1
- package/README.md +2 -2
- package/cli/dev-api.mjs +7 -1
- package/cli/dev.mjs +46 -1
- package/cli/init.mjs +6 -1
- package/dist/public/dashboard/app.js +234 -236
- package/dist/public/dashboard/app.js.map +4 -4
- package/dist/public/dashboard/index.html +6 -6
- package/dist/public/dashboard/styles.css +187 -177
- package/dist/worker/index.js +2118 -1998
- package/docs/README.md +3 -4
- package/docs/get-started/02-deploy.md +49 -18
- package/docs/get-started/03-access.md +4 -4
- package/docs/{guides/02-ses.md → get-started/04-ses.md} +75 -52
- package/docs/get-started/05-verify.md +8 -10
- package/docs/get-started/06-go-live.md +20 -7
- package/docs/guides/07-upgrade.md +2 -0
- package/docs/guides/08-try-locally.md +108 -0
- package/docs/reference/01-configuration.md +25 -14
- package/docs/reference/02-sending-domain-dns.md +19 -19
- package/migrations/0004_keyed_pending_to_resolve.sql +27 -0
- package/package.json +2 -2
- package/template/claude/CLAUDE.md +3 -1
- package/template/package.json +1 -0
- package/template/wrangler.jsonc +10 -6
- package/docs/get-started/01-overview.md +0 -47
- package/docs/get-started/04-resend.md +0 -134
|
@@ -5,26 +5,28 @@
|
|
|
5
5
|
different values on purpose; don't assume they share meaning. */
|
|
6
6
|
:root {
|
|
7
7
|
color-scheme: light dark;
|
|
8
|
-
/* Layering — one named z-index ladder (DESIGN §
|
|
8
|
+
/* Layering — one named z-index ladder (DESIGN §9). Every fixed or floating surface
|
|
9
9
|
references a rung by token, never a bare literal; the order encodes the intent.
|
|
10
10
|
z-index is theme-independent, so it lives only here (not the dark block). */
|
|
11
11
|
--z-content: 1;
|
|
12
12
|
--z-header: 10;
|
|
13
13
|
--z-menu: 20;
|
|
14
14
|
--z-bar: 30;
|
|
15
|
-
--z-
|
|
16
|
-
|
|
15
|
+
--z-modal: 40;
|
|
16
|
+
/* Toasts above modals: a toast raised while a dialog is open (an autosave that failed,
|
|
17
|
+
a send that started) must not sit dimmed beneath its backdrop. */
|
|
18
|
+
--z-toast: 50;
|
|
17
19
|
--fg: #18181b;
|
|
18
20
|
--muted: #52525b;
|
|
19
21
|
--line: #e4e4e7;
|
|
20
22
|
--bg: #fbfbfa;
|
|
21
23
|
--card: #fff;
|
|
22
24
|
--hover: #efefef;
|
|
23
|
-
/* Neutral ink — the strong near-black/near-white neutral (DESIGN §
|
|
25
|
+
/* Neutral ink — the strong near-black/near-white neutral (DESIGN §8). A text token
|
|
24
26
|
for headings and strong labels; never a button fill (Primary is --accent). */
|
|
25
27
|
--ink: #18181b;
|
|
26
28
|
--ink-on: #ffffff;
|
|
27
|
-
/* Semantic feedback — one {fg, bg, line} scale each for danger / warn / ok (DESIGN §
|
|
29
|
+
/* Semantic feedback — one {fg, bg, line} scale each for danger / warn / ok (DESIGN §8).
|
|
28
30
|
fg is the saturated hue (text on the soft bg, or a solid fill paired with an on-color);
|
|
29
31
|
bg is the soft tint behind a notice; line is its border. There is one red, not two —
|
|
30
32
|
the old --danger vs --b-bad-* split is collapsed here. Kept distinct from the
|
|
@@ -40,13 +42,11 @@
|
|
|
40
42
|
--ok-bg: #dcfce7;
|
|
41
43
|
--ok-line: #bbf7d0;
|
|
42
44
|
--ring: #2563eb;
|
|
43
|
-
--banner-bg: #eff6ff;
|
|
44
|
-
--banner-line: #bfdbfe;
|
|
45
45
|
--status-draft-bg: #f4f4f5;
|
|
46
46
|
--status-draft-fg: #52525b;
|
|
47
47
|
--status-draft-line: #d4d4d8;
|
|
48
48
|
/* Scheduled is a violet, deliberately off the slate-blue action accent (--accent)
|
|
49
|
-
so the "scheduled" status never reads as the primary action (DESIGN §
|
|
49
|
+
so the "scheduled" status never reads as the primary action (DESIGN §8); the fill
|
|
50
50
|
backs the scheduled pill, banner, and cards, and the line borders them. */
|
|
51
51
|
--status-scheduled-bg: #ece6f9;
|
|
52
52
|
--status-scheduled-fg: #6743ac;
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
--accent: #3355cc;
|
|
66
66
|
--accent-on: #ffffff;
|
|
67
67
|
--accent-soft: rgba(51, 85, 204, 0.12);
|
|
68
|
-
/* Syntax-highlight palette, shared by the template
|
|
68
|
+
/* Syntax-highlight palette, shared by the template and post
|
|
69
69
|
editors — the HTML/CSS .cx-* tokens and the Markdown .cx-md-* tokens map onto it. */
|
|
70
70
|
--syntax-bg: #fbfbfc;
|
|
71
71
|
--syntax-fg: #3f3f46;
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
--syntax-punctuation: #a1a1aa;
|
|
80
80
|
--syntax-variable: #2d47b8;
|
|
81
81
|
--syntax-variable-bg: rgba(51, 85, 204, 0.11);
|
|
82
|
-
/* Post editor's minimal scheme
|
|
82
|
+
/* Post editor's minimal scheme: marks get the color, content keeps --syntax-fg.
|
|
83
83
|
Heading # is crimson; the emphasis asterisks are a magenta-pink kept clear of the heading
|
|
84
84
|
crimson; block markers (>, list bullets, fence language) are a saturated, bright orange. */
|
|
85
85
|
--syntax-heading: #be123c;
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
/* Code surface — inline `code` chip and the fenced-block band. A hair off the editor well
|
|
89
89
|
(--syntax-bg), lighter than the neutral --chip, so code reads as code without a heavy box. */
|
|
90
90
|
--syntax-code-bg: #f2f2f4;
|
|
91
|
-
/* HTTP method badges in the API reference (DESIGN §
|
|
91
|
+
/* HTTP method badges in the API reference (DESIGN §8). A self-contained palette like
|
|
92
92
|
--syntax-*: verb colors are a naming convention, not feedback or lifecycle, so they
|
|
93
93
|
borrow no semantic scale. The badge tints each over a wash of itself (.api-method). */
|
|
94
94
|
--method-get: #166534;
|
|
@@ -122,8 +122,6 @@
|
|
|
122
122
|
--ok-bg: #14532d;
|
|
123
123
|
--ok-line: #15803d;
|
|
124
124
|
--ring: #60a5fa;
|
|
125
|
-
--banner-bg: #1e293b;
|
|
126
|
-
--banner-line: #334155;
|
|
127
125
|
--status-draft-bg: #2e2e33;
|
|
128
126
|
--status-draft-fg: #a1a1aa;
|
|
129
127
|
--status-draft-line: #52525b;
|
|
@@ -157,7 +155,7 @@
|
|
|
157
155
|
--syntax-punctuation: #6a6a72;
|
|
158
156
|
--syntax-variable: #a9bbf2;
|
|
159
157
|
--syntax-variable-bg: rgba(120, 150, 240, 0.18);
|
|
160
|
-
/* Post editor minimal scheme
|
|
158
|
+
/* Post editor minimal scheme — near-black/saturated hues lift to legible
|
|
161
159
|
tints on the dark well; the heading crimson becomes a rose, emphasis a lighter pink. */
|
|
162
160
|
--syntax-heading: #fb7185;
|
|
163
161
|
--syntax-emphasis: #f472b6;
|
|
@@ -593,7 +591,7 @@ body.nav-resizing .sidebar {
|
|
|
593
591
|
margin: 10px 2px 0;
|
|
594
592
|
line-height: 1.5;
|
|
595
593
|
}
|
|
596
|
-
/* The connected state: an inline status line reusing the --ok feedback scale (DESIGN §
|
|
594
|
+
/* The connected state: an inline status line reusing the --ok feedback scale (DESIGN §8),
|
|
597
595
|
not a notification — a quiet confirmation the agent has edited here. */
|
|
598
596
|
.conn-status {
|
|
599
597
|
display: flex;
|
|
@@ -899,6 +897,37 @@ body.nav-resizing .sidebar {
|
|
|
899
897
|
.auth-wall button {
|
|
900
898
|
margin-top: 12px;
|
|
901
899
|
}
|
|
900
|
+
/* The refused-configuration wall says more than the sign-in wall, so it reads as text: left
|
|
901
|
+
aligned, a little wider, with the variable and the server's words set apart. The locked
|
|
902
|
+
editor, before Access is set up, reads the same way. */
|
|
903
|
+
.config-wall,
|
|
904
|
+
.access-wall {
|
|
905
|
+
max-width: 560px;
|
|
906
|
+
text-align: left;
|
|
907
|
+
}
|
|
908
|
+
/* Text for a screen reader alone, such as a link's "(opens in a new tab)" beside a ↗ that is
|
|
909
|
+
hidden from it. */
|
|
910
|
+
.visually-hidden {
|
|
911
|
+
position: absolute;
|
|
912
|
+
width: 1px;
|
|
913
|
+
height: 1px;
|
|
914
|
+
margin: -1px;
|
|
915
|
+
padding: 0;
|
|
916
|
+
overflow: hidden;
|
|
917
|
+
clip-path: inset(50%);
|
|
918
|
+
white-space: nowrap;
|
|
919
|
+
border: 0;
|
|
920
|
+
}
|
|
921
|
+
.config-wall h2:focus,
|
|
922
|
+
.access-wall h2:focus {
|
|
923
|
+
outline: none;
|
|
924
|
+
}
|
|
925
|
+
.config-wall-problem {
|
|
926
|
+
padding: 10px 12px;
|
|
927
|
+
border-radius: 8px;
|
|
928
|
+
background: var(--bg);
|
|
929
|
+
overflow-wrap: anywhere;
|
|
930
|
+
}
|
|
902
931
|
|
|
903
932
|
/* The routed view. Capped for readability and centered in the space beside the
|
|
904
933
|
sidebar; the editor gets a slightly narrower canvas to match its old width. */
|
|
@@ -921,8 +950,8 @@ body.tool-mode .content {
|
|
|
921
950
|
}
|
|
922
951
|
|
|
923
952
|
/* --- Collapsed icon rail. Driven by data-nav="rail" on <html> (set from the viewport
|
|
924
|
-
width and the
|
|
925
|
-
index.html), so the collapse toggle can pin it at any width in range. The rail is the
|
|
953
|
+
width and the collapse toggle's override for this load — see client/shell.ts and the
|
|
954
|
+
<head> guard in index.html), so the collapse toggle can pin it at any width in range. The rail is the
|
|
926
955
|
default across the tablet range and small desktops; the nav keeps its one-click
|
|
927
956
|
reach, labels move to hover/focus tooltips, and the secondary chrome (brand text,
|
|
928
957
|
account, tool footer) folds down to its marks. The whole block is gated at ≥540 so a
|
|
@@ -1152,7 +1181,7 @@ button:hover {
|
|
|
1152
1181
|
button:active {
|
|
1153
1182
|
transform: translateY(0.5px);
|
|
1154
1183
|
}
|
|
1155
|
-
/* --- Buttons: five roles (DESIGN §
|
|
1184
|
+
/* --- Buttons: five roles (DESIGN §7), chosen by importance and consequence, one
|
|
1156
1185
|
class each. The bare `button` above is the neutral default (Ghost's card-filled
|
|
1157
1186
|
end); the classes below step up to Primary / Secondary or down to the quiet Ghost,
|
|
1158
1187
|
plus Danger and the square Icon. Exactly one solid brand-blue action — Primary — is
|
|
@@ -1161,7 +1190,7 @@ button:active {
|
|
|
1161
1190
|
|
|
1162
1191
|
/* One box height across the label roles, so a row that mixes them (the editor's
|
|
1163
1192
|
Save draft / Send test email / Schedule, a modal's Cancel + confirm) aligns:
|
|
1164
|
-
roles differ by treatment — fill, weight, font-size — never by height (DESIGN §
|
|
1193
|
+
roles differ by treatment — fill, weight, font-size — never by height (DESIGN §7).
|
|
1165
1194
|
A smaller-font role (Secondary, Ghost) grows to this floor with its label centered
|
|
1166
1195
|
rather than sitting a few pixels short of the Primary beside it. The bare `button`
|
|
1167
1196
|
default already meets it; the square Icon role and the segmented toggle Control keep
|
|
@@ -1490,16 +1519,10 @@ td a:hover {
|
|
|
1490
1519
|
color: var(--status-sending-fg);
|
|
1491
1520
|
border-color: var(--status-sending-line);
|
|
1492
1521
|
}
|
|
1493
|
-
.badge.canceled,
|
|
1494
|
-
.badge.failed {
|
|
1495
|
-
background: var(--danger-bg);
|
|
1496
|
-
color: var(--danger-fg);
|
|
1497
|
-
border-color: var(--danger-line);
|
|
1498
|
-
}
|
|
1499
1522
|
/* Subscriber consent states borrow a hue that matches the state's meaning: confirmed →
|
|
1500
1523
|
the sent green, unsubscribed → the draft grey, suppressed → danger red (below). Pending
|
|
1501
1524
|
awaits double-opt-in — a waiting/attention state — so it takes the amber `warn` scale,
|
|
1502
|
-
NOT `--status-sending`: the send "sending" badge is blue (DESIGN §
|
|
1525
|
+
NOT `--status-sending`: the send "sending" badge is blue (DESIGN §8), and a pending subscriber
|
|
1503
1526
|
must never read as a send in flight. (The two coincided only while `--status-sending` was
|
|
1504
1527
|
amber; when it went blue for the dispatch treatment, this pill must stay amber.) */
|
|
1505
1528
|
.badge.confirmed {
|
|
@@ -1721,7 +1744,7 @@ body.web-mode .content {
|
|
|
1721
1744
|
margin: 0;
|
|
1722
1745
|
padding: 0;
|
|
1723
1746
|
}
|
|
1724
|
-
/* The page sits under one strip that says where the post stands (DESIGN §
|
|
1747
|
+
/* The page sits under one strip that says where the post stands (DESIGN §3). The strip is
|
|
1725
1748
|
the editor's, above the frame, and the frame is as tall as the post, so the window scrolls
|
|
1726
1749
|
and the strip goes up with the page like the test pages' strip. It wears the brand accent
|
|
1727
1750
|
like the reader surface's dev pill, its tag as the pill's inverted chip, and its content
|
|
@@ -1787,8 +1810,8 @@ body.web-mode .content {
|
|
|
1787
1810
|
margin: 48px auto;
|
|
1788
1811
|
padding: 0 16px;
|
|
1789
1812
|
}
|
|
1790
|
-
/* Sent record view
|
|
1791
|
-
semantic feedback scales (--ok / --warn / --danger, DESIGN §
|
|
1813
|
+
/* Sent record view: a read-only delivery record. The outcome tiles reuse the
|
|
1814
|
+
semantic feedback scales (--ok / --warn / --danger, DESIGN §8); a status badge, not
|
|
1792
1815
|
a feedback color, still labels the send's lifecycle. */
|
|
1793
1816
|
.rec-card {
|
|
1794
1817
|
display: flex;
|
|
@@ -1888,7 +1911,7 @@ body.web-mode .content {
|
|
|
1888
1911
|
}
|
|
1889
1912
|
}
|
|
1890
1913
|
|
|
1891
|
-
/* ---- per-recipient record
|
|
1914
|
+
/* ---- per-recipient record: the rows behind the outcome tiles ---- */
|
|
1892
1915
|
.rec-people {
|
|
1893
1916
|
margin-top: 26px;
|
|
1894
1917
|
}
|
|
@@ -1903,7 +1926,7 @@ body.web-mode .content {
|
|
|
1903
1926
|
margin: 0;
|
|
1904
1927
|
font-size: 15px;
|
|
1905
1928
|
}
|
|
1906
|
-
/* The view toggle (Failures / Delivered / All) is a Control, not a button (DESIGN §
|
|
1929
|
+
/* The view toggle (Failures / Delivered / All) is a Control, not a button (DESIGN §7):
|
|
1907
1930
|
the same segmented, aria-pressed pill group as the Template preview-width toggle, its
|
|
1908
1931
|
active segment in the Secondary palette. It switches what you're looking at; it never
|
|
1909
1932
|
commits an action. */
|
|
@@ -1955,8 +1978,8 @@ body.web-mode .content {
|
|
|
1955
1978
|
font-size: 13px;
|
|
1956
1979
|
}
|
|
1957
1980
|
|
|
1958
|
-
/* ---- live in-flight watch
|
|
1959
|
-
/* The sending treatment is blue (--status-sending-*, DESIGN §
|
|
1981
|
+
/* ---- live in-flight watch: the `sending` state of the record view ---- */
|
|
1982
|
+
/* The sending treatment is blue (--status-sending-*, DESIGN §8) — distinct from the
|
|
1960
1983
|
action slate-blue — carried by the phase pill, the dispatch bar, and the swatch. */
|
|
1961
1984
|
.rec-sw.sw-sending {
|
|
1962
1985
|
background: var(--status-sending-fg);
|
|
@@ -2122,7 +2145,7 @@ body.web-mode .content {
|
|
|
2122
2145
|
}
|
|
2123
2146
|
}
|
|
2124
2147
|
|
|
2125
|
-
/* ---- active-send row (Sent page "In progress") + dashboard widget
|
|
2148
|
+
/* ---- active-send row (Sent page "In progress") + dashboard widget ---- */
|
|
2126
2149
|
/* An in-flight send as a card with a live mini dispatch bar; blue, so it reads as
|
|
2127
2150
|
"sending" — distinct from the scheduled violet cards and the neutral sent rows. */
|
|
2128
2151
|
.card.active-card {
|
|
@@ -2166,6 +2189,27 @@ body.web-mode .content {
|
|
|
2166
2189
|
font-size: 12.5px;
|
|
2167
2190
|
color: var(--warn-fg);
|
|
2168
2191
|
}
|
|
2192
|
+
/* A scheduled send while no provider is connected (DESIGN §4): the countdown stays, and a
|
|
2193
|
+
line beneath it in the same amber says the send will be held when it fires. */
|
|
2194
|
+
.sched-held {
|
|
2195
|
+
margin-top: 4px;
|
|
2196
|
+
font-size: 12.5px;
|
|
2197
|
+
font-weight: 500;
|
|
2198
|
+
color: var(--warn-fg);
|
|
2199
|
+
}
|
|
2200
|
+
/* The editor's scheduled banner stacks its line and the held line on the left. */
|
|
2201
|
+
.banner-scheduled .sched-main {
|
|
2202
|
+
display: flex;
|
|
2203
|
+
flex-direction: column;
|
|
2204
|
+
/* Takes the room and wraps its own lines (the re-made line is a sentence), so Reschedule
|
|
2205
|
+
and Cancel stay at the right rather than dropping below; on a phone the row still wraps. */
|
|
2206
|
+
flex: 1 1 18rem;
|
|
2207
|
+
min-width: 0;
|
|
2208
|
+
}
|
|
2209
|
+
/* A draft's not-connected line sits above its actions bar, which keeps its own top gap. */
|
|
2210
|
+
.health.editor-not-connected {
|
|
2211
|
+
margin: 16px 0 0;
|
|
2212
|
+
}
|
|
2169
2213
|
|
|
2170
2214
|
/* Composer: GitHub-style Write/Preview tabs + a formatting toolbar */
|
|
2171
2215
|
.composer {
|
|
@@ -2299,7 +2343,7 @@ body.web-mode .content {
|
|
|
2299
2343
|
.composer-body {
|
|
2300
2344
|
position: relative;
|
|
2301
2345
|
}
|
|
2302
|
-
/* Post editor syntax-highlight overlay
|
|
2346
|
+
/* Post editor syntax-highlight overlay: a transparent <textarea> layered over
|
|
2303
2347
|
a highlighted <pre> that shows the colored Markdown beneath the caret. The two share
|
|
2304
2348
|
identical box + font metrics so the caret sits exactly on the colored text; the textarea
|
|
2305
2349
|
is the only scroller and the <pre> is synced to it in script. Unlike the template editor
|
|
@@ -2374,7 +2418,7 @@ body.web-mode .content {
|
|
|
2374
2418
|
background: #fff;
|
|
2375
2419
|
display: block;
|
|
2376
2420
|
}
|
|
2377
|
-
/* A scheduled post's Edit tab (DESIGN §
|
|
2421
|
+
/* A scheduled post's Edit tab (DESIGN §3): the text stays fully legible, selectable, and
|
|
2378
2422
|
copyable (the fields are readonly, not disabled, since a disabled textarea cannot even be
|
|
2379
2423
|
selected), on the recessed surface Settings uses for a read-only card (`.set-card.sunken`);
|
|
2380
2424
|
the caret is hidden because the field is not for typing. The way out is the composer's
|
|
@@ -2434,7 +2478,7 @@ body.web-mode .content {
|
|
|
2434
2478
|
}
|
|
2435
2479
|
/* The locked composer's foot: where a draft's foot offers images, a scheduled post's says
|
|
2436
2480
|
the way out, "Cancel the schedule to edit", with the read-only lock Settings uses. It is
|
|
2437
|
-
a hint, not a control (Cancel stays on the banner, DESIGN §
|
|
2481
|
+
a hint, not a control (Cancel stays on the banner, DESIGN §3), so no pointer and no hover
|
|
2438
2482
|
lift. `.nudge` pulses it once, in the read-only note's tint, when an edit is attempted,
|
|
2439
2483
|
so the refusal is seen where the keystroke landed; reduced-motion holds the tint instead. */
|
|
2440
2484
|
.composer-foot-lock {
|
|
@@ -2473,7 +2517,14 @@ body.web-mode .content {
|
|
|
2473
2517
|
.actions-bar > .row:only-child {
|
|
2474
2518
|
margin-left: auto;
|
|
2475
2519
|
}
|
|
2476
|
-
/* The
|
|
2520
|
+
/* The save side takes the room left over and lets a long cue (why a save failed) wrap
|
|
2521
|
+
inside it, so the test and schedule buttons keep their place at the right. */
|
|
2522
|
+
.actions-bar > .row:first-child:not(:only-child) {
|
|
2523
|
+
flex: 1 1 16rem;
|
|
2524
|
+
min-width: 0;
|
|
2525
|
+
flex-wrap: nowrap;
|
|
2526
|
+
}
|
|
2527
|
+
/* The autosave cue (DESIGN §5, §6), beside Save draft. The editor's save lifecycle:
|
|
2477
2528
|
Unsaved (amber --warn dot) · Saving… (pulsing --accent dot) · Saved (green --ok
|
|
2478
2529
|
dot). The dot is a reinforcement, never the whole signal — the text label carries
|
|
2479
2530
|
each state too, so the cue survives a reader who can't tell the dot's color. The
|
|
@@ -2482,6 +2533,7 @@ body.web-mode .content {
|
|
|
2482
2533
|
.save-status {
|
|
2483
2534
|
display: inline-flex;
|
|
2484
2535
|
align-items: center;
|
|
2536
|
+
min-width: 0;
|
|
2485
2537
|
gap: 7px;
|
|
2486
2538
|
font-size: 13px;
|
|
2487
2539
|
color: var(--muted);
|
|
@@ -2492,9 +2544,13 @@ body.web-mode .content {
|
|
|
2492
2544
|
.save-status.is-dirty {
|
|
2493
2545
|
color: var(--fg);
|
|
2494
2546
|
}
|
|
2547
|
+
.save-status.is-error {
|
|
2548
|
+
color: var(--danger-fg);
|
|
2549
|
+
}
|
|
2495
2550
|
.save-status.is-dirty::before,
|
|
2496
2551
|
.save-status.is-saving::before,
|
|
2497
|
-
.save-status.is-saved::before
|
|
2552
|
+
.save-status.is-saved::before,
|
|
2553
|
+
.save-status.is-error::before {
|
|
2498
2554
|
content: "";
|
|
2499
2555
|
width: 8px;
|
|
2500
2556
|
height: 8px;
|
|
@@ -2511,6 +2567,9 @@ body.web-mode .content {
|
|
|
2511
2567
|
.save-status.is-saved::before {
|
|
2512
2568
|
background: var(--ok-fg);
|
|
2513
2569
|
}
|
|
2570
|
+
.save-status.is-error::before {
|
|
2571
|
+
background: var(--danger-fg);
|
|
2572
|
+
}
|
|
2514
2573
|
@keyframes save-pulse {
|
|
2515
2574
|
0%,
|
|
2516
2575
|
100% {
|
|
@@ -2609,6 +2668,18 @@ body.web-mode .content {
|
|
|
2609
2668
|
margin: 0 0 14px;
|
|
2610
2669
|
line-height: 1.5;
|
|
2611
2670
|
}
|
|
2671
|
+
/* A dialog's not-connected line (DESIGN §4) takes the health block's amber warning, so it
|
|
2672
|
+
reads as the reason the dialog's action is off rather than as one more hint. */
|
|
2673
|
+
.modal .hint.not-connected {
|
|
2674
|
+
color: var(--warn-fg);
|
|
2675
|
+
background: var(--warn-bg);
|
|
2676
|
+
border: 1px solid var(--warn-line);
|
|
2677
|
+
border-radius: 8px;
|
|
2678
|
+
padding: 10px 12px;
|
|
2679
|
+
}
|
|
2680
|
+
.modal .hint.not-connected a {
|
|
2681
|
+
color: inherit;
|
|
2682
|
+
}
|
|
2612
2683
|
.modal .actions {
|
|
2613
2684
|
display: flex;
|
|
2614
2685
|
justify-content: flex-end;
|
|
@@ -2626,10 +2697,16 @@ body.web-mode .content {
|
|
|
2626
2697
|
padding-top: 14px;
|
|
2627
2698
|
border-top: 1px solid var(--line);
|
|
2628
2699
|
}
|
|
2629
|
-
/* The alternate-path row can carry a plain note beside its link
|
|
2700
|
+
/* The alternate-path row can carry a plain note beside its link; the note wraps and the
|
|
2701
|
+
link stays whole on its line. */
|
|
2630
2702
|
.modal .altrow .altrow-note {
|
|
2631
2703
|
font-size: 12.5px;
|
|
2632
2704
|
color: var(--muted);
|
|
2705
|
+
min-width: 0;
|
|
2706
|
+
}
|
|
2707
|
+
.modal .altrow .linkbtn {
|
|
2708
|
+
flex: none;
|
|
2709
|
+
white-space: nowrap;
|
|
2633
2710
|
}
|
|
2634
2711
|
/* The back link at the top of the Send-now view needs no divider — it returns from,
|
|
2635
2712
|
rather than demotes below, the Schedule view. */
|
|
@@ -2639,7 +2716,7 @@ body.web-mode .content {
|
|
|
2639
2716
|
border-top: 0;
|
|
2640
2717
|
justify-content: flex-start;
|
|
2641
2718
|
}
|
|
2642
|
-
/* Link — a text-link-styled action (DESIGN §
|
|
2719
|
+
/* Link — a text-link-styled action (DESIGN §7), not one of the button roles. Reserved
|
|
2643
2720
|
for a deliberately subordinate choice, like the in-modal Send now that sits below
|
|
2644
2721
|
the Schedule Primary: it must read as clearly demoted, so it is a link, not a Ghost
|
|
2645
2722
|
button that would compete with Cancel. Still a real <button>, so it's keyboard- and
|
|
@@ -2660,12 +2737,12 @@ button.linkbtn:hover {
|
|
|
2660
2737
|
|
|
2661
2738
|
.countdown {
|
|
2662
2739
|
font-variant-numeric: tabular-nums;
|
|
2663
|
-
/* The scheduled violet (DESIGN §
|
|
2740
|
+
/* The scheduled violet (DESIGN §8), so the fire-time countdown on the scheduled
|
|
2664
2741
|
cards (Sent page + dashboard) carries the same "scheduled" cue as the pill. */
|
|
2665
2742
|
color: var(--status-scheduled-fg);
|
|
2666
2743
|
}
|
|
2667
2744
|
/* Past the server's missed tolerance the countdown says how late the send is, in the
|
|
2668
|
-
danger tone: a missed fire time needs a look (DESIGN §
|
|
2745
|
+
danger tone: a missed fire time needs a look (DESIGN §4). */
|
|
2669
2746
|
.countdown.countdown-missed {
|
|
2670
2747
|
color: var(--danger-fg);
|
|
2671
2748
|
}
|
|
@@ -2789,6 +2866,23 @@ td.act {
|
|
|
2789
2866
|
.menu-item.danger-item {
|
|
2790
2867
|
color: var(--danger-fg);
|
|
2791
2868
|
}
|
|
2869
|
+
/* A menu whose items say what each pick does: a name over a muted line. */
|
|
2870
|
+
.menu-described {
|
|
2871
|
+
width: 280px;
|
|
2872
|
+
max-width: min(280px, 78vw);
|
|
2873
|
+
}
|
|
2874
|
+
.menu-item-name {
|
|
2875
|
+
display: block;
|
|
2876
|
+
font-size: 13px;
|
|
2877
|
+
font-weight: 600;
|
|
2878
|
+
}
|
|
2879
|
+
.menu-item-desc {
|
|
2880
|
+
display: block;
|
|
2881
|
+
margin-top: 1px;
|
|
2882
|
+
font-size: 11.5px;
|
|
2883
|
+
color: var(--muted);
|
|
2884
|
+
line-height: 1.35;
|
|
2885
|
+
}
|
|
2792
2886
|
|
|
2793
2887
|
/* editor field hints + scheduled banner */
|
|
2794
2888
|
.field-hint {
|
|
@@ -2796,7 +2890,7 @@ td.act {
|
|
|
2796
2890
|
color: var(--muted);
|
|
2797
2891
|
margin-top: 4px;
|
|
2798
2892
|
}
|
|
2799
|
-
/* --- Field validation (DESIGN §
|
|
2893
|
+
/* --- Field validation (DESIGN §5): an error about one field lives on that
|
|
2800
2894
|
field — the input goes to an invalid state and the reason sits directly beneath it,
|
|
2801
2895
|
tied by aria-describedby. This replaces the old disable-the-button + `title` tooltip,
|
|
2802
2896
|
which never reached keyboard, touch, or screen-reader users. --- */
|
|
@@ -2816,6 +2910,11 @@ textarea.is-invalid {
|
|
|
2816
2910
|
.field-error .field-error-ico {
|
|
2817
2911
|
font-weight: 700;
|
|
2818
2912
|
}
|
|
2913
|
+
/* A dialog's own error, when no one field is at fault: just above its actions, where the
|
|
2914
|
+
reader is looking rather than in a toast at the foot of the screen (DESIGN §5). */
|
|
2915
|
+
.modal .dialog-error {
|
|
2916
|
+
margin-top: 12px;
|
|
2917
|
+
}
|
|
2819
2918
|
/* Label + info icon grouped at the top of the slug field. */
|
|
2820
2919
|
.label-row {
|
|
2821
2920
|
display: flex;
|
|
@@ -2924,11 +3023,10 @@ textarea.is-invalid {
|
|
|
2924
3023
|
#f-slug.slug-auto {
|
|
2925
3024
|
color: var(--muted);
|
|
2926
3025
|
}
|
|
2927
|
-
/* --- Editor-state banner (DESIGN §
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
a neutral notice. It replaces the old `.sched-banner` + `.fresh-banner` pair. --- */
|
|
3026
|
+
/* --- Editor-state banner (DESIGN §5): a standing, in-flow line at the top of the
|
|
3027
|
+
editor, in two variants. `scheduled` marks a frozen, soft-locked post (SPEC §6);
|
|
3028
|
+
`conflict` warns the draft changed elsewhere (another tab, or Claude), and its two
|
|
3029
|
+
actions are equal-weight Ghost, since both choices lose something. --- */
|
|
2932
3030
|
.banner {
|
|
2933
3031
|
display: flex;
|
|
2934
3032
|
align-items: center;
|
|
@@ -2957,59 +3055,21 @@ textarea.is-invalid {
|
|
|
2957
3055
|
color: var(--warn-fg);
|
|
2958
3056
|
border-color: var(--warn-line);
|
|
2959
3057
|
}
|
|
2960
|
-
.banner-info {
|
|
2961
|
-
background: var(--banner-bg);
|
|
2962
|
-
color: var(--fg);
|
|
2963
|
-
border-color: var(--banner-line);
|
|
2964
|
-
}
|
|
2965
3058
|
|
|
2966
|
-
/*
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
its layout is prose plus one control, not a status line with an actions row, so it never
|
|
2970
|
-
wraps the dismiss beneath the text the way the banner wraps its buttons. Padding is a
|
|
2971
|
-
full 1rem because it is a sentence or two, not a status line. Removed on dismiss with
|
|
2972
|
-
no animation. Both themes come through the existing tokens. --- */
|
|
2973
|
-
.notice {
|
|
3059
|
+
/* Look changed since your last test (SPEC §8): the server's condition, a line in the scheduled
|
|
3060
|
+
banner that says to send a new test, and a mark on the dashboard's scheduled card. */
|
|
3061
|
+
.sched-remade {
|
|
2974
3062
|
display: flex;
|
|
2975
|
-
|
|
2976
|
-
gap: 12px;
|
|
2977
|
-
border: 1px solid var(--banner-line);
|
|
2978
|
-
border-radius: 8px;
|
|
2979
|
-
background: var(--banner-bg);
|
|
2980
|
-
color: var(--fg);
|
|
2981
|
-
padding: 1rem;
|
|
2982
|
-
margin-bottom: 14px;
|
|
2983
|
-
font-size: 14px;
|
|
2984
|
-
line-height: 1.45;
|
|
2985
|
-
}
|
|
2986
|
-
.notice-text {
|
|
2987
|
-
flex: 1 1 auto;
|
|
2988
|
-
min-width: 0;
|
|
2989
|
-
}
|
|
2990
|
-
/* Links are allowed (buttons are not); underlined so they stay findable on the tint. */
|
|
2991
|
-
.notice a {
|
|
2992
|
-
color: var(--accent);
|
|
2993
|
-
font-weight: 550;
|
|
2994
|
-
text-decoration: underline;
|
|
2995
|
-
text-underline-offset: 2px;
|
|
2996
|
-
}
|
|
2997
|
-
/* The dismiss is the Icon role (button.icon) carrying the app's `x` glyph: a square hit
|
|
2998
|
-
target pinned to the right, pulled 4px into the padding on three sides so it centers on
|
|
2999
|
-
the first line of text without making a one-line notice taller than its text. */
|
|
3000
|
-
button.notice-dismiss {
|
|
3001
|
-
flex: 0 0 auto;
|
|
3002
|
-
display: inline-flex;
|
|
3063
|
+
flex-wrap: wrap;
|
|
3003
3064
|
align-items: center;
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
padding: 0;
|
|
3008
|
-
margin: -4px -4px -4px 0;
|
|
3065
|
+
gap: 4px 10px;
|
|
3066
|
+
margin-top: 6px;
|
|
3067
|
+
font-size: 13px;
|
|
3009
3068
|
}
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3069
|
+
.sched-remade-mark {
|
|
3070
|
+
margin-top: 4px;
|
|
3071
|
+
font-size: 12.5px;
|
|
3072
|
+
color: var(--status-scheduled-fg);
|
|
3013
3073
|
}
|
|
3014
3074
|
|
|
3015
3075
|
.grid2 {
|
|
@@ -3891,7 +3951,7 @@ button.api-filter-clear svg {
|
|
|
3891
3951
|
text-decoration: underline;
|
|
3892
3952
|
}
|
|
3893
3953
|
|
|
3894
|
-
/* Publication card: the
|
|
3954
|
+
/* Publication card: the home page's address with Copy, and the way out to it. */
|
|
3895
3955
|
.pub-card {
|
|
3896
3956
|
display: flex;
|
|
3897
3957
|
flex-direction: column;
|
|
@@ -3902,10 +3962,6 @@ button.api-filter-clear svg {
|
|
|
3902
3962
|
align-items: center;
|
|
3903
3963
|
gap: 12px;
|
|
3904
3964
|
}
|
|
3905
|
-
.pub-key {
|
|
3906
|
-
flex: 0 0 84px;
|
|
3907
|
-
font-size: 12px;
|
|
3908
|
-
}
|
|
3909
3965
|
.pub-val {
|
|
3910
3966
|
flex: 1 1 auto;
|
|
3911
3967
|
min-width: 0;
|
|
@@ -4012,10 +4068,9 @@ button.api-filter-clear svg {
|
|
|
4012
4068
|
deploy / Copy-out), editable surfaces sit on cards and read-only
|
|
4013
4069
|
facts on a recessed "sunken" card. Kestrel's own accents use the
|
|
4014
4070
|
fixed --accent (a slate-blue), independent of the reader surface.
|
|
4015
|
-
A sticky save bar collects
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
the OS via the tokens at the top of this file.
|
|
4071
|
+
A sticky save bar collects every editable section's unsaved
|
|
4072
|
+
changes; the logo saves on its own and doesn't feed it. Theme
|
|
4073
|
+
follows the OS via the tokens at the top of this file.
|
|
4019
4074
|
============================================================ */
|
|
4020
4075
|
.settings {
|
|
4021
4076
|
max-width: 940px;
|
|
@@ -4041,7 +4096,7 @@ button.api-filter-clear svg {
|
|
|
4041
4096
|
margin-bottom: 34px;
|
|
4042
4097
|
}
|
|
4043
4098
|
|
|
4044
|
-
/* --- copying text out of the app (DESIGN §
|
|
4099
|
+
/* --- copying text out of the app (DESIGN §7, ui/copy.ts) ---
|
|
4045
4100
|
A labelled block (a code block, or the API reference's query and content-type tables):
|
|
4046
4101
|
its label line, then the block. The Copy is a control outside the five roles: the word
|
|
4047
4102
|
and its glyph, no fill or border, quiet on the line it shares; the glyph turns to a
|
|
@@ -4103,7 +4158,7 @@ button.copied svg {
|
|
|
4103
4158
|
flex: 0 0 auto;
|
|
4104
4159
|
}
|
|
4105
4160
|
|
|
4106
|
-
/* --- groups: the sections kept together by whom they serve (DESIGN §
|
|
4161
|
+
/* --- groups: the sections kept together by whom they serve (DESIGN §3). A group has no
|
|
4107
4162
|
heading, since each section's title already says what it is; a wider gap than the one
|
|
4108
4163
|
between sections is all that separates one group from the next. --- */
|
|
4109
4164
|
.set-group + .set-group {
|
|
@@ -4154,7 +4209,7 @@ button.copied svg {
|
|
|
4154
4209
|
background: var(--accent-soft);
|
|
4155
4210
|
}
|
|
4156
4211
|
/* In use by scheduled posts (SPEC §9): the scheduled violet, a lifecycle fact and not a
|
|
4157
|
-
warning, the same reading as the scheduled badge (DESIGN §
|
|
4212
|
+
warning, the same reading as the scheduled badge (DESIGN §8). Adds no token. */
|
|
4158
4213
|
.set-chip.inuse {
|
|
4159
4214
|
color: var(--status-scheduled-fg);
|
|
4160
4215
|
border-color: var(--status-scheduled-line);
|
|
@@ -4167,22 +4222,12 @@ button.copied svg {
|
|
|
4167
4222
|
background: currentColor;
|
|
4168
4223
|
opacity: 0.85;
|
|
4169
4224
|
}
|
|
4225
|
+
/* Set at deploy: a fact about where the value lives, not a warning, so it stays neutral
|
|
4226
|
+
rather than borrowing the amber `warn` scale (DESIGN §8). */
|
|
4170
4227
|
.set-chip.readonly {
|
|
4171
|
-
color: var(--warn-fg);
|
|
4172
|
-
border-color: color-mix(in srgb, var(--warn-fg) 30%, var(--line-soft));
|
|
4173
|
-
background: color-mix(in srgb, var(--warn-fg) 9%, transparent);
|
|
4174
|
-
}
|
|
4175
|
-
/* A dashed "Mock" tag beside the Email-template chip — says the section is a
|
|
4176
|
-
preview of a feature not wired to the backend yet. */
|
|
4177
|
-
.set-tag-mock {
|
|
4178
|
-
font-size: 10px;
|
|
4179
|
-
font-weight: 650;
|
|
4180
|
-
letter-spacing: 0.06em;
|
|
4181
|
-
text-transform: uppercase;
|
|
4182
|
-
padding: 3px 7px;
|
|
4183
|
-
border-radius: 6px;
|
|
4184
|
-
border: 1px dashed var(--line-soft);
|
|
4185
4228
|
color: var(--muted);
|
|
4229
|
+
border-color: var(--line);
|
|
4230
|
+
background: var(--chip);
|
|
4186
4231
|
}
|
|
4187
4232
|
|
|
4188
4233
|
/* --- cards: editable surfaces raised, read-only facts recessed --- */
|
|
@@ -4371,7 +4416,7 @@ button.set-field-remove {
|
|
|
4371
4416
|
align-items: flex-start;
|
|
4372
4417
|
padding: 11px 16px;
|
|
4373
4418
|
border-bottom: 1px solid var(--line-soft);
|
|
4374
|
-
background:
|
|
4419
|
+
background: var(--chip);
|
|
4375
4420
|
font-size: 12.5px;
|
|
4376
4421
|
color: var(--muted);
|
|
4377
4422
|
line-height: 1.5;
|
|
@@ -4381,7 +4426,7 @@ button.set-field-remove {
|
|
|
4381
4426
|
height: 15px;
|
|
4382
4427
|
flex: 0 0 auto;
|
|
4383
4428
|
margin-top: 1px;
|
|
4384
|
-
color: var(--
|
|
4429
|
+
color: var(--muted);
|
|
4385
4430
|
}
|
|
4386
4431
|
.set-kv {
|
|
4387
4432
|
display: grid;
|
|
@@ -4718,14 +4763,21 @@ button.set-field-remove {
|
|
|
4718
4763
|
}
|
|
4719
4764
|
.set-recip-add {
|
|
4720
4765
|
display: flex;
|
|
4766
|
+
flex-wrap: wrap;
|
|
4721
4767
|
gap: 8px;
|
|
4722
4768
|
max-width: 420px;
|
|
4723
4769
|
}
|
|
4724
4770
|
.set-recip-add input {
|
|
4725
4771
|
flex: 1;
|
|
4726
4772
|
}
|
|
4773
|
+
/* A refused address says why on its own line under the field and its button (DESIGN §5). */
|
|
4774
|
+
.set-recip-add .field-error {
|
|
4775
|
+
flex-basis: 100%;
|
|
4776
|
+
order: 1;
|
|
4777
|
+
margin-top: 0;
|
|
4778
|
+
}
|
|
4727
4779
|
|
|
4728
|
-
/* --- ways to subscribe (DESIGN §
|
|
4780
|
+
/* --- ways to subscribe (DESIGN §3): one card, the two ways as bands in reading order,
|
|
4729
4781
|
the page then the form, split by a full-width rule like the other cards' bands. Each
|
|
4730
4782
|
band has the same head (a glyph, a title, one line) so they read as two options. The
|
|
4731
4783
|
card folds by the section's own width, since the sidebar decides how wide Settings is. --- */
|
|
@@ -4916,7 +4968,7 @@ button.set-embed-copy svg {
|
|
|
4916
4968
|
color: var(--muted);
|
|
4917
4969
|
}
|
|
4918
4970
|
|
|
4919
|
-
/* --- email template
|
|
4971
|
+
/* --- email template: a standalone sample-email frame, then a card holding
|
|
4920
4972
|
the HTML template editor and a collapsible variable reference. The example
|
|
4921
4973
|
templates are authored with a <style> block; the preview renders them in an
|
|
4922
4974
|
isolated iframe, so a template's own styles apply as a mail client would show
|
|
@@ -4951,7 +5003,7 @@ button.set-embed-copy svg {
|
|
|
4951
5003
|
font-size: 12px;
|
|
4952
5004
|
color: var(--muted);
|
|
4953
5005
|
}
|
|
4954
|
-
/* Syntax-highlight overlay editor
|
|
5006
|
+
/* Syntax-highlight overlay editor: a transparent <textarea> over a
|
|
4955
5007
|
highlighted <pre>, plus a line-number gutter. All three share identical font
|
|
4956
5008
|
metrics so the caret sits exactly over the colored text; the textarea is the only
|
|
4957
5009
|
scroller and the gutter + overlay are synced to it in script. */
|
|
@@ -5027,7 +5079,7 @@ button.set-embed-copy svg {
|
|
|
5027
5079
|
.set-tpl-editor-wrap.show-lines .set-tpl-editor {
|
|
5028
5080
|
left: 47px;
|
|
5029
5081
|
}
|
|
5030
|
-
/* Syntax colors
|
|
5082
|
+
/* Syntax colors. */
|
|
5031
5083
|
.cx-tag {
|
|
5032
5084
|
color: var(--syntax-tag);
|
|
5033
5085
|
}
|
|
@@ -5057,7 +5109,7 @@ button.set-embed-copy svg {
|
|
|
5057
5109
|
padding: 0 2px;
|
|
5058
5110
|
font-weight: 600;
|
|
5059
5111
|
}
|
|
5060
|
-
/* Markdown token classes
|
|
5112
|
+
/* Markdown token classes — the minimal scheme: the MARK carries the color, the
|
|
5061
5113
|
CONTENT keeps --syntax-fg and carries only weight/slant. So a bold word reads as weight, not
|
|
5062
5114
|
as a hue that competes with a heading. Heading text is bold with a crimson # mark; emphasis
|
|
5063
5115
|
asterisks are pink around plain weighted/slanted text; block markers (>, bullets, the fence
|
|
@@ -5269,7 +5321,7 @@ button.set-embed-copy svg {
|
|
|
5269
5321
|
background: var(--accent-soft);
|
|
5270
5322
|
border-color: color-mix(in srgb, var(--accent) 30%, var(--line-soft));
|
|
5271
5323
|
}
|
|
5272
|
-
/* Required-token pills
|
|
5324
|
+
/* Required-token pills: live green/red for the two tokens whose
|
|
5273
5325
|
absence blocks a save, shown upper-right of the editor toolbar. */
|
|
5274
5326
|
.set-tpl-required {
|
|
5275
5327
|
display: flex;
|
|
@@ -5333,10 +5385,7 @@ button.set-embed-copy svg {
|
|
|
5333
5385
|
box-shadow: 0 1px 4px rgba(15, 18, 25, 0.1);
|
|
5334
5386
|
transition: max-width 0.16s ease;
|
|
5335
5387
|
}
|
|
5336
|
-
/* "Start from example"
|
|
5337
|
-
.set-menu {
|
|
5338
|
-
position: relative;
|
|
5339
|
-
}
|
|
5388
|
+
/* "Start from example": the trigger of a shared menu (openMenu). */
|
|
5340
5389
|
.set-menu-btn {
|
|
5341
5390
|
display: inline-flex;
|
|
5342
5391
|
align-items: center;
|
|
@@ -5350,45 +5399,6 @@ button.set-embed-copy svg {
|
|
|
5350
5399
|
transform: translateY(-1px) rotate(45deg);
|
|
5351
5400
|
opacity: 0.6;
|
|
5352
5401
|
}
|
|
5353
|
-
.set-menu-list {
|
|
5354
|
-
position: absolute;
|
|
5355
|
-
left: 0;
|
|
5356
|
-
top: calc(100% + 6px);
|
|
5357
|
-
z-index: var(--z-menu);
|
|
5358
|
-
width: 280px;
|
|
5359
|
-
max-width: min(280px, 78vw);
|
|
5360
|
-
padding: 5px;
|
|
5361
|
-
background: var(--card);
|
|
5362
|
-
border: 1px solid var(--line);
|
|
5363
|
-
border-radius: 10px;
|
|
5364
|
-
box-shadow: 0 10px 28px rgba(15, 18, 25, 0.16);
|
|
5365
|
-
}
|
|
5366
|
-
.set-menu-list button {
|
|
5367
|
-
display: block;
|
|
5368
|
-
width: 100%;
|
|
5369
|
-
text-align: left;
|
|
5370
|
-
border: 0;
|
|
5371
|
-
background: transparent;
|
|
5372
|
-
border-radius: 7px;
|
|
5373
|
-
padding: 8px 10px;
|
|
5374
|
-
cursor: pointer;
|
|
5375
|
-
}
|
|
5376
|
-
.set-menu-list button:hover {
|
|
5377
|
-
background: var(--sunken);
|
|
5378
|
-
}
|
|
5379
|
-
.set-menu-name {
|
|
5380
|
-
display: block;
|
|
5381
|
-
font-size: 13px;
|
|
5382
|
-
font-weight: 600;
|
|
5383
|
-
color: var(--fg);
|
|
5384
|
-
}
|
|
5385
|
-
.set-menu-desc {
|
|
5386
|
-
display: block;
|
|
5387
|
-
margin-top: 1px;
|
|
5388
|
-
font-size: 11.5px;
|
|
5389
|
-
color: var(--muted);
|
|
5390
|
-
line-height: 1.35;
|
|
5391
|
-
}
|
|
5392
5402
|
/* Variables card heading. */
|
|
5393
5403
|
.set-tpl-vartitle {
|
|
5394
5404
|
margin: 0 0 2px;
|
|
@@ -5402,7 +5412,7 @@ button.set-embed-copy svg {
|
|
|
5402
5412
|
.set-tpl-varcard .set-tpl-vars-body {
|
|
5403
5413
|
padding: 0;
|
|
5404
5414
|
}
|
|
5405
|
-
/* The
|
|
5415
|
+
/* The note at the foot of a card: what the section is for, or where it is set. */
|
|
5406
5416
|
.set-note {
|
|
5407
5417
|
display: flex;
|
|
5408
5418
|
align-items: flex-start;
|
|
@@ -5667,7 +5677,7 @@ button.set-embed-copy svg {
|
|
|
5667
5677
|
icon rail — and collapses to edge-to-edge once the sidebar becomes the off-canvas
|
|
5668
5678
|
mobile drawer. The post editor keeps its own autosave + conflict model, so it does
|
|
5669
5679
|
NOT use this bar. Its Save is a Primary button — now brand-blue --accent app-wide
|
|
5670
|
-
(DESIGN §
|
|
5680
|
+
(DESIGN §7), so the bar needs no primary override of its own. --- */
|
|
5671
5681
|
.savebar {
|
|
5672
5682
|
position: fixed;
|
|
5673
5683
|
/* Clears the full 240px sidebar; narrowed to the rail / drawer in the rules below. */
|