◊(Local Yarn Code "Check-in [e52e53c8]")

Overview
Comment:Merge updates from trunk
Timelines: family | ancestors | descendants | both | doc-expansion
Files: files | file ages | folders
SHA3-256: e52e53c80aeef022515d121a19b92ec91bc79ba580e15ff1a080b727287a73ea
User & Date: joel on 2020-02-10 21:15:32
Other Links: branch diff | manifest | tags
Context
2020-02-12
21:12
Don’t clobber the footer when rebuilding code docs check-in: c8354d62 user: joel tags: doc-expansion
2020-02-10
21:15
Merge updates from trunk check-in: e52e53c8 user: joel tags: doc-expansion
17:28
Improved footer check-in: 75502f96 user: joel tags: trunk
2020-01-19
20:49
scribble edits check-in: ead156b1 user: joel tags: doc-expansion
Changes

Modified blog.rkt from [1dda01dc] to [75b9d900].

1
2
3
4
5
6
7
8
9

10

11
12
13
14
15
16
17
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18








-
+

+







#lang pollen/mode racket/base

; SPDX-License-Identifier: BlueOak-1.0.0
; This file is licensed under the Blue Oak Model License 1.0.0.

;; Builds the paginated “blog” HTML files (blog-pg1.html ...) from the SQLite cache
;; The files will be written out every time this module is evaluated! (see end)

(require "crystalize.rkt"
(require "cache.rkt"
         "snippets-html.rkt"
         "dust.rkt"
         racket/file
         sugar/list)

(provide main)

;; How many items per blog page
(define per-page 5)
34
35
36
37
38
39
40

41
42
43
44
45
46
47
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49







+







 <nav id="bottom-nav"><ul>◊|page-nav|</ul></nav>

 ◊html$-page-body-close[]
 </html>})

;; Grabs all the articles+notes from the cache and writes out all the blog page files
(define (build-blog)
  (listing-context 'blog) ; honor conceal directives for the blog
  (define arts-n-notes (slice-at (listing-htmls (articles+notes 'full #:series #f)) per-page))
  (define pagecount (length arts-n-notes))
  
  (for ([pagenum (in-range 1 (+ 1 pagecount))]
        [page    (in-list arts-n-notes)])
    (define filename (format "blog-pg~a.html" pagenum))
    (displayln (format "Writing: ~a" filename))

Added cache.rkt version [42c939a6].






















































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#lang racket/base

; SPDX-License-Identifier: BlueOak-1.0.0
; This file is licensed under the Blue Oak Model License 1.0.0.

(require deta
         db/base
         db/sqlite3
         threading
         pollen/setup
         racket/match
         (rename-in racket/list
                    (group-by group-list-by))
         "dust.rkt"
         (except-in pollen/core select))

(provide init-cache-db!
         cache-conn                     ; The most eligible bachelor in Neo Yokyo
         (schema-out cache:article)
         (schema-out cache:note)
         (schema-out cache:series)
         (schema-out cache:index-entry)
         delete-article!
         delete-notes!
         current-plain-title
         articles
         articles+notes
         listing-htmls
         <listing-full>
         <listing-excerpt>
         <listing-short>
         unfence
         series-grouped-list)

;; Cache DB and Schemas

(define DBFILE (build-path (current-project-root) "vitreous.sqlite"))
(define cache-conn (sqlite3-connect #:database DBFILE #:mode 'create))

(define current-plain-title (make-parameter "void"))

(define-schema cache:article #:table "articles"
  ([id                   id/f #:primary-key #:auto-increment]
   [page                 symbol/f]
   [title-plain          string/f]
   [title-html-flow      string/f]
   [title-specified?     boolean/f]
   [published            string/f]
   [updated              string/f]
   [author               string/f]
   [conceal              string/f]
   [series-page          symbol/f]
   [noun-singular        string/f]
   [note-count           integer/f]
   [doc-html             string/f]
   [disposition          string/f]
   [disp-html-anchor     string/f]
   [listing-full-html    string/f]   ; full content but without notes
   [listing-excerpt-html string/f]   ; Not used for now
   [listing-short-html   string/f])) ; Date and title only

(define-schema cache:note #:table "notes"
  ([id                   id/f #:primary-key #:auto-increment]
   [page                 symbol/f]
   [html-anchor          string/f]
   [title-html-flow      string/f] ; No block-level HTML elements
   [title-plain          string/f]
   [author               string/f]
   [author-url           string/f]
   [published            string/f]
   [disposition          string/f]
   [content-html         string/f]
   [series-page          symbol/f]
   [conceal              string/f]
   [listing-full-html    string/f]
   [listing-excerpt-html string/f]   ; Not used for now
   [listing-short-html   string/f])) ; Date and title only

(define-schema cache:series #:table "series"
  ([id            id/f #:primary-key #:auto-increment]
   [page          symbol/f]
   [title         string/f]
   [published     string/f]
   [noun-plural   string/f]
   [noun-singular string/f]))

(define-schema cache:index-entry #:table "index_entries"
  ([id          id/f #:primary-key #:auto-increment]
   [entry       string/f]
   [subentry    string/f]
   [page        symbol/f]
   [html-anchor string/f]))

(define-schema listing
  #:virtual
  ([html        string/f]
   [published   date/f]
   [series-page symbol/f]))

(define (init-cache-db!)
  (create-table! cache-conn 'cache:article)
  (create-table! cache-conn 'cache:note)
  (create-table! cache-conn 'cache:series)
  (create-table! cache-conn 'cache:index-entry))

(define (delete-article! page)
  (query-exec cache-conn
              (~> (from cache:article #:as a)
                  (where (= a.page ,(format "~a" page)))
                  delete)))

(define (delete-notes! page)
  (query-exec cache-conn
              (~> (from cache:note #:as n)
                  (where (= n.page ,(format "~a" page)))
                  delete)))

;;
;;  ~~~ Fetching articles and notes ~~~
;;

;; (Private use) Conveniece function for the WHERE `series-page` clause
(define (where-series q s)
  (define (s->p x) (format "~a/~a.html" series-folder x))
  (match s
    [(list series ...)
     (where q (in a.series-page ,(map s->p series)))] ; WHERE series-page IN (item1 ...)
    [(or (? string? series) (? symbol? series))
     (where q (= a.series-page ,(s->p series)))]      ; WHERE series-page = "item"
    [#t
     (where q (= a.series-page ,(path->string (here-output-path))))]
    [_ q]))

;; (Private use) Convenience for the WHERE `conceal` NOT LIKE clause
(define (where-not-concealed q)
  (define base-clause (where q (not (like a.conceal "%all%"))))
  (match (listing-context)
    ["" base-clause]
    [(var context) (where base-clause (not (like a.conceal ,(format "%~a%" context))))]))

;; Needed to "parameterize" column names
;; see https://github.com/Bogdanp/deta/issues/14#issuecomment-573344928
(require (prefix-in ast: deta/private/ast))

;; Builds a query to fetch articles
(define (articles type #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  (define html-field (format "listing_~a_html" type))
  (~> (from cache:article #:as a)
      (select (fragment (ast:as (ast:qualified "a" html-field) "html"))
              a.published
              a.series-page
              a.conceal)
      (where-series s)
      (where-not-concealed)
      (limit ,lim)
      (order-by ([a.published ,ord]))
      (project-onto listing-schema)))

;; Builds a query that returns articles and notes intermingled chronologically
(define (articles+notes type #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  (define html-field (format "listing_~a_html" type))
  (~> (from (subquery
             (~> (from cache:article #:as A)
                 (select (fragment (ast:as (ast:qualified "A" html-field) "html"))
                         A.published
                         A.series-page
                         A.conceal)
                 (union
                  (~> (from cache:note #:as N)
                      (select (fragment (ast:as (ast:qualified "N" html-field) "html"))
                              N.published
                              N.series-page
                              N.conceal)))))
            #:as a)
      (where-series s)
      (where-not-concealed)
      (limit ,lim)
      (order-by ([a.published ,ord]))
      (project-onto listing-schema)))

;; Get all the a list of the HTML all the results in a query
(define (listing-htmls list-query)
  (for/list ([l (in-entities cache-conn list-query)])
    (listing-html l)))

;; Return cached HTML of articles and/or notes, fenced within a style txexpr to prevent it being
;; escaped by ->html. See also: definition of `unfence`

;; E.g.: (<listing-full> articles+notes)
(define (<listing-full> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style ,@(listing-htmls (query-func 'full #:series s #:limit lim #:order ord))))
;;                                     ^^^^^

(define (<listing-excerpt> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style ,@(listing-htmls (query-func 'excerpt #:series s #:limit lim #:order ord))))
;;                                     ^^^^^^^^

(define (<listing-short> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style "<ul class=\"article-list\">"
          ,@(listing-htmls (query-func 'short #:series s #:limit lim #:order ord))
          "</ul>")) ;;                 ^^^^^^

;; Remove "<style>" and "</style>" introduced by using ->html on docs containing output from
;; listing functions
(define (unfence html-str)
  (regexp-replace* #px"<[\\/]{0,1}style>" html-str ""))

;;
;;  ~~~ Fetching series ~~~
;;
(define (series-grouped-list)
  (~> (for/list ([row (in-entities cache-conn (from cache:series #:as s))]) row)
      (group-list-by cache:series-noun-plural _ string-ci=?)))

Modified crystalize.rkt from [cb633ea2] to [c41782ea].

1
2
3
4
5
6
7
8





9
10

11
12
13
14
15



16
17
18
19
20


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
1
2
3
4
5



6
7
8
9
10
11

12
13
14



15
16
17
18




19
20












21






































































22
23

24
25
26
27
28
29
30





-
-
-
+
+
+
+
+

-
+


-
-
-
+
+
+

-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


-







#lang racket/base

; SPDX-License-Identifier: BlueOak-1.0.0
; This file is licensed under the Blue Oak Model License 1.0.0.

(require deta db/base db/sqlite3 threading txexpr gregor)

(require racket/match
(require deta
         db/base
         db/sqlite3
         threading
         racket/match
         racket/string
         pollen/pagetree
         txexpr
         pollen/template
         (except-in pollen/core select) ; avoid conflict with deta
         pollen/setup)

(require "dust.rkt" "snippets-html.rkt")
)

(require "dust.rkt" "cache.rkt" "snippets-html.rkt")

(provide init-cache-db!
         cache-conn            ; The most eligible bachelor in Neo Yokyo
         parse-and-cache-article!
         cache-series!
(provide parse-and-cache-article!
         cache-series!)
         current-plain-title
         (schema-out cache:article)
         (schema-out cache:note)
         (schema-out cache:series)
         (schema-out cache:index-entry)
         articles
         articles+notes
         listing-htmls
         <listing-full>
         <listing-excerpt>
         <listing-short>
         unfence)

;; Cache DB and Schemas

(define DBFILE (build-path (current-project-root) "vitreous.sqlite"))
(define cache-conn (sqlite3-connect #:database DBFILE #:mode 'create))

(define current-plain-title (make-parameter "void"))

(define-schema cache:article #:table "articles"
  ([id                   id/f #:primary-key #:auto-increment]
   [page                 symbol/f]
   [title-plain          string/f]
   [title-html-flow      string/f]
   [title-specified?     boolean/f]
   [published            string/f]
   [updated              string/f]
   [author               string/f]
   [conceal              string/f]
   [series-page          symbol/f]
   [noun-singular        string/f]
   [note-count           integer/f]
   [doc-html             string/f]
   [disposition          string/f]
   [disp-html-anchor     string/f]
   [listing-full-html    string/f]   ; full content but without notes
   [listing-excerpt-html string/f]   ; Not used for now
   [listing-short-html   string/f])) ; Date and title only

(define-schema cache:note #:table "notes"
  ([id                   id/f #:primary-key #:auto-increment]
   [page                 symbol/f]
   [html-anchor          string/f]
   [title-html-flow      string/f] ; No block-level HTML elements
   [title-plain          string/f]
   [author               string/f]
   [author-url           string/f]
   [published            string/f]
   [disposition          string/f]
   [content-html         string/f]
   [series-page          symbol/f]
   [listing-full-html    string/f]
   [listing-excerpt-html string/f]   ; Not used for now
   [listing-short-html   string/f])) ; Date and title only

(define-schema cache:series #:table "series"
  ([id            id/f #:primary-key #:auto-increment]
   [page          symbol/f]
   [title         string/f]
   [published     string/f]
   [noun-plural   string/f]
   [noun-singular string/f]))

(define-schema cache:index-entry #:table "index_entries"
  ([id          id/f #:primary-key #:auto-increment]
   [entry       string/f]
   [subentry    string/f]
   [page        symbol/f]
   [html-anchor string/f]))

(define-schema listing
  #:virtual
  ([html        string/f]
   [published   date/f]
   [series-page symbol/f]))

(define (init-cache-db!)
  (create-table! cache-conn 'cache:article)
  (create-table! cache-conn 'cache:note)
  (create-table! cache-conn 'cache:series)
  (create-table! cache-conn 'cache:index-entry))

;; Save an article and its notes (if any) to the database, and return the
;; rendered HTML of the complete article.
;;
(define (parse-and-cache-article! pagenode doc)
  (define-values (doc-no-title maybe-title)
    (splitf-txexpr doc (make-tag-predicate 'title)))
  (define-values (body-txpr note-txprs)
    (splitf-txexpr doc-no-title (make-tag-predicate 'note)))
  (define-values (disposition disp-note-id)
    (notes->last-disposition-values note-txprs))
127
128
129
130
131
132
133

134
135
136
137
138
139
140
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58







+







                                               disp-note-id
                                               (length note-txprs))]
         [footer (html$-article-close footertext)]
         [listing-short (html$-article-listing-short pagenode pubdate title-html)]
         [notes-section-html (cache-notes! pagenode title-plain note-txprs)])
    (cache-index-entries! pagenode doc) ; note original doc is used here
    (current-plain-title title-plain)
    (delete-article! pagenode)
    (insert-one! cache-conn
                 (make-cache:article
                  #:page pagenode
                  #:title-plain title-plain
                  #:title-html-flow title-html
                  #:title-specified? title-specified?
                  #:published pubdate
253
254
255
256
257
258
259

260
261
262
263
264
265
266
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185







+







                  #:title-html-flow title-html
                  #:title-plain (tx-strs title-tx)
                  #:published note-date
                  #:author author
                  #:author-url author-url
                  #:disposition disposition-attr
                  #:series-page (metas-series-pagenode)
                  #:conceal (or (maybe-attr 'conceal attrs #f) (maybe-meta 'conceal))
                  #:content-html content-html
                  #:listing-full-html (html$-note-listing-full pagenode
                                                               note-id
                                                               title-html
                                                               note-date
                                                               content-html
                                                               author
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
228
229
230
231
232
233
234













































































235
236
237
238
239
240
241







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







                                     (where (= entry.page ,(symbol->string pagenode))))))
  (unless (null? entry-txs)
    (void
     (apply insert! cache-conn
            (for/list ([etx (in-list entry-txs)])
              (txexpr->index-entry etx pagenode))))))

;;
;;  ~~~ Fetching articles and notes ~~~
;;

;; (Private use) Conveniece function for the WHERE `series-page` clause
(define (where-series q s)
  (define (s->p x) (format "~a/~a.html" series-folder x))
  (match s
    [(list series ...)
     (where q (in a.series-page ,(map s->p series)))] ; WHERE series-page IN (item1 ...)
    [(or (? string? series) (? symbol? series))
     (where q (= a.series-page ,(s->p series)))]      ; WHERE series-page = "item"
    [#t
     (where q (= a.series-page ,(path->string (here-output-path))))]
    [_ q]))

;; Needed to "parameterize" column names
;; see https://github.com/Bogdanp/deta/issues/14#issuecomment-573344928
(require (prefix-in ast: deta/private/ast))

;; Builds a query to fetch articles
(define (articles type #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  (define html-field (format "listing_~a_html" type))
  (~> (from cache:article #:as a)
      (select (fragment (ast:as (ast:qualified "a" html-field) "html"))
              a.published
              a.series-page)
      (where-series s)
      (limit ,lim)
      (order-by ([a.published ,ord]))
      (project-onto listing-schema)))

;; Builds a query that returns articles and notes intermingled chronologically
(define (articles+notes type #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  (define html-field (format "listing_~a_html" type))
  (~> (from (subquery
             (~> (from cache:article #:as A)
                 (select (fragment (ast:as (ast:qualified "A" html-field) "html"))
                         A.published
                         A.series-page)
                 (union
                  (~> (from cache:note #:as N)
                      (select (fragment (ast:as (ast:qualified "N" html-field) "html"))
                              N.published
                              N.series-page)))))
            #:as a)
      (where-series s)
      (limit ,lim)
      (order-by ([a.published ,ord]))
      (project-onto listing-schema)))

;; Get all the a list of the HTML all the results in a query
(define (listing-htmls list-query)
  (for/list ([l (in-entities cache-conn list-query)])
    (listing-html l)))

;; Return cached HTML of articles and/or notes, fenced within a style txexpr to prevent it being
;; escaped by ->html. See also: definition of `unfence`

;; E.g.: (<listing-full> articles+notes)
(define (<listing-full> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style ,@(listing-htmls (query-func 'full #:series s #:limit lim #:order ord))))
;;                                     ^^^^^

(define (<listing-excerpt> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style ,@(listing-htmls (query-func 'excerpt #:series s #:limit lim #:order ord))))
;;                                     ^^^^^^^^

(define (<listing-short> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style "<ul class=\"article-list\">"
          ,@(listing-htmls (query-func 'short #:series s #:limit lim #:order ord))
          "</ul>")) ;;                 ^^^^^^

;; Remove "<style>" and "</style>" introduced by using ->html on docs containing output from
;; listing functions
(define (unfence html-str)
  (regexp-replace* #px"<[\\/]{0,1}style>" html-str ""))

;; Save the current article to the `series` table of the SQLite cache
;; Should be called from a template for series pages
(define (cache-series!)
  (define here-page (path->string (here-output-path)))
  (query-exec cache-conn
              (delete (~> (from cache:series #:as s)

Modified dust.rkt from [c096eb0e] to [dffdbbe2].

17
18
19
20
21
22
23

24
25
26
27
28
29
30
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31







+








;; Provides common helper functions used throughout the project

(provide maybe-meta     ; Select from (current-metas) or default value ("") if not available
         maybe-attr     ; Return an attribute’s value or a default ("") if not available
         here-output-path
         here-id
         listing-context
         series-metas-noun    ; Retrieve noun-singular from current 'series meta, or ""
         series-metas-title   ; Retrieve title of series in current 'series meta, or ""
         metas-series-pagenode
         invalidate-series
         make-tag-predicate
         tx-strs
         ymd->english
62
63
64
65
66
67
68


69
70
71
72
73
74
75
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78







+
+







(define (here-output-path)
  (cond [(current-metas)
         (define-values (_ rel-path-parts)
           (drop-common-prefix (explode-path (current-project-root))
                               (explode-path (string->path (select-from-metas 'here-path (current-metas))))))
         (->output-path (apply build-path rel-path-parts))]
        [else (string->path ".")]))

(define listing-context (make-parameter ""))

;; Checks current-metas for a 'series meta and returns the pagenode of that series,
;; or '|| if no series is specified.
(define (metas-series-pagenode)
  (define maybe-series (or (select-from-metas 'series (current-metas)) ""))
  (cond
    [(non-empty-string? maybe-series)

Modified index.html.pp from [6ce20187] to [511f6fe5].

1
2
3
4
5
6
7


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1
2
3
4
5


6
7



















8
9
10
11
12
13
14





-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







#lang pollen

◊; SPDX-License-Identifier: BlueOak-1.0.0
◊; This file is licensed under the Blue Oak Model License 1.0.0.

◊(require pollen/template db/base racket/list racket/match)

◊(require pollen/template racket/file)
            
◊(define (fetch-series)
  (define q "SELECT noun_plural, page, title FROM series ORDER BY noun_plural DESC")
  (query-rows cache-conn q))

◊(define (series-item->txpr s)
  (match-define (list n pagenode title) s)
  `(li (a [[href ,pagenode]] (i ,title))))

◊(define (series-grouped-list)
  ;; Produces '((("noun1" "p.html" "Title") ("noun1" "q.html" "Title")) (("noun2" ...) ...))
  (define init-group
    (group-by first (map vector->list (fetch-series)) string-ci=?))
  
  (define series-list-items
    (for/list ([group (in-list init-group)])
      `(div (h2 ,(first (first group))) (ul ,@(map series-item->txpr group)))))
  `(section [[class "series-list"] [style "margin-top: 1.3rem"]] ,@series-list-items))
            

<!DOCTYPE html>
<html lang="en">
◊html$-page-head["The Local Yarn" #f]
<style>
  header#front-page {
    text-align: center;
  }
51
52
53
54
55
56
57
58


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56







-
+
+









-







  @keyframes slideFromRight {
    0%   { margin-right: -200%; }
    100% { margin-right: 0%; }
  }

  #front-page-logo {
    shape-outside: url(web-extra/mark.svg);
    filter: brightness(0) saturate(100%) invert(20%) sepia(16%) saturate(903%) hue-rotate(153deg) brightness(93%) contrast(90%);
    /* filter: brightness(0) saturate(100%) invert(20%) sepia(16%) saturate(903%) hue-rotate(153deg)
     * brightness(93%) contrast(90%); */
    shape-margin: 1rem;
    float: left;
    margin-left: -20vw;
    margin-right: 1rem;
    z-index: -100;
  }

  main {
    background: transparent;
    font-size: 1.3em;
    line-height: 1.2em;
  }
</style>
</head>

<body style="overflow-x:hidden; hyphens: auto">
 <header id="front-page"><div>
85
86
87
88
89
90
91

92
93
94
95

96
97
98
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80







+



-
+



(almost) is ◊link[1]{arranged in time order, newest first}. There are also a few arranged into named
collections:

◊url[1]{/blog-pg1.html}

}) 
◊; stop for now: (crystalize-index-entries! '|index.html| front-page-body)
◊(display-to-file (html$-page-footer) "scribbled/site-footer.html" #:exists 'replace)

<main> 
  ◊(->html front-page-body #:splice? #t)
  ◊(->html (series-grouped-list))
  ◊(html$-series-list)
</main> 
</body>
</html>

Modified keyword-index.rkt from [50258609] to [d37c4752].

10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24







-
+







         racket/list
         racket/file
         racket/string
         db/base
         net/uri-codec
         pollen/template)

(require "crystalize.rkt"
(require "cache.rkt"
         "dust.rkt"
         "snippets-html.rkt")

(provide main)

;; Terminology (because these things get confusing fast)
;;

Modified makefile from [f9d9c092] to [9872e7c1].

1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17









-
+







# SPDX-License-Identifier: BlueOak-1.0.0
# This file is licensed under the Blue Oak Model License 1.0.0.

SHELL = /bin/bash

# ~~~ Variables used by rules ~~~
#

core-files := pollen.rkt dust.rkt
html-deps  := snippets-html.rkt tags-html.rkt crystalize.rkt
html-deps  := snippets-html.rkt tags-html.rkt crystalize.rkt cache.rkt

article-sources := $(wildcard articles/*.poly.pm)
articles-html   := $(patsubst %.poly.pm, %.html, $(article-sources))
articles-pdf    := $(patsubst %.poly.pm, %.pdf,  $(article-sources))

series-sources  := $(wildcard series/*.poly.pm)
series-html     := $(patsubst %.poly.pm, %.html, $(series-sources))
29
30
31
32
33
34
35
36
37


38
39
40

41
42
43
44

45
46
47
48
49
50

51
52
53

54
55
56
57
58
59

60
61
62
63

64
65
66
67
68
69
70
29
30
31
32
33
34
35


36
37
38
39

40
41
42
43

44
45
46
47
48
49

50
51
52
53
54
55
56
57
58
59

60
61
62
63

64
65
66
67
68
69
70
71







-
-
+
+


-
+



-
+





-
+



+





-
+



-
+







# the Pollen cache watchlist (see pollen.rkt)
vitreous.sqlite: $(core-files) $(html-deps) template.html.p
	raco pollen setup -p articles/
	raco pollen render -p -t html articles/*.poly.pm
	raco pollen setup -p series/
	raco pollen render -p -t html series/*.poly.pm
	rm -f template.html series/template.html
	# tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no articles/*.html || true
	# tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no series/*.html || true
	tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no articles/*.html || true
	tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no series/*.html || true

# If the rule for vitreous.sqlite was triggered, all the article HTML files will already have been
# re-rendered.  (That rule comes before this one in the list of dependencies for "all") But if not,
# re-rendered.  (That rule comes before this one in the list of dependencies for "web") But if not,
# any individual files that have been edited will get re-rendered.
$(articles-html): %.html: %.poly.pm 
	raco pollen render $@
	# tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no $@ || true
	tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no $@ || true

# Note that if any article is part of a series, it will touch its series .poly.pm file during its
# render, triggering this rule for that series.
$(series-html): %.html: %.poly.pm
	raco pollen render $@
	# tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no $@ || true
	tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no $@ || true

index.html: $(core-files) $(html-deps) $(series-html) index.html.pp
	raco pollen render index.html
	fossil uv add scribbled/site-footer.html

# This target will also rebuild pg2, pg3, etc. as needed
blog-pg1.html: $(core-files) $(html-deps) $(articles-html) blog.rkt
	rm -f blog*.html
	racket -tm blog.rkt
	# tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no blog*.html || true
	tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no blog*.html || true

keyword-index.html: $(core-files) $(html-deps) $(articles-html) keyword-index.rkt
	racket -tm keyword-index.rkt
	# tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no $@ || true
	tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no $@ || true

web-extra/martin.css: web-extra/martin.css.pp
	raco pollen render $@

feed.xml: vitreous.sqlite rss-feed.rkt
	racket -tm rss-feed.rkt

90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124
125
126
127







+


















-
+










		--exclude=.fslckout \
		--exclude='*.ltx' \
		--exclude='*.swp' \
		--exclude=.DS_Store \
	    --exclude='template*.*' \
		--exclude=makefile 
	rm -rf ~/Desktop/publish
	fossil uv sync

scribble: ## Rebuild code documentation and update Fossil repo
	scribble --htmls +m --redirect https://docs.racket-lang.org/local-redirect/ code-docs/main.scrbl
	fossil uv rm scribbled/*
	rm -rf scribbled/*
	mv main/* scribbled/
	cp code-docs/scribble-iframe.html scribbled/scribble.html
	rm -rf main
	fossil uv add scribbled/*
	fossil uv sync

# Self-documenting makefile (http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html)
help: ## Displays this help screen
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'

article: ## Start a new article from a template
	racket -tm util/newpost.rkt

.PHONY: all scribble help zap article publish check-env
.PHONY: web scribble help zap article publish check-env

.DEFAULT_GOAL := help

check-env:
ifndef LOCALYARN_SRV
	$(error LOCALYARN_SRV env variable not set, should be a destination valid for rsync)
endif
ifndef WEB_SRV_PORT
	$(error WEB_SRV_PORT env variable not set, should be SSH port number for web server)
endif

Modified pollen.rkt from [ec6e30b5] to [5f7205ca].

9
10
11
12
13
14
15

16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34

35
36
37
38
39

40
41
42
43
44
45
46
9
10
11
12
13
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49







+





-
+













+





+







                     racket/syntax
                     syntax/parse
                     pollen/setup))

(require pollen/tag
         pollen/setup
         racket/function
         "cache.rkt"
         "tags-html.rkt"
         "snippets-html.rkt"
         "crystalize.rkt")

(provide (all-defined-out)
         (all-from-out "crystalize.rkt" "snippets-html.rkt"))
         (all-from-out "crystalize.rkt" "snippets-html.rkt" "cache.rkt"))

(module setup racket/base
  (require syntax/modresolve
           racket/runtime-path
           pollen/setup)
  (provide (all-defined-out))
  (define poly-targets '(html))
  (define block-tags (append '(title style dt note) default-block-tags))

  (define-runtime-path tags-html.rkt     "tags-html.rkt")
  (define-runtime-path snippets-html.rkt "snippets-html.rkt")
  (define-runtime-path dust.rkt          "dust.rkt")
  (define-runtime-path crystalize.rkt    "crystalize.rkt")
  (define-runtime-path cache.rkt         "cache.rkt")
  (define cache-watchlist
    (map resolve-module-path
         (list tags-html.rkt
               snippets-html.rkt
               dust.rkt
               cache.rkt
               crystalize.rkt))))

(case (current-poly-target)
  [(html) (init-cache-db!)])

;; Macro for defining tag functions that automatically branch based on the 
;; current output format and the list of poly-targets in the setup module.

Modified rss-feed.rkt from [9efceb86] to [502871b7].

8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22







-
+







(require txexpr
         racket/match
         racket/file
         racket/date
         racket/string
         db/base
         "dust.rkt"
         "crystalize.rkt")
         "cache.rkt")

(provide main)

(define feed-author default-authorname)
(define feed-author-email "joel@jdueck.net")
(define feed-title "The Local Yarn (Beta)")
(define feed-site-url "https://thelocalyarn.com")
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72







-
+







-
+







     SELECT `path`, `title`, `published`, `updated`, `author`, `entry_contents` FROM
       (SELECT `page` AS `path`,
               `title_plain` AS `title`,
               `published`,
               `updated`,
               `author`,
               `doc_html` AS `entry_contents`
        FROM `articles`
        FROM `articles` WHERE (NOT (`conceal` LIKE "%all%")) AND (NOT (`conceal` LIKE "%feed%"))
        UNION
        SELECT `page` || '#' || `html_anchor` AS `path`,
               `title_plain` AS `title`,
               `published`,
               "" AS `updated`,
               `author`,
               `content_html` as `entry_contents`
        FROM `notes`)
        FROM `notes` WHERE (NOT (`conceal` LIKE "%all%")) AND (NOT (`conceal` LIKE "%feed%")))
        ORDER BY `published` DESC LIMIT ~a
---
    )
  (query-rows cache-conn (format select feed-item-limit)))

(define (vector->rss-item vec)
  (match-define

Modified snippets-html.rkt from [9386bc7c] to [f524862a].

9
10
11
12
13
14
15

16
17
18
19

20
21
22

23
24
25
26
27
28
29
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32







+




+



+







         pollen/decode
         pollen/private/version
         racket/string
         racket/function
         racket/list
         txexpr
         openssl/sha1
         "cache.rkt"
         "dust.rkt")

(provide html$-page-head
         html$-page-body-open
         html$-series-list
         html$-article-open
         html$-article-close
         html$-article-listing-short
         html$-page-footer
         html$-page-body-close
         html$-note-contents
         html$-note-listing-full
         html$-note-in-article
         html$-notes-section
         html$-paginate-navlinks)

69
70
71
72
73
74
75
76












77
78
79




80
81
82
83
84
85
86
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92


93
94
95
96
97
98
99
100
101
102
103








+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+








(define (html$-article-listing-short pagenode pubdate title)
  ◊string-append{
 <li><a href="/◊(symbol->string pagenode)">
 <div class="article-list-date caps">◊(ymd->english pubdate)</div>
 <div class="article-list-title">◊|title|</div>
 </a></li>})

(define (html$-page-footer)
  ◊string-append{
<footer id="main">
 <p class="title">The Local Yarn</p>
 <nav><a href="/">Home</a> •
    <a href="/blog-pg1.html">Blog</a> •
    <a href="/keyword-index.html">Keyword Index</a> •
    <a href="/code"><i><code>◊"◊"(Source&nbsp;Code)</code></i></a>
 </nav>
 ◊(html$-series-list)
 </footer>})

(define (html$-page-body-close)
  ◊string-append{<footer>By Joel Dueck</footer>
 </main></body>})
  ◊string-append{
 </main>
 ◊(html$-page-footer)
 </body>})

;; Notes
;;
(define (html$-note-contents disposition-mark disposition-verb elems)
  (define disposition
    (cond [(non-empty-string? disposition-mark)
           `(abbr [[class "disposition-mark-in-note"]
194
195
196
197
198
199
200










211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227







+
+
+
+
+
+
+
+
+
+

  (define next-link
    (if (eq? pagecount pagenum)
        "<li class=\"nav-text inactive-link\">Older&rarr;</li>"
        (page-func (+ pagenum 1) "Older&thinsp;&rarr;" "nav-text")))

  (string-join `(,prev-link ,@page-group ,next-link)))

(define (series->txpr s)
  `(li (a [[href ,(symbol->string (cache:series-page s))]]
          (i ,(cache:series-title s)))))

(define (html$-series-list)
  (define series-list-items
    (for/list ([group (in-list (series-grouped-list))])
      `(div (h2 ,(cache:series-noun-plural (first group))) (ul ,@(map series->txpr group)))))
  (->html `(section [[class "column-list"] [style "margin-top: 1.3rem"]] ,@series-list-items)))

Modified util/newpost.rkt from [89e8b78f] to [d7b70e83].

31
32
33
34
35
36
37

38

39
40
41
42
43
44
45
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46







+
-
+







(define (make-template-contents title)
  ◊string-append{
 #lang pollen

 ◊comment{Copyright ◊(substring date-string 0 4) by ◊|default-authorname|. All Rights Reserved.}

 ◊"◊"(define-meta published "◊date-string")
 ◊"◊"(define-meta conceal   "blog,feed")  ; Edit/delete this line when ready to publish
 ◊"◊"(define-meta series "seriesname")
 ◊"◊;"(define-meta series "seriesname")

 ◊"◊"title{◊title}

 Write here!})

(define (main)
  (display "Enter title: ")

Modified web-extra/font.css from [fb953054] to [81f7d56c].

18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42






18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48







-
+

















+
+
+
+
+
+
  font-weight:  400;
}

@font-face {
  font-family: 'Fabiol';
  src: url('LDFabiolPro-Bold.woff2') format('woff2'),
       url('LDFabiolPro-Bold.woff') format('woff');
  font-style:   bold;
  font-style:   normal;
  font-weight:  700;
}

@font-face {
  font-family: 'Triplicate T4c';
  src: url('Triplicate-T4c.woff') format('woff');
  font-style: normal;
  font-weight: 400;
  }

@font-face {
  font-family: 'Triplicate T4c';
  src: url('Triplicate-T4c-italic.woff') format('woff');
  font-style: italic;
  font-weight: 400;
  }

@font-face {
  font-family: 'Triplicate T4c';
  src: url('Triplicate-T4c-bold.woff') format('woff');
  font-style: normal;
  font-weight: bold;
  }

Modified web-extra/martin.css.pp from [62e09f38] to [6da40fde].

25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42

43
44
45
46

47
48
49
50
51
52
53
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41

42
43
44
45

46
47
48
49
50
51
52
53







-
+









-
+



-
+








/* Let us first address the matter of font size in different screen sizes. */

/* Mobile portrait screens will see a minimum 18px font. */
html { font-size: 22px; } 

/* Start increasing type size dynamically at screen widths of 768px */
@media only screen and (min-width: 768px) { 
@media (min-width: 768px) { 
    html { font-size: 2.8vw; } 
}

/* Top out at 23px for screens up to 800px TALL */
◊; @media only screen and (min-width: 1000px) and (max-height: 800px) {
◊;    html { font-size: 26px; } /* =  2.6% of 1000px (min-width) */
◊; }

/* Top out at 28px for screens 801px-1000px TALL */
@media only screen and (min-width: 1000px) and (max-height: 920px) { 
@media (min-width: 1000px) and (max-height: 920px) { 
   html { font-size: 28px; }     /* =  2.8% of 1000px (min-width) */
}
/* For screens taller than 1000 px, top out at 32px */
@media only screen and (min-width: 1178px) and (min-height: 921px) {
@media (min-width: 1178px) and (min-height: 921px) {
    html { font-size: 33px; }    /* =  2.8% of 1178px (min-width) */
}

◊; Since line height is used in so many places...
◊(define LINEHEIGHT 1.3)
◊(define lineheight (string-append (number->string LINEHEIGHT) "rem"))
◊(define (x-lineheight multiple) 
70
71
72
73
74
75
76
77
78
79
80



81
82
83
84
85




86
87
88
89



90
91
92
93
94
95





96
97
98
99
100
101
102






103
104
105
106
107
108





109
110
111
112



113
114
115
116
117
118
119
120
121
122
123
124











125
126
127
128
129
130





131
132
133
134
135




136
137
138
139
140
141
142
143
144
145
146










147
148
149
150
151




152
153
154
155



156
157
158
159



160
161
162
163
164
165
166






167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
















184
185
186
187



188
189
190
191
192
193
194
195
196








197
198
199
200
201
202





203
204
205
206
207




208
209
210
211
212
213
214






215
216
217
218



219
220
221
222
223
224





225
226
227
228



229
230
231
232



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248















249
250
251
252
253




254
255
256
257
258
259
260
261
262
263
264










265
266
267
268
269
270
271
272







273
274
275
276



277
278
279
280
281
282
283






284
285
286
287
288
289
290
291
292
293









294
295
296
297
298
299
300






301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323






















324
325
326
327



328
329
330
331
332




333
334
335
336
337
338





339
340
341
342
343
344





345
346
347
348
349
350
351
352
353
354









355
356
357
358
359
360
361
362
363








364
365
366
367



368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385

















386
387
388
389
390




391
392
393
394



395
396
397
398
399
400





401
402
403
404



405
406
407
408



409
410
411
412
413
414





415
416
417
418
419
420
421






422
423
424
425
426




427
428
429
430
431




432
433
434
435
436




437
438
439
440
441
442





443
444
445
446
447
448





449
450
451
452
453




454
455
456
457
458
459
460






461
462
463
464
465
466
467






468
469
470
471
472




473
474
475
476
477
478
479
480
481
482
483
484











485
486
487
488
489
490
491
492
493
494
495










496
497
498
499
500
501
502
503
504
505









506
507
508
509
510
511
512






513
514
515
516



517
518
519
520
521
522





523
524
525
526
527




528
529
530
531
532
533





534
535
536
537
538
539
540
541
542








543
544
545
546



547
548
549


550
551
552
553



554
555
556
557
558
559
560






561
562
563
564
565
566
567
568
569
570
571
572











573
574
575
576
577
578





579
580
581
582



583
584
585
586



587
588
589
590
591
592
593






594
595
596
597
598
599
600
601
602








603
604
605


606
607
608


609
610
611
612
613




614
615
616
617
618
619
620
621







622
623
624
625



626
627
628
629



630
631
632
633



634
635
636


637
638
639
640





641

642
643





644
645
646





647
648
649
650
651
652
653
654







655
656
657
658
659
660






661
662
663


664
665
666
667
668
669
670
671
672
673
674










675
676
677
678



679
680
681
682
683
684





685
686
687
688
689




690

691







692




























693
694
695
696
697
698

699
700
701
702
703
704
705
706
707

708
709
710
711
712
713
714
70
71
72
73
74
75
76




77
78
79
80




81
82
83
84
85



86
87
88
89





90
91
92
93
94
95






96
97
98
99
100
101
102





103
104
105
106
107
108



109
110
111
112











113
114
115
116
117
118
119
120
121
122
123
124





125
126
127
128
129
130




131
132
133
134
135










136
137
138
139
140
141
142
143
144
145
146




147
148
149
150
151



152
153
154
155



156
157
158
159






160
161
162
163
164
165
166
















167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183



184
185
186
187








188
189
190
191
192
193
194
195
196





197
198
199
200
201
202




203
204
205
206
207






208
209
210
211
212
213
214



215
216
217
218





219
220
221
222
223
224



225
226
227
228



229
230
231
232















233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248




249
250
251
252
253










254
255
256
257
258
259
260
261
262
263
264







265
266
267
268
269
270
271
272



273
274
275
276






277
278
279
280
281
282
283









284
285
286
287
288
289
290
291
292
293






294
295
296
297
298
299
300






















301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323



324
325
326
327




328
329
330
331
332





333
334
335
336
337
338





339
340
341
342
343
344









345
346
347
348
349
350
351
352
353
354








355
356
357
358
359
360
361
362
363



364
365
366
367

















368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385




386
387
388
389
390



391
392
393
394





395
396
397
398
399
400



401
402
403
404



405
406
407
408





409
410
411
412
413
414






415
416
417
418
419
420
421




422
423
424
425
426




427
428
429
430
431




432
433
434
435
436





437
438
439
440
441
442





443
444
445
446
447
448




449
450
451
452
453






454
455
456
457
458
459
460






461
462
463
464
465
466
467




468
469
470
471
472











473
474
475
476
477
478
479
480
481
482
483
484










485
486
487
488
489
490
491
492
493
494
495









496
497
498
499
500
501
502
503
504
505






506
507
508
509
510
511
512



513
514
515
516





517
518
519
520
521
522




523
524
525
526
527





528
529
530
531
532
533








534
535
536
537
538
539
540
541
542



543
544
545
546


547
548
549



550
551
552
553






554
555
556
557
558
559
560











561
562
563
564
565
566
567
568
569
570
571
572





573
574
575
576
577
578



579
580
581
582



583
584
585
586






587
588
589
590
591
592
593








594
595
596
597
598
599
600
601
602


603
604
605


606
607
608




609
610
611
612
613







614
615
616
617
618
619
620
621



622
623
624
625



626
627
628
629



630
631
632
633


634
635
636



637
638
639
640
641
642
643


644
645
646
647
648



649
650
651
652
653
654







655
656
657
658
659
660
661
662





663
664
665
666
667
668
669


670
671
672










673
674
675
676
677
678
679
680
681
682
683



684
685
686
687





688
689
690
691
692
693




694
695
696
697
698
699

700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740

741
742
743
744
745
746
747
748
749

750
751
752
753
754
755
756
757







-
-
-
-
+
+
+

-
-
-
-
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
+
+

-
-
-
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
+
+

-
-
-
+
+
+
+
+

+
-
-
+
+
+
+
+
-
-
-
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+

-
-
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+

+
-
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+








-
+







 **** 1. Mobile-first layout ***
      1.1 All Pages
      1.2 Front page
      1.3 Individual post body markup
      1.4 Journal views (article listings)  
 */

@media screen {
    body {
        margin: 0;
        background: ◊color-background;
body {
    margin: 0;
    background: ◊color-background;

        /* Typography: `line-height` is important!
           All verticle rhythm based on this value. */
        line-height: ◊lineheight;
        font-family: ◊body-font;
    /* Typography: `line-height` is important!
       All verticle rhythm based on this value. */
    line-height: ◊lineheight;
    font-family: ◊body-font;

        font-feature-settings: "calt" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
        color: ◊color-bodytext; /* Japanese Indigo, baby */
    }
    font-feature-settings: "calt" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
    color: ◊color-bodytext; /* Japanese Indigo, baby */
}

    /* This is used to hide certain punctuation and things as long as CSS is available. 
       Turn off CSS and voila: stuff appears. */
    .x {
        display: none;
    }
/* This is used to hide certain punctuation and things as long as CSS is available. 
   Turn off CSS and voila: stuff appears. */
.x {
    display: none;
}

    .hist p {
        font-variant-alternates: historical-forms;
    }
    .nonhist {
        font-variant-alternates: normal;
    }
.hist p {
    font-variant-alternates: historical-forms;
}
.nonhist {
    font-variant-alternates: normal;
}

    main {
        background: white;
        margin: 0;
        padding: ◊x-lineheight[0.5] ◊x-lineheight[1];
    }
main {
    background: white;
    margin: 0;
    padding: ◊x-lineheight[0.5] ◊x-lineheight[1];
}

    main > a > header {
        text-align: center;
    }
main > a > header {
    text-align: center;
}

    main > a > header h1 {
        display: none;
        /* font-size: ◊x-lineheight[1.5];
        line-height: ◊x-lineheight[2];
        margin: 0 0 ◊x-lineheight[0.5] 0;
        font-weight: normal;
        font-style: italic;
        text-transform: lowercase;
        color: ◊color-pagehead;
        transition: color 0.25s ease; */
    }
main > a > header h1 {
    display: none;
    /* font-size: ◊x-lineheight[1.5];
    line-height: ◊x-lineheight[2];
    margin: 0 0 ◊x-lineheight[0.5] 0;
    font-weight: normal;
    font-style: italic;
    text-transform: lowercase;
    color: ◊color-pagehead;
    transition: color 0.25s ease; */
}

    img.logo {
        height: ◊x-lineheight[3];
        filter: none;
        transition: filter 0.5s ease;
    }
img.logo {
    height: ◊x-lineheight[3];
    filter: none;
    transition: filter 0.5s ease;
}

    header:hover img.logo {
        filter: invert(12%) sepia(87%) saturate(2559%) hue-rotate(348deg) brightness(125%) contrast(88%);
        transition: filter 0.5s ease;
    }
header:hover img.logo {
    filter: invert(12%) sepia(87%) saturate(2559%) hue-rotate(348deg) brightness(125%) contrast(88%);
    transition: filter 0.5s ease;
}

    /* I read somewhere years ago that you should specify styling for links in the
       following order: Link, Visited, Hover, Active. 
           → Remember the mnemonic: Lord Vader Has Arrived.
       
       Not sure if that's relevant advice any more but I still follow it. It can't hurt. */
       
    a:link, a:visited {         /* [L]ord [V]ader… */
        color: ◊color-link;
        text-decoration: none;
    }
/* I read somewhere years ago that you should specify styling for links in the
   following order: Link, Visited, Hover, Active. 
       → Remember the mnemonic: Lord Vader Has Arrived.
   
   Not sure if that's relevant advice any more but I still follow it. It can't hurt. */
   
a:link, a:visited {         /* [L]ord [V]ader… */
    color: ◊color-link;
    text-decoration: none;
}

    a:hover, a:active {         /* …[H]as [A]rrived */
        color: ◊color-linkhover;
        background: ◊color-linkbackground;
    }
a:hover, a:active {         /* …[H]as [A]rrived */
    color: ◊color-linkhover;
    background: ◊color-linkbackground;
}

    a.index-link:link, a.index-link:visited {
        color: ◊color-bodytext;
    }
a.index-link:link, a.index-link:visited {
    color: ◊color-bodytext;
}

    a.index-link:hover, a.index-link:active {
        color: #006400;
    }
a.index-link:hover, a.index-link:active {
    color: #006400;
}

    a.index-link::after {
        content: '\f89b';
        color: #809102;
        position: relative;
        top: -0.3em;
    }
a.index-link::after {
    content: '\f89b';
    color: #809102;
    position: relative;
    top: -0.3em;
}

    main>a {
        color: inherit;
    }
    
    span.links-footnote {
        display: inline-block; /* allows keyframe animation to work */
    }
    
    :target {
        animation: hilite 2.5s;
    }
    @keyframes hilite {
        0% {background: transparent;}
        10% {background: #feffc1;}
        100% {background: transparent;}
    }
main>a {
    color: inherit;
}

span.links-footnote {
    display: inline-block; /* allows keyframe animation to work */
}

:target {
    animation: hilite 2.5s;
}
@keyframes hilite {
    0% {background: transparent;}
    10% {background: #feffc1;}
    100% {background: transparent;}
}

    main > aside {
        text-align: center;
    }
main > aside {
    text-align: center;
}

    nav {
        font-family: 'Triplicate T4c', monospace;
        font-feature-settings: "onum" off;
        font-size: 0.7rem;
        margin: 0;
        margin-top: 0.5rem;
        text-align: center;
    }
main nav {
    font-family: 'Triplicate T4c', monospace;
    font-feature-settings: "onum" off;
    font-size: 0.7rem;
    margin: 0;
    margin-top: 0.5rem;
    text-align: center;
}

    nav ul {
        list-style-type: none;
        margin: 0.2em auto;
        padding: 0;
    }
main nav ul {
    list-style-type: none;
    margin: 0.2em auto;
    padding: 0;
}

    nav li {
        display: none; /* Numbers not displayed on mobile */
        color: gray;
    }
main nav li {
    display: none; /* Numbers not displayed on mobile */
    color: gray;
}

    nav li.nav-text {
        display: inline;
        text-transform: uppercase;
        letter-spacing: 0.05rem;
        font-size: 0.6rem;
    }
main nav li.nav-text {
    display: inline;
    text-transform: uppercase;
    letter-spacing: 0.05rem;
    font-size: 0.6rem;
}

    nav li.inactive-link {
        color: #545454; /* Accessibility (contrast) */
    }
main nav li.inactive-link {
    color: #545454; /* Accessibility (contrast) */
}

    nav li.current-page {
        color: ◊color-bodytext;
        padding: 0.2rem 0.5rem;
        border-bottom: dotted ◊color-bodytext 2px;
    }
main nav li.current-page {
    color: ◊color-bodytext;
    padding: 0.2rem 0.5rem;
    border-bottom: dotted ◊color-bodytext 2px;
}

    nav li a {
        padding: 0.2rem 0.5rem;
    }
main nav li a {
    padding: 0.2rem 0.5rem;
}

    nav li a:link, nav li a:visited {
        color: ◊color-bodytext;
    }
main nav li a:link, nav li a:visited {
    color: ◊color-bodytext;
}

    nav li a:hover, nav li a:active {
        color: ◊color-linkhover;
        background: #ebebeb;
    }
    
    i > em { font-style: normal; }
    
    /* On mobile, an <ARTICLE> is just a box. Later we'll do fancy stuff if grid support is detected. */
    article {
        margin: ◊x-lineheight[2] 0 ◊x-lineheight[1] 0;
        padding-top: ◊x-lineheight[1];
    }
    article:first-of-type {
        margin-top: ◊x-lineheight[1];
    }
main nav li a:hover, nav li a:active {
    color: ◊color-linkhover;
    background: #ebebeb;
}

i > em { font-style: normal; }

/* On mobile, an <ARTICLE> is just a box. Later we'll do fancy stuff if grid support is detected. */
article {
    margin: ◊x-lineheight[2] 0 ◊x-lineheight[1] 0;
    padding-top: ◊x-lineheight[1];
}
article:first-of-type {
    margin-top: ◊x-lineheight[1];
}

    /* Here's my thing these days about article titles: they shouldn't be required. When you write in a
       paper journal, do you think of a title for every entry? No! You just write the date. The date is
       the heading. Makes sense. But, this means I've had to think long and hard about how to present two
       different types of articles (those with titles AND dates, and those with just a date for the title). 
/* Here's my thing these days about article titles: they shouldn't be required. When you write in a
   paper journal, do you think of a title for every entry? No! You just write the date. The date is
   the heading. Makes sense. But, this means I've had to think long and hard about how to present two
   different types of articles (those with titles AND dates, and those with just a date for the title). 

        filter: invert(12%) sepia(87%) saturate(359%) hue-rotate(348deg) brightness(105%) contrast(88%);
       For now: By default, the title-less article is assumed to be the norm. On these, we use <H1> to 
       show the date in italics. */
    article>h1 {
        font-size: ◊x-lineheight[1];
        line-height: ◊x-lineheight[1];
        margin: 0 0 ◊x-lineheight[1] 0;
        font-style: italic;
        font-weight: normal;
    }
    filter: invert(12%) sepia(87%) saturate(359%) hue-rotate(348deg) brightness(105%) contrast(88%);
   For now: By default, the title-less article is assumed to be the norm. On these, we use <H1> to 
   show the date in italics. */
article>h1 {
    font-size: ◊x-lineheight[1];
    line-height: ◊x-lineheight[1];
    margin: 0 0 ◊x-lineheight[1] 0;
    font-style: italic;
    font-weight: normal;
}

    /* Titles non-bold, non-smallcaps by default. This can be overridden in document markup. */
    h1.entry-title {
        margin: 0 0 0 0;
        text-transform: none;
        font-style: normal;
        line-height: 1.7rem;
    }
/* Titles non-bold, non-smallcaps by default. This can be overridden in document markup. */
h1.entry-title {
    margin: 0 0 0 0;
    text-transform: none;
    font-style: normal;
    line-height: 1.7rem;
}

    h1.entry-title.note-full {
        font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
    }
h1.entry-title.note-full {
    font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
}

    /* This <SPAN> class is used in titles for Notes appended to earlier articles */
    h1.entry-title .cross-reference {
        font-feature-settings: "smcp" off, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
        font-style: italic;
        text-transform: none;
    }
/* This <SPAN> class is used in titles for Notes appended to earlier articles */
h1.entry-title .cross-reference {
    font-feature-settings: "smcp" off, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
    font-style: italic;
    text-transform: none;
}

    /* `a.rel-bookmark` is only used in individual article or ‘journal’ views, 
        not in listings; see the design docs.
    */
    a.rel-bookmark:link,        /* Lord  */
    a.rel-bookmark:visited {    /* Vader */
        text-decoration: none;
        background: none;
        color: ◊color-bodytext;
    }
/* `a.rel-bookmark` is only used in individual article or ‘journal’ views, 
    not in listings; see the design docs.
*/
a.rel-bookmark:link,        /* Lord  */
a.rel-bookmark:visited {    /* Vader */
    text-decoration: none;
    background: none;
    color: ◊color-bodytext;
}

    a.rel-bookmark:hover,       /* Has     */
    a.rel-bookmark:active {     /* Arrived */
        text-decoration: none;
        background: ◊color-linkbackground;
        color: #006400;
    }
a.rel-bookmark:hover,       /* Has     */
a.rel-bookmark:active {     /* Arrived */
    text-decoration: none;
    background: ◊color-linkbackground;
    color: #006400;
}

    /* Here's where we add the minty fresh maple leaf glyph. */
    a.rel-bookmark::after {
        content: '\f894'; 
        margin-left: 4px;
        font-style: normal;
        color: #809102;
    }
    a.rel-bookmark.note-permlink::after {
        content: '\f897';
        margin-left: 4px;
    }
    div.note a.rel-bookmark.note-permlink::after {
        content: '';
    }
    div.note a.rel-bookmark.note-permlink::before {
       font-family: ◊body-font;
       font-size: 1rem;
       content: '\00b6';
       margin-left: -0.9rem;
       float: left;
       margin-top: -2px;
    }
/* Here's where we add the minty fresh maple leaf glyph. */
a.rel-bookmark::after {
    content: '\f894'; 
    margin-left: 4px;
    font-style: normal;
    color: #809102;
}
a.rel-bookmark.note-permlink::after {
    content: '\f897';
    margin-left: 4px;
}
div.note a.rel-bookmark.note-permlink::after {
    content: '';
}
div.note a.rel-bookmark.note-permlink::before {
   font-family: ◊body-font;
   font-size: 1rem;
   content: '\00b6';
   margin-left: -0.9rem;
   float: left;
   margin-top: -2px;
}

    a.rel-bookmark:hover::after {
        color: #aaba16;
    }
a.rel-bookmark:hover::after {
    color: #aaba16;
}

    a.cross-reference:link,
    a.cross-reference:visited {
        color: ◊color-bodytext;
    }
a.cross-reference:link,
a.cross-reference:visited {
    color: ◊color-bodytext;
}

    a.cross-reference::before {
        content: '☞\00a0';      /* Non-breaking space */
        font-style: normal;
        color: ◊color-xrefmark;
    }
a.cross-reference::before {
    content: '☞\00a0';      /* Non-breaking space */
    font-style: normal;
    color: ◊color-xrefmark;
}

    /* Footnote links */
    sup a {
        font-weight: bold;
        margin-left: 3px;
    }
/* Footnote links */
sup a {
    font-weight: bold;
    margin-left: 3px;
}

    p.time {
        margin: 0 0 ◊x-lineheight[1] 0;
    }
    footer.article-info {
        margin: ◊x-lineheight[1] 0;
        text-align: center;
        font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
        color: #555555; /* Accessibility (contrast) */
    }
p.time {
    margin: 0 0 ◊x-lineheight[1] 0;
}
footer.article-info {
    margin: ◊x-lineheight[1] 0;
    text-align: center;
    font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
    color: #555555; /* Accessibility (contrast) */
}

    footer.article-info::before {
        content: "☞\00a0";
    }
    
    /* Within article info, don’t display series info when on a series page (redundant) */
    body.series-page .series-part {
        display: none;
    }
footer.article-info::before {
    content: "☞\00a0";
}

/* Within article info, don’t display series info when on a series page (redundant) */
body.series-page .series-part {
    display: none;
}

    p.time::before {
        content: none;
    }
p.time::before {
    content: none;
}

    p.time {
        font-size: ◊x-lineheight[0.75];
        line-height: ◊x-lineheight[1];
        font-feature-settings: "scmp" off, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
        text-transform: none;
        font-style: italic;
    }
    
    article>:last-child::after {
        content: '\f88d';  /* Round glyph */
        color: ◊color-bodytext;
        font-size: ◊x-lineheight[1];
        line-height: ◊x-lineheight[1];
        text-align: center;
        display: block;
        margin: ◊x-lineheight[1] 0;
    }
p.time {
    font-size: ◊x-lineheight[0.75];
    line-height: ◊x-lineheight[1];
    font-feature-settings: "scmp" off, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
    text-transform: none;
    font-style: italic;
}

article>:last-child::after {
    content: '\f88d';  /* Round glyph */
    color: ◊color-bodytext;
    font-size: ◊x-lineheight[1];
    line-height: ◊x-lineheight[1];
    text-align: center;
    display: block;
    margin: ◊x-lineheight[1] 0;
}

    p {
        margin: 0;
        text-indent: 0;
    }
p {
    margin: 0;
    text-indent: 0;
}

    p + p {
        text-indent: 2em;
    }
p + p {
    text-indent: 2em;
}

    section.entry-content blockquote {
        font-size: ◊x-lineheight[0.7];
        line-height: ◊derive-lineheight[7 #:per-lines 6];
        margin: ◊x-lineheight[1.0] 2em;
    }
section.entry-content blockquote {
    font-size: ◊x-lineheight[0.7];
    line-height: ◊derive-lineheight[7 #:per-lines 6];
    margin: ◊x-lineheight[1.0] 2em;
}

    section.entry-content blockquote:first-child {
        margin-top: 0;
    }
section.entry-content blockquote:first-child {
    margin-top: 0;
}

    section.entry-content blockquote footer::before {
        content: '—'; /* Em-dash */
    }
section.entry-content blockquote footer::before {
    content: '—'; /* Em-dash */
}

    section.entry-content blockquote footer {
        margin-top: ◊x-lineheight[1];
        text-align: right;
        width: calc(100% + 2em);
    }
section.entry-content blockquote footer {
    margin-top: ◊x-lineheight[1];
    text-align: right;
    width: calc(100% + 2em);
}

    section.entry-content h2 {
        font-size: 1.2rem;
        font-style: italic;
        margin: ◊x-lineheight[1] 0;
        font-weight: normal;
    }
section.entry-content h2 {
    font-size: 1.2rem;
    font-style: italic;
    margin: ◊x-lineheight[1] 0;
    font-weight: normal;
}

    .caps, span.smallcaps, span.newthought {
        font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
        font-style: normal;
    }
.caps, span.smallcaps, span.newthought {
    font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
    font-style: normal;
}

    .caps {
        text-transform: lowercase;
        font-size: 1.1em;
    }
.caps {
    text-transform: lowercase;
    font-size: 1.1em;
}

    p.pause-before {
        margin-top: ◊x-lineheight[2];
        text-indent: 0;
    }
p.pause-before {
    margin-top: ◊x-lineheight[2];
    text-indent: 0;
}

    section.entry-content ul,
    section.entry-content ol {
        margin: ◊x-lineheight[0.5] 0 ◊x-lineheight[0.5] 0;
        padding: 0;
    }
section.entry-content ul,
section.entry-content ol {
    margin: ◊x-lineheight[0.5] 0 ◊x-lineheight[0.5] 0;
    padding: 0;
}

    section.entry-content li {
        margin: 0 0 ◊x-lineheight[0.5] 0;
        padding: 0;
        text-indent: 0; 
    }
section.entry-content li {
    margin: 0 0 ◊x-lineheight[0.5] 0;
    padding: 0;
    text-indent: 0; 
}

    code {
        font-size: 0.75rem;
        font-family: ◊mono-font;
    }
code {
    font-size: 0.75rem;
    font-family: ◊mono-font;
}

    pre {
        line-height: ◊derive-lineheight[7 #:per-lines 6];
        max-width: 100%;
        overflow-x: auto;
        tab-size: 4;
    }
pre {
    line-height: ◊derive-lineheight[7 #:per-lines 6];
    max-width: 100%;
    overflow-x: auto;
    tab-size: 4;
}

    pre code {
        border: 0;
        background: none;
        font-style: italic;
        font-size: 0.75rem;
    }
pre code {
    border: 0;
    background: none;
    font-style: italic;
    font-size: 0.75rem;
}

    pre.code {
        border: dotted #aaa 2px;
        padding-left: 0.2em;
    }
pre.code {
    border: dotted #aaa 2px;
    padding-left: 0.2em;
}

    pre.verse {
        font-family: ◊body-font;
        font-size: 1rem;
        line-height: ◊x-lineheight[1];
        width: auto;
        margin: ◊x-lineheight[1] auto;
        display: table;
        white-space: pre-wrap;
        /* Whitespace is preserved by the browser.
           Text will wrap when necessary, and on line breaks */
    }
pre.verse {
    font-family: ◊body-font;
    font-size: 1rem;
    line-height: ◊x-lineheight[1];
    width: auto;
    margin: ◊x-lineheight[1] auto;
    display: table;
    white-space: pre-wrap;
    /* Whitespace is preserved by the browser.
       Text will wrap when necessary, and on line breaks */
}

    p.verse-heading {
        font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
        text-align: center;
        font-size: 1.3rem;
    }
        
    section.entry-content figure {
        margin: ◊x-lineheight[1] 0;
        padding: 0;
    }
p.verse-heading {
    font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
    text-align: center;
    font-size: 1.3rem;
}
    
section.entry-content figure {
    margin: ◊x-lineheight[1] 0;
    padding: 0;
}

    figure>a {
        margin: 0;
        padding: 0;
        font-family: arial, sans-serif;
    }
    figure img {
        max-width: 100%;
        margin: 0 auto;
    }
figure>a {
    margin: 0;
    padding: 0;
    font-family: arial, sans-serif;
}
figure img {
    max-width: 100%;
    margin: 0 auto;
}

    figcaption {
        font-size: 0.8rem;
        line-height: ◊derive-lineheight[4 #:per-lines 3];
        margin-bottom: 0.3rem;
        text-align: left;
    }
figcaption {
    font-size: 0.8rem;
    line-height: ◊derive-lineheight[4 #:per-lines 3];
    margin-bottom: 0.3rem;
    text-align: left;
}

    dl {
        margin: ◊x-lineheight[1] 0;
    }
dl {
    margin: ◊x-lineheight[1] 0;
}

    dl.dialogue dt {
        font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
        font-style: normal;
        text-transform: lowercase;
    }
dl.dialogue dt {
    font-feature-settings: "smcp" on, "liga" on, "clig" on, "dlig" on, "kern" on, "onum" on, "pnum" on;
    font-style: normal;
    text-transform: lowercase;
}

    section.entry-content p.signoff {
        margin-top: ◊x-lineheight[1];
        font-size: 1.2rem;
    }
section.entry-content p.signoff {
    margin-top: ◊x-lineheight[1];
    font-size: 1.2rem;
}

    section.footnotes {
        margin-top: ◊x-lineheight[1];
        font-size: 0.9rem;
        line-height: ◊derive-lineheight[7 #:per-lines 6];
    }
section.footnotes {
    margin-top: ◊x-lineheight[1];
    font-size: 0.9rem;
    line-height: ◊derive-lineheight[7 #:per-lines 6];
}

    section.footnotes hr {
        border: 0;
        background: ◊color-bodytext;
        height: 1px;
        margin: 0;
        width: 75%;
        text-align: left;
    }
section.footnotes hr {
    border: 0;
    background: ◊color-bodytext;
    height: 1px;
    margin: 0;
    width: 75%;
    text-align: left;
}

    section.footnotes ol {
        margin: ◊x-lineheight[0.5] 0 0 0;
    }
section.footnotes ol {
    margin: ◊x-lineheight[0.5] 0 0 0;
}

    /* ******* “Further Notes” added to articles ********
     */
/* ******* “Further Notes” added to articles ********
 */

    div.further-notes {
        margin-top: ◊x-lineheight[3];
    }
div.further-notes {
    margin-top: ◊x-lineheight[3];
}

    div.further-notes>h2 {
        font-style: normal;
        font-feature-settings: "smcp" on;
        border-top: solid 2px ◊color-bodytext;
        text-transform: lowercase;
    }
div.further-notes>h2 {
    font-style: normal;
    font-feature-settings: "smcp" on;
    border-top: solid 2px ◊color-bodytext;
    text-transform: lowercase;
}

    div.note h3 {
        margin-top: 0;
        font-size: 1rem;
        font-weight: normal;
        font-family: ◊mono-font;
        font-size: 0.7rem;
        background: #f3f3f3;
        border-top: solid #b6b6b6 1px;
        padding-left: 0.2rem;
        margin-bottom: 0.3rem;
    }
div.note h3 {
    margin-top: 0;
    font-size: 1rem;
    font-weight: normal;
    font-family: ◊mono-font;
    font-size: 0.7rem;
    background: #f3f3f3;
    border-top: solid #b6b6b6 1px;
    padding-left: 0.2rem;
    margin-bottom: 0.3rem;
}

    div.note-meta {
        margin-top: ◊x-lineheight[1];
        color: #888;
        font-size: 1.2rem;
    }
div.note-meta {
    margin-top: ◊x-lineheight[1];
    color: #888;
    font-size: 1.2rem;
}

    .by-proprietor .note-meta {
        display: none;
    }
.by-proprietor .note-meta {
    display: none;
}

    div.note + div.note {
        margin-top: ◊x-lineheight[2];
    }
div.note + div.note {
    margin-top: ◊x-lineheight[2];
}

    .disposition-mark {
        color: ◊color-xrefmark;
        position: relative;
        top: -0.5em;
        font-size: 0.83em;
    }
.disposition-mark {
    color: ◊color-xrefmark;
    position: relative;
    top: -0.5em;
    font-size: 0.83em;
}

    .disposition-mark-in-note {
        background: ◊color-xrefmark;
        color: white;
        border-radius: 0.08em;
        padding: 0 0.1em;
        margin: 0 0.4em 0 0;
        text-decoration: none !important;
    }
.disposition-mark-in-note {
    background: ◊color-xrefmark;
    color: white;
    border-radius: 0.08em;
    padding: 0 0.1em;
    margin: 0 0.4em 0 0;
    text-decoration: none !important;
}

    /* ******* (Mobile first) Journal View styling *******
     */
/* ******* (Mobile first) Journal View styling *******
 */

    section.content-block {
    }
section.content-block {
}

    ul.article-list {
        margin-top: ◊x-lineheight[1];
        padding: 0;
    }
ul.article-list {
    margin-top: ◊x-lineheight[1];
    padding: 0;
}

    ul.article-list li {
        list-style-type: none;
        display: block;
        margin: 0;
        padding: 0; 
        margin-bottom: ◊x-lineheight[1];
    }
ul.article-list li {
    list-style-type: none;
    display: block;
    margin: 0;
    padding: 0; 
    margin-bottom: ◊x-lineheight[1];
}

    ul.article-list li>a {
        display: block;
    }
ul.article-list li>a {
    display: block;
}

    div.article-list-date {
        color: #999;
    }
div.article-list-date {
    color: #999;
}

    div.article-list-title {
        font-size: 1.2rem;
    }
div.article-list-title {
    font-size: 1.2rem;
}

    /* ******* (Mobile first) Columnar series list styling *******
     */
/* ******* (Mobile first) Columnar series list styling *******
 */

    .series-list {
        column-width: 9rem;
    }
.column-list {
    max-width: 17rem;
    margin: 0;
    columns: 8rem auto;
}

@media (min-width: 667px) {
    .series-list div {
        -webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
  .column-list {
    margin: 0 auto;
  }
}

        page-break-inside: avoid; /* Firefox */
        break-inside: avoid; /* IE 10+ */
    }
.column-list div {
    padding-left: 0.25em; /* Keeps some italic descenders inside the box */
    text-align: left;
    break-inside: avoid;
}

    .series-list h2 {
        font-feature-settings: "smcp" on;
        text-transform: lowercase;
        font-weight: normal;
        font-size: 1em;
        margin: 0;
    }
.column-list h2 {
    font-feature-settings: "smcp" on;
    text-transform: lowercase;
    font-weight: normal;
    font-size: 1em;
    margin: 0;
}

    .series-list ul {
        margin-top: 0;
        list-style-type: none;
        padding: 0;
    }
.column-list ul {
    margin-top: 0;
    list-style-type: none;
    padding: 0;
    margin-bottom: 0.5rem;
}

    /* ******* (Mobile first) Keyword Index styling *******
     */
/* ******* (Mobile first) Keyword Index styling *******
 */

    #keywordindex {
        column-width: 7rem;
        margin-top: ◊x-lineheight[2];
    }
    
    #keywordindex section {
        -webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
        page-break-inside: avoid; /* Firefox */
        break-inside: avoid; /* IE 10+ */
    }
#keywordindex {
    column-width: 7rem;
    margin-top: ◊x-lineheight[2];
}

#keywordindex section {
    -webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
    page-break-inside: avoid; /* Firefox */
    break-inside: avoid; /* IE 10+ */
}

    #keywordindex h2 {
        margin: 0;
    }
#keywordindex h2 {
    margin: 0;
}

    #keywordindex ul {
        margin-top: 0;
        list-style-type: none;
        padding: 0;
    }
#keywordindex ul {
    margin-top: 0;
    list-style-type: none;
    padding: 0;
}

    #keywordindex ul ul {
        margin-left: 0.5em;
        font-size: smaller;
    }
#keywordindex ul ul {
    margin-left: 0.5em;
    font-size: smaller;
}

/* Footer ***** */
    /* End of mobile-first typography and layout */

footer#main {
  text-align: left;
  font-size: 0.8rem;
  line-height: 1rem;
  width: 90%;
  margin: 0 auto;
}

@media (min-width: 667px) {
  footer#main {
    text-align: center;
    width: 100%;
    background: linear-gradient(◊color-background 5%, #dedede 100%);
  }
}

footer#main p.title {
  font-size: 1rem;
  font-feature-settings: "smcp" on;
  text-transform: lowercase;
  margin-top: ◊x-lineheight[2];
  margin-bottom: ◊x-lineheight[0.5];
}

footer#main nav {
  font-feature-settings: "smcp" on;
  text-transform: lowercase;
}

footer#main nav code {
  font-size: 0.6rem;
}
  

/* End of mobile-first typography and layout */


/* Here’s where we start getting funky for any viewport wider than mobile portrait.
   An iPhone 6 is 667px wide in landscape mode, so that’s our breakpoint. */

@media only screen and (min-width: 667px) {
@media (min-width: 667px) {
    main {
        margin: 0 auto;
        padding-left: 1rem;
        padding-right: 1rem;
        width: 30rem;
        max-width: 90%;
    }

    nav li { display: inline; } /* Display page numbers on larger screens */
    main nav li { display: inline; } /* Display page numbers on larger screens */

    @supports (grid-area: auto) {
        main { 
            width: 42rem;
            background: none;
        }

741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
784
785
786
787
788
789
790


791
792
793
794
795
796
797







-
-







            border-radius: 2px;
        }
        
        article>h1 {
            grid-area: margin;
            font-size: 1.1rem;
            text-align: right;
            /* padding-right: 1rem; */
            color: ◊color-bodytext;
        }

        article>h1.entry-title {
            grid-area: title;
            font-size: ◊x-lineheight[1];
            text-align: left;
            padding-right: 0;
859
860
861
862
863
864
865
















900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
        }

        div.content-block-main > :first-child {
            margin-top: 0 !important;
        }
    }
}

@media print {
    html { font-size: 13pt; }
    body { background: white; color: black; }
    main { width: 100%; }
    footer.article-info { color: black; }
    article, section.content-block {
        box-shadow: none;
        border: none;
        }
    a:link, a:visited, a:hover, a:active {
        color: black;
        border-bottom: dotted 1px black;
    }
    footer#main { display: none; }
}