@kurtbruns/kestrel 1.7.0 → 1.9.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 +13 -8
- package/CHANGELOG.md +72 -2
- 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 +218 -223
- package/dist/public/dashboard/app.js.map +4 -4
- package/dist/public/dashboard/index.html +6 -6
- package/dist/public/dashboard/styles.css +173 -180
- package/dist/worker/index.js +4793 -3097
- package/docs/README.md +2 -2
- package/docs/get-started/02-deploy.md +23 -8
- package/docs/get-started/03-access.md +2 -2
- 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 +22 -6
- package/docs/guides/07-upgrade.md +2 -0
- package/docs/reference/01-configuration.md +22 -16
- package/docs/reference/02-sending-domain-dns.md +19 -19
- package/docs/reference/03-import-format.md +103 -0
- package/migrations/0004_keyed_pending_to_resolve.sql +27 -0
- package/migrations/0005_imports.sql +74 -0
- package/migrations/0006_deliveries_fewer_indexes.sql +19 -0
- package/package.json +1 -1
- package/template/claude/CLAUDE.md +3 -1
- package/template/package.json +1 -0
- package/template/wrangler.jsonc +6 -3
- package/docs/get-started/04-resend.md +0 -136
|
@@ -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;
|
|
@@ -900,8 +898,10 @@ body.nav-resizing .sidebar {
|
|
|
900
898
|
margin-top: 12px;
|
|
901
899
|
}
|
|
902
900
|
/* The refused-configuration wall says more than the sign-in wall, so it reads as text: left
|
|
903
|
-
aligned, a little wider, with the variable and the server's words set apart.
|
|
904
|
-
.
|
|
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
905
|
max-width: 560px;
|
|
906
906
|
text-align: left;
|
|
907
907
|
}
|
|
@@ -918,7 +918,8 @@ body.nav-resizing .sidebar {
|
|
|
918
918
|
white-space: nowrap;
|
|
919
919
|
border: 0;
|
|
920
920
|
}
|
|
921
|
-
.config-wall h2:focus
|
|
921
|
+
.config-wall h2:focus,
|
|
922
|
+
.access-wall h2:focus {
|
|
922
923
|
outline: none;
|
|
923
924
|
}
|
|
924
925
|
.config-wall-problem {
|
|
@@ -949,8 +950,8 @@ body.tool-mode .content {
|
|
|
949
950
|
}
|
|
950
951
|
|
|
951
952
|
/* --- Collapsed icon rail. Driven by data-nav="rail" on <html> (set from the viewport
|
|
952
|
-
width and the
|
|
953
|
-
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
|
|
954
955
|
default across the tablet range and small desktops; the nav keeps its one-click
|
|
955
956
|
reach, labels move to hover/focus tooltips, and the secondary chrome (brand text,
|
|
956
957
|
account, tool footer) folds down to its marks. The whole block is gated at ≥540 so a
|
|
@@ -1180,7 +1181,7 @@ button:hover {
|
|
|
1180
1181
|
button:active {
|
|
1181
1182
|
transform: translateY(0.5px);
|
|
1182
1183
|
}
|
|
1183
|
-
/* --- Buttons: five roles (DESIGN §
|
|
1184
|
+
/* --- Buttons: five roles (DESIGN §7), chosen by importance and consequence, one
|
|
1184
1185
|
class each. The bare `button` above is the neutral default (Ghost's card-filled
|
|
1185
1186
|
end); the classes below step up to Primary / Secondary or down to the quiet Ghost,
|
|
1186
1187
|
plus Danger and the square Icon. Exactly one solid brand-blue action — Primary — is
|
|
@@ -1189,7 +1190,7 @@ button:active {
|
|
|
1189
1190
|
|
|
1190
1191
|
/* One box height across the label roles, so a row that mixes them (the editor's
|
|
1191
1192
|
Save draft / Send test email / Schedule, a modal's Cancel + confirm) aligns:
|
|
1192
|
-
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).
|
|
1193
1194
|
A smaller-font role (Secondary, Ghost) grows to this floor with its label centered
|
|
1194
1195
|
rather than sitting a few pixels short of the Primary beside it. The bare `button`
|
|
1195
1196
|
default already meets it; the square Icon role and the segmented toggle Control keep
|
|
@@ -1518,16 +1519,10 @@ td a:hover {
|
|
|
1518
1519
|
color: var(--status-sending-fg);
|
|
1519
1520
|
border-color: var(--status-sending-line);
|
|
1520
1521
|
}
|
|
1521
|
-
.badge.canceled,
|
|
1522
|
-
.badge.failed {
|
|
1523
|
-
background: var(--danger-bg);
|
|
1524
|
-
color: var(--danger-fg);
|
|
1525
|
-
border-color: var(--danger-line);
|
|
1526
|
-
}
|
|
1527
1522
|
/* Subscriber consent states borrow a hue that matches the state's meaning: confirmed →
|
|
1528
1523
|
the sent green, unsubscribed → the draft grey, suppressed → danger red (below). Pending
|
|
1529
1524
|
awaits double-opt-in — a waiting/attention state — so it takes the amber `warn` scale,
|
|
1530
|
-
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
|
|
1531
1526
|
must never read as a send in flight. (The two coincided only while `--status-sending` was
|
|
1532
1527
|
amber; when it went blue for the dispatch treatment, this pill must stay amber.) */
|
|
1533
1528
|
.badge.confirmed {
|
|
@@ -1749,7 +1744,7 @@ body.web-mode .content {
|
|
|
1749
1744
|
margin: 0;
|
|
1750
1745
|
padding: 0;
|
|
1751
1746
|
}
|
|
1752
|
-
/* 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
|
|
1753
1748
|
the editor's, above the frame, and the frame is as tall as the post, so the window scrolls
|
|
1754
1749
|
and the strip goes up with the page like the test pages' strip. It wears the brand accent
|
|
1755
1750
|
like the reader surface's dev pill, its tag as the pill's inverted chip, and its content
|
|
@@ -1815,8 +1810,8 @@ body.web-mode .content {
|
|
|
1815
1810
|
margin: 48px auto;
|
|
1816
1811
|
padding: 0 16px;
|
|
1817
1812
|
}
|
|
1818
|
-
/* Sent record view
|
|
1819
|
-
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
|
|
1820
1815
|
a feedback color, still labels the send's lifecycle. */
|
|
1821
1816
|
.rec-card {
|
|
1822
1817
|
display: flex;
|
|
@@ -1916,7 +1911,7 @@ body.web-mode .content {
|
|
|
1916
1911
|
}
|
|
1917
1912
|
}
|
|
1918
1913
|
|
|
1919
|
-
/* ---- per-recipient record
|
|
1914
|
+
/* ---- per-recipient record: the rows behind the outcome tiles ---- */
|
|
1920
1915
|
.rec-people {
|
|
1921
1916
|
margin-top: 26px;
|
|
1922
1917
|
}
|
|
@@ -1931,7 +1926,7 @@ body.web-mode .content {
|
|
|
1931
1926
|
margin: 0;
|
|
1932
1927
|
font-size: 15px;
|
|
1933
1928
|
}
|
|
1934
|
-
/* 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):
|
|
1935
1930
|
the same segmented, aria-pressed pill group as the Template preview-width toggle, its
|
|
1936
1931
|
active segment in the Secondary palette. It switches what you're looking at; it never
|
|
1937
1932
|
commits an action. */
|
|
@@ -1983,8 +1978,8 @@ body.web-mode .content {
|
|
|
1983
1978
|
font-size: 13px;
|
|
1984
1979
|
}
|
|
1985
1980
|
|
|
1986
|
-
/* ---- live in-flight watch
|
|
1987
|
-
/* 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
|
|
1988
1983
|
action slate-blue — carried by the phase pill, the dispatch bar, and the swatch. */
|
|
1989
1984
|
.rec-sw.sw-sending {
|
|
1990
1985
|
background: var(--status-sending-fg);
|
|
@@ -2150,7 +2145,7 @@ body.web-mode .content {
|
|
|
2150
2145
|
}
|
|
2151
2146
|
}
|
|
2152
2147
|
|
|
2153
|
-
/* ---- active-send row (Sent page "In progress") + dashboard widget
|
|
2148
|
+
/* ---- active-send row (Sent page "In progress") + dashboard widget ---- */
|
|
2154
2149
|
/* An in-flight send as a card with a live mini dispatch bar; blue, so it reads as
|
|
2155
2150
|
"sending" — distinct from the scheduled violet cards and the neutral sent rows. */
|
|
2156
2151
|
.card.active-card {
|
|
@@ -2194,6 +2189,27 @@ body.web-mode .content {
|
|
|
2194
2189
|
font-size: 12.5px;
|
|
2195
2190
|
color: var(--warn-fg);
|
|
2196
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
|
+
}
|
|
2197
2213
|
|
|
2198
2214
|
/* Composer: GitHub-style Write/Preview tabs + a formatting toolbar */
|
|
2199
2215
|
.composer {
|
|
@@ -2327,7 +2343,7 @@ body.web-mode .content {
|
|
|
2327
2343
|
.composer-body {
|
|
2328
2344
|
position: relative;
|
|
2329
2345
|
}
|
|
2330
|
-
/* Post editor syntax-highlight overlay
|
|
2346
|
+
/* Post editor syntax-highlight overlay: a transparent <textarea> layered over
|
|
2331
2347
|
a highlighted <pre> that shows the colored Markdown beneath the caret. The two share
|
|
2332
2348
|
identical box + font metrics so the caret sits exactly on the colored text; the textarea
|
|
2333
2349
|
is the only scroller and the <pre> is synced to it in script. Unlike the template editor
|
|
@@ -2402,7 +2418,7 @@ body.web-mode .content {
|
|
|
2402
2418
|
background: #fff;
|
|
2403
2419
|
display: block;
|
|
2404
2420
|
}
|
|
2405
|
-
/* A scheduled post's Edit tab (DESIGN §
|
|
2421
|
+
/* A scheduled post's Edit tab (DESIGN §3): the text stays fully legible, selectable, and
|
|
2406
2422
|
copyable (the fields are readonly, not disabled, since a disabled textarea cannot even be
|
|
2407
2423
|
selected), on the recessed surface Settings uses for a read-only card (`.set-card.sunken`);
|
|
2408
2424
|
the caret is hidden because the field is not for typing. The way out is the composer's
|
|
@@ -2462,7 +2478,7 @@ body.web-mode .content {
|
|
|
2462
2478
|
}
|
|
2463
2479
|
/* The locked composer's foot: where a draft's foot offers images, a scheduled post's says
|
|
2464
2480
|
the way out, "Cancel the schedule to edit", with the read-only lock Settings uses. It is
|
|
2465
|
-
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
|
|
2466
2482
|
lift. `.nudge` pulses it once, in the read-only note's tint, when an edit is attempted,
|
|
2467
2483
|
so the refusal is seen where the keystroke landed; reduced-motion holds the tint instead. */
|
|
2468
2484
|
.composer-foot-lock {
|
|
@@ -2501,7 +2517,14 @@ body.web-mode .content {
|
|
|
2501
2517
|
.actions-bar > .row:only-child {
|
|
2502
2518
|
margin-left: auto;
|
|
2503
2519
|
}
|
|
2504
|
-
/* 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:
|
|
2505
2528
|
Unsaved (amber --warn dot) · Saving… (pulsing --accent dot) · Saved (green --ok
|
|
2506
2529
|
dot). The dot is a reinforcement, never the whole signal — the text label carries
|
|
2507
2530
|
each state too, so the cue survives a reader who can't tell the dot's color. The
|
|
@@ -2510,6 +2533,7 @@ body.web-mode .content {
|
|
|
2510
2533
|
.save-status {
|
|
2511
2534
|
display: inline-flex;
|
|
2512
2535
|
align-items: center;
|
|
2536
|
+
min-width: 0;
|
|
2513
2537
|
gap: 7px;
|
|
2514
2538
|
font-size: 13px;
|
|
2515
2539
|
color: var(--muted);
|
|
@@ -2520,9 +2544,13 @@ body.web-mode .content {
|
|
|
2520
2544
|
.save-status.is-dirty {
|
|
2521
2545
|
color: var(--fg);
|
|
2522
2546
|
}
|
|
2547
|
+
.save-status.is-error {
|
|
2548
|
+
color: var(--danger-fg);
|
|
2549
|
+
}
|
|
2523
2550
|
.save-status.is-dirty::before,
|
|
2524
2551
|
.save-status.is-saving::before,
|
|
2525
|
-
.save-status.is-saved::before
|
|
2552
|
+
.save-status.is-saved::before,
|
|
2553
|
+
.save-status.is-error::before {
|
|
2526
2554
|
content: "";
|
|
2527
2555
|
width: 8px;
|
|
2528
2556
|
height: 8px;
|
|
@@ -2539,6 +2567,9 @@ body.web-mode .content {
|
|
|
2539
2567
|
.save-status.is-saved::before {
|
|
2540
2568
|
background: var(--ok-fg);
|
|
2541
2569
|
}
|
|
2570
|
+
.save-status.is-error::before {
|
|
2571
|
+
background: var(--danger-fg);
|
|
2572
|
+
}
|
|
2542
2573
|
@keyframes save-pulse {
|
|
2543
2574
|
0%,
|
|
2544
2575
|
100% {
|
|
@@ -2637,7 +2668,7 @@ body.web-mode .content {
|
|
|
2637
2668
|
margin: 0 0 14px;
|
|
2638
2669
|
line-height: 1.5;
|
|
2639
2670
|
}
|
|
2640
|
-
/* A dialog's not-connected line (DESIGN §
|
|
2671
|
+
/* A dialog's not-connected line (DESIGN §4) takes the health block's amber warning, so it
|
|
2641
2672
|
reads as the reason the dialog's action is off rather than as one more hint. */
|
|
2642
2673
|
.modal .hint.not-connected {
|
|
2643
2674
|
color: var(--warn-fg);
|
|
@@ -2649,6 +2680,15 @@ body.web-mode .content {
|
|
|
2649
2680
|
.modal .hint.not-connected a {
|
|
2650
2681
|
color: inherit;
|
|
2651
2682
|
}
|
|
2683
|
+
/* On Workers Free's limits, a send too large for D1's daily writes (DESIGN §4): the same amber
|
|
2684
|
+
box, since it is a warning to read before scheduling, though it disables nothing. */
|
|
2685
|
+
.modal .hint.daily-writes {
|
|
2686
|
+
color: var(--warn-fg);
|
|
2687
|
+
background: var(--warn-bg);
|
|
2688
|
+
border: 1px solid var(--warn-line);
|
|
2689
|
+
border-radius: 8px;
|
|
2690
|
+
padding: 10px 12px;
|
|
2691
|
+
}
|
|
2652
2692
|
.modal .actions {
|
|
2653
2693
|
display: flex;
|
|
2654
2694
|
justify-content: flex-end;
|
|
@@ -2666,10 +2706,16 @@ body.web-mode .content {
|
|
|
2666
2706
|
padding-top: 14px;
|
|
2667
2707
|
border-top: 1px solid var(--line);
|
|
2668
2708
|
}
|
|
2669
|
-
/* The alternate-path row can carry a plain note beside its link
|
|
2709
|
+
/* The alternate-path row can carry a plain note beside its link; the note wraps and the
|
|
2710
|
+
link stays whole on its line. */
|
|
2670
2711
|
.modal .altrow .altrow-note {
|
|
2671
2712
|
font-size: 12.5px;
|
|
2672
2713
|
color: var(--muted);
|
|
2714
|
+
min-width: 0;
|
|
2715
|
+
}
|
|
2716
|
+
.modal .altrow .linkbtn {
|
|
2717
|
+
flex: none;
|
|
2718
|
+
white-space: nowrap;
|
|
2673
2719
|
}
|
|
2674
2720
|
/* The back link at the top of the Send-now view needs no divider — it returns from,
|
|
2675
2721
|
rather than demotes below, the Schedule view. */
|
|
@@ -2679,7 +2725,7 @@ body.web-mode .content {
|
|
|
2679
2725
|
border-top: 0;
|
|
2680
2726
|
justify-content: flex-start;
|
|
2681
2727
|
}
|
|
2682
|
-
/* Link — a text-link-styled action (DESIGN §
|
|
2728
|
+
/* Link — a text-link-styled action (DESIGN §7), not one of the button roles. Reserved
|
|
2683
2729
|
for a deliberately subordinate choice, like the in-modal Send now that sits below
|
|
2684
2730
|
the Schedule Primary: it must read as clearly demoted, so it is a link, not a Ghost
|
|
2685
2731
|
button that would compete with Cancel. Still a real <button>, so it's keyboard- and
|
|
@@ -2700,12 +2746,12 @@ button.linkbtn:hover {
|
|
|
2700
2746
|
|
|
2701
2747
|
.countdown {
|
|
2702
2748
|
font-variant-numeric: tabular-nums;
|
|
2703
|
-
/* The scheduled violet (DESIGN §
|
|
2749
|
+
/* The scheduled violet (DESIGN §8), so the fire-time countdown on the scheduled
|
|
2704
2750
|
cards (Sent page + dashboard) carries the same "scheduled" cue as the pill. */
|
|
2705
2751
|
color: var(--status-scheduled-fg);
|
|
2706
2752
|
}
|
|
2707
2753
|
/* Past the server's missed tolerance the countdown says how late the send is, in the
|
|
2708
|
-
danger tone: a missed fire time needs a look (DESIGN §
|
|
2754
|
+
danger tone: a missed fire time needs a look (DESIGN §4). */
|
|
2709
2755
|
.countdown.countdown-missed {
|
|
2710
2756
|
color: var(--danger-fg);
|
|
2711
2757
|
}
|
|
@@ -2829,6 +2875,23 @@ td.act {
|
|
|
2829
2875
|
.menu-item.danger-item {
|
|
2830
2876
|
color: var(--danger-fg);
|
|
2831
2877
|
}
|
|
2878
|
+
/* A menu whose items say what each pick does: a name over a muted line. */
|
|
2879
|
+
.menu-described {
|
|
2880
|
+
width: 280px;
|
|
2881
|
+
max-width: min(280px, 78vw);
|
|
2882
|
+
}
|
|
2883
|
+
.menu-item-name {
|
|
2884
|
+
display: block;
|
|
2885
|
+
font-size: 13px;
|
|
2886
|
+
font-weight: 600;
|
|
2887
|
+
}
|
|
2888
|
+
.menu-item-desc {
|
|
2889
|
+
display: block;
|
|
2890
|
+
margin-top: 1px;
|
|
2891
|
+
font-size: 11.5px;
|
|
2892
|
+
color: var(--muted);
|
|
2893
|
+
line-height: 1.35;
|
|
2894
|
+
}
|
|
2832
2895
|
|
|
2833
2896
|
/* editor field hints + scheduled banner */
|
|
2834
2897
|
.field-hint {
|
|
@@ -2836,7 +2899,7 @@ td.act {
|
|
|
2836
2899
|
color: var(--muted);
|
|
2837
2900
|
margin-top: 4px;
|
|
2838
2901
|
}
|
|
2839
|
-
/* --- Field validation (DESIGN §
|
|
2902
|
+
/* --- Field validation (DESIGN §5): an error about one field lives on that
|
|
2840
2903
|
field — the input goes to an invalid state and the reason sits directly beneath it,
|
|
2841
2904
|
tied by aria-describedby. This replaces the old disable-the-button + `title` tooltip,
|
|
2842
2905
|
which never reached keyboard, touch, or screen-reader users. --- */
|
|
@@ -2856,6 +2919,11 @@ textarea.is-invalid {
|
|
|
2856
2919
|
.field-error .field-error-ico {
|
|
2857
2920
|
font-weight: 700;
|
|
2858
2921
|
}
|
|
2922
|
+
/* A dialog's own error, when no one field is at fault: just above its actions, where the
|
|
2923
|
+
reader is looking rather than in a toast at the foot of the screen (DESIGN §5). */
|
|
2924
|
+
.modal .dialog-error {
|
|
2925
|
+
margin-top: 12px;
|
|
2926
|
+
}
|
|
2859
2927
|
/* Label + info icon grouped at the top of the slug field. */
|
|
2860
2928
|
.label-row {
|
|
2861
2929
|
display: flex;
|
|
@@ -2964,11 +3032,10 @@ textarea.is-invalid {
|
|
|
2964
3032
|
#f-slug.slug-auto {
|
|
2965
3033
|
color: var(--muted);
|
|
2966
3034
|
}
|
|
2967
|
-
/* --- Editor-state banner (DESIGN §
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
a neutral notice. It replaces the old `.sched-banner` + `.fresh-banner` pair. --- */
|
|
3035
|
+
/* --- Editor-state banner (DESIGN §5): a standing, in-flow line at the top of the
|
|
3036
|
+
editor, in two variants. `scheduled` marks a frozen, soft-locked post (SPEC §6);
|
|
3037
|
+
`conflict` warns the draft changed elsewhere (another tab, or Claude), and its two
|
|
3038
|
+
actions are equal-weight Ghost, since both choices lose something. --- */
|
|
2972
3039
|
.banner {
|
|
2973
3040
|
display: flex;
|
|
2974
3041
|
align-items: center;
|
|
@@ -2997,59 +3064,35 @@ textarea.is-invalid {
|
|
|
2997
3064
|
color: var(--warn-fg);
|
|
2998
3065
|
border-color: var(--warn-line);
|
|
2999
3066
|
}
|
|
3000
|
-
.banner-info {
|
|
3001
|
-
background: var(--banner-bg);
|
|
3002
|
-
color: var(--fg);
|
|
3003
|
-
border-color: var(--banner-line);
|
|
3004
|
-
}
|
|
3005
3067
|
|
|
3006
|
-
/*
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
its layout is prose plus one control, not a status line with an actions row, so it never
|
|
3010
|
-
wraps the dismiss beneath the text the way the banner wraps its buttons. Padding is a
|
|
3011
|
-
full 1rem because it is a sentence or two, not a status line. Removed on dismiss with
|
|
3012
|
-
no animation. Both themes come through the existing tokens. --- */
|
|
3013
|
-
.notice {
|
|
3068
|
+
/* Look changed since your last test (SPEC §8): the server's condition, a line in the scheduled
|
|
3069
|
+
banner that says to send a new test, and a mark on the dashboard's scheduled card. */
|
|
3070
|
+
.sched-remade {
|
|
3014
3071
|
display: flex;
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
color: var(--fg);
|
|
3021
|
-
padding: 1rem;
|
|
3022
|
-
margin-bottom: 14px;
|
|
3023
|
-
font-size: 14px;
|
|
3024
|
-
line-height: 1.45;
|
|
3025
|
-
}
|
|
3026
|
-
.notice-text {
|
|
3027
|
-
flex: 1 1 auto;
|
|
3028
|
-
min-width: 0;
|
|
3072
|
+
flex-wrap: wrap;
|
|
3073
|
+
align-items: center;
|
|
3074
|
+
gap: 4px 10px;
|
|
3075
|
+
margin-top: 6px;
|
|
3076
|
+
font-size: 13px;
|
|
3029
3077
|
}
|
|
3030
|
-
/*
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3078
|
+
/* The scheduled banner's line for a send too large for D1's daily writes on Workers Free's
|
|
3079
|
+
limits (SPEC §8): the server's condition, in the banner's own violet so a warning that
|
|
3080
|
+
blocks nothing never outweighs the send, with an amber ⚠️ to say it's worth a look. */
|
|
3081
|
+
.sched-daily-writes {
|
|
3082
|
+
display: flex;
|
|
3083
|
+
gap: 6px;
|
|
3084
|
+
align-items: baseline;
|
|
3085
|
+
margin-top: 6px;
|
|
3086
|
+
font-size: 12.5px;
|
|
3087
|
+
color: var(--status-scheduled-fg);
|
|
3036
3088
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
the first line of text without making a one-line notice taller than its text. */
|
|
3040
|
-
button.notice-dismiss {
|
|
3041
|
-
flex: 0 0 auto;
|
|
3042
|
-
display: inline-flex;
|
|
3043
|
-
align-items: center;
|
|
3044
|
-
justify-content: center;
|
|
3045
|
-
width: 28px;
|
|
3046
|
-
height: 28px;
|
|
3047
|
-
padding: 0;
|
|
3048
|
-
margin: -4px -4px -4px 0;
|
|
3089
|
+
.sched-note-mark {
|
|
3090
|
+
flex: none;
|
|
3049
3091
|
}
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3092
|
+
.sched-remade-mark {
|
|
3093
|
+
margin-top: 4px;
|
|
3094
|
+
font-size: 12.5px;
|
|
3095
|
+
color: var(--status-scheduled-fg);
|
|
3053
3096
|
}
|
|
3054
3097
|
|
|
3055
3098
|
.grid2 {
|
|
@@ -3931,7 +3974,7 @@ button.api-filter-clear svg {
|
|
|
3931
3974
|
text-decoration: underline;
|
|
3932
3975
|
}
|
|
3933
3976
|
|
|
3934
|
-
/* Publication card: the
|
|
3977
|
+
/* Publication card: the home page's address with Copy, and the way out to it. */
|
|
3935
3978
|
.pub-card {
|
|
3936
3979
|
display: flex;
|
|
3937
3980
|
flex-direction: column;
|
|
@@ -3942,10 +3985,6 @@ button.api-filter-clear svg {
|
|
|
3942
3985
|
align-items: center;
|
|
3943
3986
|
gap: 12px;
|
|
3944
3987
|
}
|
|
3945
|
-
.pub-key {
|
|
3946
|
-
flex: 0 0 84px;
|
|
3947
|
-
font-size: 12px;
|
|
3948
|
-
}
|
|
3949
3988
|
.pub-val {
|
|
3950
3989
|
flex: 1 1 auto;
|
|
3951
3990
|
min-width: 0;
|
|
@@ -4052,10 +4091,9 @@ button.api-filter-clear svg {
|
|
|
4052
4091
|
deploy / Copy-out), editable surfaces sit on cards and read-only
|
|
4053
4092
|
facts on a recessed "sunken" card. Kestrel's own accents use the
|
|
4054
4093
|
fixed --accent (a slate-blue), independent of the reader surface.
|
|
4055
|
-
A sticky save bar collects
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
the OS via the tokens at the top of this file.
|
|
4094
|
+
A sticky save bar collects every editable section's unsaved
|
|
4095
|
+
changes; the logo saves on its own and doesn't feed it. Theme
|
|
4096
|
+
follows the OS via the tokens at the top of this file.
|
|
4059
4097
|
============================================================ */
|
|
4060
4098
|
.settings {
|
|
4061
4099
|
max-width: 940px;
|
|
@@ -4081,7 +4119,7 @@ button.api-filter-clear svg {
|
|
|
4081
4119
|
margin-bottom: 34px;
|
|
4082
4120
|
}
|
|
4083
4121
|
|
|
4084
|
-
/* --- copying text out of the app (DESIGN §
|
|
4122
|
+
/* --- copying text out of the app (DESIGN §7, ui/copy.ts) ---
|
|
4085
4123
|
A labelled block (a code block, or the API reference's query and content-type tables):
|
|
4086
4124
|
its label line, then the block. The Copy is a control outside the five roles: the word
|
|
4087
4125
|
and its glyph, no fill or border, quiet on the line it shares; the glyph turns to a
|
|
@@ -4143,7 +4181,7 @@ button.copied svg {
|
|
|
4143
4181
|
flex: 0 0 auto;
|
|
4144
4182
|
}
|
|
4145
4183
|
|
|
4146
|
-
/* --- groups: the sections kept together by whom they serve (DESIGN §
|
|
4184
|
+
/* --- groups: the sections kept together by whom they serve (DESIGN §3). A group has no
|
|
4147
4185
|
heading, since each section's title already says what it is; a wider gap than the one
|
|
4148
4186
|
between sections is all that separates one group from the next. --- */
|
|
4149
4187
|
.set-group + .set-group {
|
|
@@ -4194,7 +4232,7 @@ button.copied svg {
|
|
|
4194
4232
|
background: var(--accent-soft);
|
|
4195
4233
|
}
|
|
4196
4234
|
/* In use by scheduled posts (SPEC §9): the scheduled violet, a lifecycle fact and not a
|
|
4197
|
-
warning, the same reading as the scheduled badge (DESIGN §
|
|
4235
|
+
warning, the same reading as the scheduled badge (DESIGN §8). Adds no token. */
|
|
4198
4236
|
.set-chip.inuse {
|
|
4199
4237
|
color: var(--status-scheduled-fg);
|
|
4200
4238
|
border-color: var(--status-scheduled-line);
|
|
@@ -4207,22 +4245,12 @@ button.copied svg {
|
|
|
4207
4245
|
background: currentColor;
|
|
4208
4246
|
opacity: 0.85;
|
|
4209
4247
|
}
|
|
4248
|
+
/* Set at deploy: a fact about where the value lives, not a warning, so it stays neutral
|
|
4249
|
+
rather than borrowing the amber `warn` scale (DESIGN §8). */
|
|
4210
4250
|
.set-chip.readonly {
|
|
4211
|
-
color: var(--warn-fg);
|
|
4212
|
-
border-color: color-mix(in srgb, var(--warn-fg) 30%, var(--line-soft));
|
|
4213
|
-
background: color-mix(in srgb, var(--warn-fg) 9%, transparent);
|
|
4214
|
-
}
|
|
4215
|
-
/* A dashed "Mock" tag beside the Email-template chip — says the section is a
|
|
4216
|
-
preview of a feature not wired to the backend yet. */
|
|
4217
|
-
.set-tag-mock {
|
|
4218
|
-
font-size: 10px;
|
|
4219
|
-
font-weight: 650;
|
|
4220
|
-
letter-spacing: 0.06em;
|
|
4221
|
-
text-transform: uppercase;
|
|
4222
|
-
padding: 3px 7px;
|
|
4223
|
-
border-radius: 6px;
|
|
4224
|
-
border: 1px dashed var(--line-soft);
|
|
4225
4251
|
color: var(--muted);
|
|
4252
|
+
border-color: var(--line);
|
|
4253
|
+
background: var(--chip);
|
|
4226
4254
|
}
|
|
4227
4255
|
|
|
4228
4256
|
/* --- cards: editable surfaces raised, read-only facts recessed --- */
|
|
@@ -4411,7 +4439,7 @@ button.set-field-remove {
|
|
|
4411
4439
|
align-items: flex-start;
|
|
4412
4440
|
padding: 11px 16px;
|
|
4413
4441
|
border-bottom: 1px solid var(--line-soft);
|
|
4414
|
-
background:
|
|
4442
|
+
background: var(--chip);
|
|
4415
4443
|
font-size: 12.5px;
|
|
4416
4444
|
color: var(--muted);
|
|
4417
4445
|
line-height: 1.5;
|
|
@@ -4421,7 +4449,7 @@ button.set-field-remove {
|
|
|
4421
4449
|
height: 15px;
|
|
4422
4450
|
flex: 0 0 auto;
|
|
4423
4451
|
margin-top: 1px;
|
|
4424
|
-
color: var(--
|
|
4452
|
+
color: var(--muted);
|
|
4425
4453
|
}
|
|
4426
4454
|
.set-kv {
|
|
4427
4455
|
display: grid;
|
|
@@ -4758,14 +4786,21 @@ button.set-field-remove {
|
|
|
4758
4786
|
}
|
|
4759
4787
|
.set-recip-add {
|
|
4760
4788
|
display: flex;
|
|
4789
|
+
flex-wrap: wrap;
|
|
4761
4790
|
gap: 8px;
|
|
4762
4791
|
max-width: 420px;
|
|
4763
4792
|
}
|
|
4764
4793
|
.set-recip-add input {
|
|
4765
4794
|
flex: 1;
|
|
4766
4795
|
}
|
|
4796
|
+
/* A refused address says why on its own line under the field and its button (DESIGN §5). */
|
|
4797
|
+
.set-recip-add .field-error {
|
|
4798
|
+
flex-basis: 100%;
|
|
4799
|
+
order: 1;
|
|
4800
|
+
margin-top: 0;
|
|
4801
|
+
}
|
|
4767
4802
|
|
|
4768
|
-
/* --- ways to subscribe (DESIGN §
|
|
4803
|
+
/* --- ways to subscribe (DESIGN §3): one card, the two ways as bands in reading order,
|
|
4769
4804
|
the page then the form, split by a full-width rule like the other cards' bands. Each
|
|
4770
4805
|
band has the same head (a glyph, a title, one line) so they read as two options. The
|
|
4771
4806
|
card folds by the section's own width, since the sidebar decides how wide Settings is. --- */
|
|
@@ -4956,7 +4991,7 @@ button.set-embed-copy svg {
|
|
|
4956
4991
|
color: var(--muted);
|
|
4957
4992
|
}
|
|
4958
4993
|
|
|
4959
|
-
/* --- email template
|
|
4994
|
+
/* --- email template: a standalone sample-email frame, then a card holding
|
|
4960
4995
|
the HTML template editor and a collapsible variable reference. The example
|
|
4961
4996
|
templates are authored with a <style> block; the preview renders them in an
|
|
4962
4997
|
isolated iframe, so a template's own styles apply as a mail client would show
|
|
@@ -4991,7 +5026,7 @@ button.set-embed-copy svg {
|
|
|
4991
5026
|
font-size: 12px;
|
|
4992
5027
|
color: var(--muted);
|
|
4993
5028
|
}
|
|
4994
|
-
/* Syntax-highlight overlay editor
|
|
5029
|
+
/* Syntax-highlight overlay editor: a transparent <textarea> over a
|
|
4995
5030
|
highlighted <pre>, plus a line-number gutter. All three share identical font
|
|
4996
5031
|
metrics so the caret sits exactly over the colored text; the textarea is the only
|
|
4997
5032
|
scroller and the gutter + overlay are synced to it in script. */
|
|
@@ -5067,7 +5102,7 @@ button.set-embed-copy svg {
|
|
|
5067
5102
|
.set-tpl-editor-wrap.show-lines .set-tpl-editor {
|
|
5068
5103
|
left: 47px;
|
|
5069
5104
|
}
|
|
5070
|
-
/* Syntax colors
|
|
5105
|
+
/* Syntax colors. */
|
|
5071
5106
|
.cx-tag {
|
|
5072
5107
|
color: var(--syntax-tag);
|
|
5073
5108
|
}
|
|
@@ -5097,7 +5132,7 @@ button.set-embed-copy svg {
|
|
|
5097
5132
|
padding: 0 2px;
|
|
5098
5133
|
font-weight: 600;
|
|
5099
5134
|
}
|
|
5100
|
-
/* Markdown token classes
|
|
5135
|
+
/* Markdown token classes — the minimal scheme: the MARK carries the color, the
|
|
5101
5136
|
CONTENT keeps --syntax-fg and carries only weight/slant. So a bold word reads as weight, not
|
|
5102
5137
|
as a hue that competes with a heading. Heading text is bold with a crimson # mark; emphasis
|
|
5103
5138
|
asterisks are pink around plain weighted/slanted text; block markers (>, bullets, the fence
|
|
@@ -5309,7 +5344,7 @@ button.set-embed-copy svg {
|
|
|
5309
5344
|
background: var(--accent-soft);
|
|
5310
5345
|
border-color: color-mix(in srgb, var(--accent) 30%, var(--line-soft));
|
|
5311
5346
|
}
|
|
5312
|
-
/* Required-token pills
|
|
5347
|
+
/* Required-token pills: live green/red for the two tokens whose
|
|
5313
5348
|
absence blocks a save, shown upper-right of the editor toolbar. */
|
|
5314
5349
|
.set-tpl-required {
|
|
5315
5350
|
display: flex;
|
|
@@ -5373,10 +5408,7 @@ button.set-embed-copy svg {
|
|
|
5373
5408
|
box-shadow: 0 1px 4px rgba(15, 18, 25, 0.1);
|
|
5374
5409
|
transition: max-width 0.16s ease;
|
|
5375
5410
|
}
|
|
5376
|
-
/* "Start from example"
|
|
5377
|
-
.set-menu {
|
|
5378
|
-
position: relative;
|
|
5379
|
-
}
|
|
5411
|
+
/* "Start from example": the trigger of a shared menu (openMenu). */
|
|
5380
5412
|
.set-menu-btn {
|
|
5381
5413
|
display: inline-flex;
|
|
5382
5414
|
align-items: center;
|
|
@@ -5390,45 +5422,6 @@ button.set-embed-copy svg {
|
|
|
5390
5422
|
transform: translateY(-1px) rotate(45deg);
|
|
5391
5423
|
opacity: 0.6;
|
|
5392
5424
|
}
|
|
5393
|
-
.set-menu-list {
|
|
5394
|
-
position: absolute;
|
|
5395
|
-
left: 0;
|
|
5396
|
-
top: calc(100% + 6px);
|
|
5397
|
-
z-index: var(--z-menu);
|
|
5398
|
-
width: 280px;
|
|
5399
|
-
max-width: min(280px, 78vw);
|
|
5400
|
-
padding: 5px;
|
|
5401
|
-
background: var(--card);
|
|
5402
|
-
border: 1px solid var(--line);
|
|
5403
|
-
border-radius: 10px;
|
|
5404
|
-
box-shadow: 0 10px 28px rgba(15, 18, 25, 0.16);
|
|
5405
|
-
}
|
|
5406
|
-
.set-menu-list button {
|
|
5407
|
-
display: block;
|
|
5408
|
-
width: 100%;
|
|
5409
|
-
text-align: left;
|
|
5410
|
-
border: 0;
|
|
5411
|
-
background: transparent;
|
|
5412
|
-
border-radius: 7px;
|
|
5413
|
-
padding: 8px 10px;
|
|
5414
|
-
cursor: pointer;
|
|
5415
|
-
}
|
|
5416
|
-
.set-menu-list button:hover {
|
|
5417
|
-
background: var(--sunken);
|
|
5418
|
-
}
|
|
5419
|
-
.set-menu-name {
|
|
5420
|
-
display: block;
|
|
5421
|
-
font-size: 13px;
|
|
5422
|
-
font-weight: 600;
|
|
5423
|
-
color: var(--fg);
|
|
5424
|
-
}
|
|
5425
|
-
.set-menu-desc {
|
|
5426
|
-
display: block;
|
|
5427
|
-
margin-top: 1px;
|
|
5428
|
-
font-size: 11.5px;
|
|
5429
|
-
color: var(--muted);
|
|
5430
|
-
line-height: 1.35;
|
|
5431
|
-
}
|
|
5432
5425
|
/* Variables card heading. */
|
|
5433
5426
|
.set-tpl-vartitle {
|
|
5434
5427
|
margin: 0 0 2px;
|
|
@@ -5442,7 +5435,7 @@ button.set-embed-copy svg {
|
|
|
5442
5435
|
.set-tpl-varcard .set-tpl-vars-body {
|
|
5443
5436
|
padding: 0;
|
|
5444
5437
|
}
|
|
5445
|
-
/* The
|
|
5438
|
+
/* The note at the foot of a card: what the section is for, or where it is set. */
|
|
5446
5439
|
.set-note {
|
|
5447
5440
|
display: flex;
|
|
5448
5441
|
align-items: flex-start;
|
|
@@ -5707,7 +5700,7 @@ button.set-embed-copy svg {
|
|
|
5707
5700
|
icon rail — and collapses to edge-to-edge once the sidebar becomes the off-canvas
|
|
5708
5701
|
mobile drawer. The post editor keeps its own autosave + conflict model, so it does
|
|
5709
5702
|
NOT use this bar. Its Save is a Primary button — now brand-blue --accent app-wide
|
|
5710
|
-
(DESIGN §
|
|
5703
|
+
(DESIGN §7), so the bar needs no primary override of its own. --- */
|
|
5711
5704
|
.savebar {
|
|
5712
5705
|
position: fixed;
|
|
5713
5706
|
/* Clears the full 240px sidebar; narrowed to the rail / drawer in the rules below. */
|