@kurtbruns/kestrel 1.12.0 → 1.13.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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Kestrel</title>
7
7
  <link rel="icon" href="/favicon.svg">
8
- <link rel="stylesheet" href="./styles.css?v=1396c31b">
8
+ <link rel="stylesheet" href="./styles.css?v=7f1a9a46">
9
9
  <script>
10
10
  // Sidebar mode guard — set the width-based data-nav on <html> before first paint so the
11
11
  // sidebar renders in its final width with no flash. The collapse toggle's override is
@@ -113,6 +113,6 @@
113
113
  </section>
114
114
  <div id="toasts" role="status" aria-live="polite"></div>
115
115
 
116
- <script src="./app.js?v=fc72c770"></script>
116
+ <script src="./app.js?v=1aa2dbe5"></script>
117
117
  </body>
118
118
  </html>
@@ -1854,8 +1854,10 @@ td a:hover {
1854
1854
  .dash-drafts {
1855
1855
  table-layout: fixed;
1856
1856
  }
1857
+ /* Wide enough for the longest "edited <date>", a two-digit day and hour included, with the
1858
+ cell's padding: the fixed layout clips anything wider rather than growing the column. */
1857
1859
  .dash-drafts td.edited {
1858
- width: 11.5em;
1860
+ width: 15em;
1859
1861
  white-space: nowrap;
1860
1862
  }
1861
1863
  @media (min-width: 721px) {
@@ -1867,13 +1869,15 @@ td a:hover {
1867
1869
  .list-table .c-act {
1868
1870
  width: 56px;
1869
1871
  }
1870
- /* Subscribers: how each joined ("Imported · Sendy, single opt-in"), which may wrap to two
1871
- lines; the table takes a wider floor so the email column keeps room beside it. */
1872
- .list-table .c-how {
1873
- width: 152px;
1874
- }
1872
+ /* Subscribers: a floor that keeps the email column about 250px beside its two others. With
1873
+ only Status and Joined beside an address, which rarely needs more than half the row, fixed
1874
+ widths left one wide gap before them; a share of the row each spreads the space evenly. */
1875
1875
  .list-table.subs-table {
1876
- min-width: 680px;
1876
+ min-width: 610px;
1877
+ }
1878
+ .list-table.subs-table .c-status,
1879
+ .list-table.subs-table .c-date {
1880
+ width: 24%;
1877
1881
  }
1878
1882
 
1879
1883
  .dash-section table {
@@ -1934,20 +1938,143 @@ th.num .th-sort {
1934
1938
  cursor: default;
1935
1939
  }
1936
1940
 
1937
- /* API reference: query-param table inside a route card */
1938
- .api-query {
1941
+ /* A route's or an object's sections (query, body, returns, refusals, fields) as Stripe's and
1942
+ Mux's references set theirs: a heading over a rule, then unboxed rows, each with the name,
1943
+ its type, and Required on one line and what it means under it, so a long name never
1944
+ squeezes its description. A name breaks only where its words join (the <wbr> the page puts
1945
+ after each _ . /). */
1946
+ .block.api-sec {
1947
+ margin-top: 22px;
1948
+ }
1949
+ .api-sec > .block-head {
1939
1950
  margin: 0;
1951
+ padding-bottom: 8px;
1952
+ border-bottom: 1px solid var(--line-soft);
1940
1953
  }
1941
- .api-query td {
1942
- padding: 5px 10px;
1954
+ .api-sec > .block-head .block-label {
1955
+ font-size: 15px;
1956
+ font-weight: 600;
1957
+ color: var(--fg);
1958
+ }
1959
+ .api-sec-meta {
1960
+ margin-left: 2px;
1961
+ font: 400 12px var(--mono);
1962
+ color: var(--muted);
1963
+ }
1964
+ /* Rules divide rows; the last row in a section has none under it. */
1965
+ .api-row,
1966
+ .api-lrow {
1967
+ padding: 11px 0;
1968
+ border-bottom: 1px solid var(--line);
1969
+ }
1970
+ .api-row:last-child,
1971
+ .api-lrow:last-child {
1972
+ border-bottom: 0;
1973
+ }
1974
+ .api-row-head {
1975
+ display: flex;
1976
+ align-items: baseline;
1977
+ flex-wrap: wrap;
1978
+ gap: 8px;
1979
+ }
1980
+ code.api-row-name {
1943
1981
  font-size: 13px;
1944
- vertical-align: top;
1982
+ font-weight: 600;
1983
+ overflow-wrap: break-word;
1945
1984
  }
1946
- .api-query td:first-child {
1947
- white-space: nowrap;
1948
- width: 1%;
1985
+ .api-row-type {
1986
+ font: 12px var(--mono);
1987
+ color: var(--muted);
1988
+ }
1989
+ /* A type that names an object links to it, and reads as a link. */
1990
+ .api-row-type a {
1991
+ color: var(--fg);
1992
+ text-decoration: underline;
1993
+ text-underline-offset: 2px;
1994
+ }
1995
+ .api-row-desc {
1996
+ margin-top: 3px;
1997
+ font-size: 13.5px;
1998
+ }
1999
+ /* The refusals and the statuses are a ledger: the status in a column of its own, tinted by
2000
+ its class, so a reader scans by status. */
2001
+ .api-lrow {
2002
+ display: grid;
2003
+ grid-template-columns: 44px minmax(0, 1fr);
2004
+ gap: 6px;
2005
+ }
2006
+ .api-st {
2007
+ font: 600 12.5px / 20px var(--mono);
2008
+ color: var(--muted);
2009
+ }
2010
+ .api-st-4xx {
2011
+ color: var(--warn-fg);
2012
+ }
2013
+ .api-st-5xx {
2014
+ color: var(--danger-fg);
2015
+ }
2016
+ .api-st-2xx {
2017
+ color: var(--ok-fg);
2018
+ }
2019
+ .api-with {
2020
+ margin-top: 2px;
2021
+ font-size: 12px;
2022
+ color: var(--muted);
2023
+ }
2024
+ /* A status's name is words, not a code. */
2025
+ .api-statuses code.api-row-name {
2026
+ font-family: inherit;
2027
+ }
2028
+ /* A value's short cells beside its name (a condition kind's severity and fields), each led by
2029
+ its column's name, and the label of a later long line ("Remedy:"). */
2030
+ .api-row-meta {
2031
+ font-size: 12.5px;
2032
+ color: var(--muted);
2033
+ }
2034
+ .api-row-key {
2035
+ font-size: 12px;
2036
+ font-weight: 600;
2037
+ color: var(--muted);
2038
+ }
2039
+ /* What every route shares, described once ahead of the routes: concept prose, an object's
2040
+ description, an enum's values under a field's line, and the refusal table's next moves. */
2041
+ /* The reference's paragraphs, each with space below it, as a doc page's are. */
2042
+ .api-prose {
2043
+ margin: 0 0 10px;
2044
+ font-size: 14px;
2045
+ }
2046
+ /* A list in the reference's prose (the status ranges under Requests), set like its paragraphs. */
2047
+ .prose-list {
2048
+ padding-left: 20px;
2049
+ }
2050
+ .prose-list li {
2051
+ margin: 6px 0;
2052
+ }
2053
+ .api-values {
2054
+ margin: 4px 0 0;
2055
+ padding-left: 16px;
2056
+ }
2057
+ .api-values li {
2058
+ margin: 2px 0;
2059
+ }
2060
+ /* The name of a group an enum's values fall into, over its bullets. */
2061
+ .api-values-group {
2062
+ margin: 8px 0 0;
2063
+ font-size: 12.5px;
2064
+ font-weight: 600;
2065
+ color: var(--muted);
2066
+ }
2067
+ .api-returns {
2068
+ margin: 4px 0 6px;
2069
+ font-size: 13.5px;
2070
+ }
2071
+ /* A refusal's next move, under what it means: quieter, the step a client takes from it. */
2072
+ .api-move-next {
2073
+ display: block;
2074
+ margin-top: 2px;
2075
+ font-size: 12.5px;
2076
+ color: var(--muted);
1949
2077
  }
1950
-
1951
2078
  /* editor header */
1952
2079
  .editor-head {
1953
2080
  display: flex;
@@ -2242,7 +2369,7 @@ body.web-mode .content {
2242
2369
  font-size: 22px;
2243
2370
  letter-spacing: -0.01em;
2244
2371
  }
2245
- /* The derived-phase pill (progressing / retrying / backing-off / needs-attention /
2372
+ /* The derived-phase pill (progressing / retrying / backing_off / needs_attention /
2246
2373
  settling / …). Tones reuse the status + semantic palettes so a phase reads the same
2247
2374
  hue as its badge elsewhere. A plain label, so the default cursor (DESIGN §11). */
2248
2375
  .phase-pill {
@@ -4016,16 +4143,27 @@ button.api-filter-clear svg {
4016
4143
  color: var(--fg);
4017
4144
  background: var(--hover);
4018
4145
  }
4146
+ /* The reference reads as a document: a doc page's line height, not the browser's default. */
4019
4147
  .api-content {
4020
4148
  min-width: 0;
4021
- max-width: 820px;
4149
+ max-width: 1440px;
4150
+ line-height: 1.5;
4151
+ }
4152
+ /* One reading measure for the page, a doc page's (720px): the intro, the topics, and each
4153
+ route's and object's prose and tables keep it, and only a route's examples, beside its
4154
+ prose on a wide screen, reach past it. */
4155
+ .api-head,
4156
+ .api-blurb,
4157
+ .api-section[data-tier="overview"] .api-res,
4158
+ .api-op-body {
4159
+ max-width: 720px;
4022
4160
  }
4023
4161
  .api-head {
4024
4162
  margin: 0 0 12px;
4025
4163
  }
4026
4164
  .api-head h1 {
4027
4165
  margin: 0 0 4px;
4028
- font-size: 22px;
4166
+ font-size: 28px;
4029
4167
  }
4030
4168
  .api-section {
4031
4169
  scroll-margin-top: calc(var(--bar-h) + 2px);
@@ -4036,35 +4174,53 @@ button.api-filter-clear svg {
4036
4174
  .api-section.api-last {
4037
4175
  min-height: calc(100vh - var(--bar-h));
4038
4176
  }
4177
+ /* A tier is a page heading here, not the app's small-caps section label that h2 is elsewhere. */
4039
4178
  .api-section h2 {
4040
- font-size: 18px;
4041
- margin: 24px 0 4px;
4179
+ font-size: 24px;
4180
+ font-weight: 650;
4181
+ color: var(--fg);
4182
+ text-transform: none;
4183
+ letter-spacing: normal;
4184
+ margin: 28px 0 4px;
4185
+ }
4186
+ /* Where a jump from the rail or an address moves focus: a place to read from, not a control,
4187
+ so it takes no ring. */
4188
+ .api-section h2:focus,
4189
+ .api-res-head h3:focus {
4190
+ outline: none;
4042
4191
  }
4043
4192
  .api-blurb {
4044
- margin: 0;
4193
+ margin: 0 0 10px;
4045
4194
  font-size: 14px;
4046
- max-width: 62ch;
4047
4195
  }
4048
- /* A resource: its heading stays pinned under the room bar while its rows scroll by. */
4196
+ /* A resource: its heading over a rule, scrolling with its routes. */
4049
4197
  .api-res {
4050
4198
  margin-top: 16px;
4051
4199
  scroll-margin-top: var(--bar-h);
4052
4200
  }
4201
+ /* A resource's heading, set like the page's other headings, flush with the text: it only
4202
+ pins under the bar, its vertical padding the band the rows scroll beneath. */
4053
4203
  .api-res-head {
4054
- position: sticky;
4055
- top: var(--bar-h);
4056
- z-index: var(--z-content);
4057
4204
  display: flex;
4058
4205
  align-items: center;
4059
4206
  gap: 8px;
4060
- padding: 7px 6px;
4207
+ margin: 0;
4208
+ padding: 6px 0;
4209
+ border-bottom: 1px solid var(--line-soft);
4061
4210
  background: var(--bg);
4062
- border-radius: 6px;
4063
4211
  }
4064
4212
 
4065
4213
  .api-res-head h3 {
4066
4214
  margin: 0;
4067
- font-size: 14px;
4215
+ font-size: 20px;
4216
+ font-weight: 650;
4217
+ }
4218
+ /* A Getting started topic is a heading over prose, not a list's head: no rule, a step smaller. */
4219
+ .api-section[data-tier="overview"] .api-res-head {
4220
+ border-bottom: none;
4221
+ }
4222
+ .api-section[data-tier="overview"] .api-res-head h3 {
4223
+ font-size: 18px;
4068
4224
  }
4069
4225
  .api-rows {
4070
4226
  list-style: none;
@@ -4073,46 +4229,8 @@ button.api-filter-clear svg {
4073
4229
  }
4074
4230
  .api-rows > li {
4075
4231
  border-bottom: 1px solid var(--line);
4076
- }
4077
- /* One route per row: badge, path, and summary line, a native disclosure that opens in
4078
- place to the full documentation. */
4079
- .api-route-sum {
4080
- display: grid;
4081
- grid-template-columns: 62px minmax(0, max-content) minmax(0, 1fr) 20px;
4082
- gap: 14px;
4083
- align-items: center;
4084
- padding: 9px 6px;
4085
- border-radius: 6px;
4086
- list-style: none;
4087
- cursor: pointer;
4088
- }
4089
- .api-route-sum::-webkit-details-marker {
4090
- display: none;
4091
- }
4092
- .api-route-sum:hover {
4093
- background: var(--hover);
4094
- }
4095
- .api-route-line {
4096
- color: var(--muted);
4097
- font-size: 13.5px;
4098
- white-space: nowrap;
4099
- overflow: hidden;
4100
- text-overflow: ellipsis;
4101
- }
4102
- .api-route[open] .api-route-line {
4103
- visibility: hidden;
4104
- }
4105
- .api-chev {
4106
- width: 6px;
4107
- height: 6px;
4108
- border-right: 1.6px solid var(--muted);
4109
- border-bottom: 1.6px solid var(--muted);
4110
- justify-self: center;
4111
- transform: rotate(-45deg);
4112
- transition: transform 0.15s;
4113
- }
4114
- .api-route[open] .api-chev {
4115
- transform: rotate(45deg);
4232
+ /* A route reached from its address lands just under the bar, the rule above it hidden. */
4233
+ scroll-margin-top: calc(var(--bar-h) - 1px);
4116
4234
  }
4117
4235
  /* The verb as a tinted label: its own --method-* color as text over a wash of itself. */
4118
4236
  .api-method {
@@ -4136,6 +4254,13 @@ button.api-filter-clear svg {
4136
4254
  .api-method.m-DELETE {
4137
4255
  --m: var(--method-delete);
4138
4256
  }
4257
+ /* An object heads its resource as a row of its own, marked in the verb's column. */
4258
+ .api-method.m-OBJECT {
4259
+ text-transform: uppercase;
4260
+ color: var(--muted);
4261
+ background: var(--card);
4262
+ box-shadow: inset 0 0 0 1px var(--line-soft);
4263
+ }
4139
4264
  .api-path {
4140
4265
  font: 13.5px var(--mono);
4141
4266
  overflow-wrap: anywhere;
@@ -4146,30 +4271,128 @@ button.api-filter-clear svg {
4146
4271
  border-radius: 3px;
4147
4272
  padding: 0 2px;
4148
4273
  }
4149
- .api-route-detail {
4150
- padding: 2px 6px 18px 82px;
4151
- }
4152
4274
  .api-summary {
4153
- margin: 2px 0 0;
4154
- font-size: 14.5px;
4275
+ margin: 0 0 10px;
4276
+ font-size: 15px;
4155
4277
  }
4156
4278
  .api-desc {
4157
- margin: 6px 0 0;
4279
+ margin: 0 0 10px;
4158
4280
  font-size: 14px;
4159
4281
  }
4160
- /* A query parameter the route can't be called without. */
4282
+ /* A field or parameter the request can't do without. */
4161
4283
  .api-req {
4162
- margin-left: 4px;
4163
- font-size: 11px;
4164
- color: var(--muted);
4165
- border: 1px solid var(--line);
4166
- border-radius: 4px;
4167
- padding: 0 4px;
4284
+ font-size: 11.5px;
4285
+ font-weight: 600;
4286
+ color: var(--warn-fg);
4168
4287
  }
4169
4288
 
4170
4289
  .api-empty {
4171
4290
  margin: 20px 0;
4172
4291
  }
4292
+ /* An object, first under the resource whose routes return it: a row like a route's, that
4293
+ opens to its fields and example. */
4294
+ /* A route or an object, laid out in full as Stripe and Mux lay theirs: its name and tools,
4295
+ its method and path, then what it does beside its examples on a wide screen, under them
4296
+ on a narrow one. The examples scroll with the page: pinned, one taller than the window
4297
+ (the send view's) would never show its end. */
4298
+ /* Each route and object its own block, with room around it so the page reads as one at a time. */
4299
+ .api-op {
4300
+ padding: 36px 0 40px;
4301
+ }
4302
+ .api-op-head {
4303
+ display: flex;
4304
+ flex-wrap: wrap;
4305
+ align-items: baseline;
4306
+ justify-content: space-between;
4307
+ gap: 8px 16px;
4308
+ }
4309
+ .api-op-head h4 {
4310
+ margin: 0;
4311
+ font-size: 18px;
4312
+ font-weight: 650;
4313
+ }
4314
+ .api-op-head h4:focus {
4315
+ outline: none;
4316
+ }
4317
+ /* A heading is a link to its own address, in the heading's own ink, with a link icon in the
4318
+ margin to its left while it is hovered or focused, as GitHub's headings show theirs. */
4319
+ a.api-self {
4320
+ position: relative;
4321
+ color: inherit;
4322
+ text-decoration: none;
4323
+ }
4324
+ a.api-self svg {
4325
+ position: absolute;
4326
+ left: -20px;
4327
+ top: 50%;
4328
+ width: 14px;
4329
+ height: 14px;
4330
+ transform: translateY(-50%);
4331
+ color: var(--muted);
4332
+ opacity: 0;
4333
+ }
4334
+ a.api-self:hover svg,
4335
+ a.api-self:focus-visible svg {
4336
+ opacity: 1;
4337
+ }
4338
+ .api-op-call {
4339
+ display: flex;
4340
+ align-items: center;
4341
+ gap: 8px;
4342
+ margin: 6px 0 10px;
4343
+ }
4344
+ .api-op-call .api-method {
4345
+ padding: 0 6px;
4346
+ }
4347
+ /* Copy sits right after the path it copies, an icon alone, so the two read as one. */
4348
+ button.copy-btn.api-copy-path {
4349
+ padding: 3px 5px;
4350
+ }
4351
+ button.copy-btn.api-copy-path span:empty {
4352
+ display: none;
4353
+ }
4354
+ .api-op-body {
4355
+ display: grid;
4356
+ gap: 4px 28px;
4357
+ }
4358
+ .api-op-text,
4359
+ .api-op-code {
4360
+ min-width: 0;
4361
+ }
4362
+ @media (min-width: 1280px) {
4363
+ .api-op-body {
4364
+ max-width: none;
4365
+ /* The examples take what the window has beyond the prose's measure, never under 360px. */
4366
+ grid-template-columns: minmax(0, 720px) minmax(360px, 1fr);
4367
+ }
4368
+ .api-op-code {
4369
+ align-self: start;
4370
+ }
4371
+ .api-op-code > .block:first-child {
4372
+ margin-top: 0;
4373
+ }
4374
+ }
4375
+ /* The rail lists every route by name under its resource, so it reads as the page's contents. */
4376
+ .api-nav-ops {
4377
+ display: none;
4378
+ }
4379
+ .api-nav-ops.open,
4380
+ .api-nav.filtering .api-nav-ops {
4381
+ display: flex;
4382
+ flex-direction: column;
4383
+ }
4384
+ .api-nav a.api-nav-op {
4385
+ padding-left: 30px;
4386
+ font-size: 13px;
4387
+ }
4388
+ .api-nav-tier + .api-nav-ops a.api-nav-op {
4389
+ padding-left: 20px;
4390
+ }
4391
+ .api-object {
4392
+ border-bottom: 1px solid var(--line);
4393
+ /* Reached from a route's Returns, it lands just under the bar. */
4394
+ scroll-margin-top: calc(var(--bar-h) - 1px);
4395
+ }
4173
4396
  /* Reference room on mobile. The bar stays ONE row at --bar-h, the same as on desktop, so
4174
4397
  the in-page anchors (scroll-margin-top) and the scroll-spy line keep clearing it: the back
4175
4398
  link is a --bar-h square holding its arrow, the wordmark and the two tabs follow, and the
@@ -4284,11 +4507,13 @@ button.api-filter-clear svg {
4284
4507
  font-size: 13px;
4285
4508
  }
4286
4509
  /* The API rail is the tier chips alone on a phone: its resources need the column the
4287
- rail no longer has. A route row stacks its summary under its path. */
4510
+ rail no longer has. */
4288
4511
  .api-filter {
4289
4512
  max-width: none;
4290
4513
  }
4291
- .api-nav a.api-nav-res {
4514
+ .api-nav a.api-nav-res,
4515
+ .api-nav .api-nav-ops,
4516
+ .api-nav.filtering .api-nav-ops {
4292
4517
  display: none;
4293
4518
  }
4294
4519
  .api-nav a.api-nav-tier {
@@ -4297,25 +4522,6 @@ button.api-filter-clear svg {
4297
4522
  .api-nav a.api-nav-tier ~ a.api-nav-tier {
4298
4523
  margin-top: 0;
4299
4524
  }
4300
- .api-route-sum {
4301
- grid-template-columns: 62px minmax(0, 1fr) 20px;
4302
- gap: 4px 10px;
4303
- }
4304
- .api-route-line {
4305
- grid-column: 2;
4306
- grid-row: 2;
4307
- white-space: normal;
4308
- }
4309
- .api-chev {
4310
- grid-column: 3;
4311
- grid-row: 1;
4312
- }
4313
- .api-route[open] .api-route-line {
4314
- display: none;
4315
- }
4316
- .api-route-detail {
4317
- padding: 0 4px 14px;
4318
- }
4319
4525
  /* inline-flex centres the chip's text in its 32px; the gap stands in for the space
4320
4526
  between the label and its ↗ that flex drops. */
4321
4527
  .rail-link {
@@ -4734,12 +4940,22 @@ button.copied svg {
4734
4940
  font-weight: 600;
4735
4941
  color: var(--muted);
4736
4942
  }
4737
- .block pre {
4943
+ /* A code block (an example, a curl command, a template): a raised card with its label and Copy
4944
+ in a header inside it, so the code stands apart from the page in both themes. */
4945
+ .code-block {
4946
+ background: var(--card);
4947
+ border: 1px solid var(--line);
4948
+ border-radius: 8px;
4949
+ overflow: hidden;
4950
+ }
4951
+ .code-block > .block-head {
4952
+ margin: 0;
4953
+ padding: 4px 6px 4px 12px;
4954
+ border-bottom: 1px solid var(--line);
4955
+ }
4956
+ .code-block pre {
4738
4957
  margin: 0;
4739
4958
  overflow-x: auto;
4740
- background: var(--bg);
4741
- border: 1px solid var(--line);
4742
- border-radius: 6px;
4743
4959
  padding: 10px 12px;
4744
4960
  font: 12.5px / 1.5 var(--mono);
4745
4961
  }
@@ -6515,7 +6731,7 @@ body.has-savebar .content {
6515
6731
  .subs-wrap {
6516
6732
  container-type: inline-size;
6517
6733
  }
6518
- @container (max-width: 759px) {
6734
+ @container (max-width: 609px) {
6519
6735
  .list-table.subs-table.stacks,
6520
6736
  .list-table.subs-table.stacks tbody,
6521
6737
  .list-table.subs-table.stacks tr {
@@ -6556,10 +6772,7 @@ body.has-savebar .content {
6556
6772
  .list-table.subs-table.stacks td.empty {
6557
6773
  display: none;
6558
6774
  }
6559
- /* Subscribers: status pill · how they joined · joined <date>. */
6560
- .subs-table td.how::before {
6561
- content: " · ";
6562
- }
6775
+ /* Subscribers: status pill · joined <date>. */
6563
6776
  .subs-table td.joined::before {
6564
6777
  content: " · joined ";
6565
6778
  }
@@ -6687,8 +6900,10 @@ body.has-savebar .content {
6687
6900
  font-weight: 600;
6688
6901
  margin: 14px 0 4px;
6689
6902
  }
6690
- /* The same amber box the Schedule dialog gives D1's daily-writes warning (DESIGN §4). */
6691
- .hint.daily-writes.imp-warn {
6903
+ /* On Workers Free's limits, the advice to move to Workers Paid on a list past what Free suits,
6904
+ on the Dashboard, the Subscribers screen, and an import's plan (DESIGN §4): the same amber
6905
+ box the Schedule dialog gives D1's daily-writes warning. */
6906
+ .workers-plan {
6692
6907
  color: var(--warn-fg);
6693
6908
  background: var(--warn-bg);
6694
6909
  border: 1px solid var(--warn-line);
@@ -6729,9 +6944,73 @@ body.has-savebar .content {
6729
6944
  gap: 14px;
6730
6945
  margin-top: 18px;
6731
6946
  }
6732
- /* View subscriber's title is the address, so a long one breaks rather than widens the dialog. */
6947
+ /* A subscriber's page is titled with the address, so a long one breaks rather than widens
6948
+ the head; the status pill and the flag sit beside it, as the roster draws them. */
6733
6949
  .sub-title {
6734
6950
  overflow-wrap: anywhere;
6951
+ margin: 0;
6952
+ }
6953
+ .sub-page-title {
6954
+ display: flex;
6955
+ align-items: center;
6956
+ gap: 12px;
6957
+ flex-wrap: wrap;
6958
+ min-width: 0;
6959
+ }
6960
+ .sub-page-title .sub-pills {
6961
+ white-space: nowrap;
6962
+ }
6963
+ /* Whether they receive posts: the page's first answer, under the head, in the record's
6964
+ meta-line place; muted, with a green dot only when they do, as a confirmed pill is. */
6965
+ .sub-receives {
6966
+ margin: -6px 0 14px;
6967
+ font-size: 13.5px;
6968
+ color: var(--muted);
6969
+ }
6970
+ .sub-receives.yes::before {
6971
+ content: "";
6972
+ display: inline-block;
6973
+ width: 8px;
6974
+ height: 8px;
6975
+ margin-right: 7px;
6976
+ border-radius: 50%;
6977
+ background: var(--ok-fg);
6978
+ }
6979
+ /* The import note: the card's own small print, outside a dialog's hint styles. */
6980
+ .sub-note {
6981
+ margin: 10px 0 0;
6982
+ font-size: 12.5px;
6983
+ color: var(--muted);
6984
+ }
6985
+ .sub-sec .sub-meaning {
6986
+ margin: 8px 0;
6987
+ font-size: 13.5px;
6988
+ }
6989
+ .sub-sec p:last-child {
6990
+ margin-bottom: 0;
6991
+ }
6992
+ .sub-sup-line,
6993
+ .sub-left-line {
6994
+ margin: 0;
6995
+ }
6996
+ /* Sends is a table, and a table is already a card, so its section is no card of its own:
6997
+ the label sits on the page above it, as the dashboard's sections do. */
6998
+ .sub-sends {
6999
+ margin-top: 22px;
7000
+ }
7001
+ .sub-sends-table .c-out {
7002
+ width: 160px;
7003
+ }
7004
+ .sub-sends-table .sub-send a {
7005
+ font-weight: 500;
7006
+ }
7007
+ .sub-more {
7008
+ margin-top: 12px;
7009
+ }
7010
+ /* The roster's address is its row's link, as a Sent row's subject is, in the text's color
7011
+ since the whole row is the click target. */
7012
+ .subs-table a.sub-email {
7013
+ color: inherit;
6735
7014
  }
6736
7015
  .imp-dl,
6737
7016
  .sub-dl {