Current rework

This commit is contained in:
Levi Neuwirth 2026-04-26 19:42:47 -04:00
parent 53e053e9a7
commit 42ba2bf972
10 changed files with 639 additions and 14 deletions

25
.gitignore vendored
View File

@ -3,10 +3,28 @@ _site/
_cache/ _cache/
.DS_Store .DS_Store
.env .env
# Defense-in-depth: catch any stray .env / .env.* anywhere in the tree # Defense-in-depth: the auto-snapshot in `make build` stages content/
# (the auto-snapshot in the Makefile stages content/ on every build). # wholesale. These patterns prevent any stray credential-shaped file
# (dropped accidentally during writing) from being staged + pushed.
# To intentionally commit one of these (rare), use `git add -f`.
**/.env **/.env
**/.env.* **/.env.*
**/*.env
**/*.key
**/*.pem
**/*.p12
**/*.pfx
**/id_rsa*
**/id_dsa*
**/id_ecdsa*
**/id_ed25519*
**/.netrc
**/.npmrc
**/.pypirc
**/credentials
**/credentials.json
**/credentials.yaml
**/credentials.yml
# Editor backup/swap files # Editor backup/swap files
*~ *~
@ -58,6 +76,9 @@ data/semantic-meta.json
# IGNORE.txt is for the local build and need not be synced. # IGNORE.txt is for the local build and need not be synced.
IGNORE.txt IGNORE.txt
# Working notes / planning docs at the repo root (not site content).
checklist.md
# CV/résumé build pipeline (YAML → Jinja → xelatex). The canonical PDFs # CV/résumé build pipeline (YAML → Jinja → xelatex). The canonical PDFs
# live under static/ and ship with the site; the pipeline itself is # live under static/ and ship with the site; the pipeline itself is
# kept locally for regeneration but not version-controlled here. # kept locally for regeneration but not version-controlled here.

View File

@ -16,17 +16,12 @@ build:
# either reuses it (no new content/ changes) or appends another # either reuses it (no new content/ changes) or appends another
# snapshot on top, so failures don't disappear from the log. # snapshot on top, so failures don't disappear from the log.
# #
# Pathspec is explicit (not `git add content/`) so a stray .env, # `git add content/` respects .gitignore, which excludes credential-
# credential file, or other non-content artifact dropped under # shaped patterns (.env, *.key, *.pem, id_rsa*, credentials*, etc.)
# content/ is NOT auto-staged. The :(glob) magic prefix makes `**` # so a stray secret dropped under content/ is NOT auto-staged. To
# match across path components (git default fnmatch does not). # intentionally commit a normally-ignored file, use `git add -f`
# Add new extensions here if a new asset type is introduced. # manually before running `make build`.
@git add ':(glob)content/**/*.md' ':(glob)content/**/*.html' ':(glob)content/**/*.bib' \ @git add content/
':(glob)content/**/*.png' ':(glob)content/**/*.jpg' ':(glob)content/**/*.jpeg' \
':(glob)content/**/*.svg' ':(glob)content/**/*.gif' ':(glob)content/**/*.pdf' \
':(glob)content/**/*.mp3' ':(glob)content/**/*.ogg' ':(glob)content/**/*.flac' \
':(glob)content/**/*.yaml' ':(glob)content/**/*.yml' ':(glob)content/**/*.json' \
':(glob)content/**/*.css' ':(glob)content/**/*.tex'
@git diff --cached --quiet || git commit -m "auto: $$(date -u +%Y-%m-%dT%H:%M:%SZ) [skip ci]" @git diff --cached --quiet || git commit -m "auto: $$(date -u +%Y-%m-%dT%H:%M:%SZ) [skip ci]"
@mkdir -p data @mkdir -p data
@date +%s > data/build-start.txt @date +%s > data/build-start.txt

281
build/Now.hs Normal file
View File

@ -0,0 +1,281 @@
{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Now page: loads data/now.yaml and renders the active-projects view
-- and the recently-shipped archive for /current.html. Page-level
-- "Last updated" stamp is exposed as a context field; relative time
-- ("4 days ago") is computed at build time from getCurrentTime.
module Now
( nowCtx
) where
import Data.Aeson (FromJSON (..), withObject, (.:), (.:?), (.!=))
import Data.Char (toUpper)
import Data.List (nub, sortBy)
import Data.Maybe (fromMaybe)
import Data.Ord (Down (..), comparing)
import Data.Time.Calendar (Day, diffDays)
import Data.Time.Clock (UTCTime (..), getCurrentTime)
import Data.Time.Format (defaultTimeLocale, formatTime, parseTimeM)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Yaml as Y
import Hakyll hiding (escapeHtml)
import Contexts (siteCtx)
import Utils (escapeHtml)
-- ---------------------------------------------------------------------------
-- Entry types
-- ---------------------------------------------------------------------------
data NowEntry = NowEntry
{ neTitle :: String
, neSection :: String
, neStatus :: String
, neUpdated :: String
, neLink :: Maybe String
, neNote :: Maybe String
, nePriority :: Int
}
instance FromJSON NowEntry where
parseJSON = withObject "NowEntry" $ \o -> NowEntry
<$> o .: "title"
<*> o .: "section"
<*> o .: "status"
<*> o .: "updated"
<*> o .:? "link"
<*> o .:? "note"
<*> o .:? "priority" .!= 0
data NowShipped = NowShipped
{ nsTitle :: String
, nsCompleted :: String
, nsLink :: Maybe String
, nsNote :: Maybe String
}
instance FromJSON NowShipped where
parseJSON = withObject "NowShipped" $ \o -> NowShipped
<$> o .: "title"
<*> o .: "completed"
<*> o .:? "link"
<*> o .:? "note"
data NowDoc = NowDoc
{ nLastUpdated :: String
, nEntries :: [NowEntry]
, nShipped :: [NowShipped]
}
instance FromJSON NowDoc where
parseJSON = withObject "NowDoc" $ \o -> NowDoc
<$> o .: "last-updated"
<*> o .:? "entries" .!= []
<*> o .:? "shipped" .!= []
-- ---------------------------------------------------------------------------
-- Helpers
-- ---------------------------------------------------------------------------
-- | Section ordering follows first-appearance in entries. Reorder the
-- YAML to reorder the page; no separate ordering key required.
sectionOrder :: [NowEntry] -> [String]
sectionOrder = nub . map neSection
-- | Status ordering — "how close to shipping." Lower rank sorts first.
-- Statuses not listed sort below all known ones (rank 99) so a typo
-- surfaces visibly at the bottom of its section instead of silently
-- ranking next-to-the-top.
statusRanks :: [(String, Int)]
statusRanks =
[ ("in-review", 1)
, ("revising", 2)
, ("drafting", 3)
, ("building", 4)
, ("early-stage", 5)
, ("paused", 6)
]
statusRank :: String -> Int
statusRank s = fromMaybe 99 (lookup s statusRanks)
-- | Three-tier sort key for active entries:
-- 1. priority — manual override; higher floats up (default 0)
-- 2. statusRank — how close to shipping (lower is closer)
-- 3. updated — recency tiebreaker within the same rank
-- Sectioning is applied to the *unsorted* list so section ordering
-- continues to follow YAML source order; sorting happens within each
-- section's filtered slice.
entrySortKey :: NowEntry -> (Down Int, Int, Down String)
entrySortKey e =
( Down (nePriority e)
, statusRank (neStatus e)
, Down (neUpdated e)
)
-- | "early-stage" → "Early Stage", "research" → "Research".
titleCaseWords :: String -> String
titleCaseWords = unwords . map cap . wordsOnDash
where
cap [] = []
cap (x:xs) = toUpper x : xs
wordsOnDash s = case break (== '-') s of
(a, []) -> [a]
(a, _:rest) -> a : wordsOnDash rest
-- ---------------------------------------------------------------------------
-- HTML rendering
-- ---------------------------------------------------------------------------
renderStatusChip :: String -> String
renderStatusChip s = concat
[ "<span class=\"now-status now-status--", escapeHtml s, "\">"
, escapeHtml (titleCaseWords s)
, "</span>"
]
-- | Active-entry card. Reuses the .item-card / .item-card-* classes from
-- item-card.css so the Now page picks up the existing typographic
-- register; the .now-* classes layer status-chip + spacing on top.
renderEntry :: NowEntry -> String
renderEntry e = concat
[ "<li class=\"item-card now-card\">"
, "<span class=\"item-card-kind now-kind\">"
, renderStatusChip (neStatus e)
, "</span>"
, "<div class=\"item-card-main\">"
, "<div class=\"item-card-header\">"
, renderTitle (neLink e) (neTitle e)
, "<time class=\"item-card-date\" datetime=\"", escapeHtml (neUpdated e), "\">"
, escapeHtml (neUpdated e)
, "</time>"
, "</div>"
, maybe "" (\n -> "<p class=\"item-card-abstract is-full\">" ++ escapeHtml n ++ "</p>") (neNote e)
, "</div>"
, "</li>"
]
renderShippedEntry :: NowShipped -> String
renderShippedEntry s = concat
[ "<li class=\"item-card now-card now-card--shipped\">"
, "<span class=\"item-card-kind now-kind\">"
, renderStatusChip "shipped"
, "</span>"
, "<div class=\"item-card-main\">"
, "<div class=\"item-card-header\">"
, renderTitle (nsLink s) (nsTitle s)
, "<time class=\"item-card-date\" datetime=\"", escapeHtml (nsCompleted s), "\">"
, escapeHtml (nsCompleted s)
, "</time>"
, "</div>"
, maybe "" (\n -> "<p class=\"item-card-abstract is-full\">" ++ escapeHtml n ++ "</p>") (nsNote s)
, "</div>"
, "</li>"
]
renderTitle :: Maybe String -> String -> String
renderTitle mu title = case mu of
Just url -> "<a class=\"item-card-title\" href=\"" ++ escapeHtml url ++ "\">" ++ escapeHtml title ++ "</a>"
Nothing -> "<span class=\"item-card-title\">" ++ escapeHtml title ++ "</span>"
renderSection :: String -> [NowEntry] -> String
renderSection sec es = concat
[ "<section class=\"now-section library-section\">"
, "<h2 class=\"now-section-heading\">"
, escapeHtml (titleCaseWords sec)
, "</h2>"
, "<ul class=\"item-card-list\">"
, concatMap renderEntry es
, "</ul>"
, "</section>"
]
renderEntries :: [NowEntry] -> String
renderEntries [] = ""
renderEntries entries = concatMap renderOne (sectionOrder entries)
where
renderOne sec =
let inSec = filter ((== sec) . neSection) entries
sorted = sortBy (comparing entrySortKey) inSec
in renderSection sec sorted
renderShippedAll :: [NowShipped] -> String
renderShippedAll [] = ""
renderShippedAll items = concat
[ "<section class=\"now-section now-section--shipped library-section\">"
, "<h2 class=\"now-section-heading\">Recently Shipped</h2>"
, "<ul class=\"item-card-list\">"
, concatMap renderShippedEntry sorted
, "</ul>"
, "</section>"
]
where
sorted = sortBy (comparing (Down . nsCompleted)) items
-- ---------------------------------------------------------------------------
-- Date formatters — runs at build time
-- ---------------------------------------------------------------------------
-- | "2026-04-26" → "26 April 2026". Falls back to the raw ISO string
-- if the date is unparseable, so a typo in @last-updated@ surfaces
-- in the rendered page rather than blowing up the build.
formatWriterly :: String -> String
formatWriterly iso =
case parseTimeM True defaultTimeLocale "%Y-%m-%d" iso :: Maybe Day of
Nothing -> iso
Just d -> formatTime defaultTimeLocale "%-d %B %Y" d
relativeTime :: Day -> String -> String
relativeTime today iso =
case parseTimeM True defaultTimeLocale "%Y-%m-%d" iso :: Maybe Day of
Nothing -> ""
Just d -> bucket (diffDays today d)
where
bucket n
| n < 0 = ""
| n == 0 = "today"
| n == 1 = "yesterday"
| n < 7 = show n ++ " days ago"
| n < 28 = pluralize (n `div` 7) "week"
| n < 365 = pluralize (n `div` 30) "month"
| otherwise = pluralize (n `div` 365) "year"
pluralize 1 unit = "1 " ++ unit ++ " ago"
pluralize k unit = show k ++ " " ++ unit ++ "s ago"
-- ---------------------------------------------------------------------------
-- Load
-- ---------------------------------------------------------------------------
-- | UTF-8 round-trip String → ByteString. Hakyll's @getResourceBody@
-- hands us a 'String' (Unicode codepoints); the yaml library wants
-- a UTF-8 'ByteString'. 'Data.ByteString.Char8.pack' would truncate
-- each 'Char' to 8 bits — fine for ASCII, silent corruption for any
-- codepoint above 0x7F (e.g. em-dash 0x2014 → control char 0x14).
loadNow :: Compiler NowDoc
loadNow = do
rawItem <- load (fromFilePath "data/now.yaml") :: Compiler (Item String)
case Y.decodeEither' (TE.encodeUtf8 (T.pack (itemBody rawItem))) of
Left err -> fail ("now.yaml: " ++ show err)
Right doc -> return doc
-- ---------------------------------------------------------------------------
-- Context
-- ---------------------------------------------------------------------------
nowCtx :: Context String
nowCtx =
constField "now" "true"
<> field "now-last-updated" (\_ -> nLastUpdated <$> loadNow)
<> field "now-last-updated-display" (\_ -> formatWriterly . nLastUpdated <$> loadNow)
<> field "now-last-updated-relative" (\_ -> do
doc <- loadNow
nowT <- unsafeCompiler getCurrentTime
let today = utctDay nowT
rel = relativeTime today (nLastUpdated doc)
if null rel
then noResult "no relative time"
else return rel
)
<> field "now-entries-html" (\_ -> renderEntries . nEntries <$> loadNow)
<> field "now-shipped-html" (\_ -> renderShippedAll . nShipped <$> loadNow)
<> siteCtx

View File

@ -27,6 +27,7 @@ import Compilers (essayCompiler, postCompiler, pageCompiler, poetryCompiler, fi
compositionCompiler, sidecarCompiler) compositionCompiler, sidecarCompiler)
import Catalog (musicCatalogCtx) import Catalog (musicCatalogCtx)
import Commonplace (commonplaceCtx) import Commonplace (commonplaceCtx)
import Now (nowCtx)
import Contexts (siteCtx, essayCtx, postCtx, pageCtx, poetryCtx, fictionCtx, compositionCtx, import Contexts (siteCtx, essayCtx, postCtx, pageCtx, poetryCtx, fictionCtx, compositionCtx,
contentKindField, recentFirstByDisplay, contentKindField, recentFirstByDisplay,
tagLinksFieldExcludingTopSegment) tagLinksFieldExcludingTopSegment)
@ -206,6 +207,10 @@ rules = do
-- with dependency tracking by the commonplace page compiler. -- with dependency tracking by the commonplace page compiler.
match "data/commonplace.yaml" $ compile getResourceBody match "data/commonplace.yaml" $ compile getResourceBody
-- Now YAML — same pattern as commonplace. Loaded by Now.nowCtx for
-- /current.html. Re-compiles current.html when the YAML changes.
match "data/now.yaml" $ compile getResourceBody
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Homepage -- Homepage
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -261,6 +266,19 @@ rules = do
>>= loadAndApplyTemplate "templates/default.html" commonplaceCtx >>= loadAndApplyTemplate "templates/default.html" commonplaceCtx
>>= relativizeUrls >>= relativizeUrls
-- ---------------------------------------------------------------------------
-- Now — research-first status page driven by data/now.yaml. Same
-- structural pattern as the commonplace page: markdown body
-- (optional intro prose) + structured YAML rendered into HTML by
-- Now.nowCtx, then assembled by templates/current.html.
-- ---------------------------------------------------------------------------
match "content/current.md" $ do
route $ constRoute "current.html"
compile $ pageCompiler
>>= loadAndApplyTemplate "templates/current.html" nowCtx
>>= loadAndApplyTemplate "templates/default.html" nowCtx
>>= relativizeUrls
match "content/colophon.md" $ do match "content/colophon.md" $ do
route $ constRoute "colophon.html" route $ constRoute "colophon.html"
compile $ essayCompiler compile $ essayCompiler
@ -272,6 +290,7 @@ rules = do
.&&. complement "content/index.md" .&&. complement "content/index.md"
.&&. complement "content/commonplace.md" .&&. complement "content/commonplace.md"
.&&. complement "content/colophon.md" .&&. complement "content/colophon.md"
.&&. complement "content/current.md"
.&&. complement "content/library.md") $ do .&&. complement "content/library.md") $ do
route $ gsubRoute "content/" (const "") route $ gsubRoute "content/" (const "")
`composeRoutes` setExtension "html" `composeRoutes` setExtension "html"

91
data/now.yaml Normal file
View File

@ -0,0 +1,91 @@
# Now — research-first status feed for /current.html.
#
# `last-updated` is the page-level temporal stamp. Bump it whenever
# you push any change here, even a one-line note edit; the value is
# rendered prominently and a relative-time tail ("4 days ago") is
# computed at build time.
#
# `entries:` are active items. Each carries:
# - title : display name
# - section : free-form section key (research, engineering, …);
# sections render in first-appearance order
# - status : in-review | revising | drafting | building | early-stage |
# paused. Free-form, but the listed values are the canonical
# ladder (closest-to-shipping → furthest) and have CSS accents.
# - updated : YYYY-MM-DD when this entry last moved (NOT page-stamp)
# - link : optional URL (artifact, preprint, repo, etc.)
# - note : optional one-sentence what's-happening-now line
# - priority : optional integer (default 0). Manual override on the sort
# order: positive floats up, negative sinks down. Use sparingly
# — the status ladder already captures most of the signal;
# priority is for "this is the headline regardless of where
# its status puts it" (or vice versa).
#
# Sort within each section: priority desc → status rank asc → updated desc.
# Section order: first-appearance in this file. Rearrange to taste.
#
# `shipped:` are recently-completed items. Each carries:
# - title, completed (date), optional link, optional note
# Sorted newest-first at render time. Curate by hand — items don't
# auto-prune. Move things off the list when they stop earning the slot.
last-updated: 2026-04-26
entries:
- title: "Order-Invariant ICD-10-CM Embedding (JAMA submission)"
section: research
status: in-review
updated: 2026-04-10
link: /essays/beyond-comorbidity-indices/
note: "Under review at JAMA Network Open. Calculator deployed at levineuwirth.github.io/icd_embeddings."
- title: "Semantic-embeddings citation work"
section: research
status: drafting
updated: 2026-04-22
link: https://neuroai.health
note: "Early-stage manuscript with NeuroAI. Preprint expected summer 2026."
- title: "SIMD / PQC, Phase 2 & 3"
section: research
status: building
updated: 2026-04-19
link: /essays/where-does-simd-help-post-quantum-cryptography/
note: "Hardware performance counters (PAPI), RAPL energy, and cross-ISA ports (ARM NEON/SVE, RISC-V V) on Brown's OSCAR cluster."
- title: "NeuroPose clinical-implications manuscript"
section: research
status: drafting
updated: 2026-04-08
link: /essays/neuropose/
note: "In preparation; target submission 20262027."
- title: "Magic: The Gathering reinforcement learning"
section: research
status: early-stage
updated: 2026-04-15
note: "Early-stage RL on Brown's OSCAR HPC cluster; expected late 2026."
- title: "CHASE 2026 conference submission"
section: research
status: in-review
updated: 2026-04-05
note: "IEEE/ACM Conference on Connected Health (CHASE), August 2026, in review."
- title: "Levshell"
section: engineering
status: building
updated: 2026-04-26
note: "Comprehensive rewrite of my Wayland-targeted productivity and research shell. Major focus over the summer of 2026."
- title: "Pmacs"
section: engineering
status: building
updated: 2026-04-20
note: "Emacs-inspired IDE in Zig with first-class parallelism."
shipped:
- title: "Where Does SIMD Help Post-Quantum Cryptography?"
completed: 2026-04-15
link: /essays/where-does-simd-help-post-quantum-cryptography/
note: "Phase 1 technical report and reproducible artifact. Brown CS Department."

View File

@ -16,6 +16,7 @@ executable site
Authors Authors
Catalog Catalog
Commonplace Commonplace
Now
Backlinks Backlinks
Dingbat Dingbat
SimilarLinks SimilarLinks

198
static/css/now.css Normal file
View File

@ -0,0 +1,198 @@
/* now.css /current.html.
*
* The Now page is a research-first status surface curated like the
* Library, with explicit per-item temporality. It composes on top of
* item-card.css (gated separately in head.html via $if(now)$) and
* library.css's section primitives are deliberately not pulled in
* Now uses its own header treatment without dingbats or shelf
* dividers, since sections here are project-states rather than
* content shelves.
*/
/* ============================================================
PAGE-LEVEL "LAST UPDATED" MASTHEAD
Title leads, date follows in display-weight Spectral italic.
Descending visual hierarchy BIG sans title medium serif
italic date tiny italic relative. The date carries enough
typographic weight to be the page's thesis without competing
with the title for the "what page is this" anchor.
============================================================ */
.now-header {
margin-bottom: 3rem;
}
/* The page title remains the primary anchor sized & weighted
by .page-title in components.css. Generous bottom margin gives
the masthead-date its own breathing room below. */
.now-header .page-title {
margin-top: 0;
margin-bottom: 1.1rem;
}
.now-stamp {
margin: 0;
display: flex;
flex-direction: column;
gap: 0.15rem;
line-height: 1.15;
}
.now-stamp-label {
font-family: var(--font-sans);
font-size: 0.72rem;
font-variant: all-small-caps;
letter-spacing: 0.12em;
color: var(--text-faint);
}
/* The masthead datum. Spectral italic at ~1.85rem so it carries
presence as the page's thesis without competing with the
2.6rem sans title above it. Old-style numerals keep the
literary register; tabular-nums stay aligned if the day
width changes. */
.now-stamp-date {
font-family: var(--font-serif);
font-size: 1.85rem;
font-style: italic;
font-weight: 400;
color: var(--text);
line-height: 1.1;
margin-top: 0.05rem;
font-feature-settings: "onum" 1, "tnum" 1;
}
/* Small italic footer line under the masthead qualifies
the absolute date with a human-readable "how recent." */
.now-stamp-relative {
font-family: var(--font-serif);
font-size: 0.92rem;
font-style: italic;
color: var(--text-faint);
margin-top: 0.3rem;
}
/* ============================================================
INTRO PROSE
Optional body content from current.md, rendered between the
stamp and the first section. Sits in normal page measure; no
drop cap, no special treatment.
============================================================ */
.now-intro {
margin: 0 0 2.5rem;
font-family: var(--font-serif);
font-size: 1rem;
color: var(--text-muted);
line-height: 1.6;
}
.now-intro p:last-child {
margin-bottom: 0;
}
/* ============================================================
SECTION HEADINGS
Spectral small-caps in a quieter accent than library shelves.
No ornaments sections are project-states, not content shelves,
and shouldn't carry the same identity weight.
============================================================ */
.now-section {
margin-bottom: 2.75rem;
}
.now-section-heading {
font-family: var(--font-serif);
font-size: 1.15rem;
font-variant: all-small-caps;
font-feature-settings: "smcp" 1;
letter-spacing: 0.09em;
color: var(--text-muted);
text-transform: none;
font-weight: 400;
margin: 0 0 0.85rem 0;
}
/* The recently-shipped section uses a slightly fainter heading +
a thin top divider to separate it visually from active work. */
.now-section--shipped {
margin-top: 3.5rem;
padding-top: 2.25rem;
border-top: 1px solid var(--border);
}
.now-section--shipped .now-section-heading {
color: var(--text-faint);
font-style: italic;
letter-spacing: 0.11em;
}
/* ============================================================
STATUS CHIP
Replaces the kind badge slot in item-card. Same min-width as
.item-card-kind so titles align across active and shipped
sections. Tabular sans-caps, very low-contrast frame.
============================================================ */
.now-card .now-kind {
/* Override item-card-kind text-only treatment with chip layout */
display: flex;
align-items: flex-start;
margin-top: 0.25em;
}
.now-status {
display: inline-block;
font-family: var(--font-sans);
font-size: 0.66rem;
font-variant: all-small-caps;
letter-spacing: 0.08em;
line-height: 1;
padding: 0.32em 0.55em 0.28em;
border: 1px solid var(--border);
border-radius: 2px;
color: var(--text-muted);
background: transparent;
white-space: nowrap;
}
/* Per-status accents each is intentionally restrained. Active
states (in-review, drafting, building) get slightly stronger
ink; passive states (paused, shipped) recede. */
.now-status--in-review { color: var(--text); border-color: var(--text-muted); }
.now-status--revising { color: var(--text); border-color: var(--text-muted); }
.now-status--drafting { color: var(--text); }
.now-status--building { color: var(--text); }
.now-status--early-stage { color: var(--text-muted); border-style: dashed; }
.now-status--paused { color: var(--text-faint); border-style: dashed; opacity: 0.75; }
.now-status--shipped { color: var(--text-faint); border-color: var(--text-faint); }
.now-card--shipped {
opacity: 0.92;
}
/* ============================================================
MOBILE
Mirror item-card.css's mobile rules for header stacking,
then shrink the chip column so titles get real room.
============================================================ */
@media (max-width: 540px) {
.now-card .now-kind {
margin-top: 0;
}
.now-status {
font-size: 0.62rem;
padding: 0.28em 0.45em 0.24em;
}
.now-stamp-date {
font-size: 1.55rem;
}
.now-stamp-relative {
font-size: 0.88rem;
}
}

15
templates/current.html Normal file
View File

@ -0,0 +1,15 @@
<main id="markdownBody" data-pagefind-body>
<header class="now-header">
<h1 class="page-title">$title$</h1>
<p class="now-stamp">
<span class="now-stamp-label">Last updated</span>
<time class="now-stamp-date" datetime="$now-last-updated$">$now-last-updated-display$</time>
$if(now-last-updated-relative)$<span class="now-stamp-relative">$now-last-updated-relative$</span>$endif$
</p>
</header>
$if(body)$<div class="now-intro">$body$</div>$endif$
$now-entries-html$
$now-shipped-html$
</main>

View File

@ -40,6 +40,8 @@ $if(list-page)$<link rel="stylesheet" href="/css/item-card.css">$endif$
$if(memento-mori)$<link rel="stylesheet" href="/css/memento-mori.css">$endif$ $if(memento-mori)$<link rel="stylesheet" href="/css/memento-mori.css">$endif$
$if(catalog)$<link rel="stylesheet" href="/css/catalog.css">$endif$ $if(catalog)$<link rel="stylesheet" href="/css/catalog.css">$endif$
$if(commonplace)$<link rel="stylesheet" href="/css/commonplace.css">$endif$ $if(commonplace)$<link rel="stylesheet" href="/css/commonplace.css">$endif$
$if(now)$<link rel="stylesheet" href="/css/item-card.css">$endif$
$if(now)$<link rel="stylesheet" href="/css/now.css">$endif$
$if(build)$<link rel="stylesheet" href="/css/build.css">$endif$ $if(build)$<link rel="stylesheet" href="/css/build.css">$endif$
$if(reading)$<link rel="stylesheet" href="/css/reading.css">$endif$ $if(reading)$<link rel="stylesheet" href="/css/reading.css">$endif$
$if(composition)$<link rel="stylesheet" href="/css/score-reader.css">$endif$ $if(composition)$<link rel="stylesheet" href="/css/score-reader.css">$endif$

View File

@ -6,10 +6,12 @@
<div class="nav-primary"> <div class="nav-primary">
<a href="/">Home</a> <a href="/">Home</a>
<a href="/library.html">Library</a> <a href="/library.html">Library</a>
<a href="/links.html">Links</a>
<a href="/me.html">Me</a> <a href="/me.html">Me</a>
<a href="/new.html">New</a> <a href="/new.html">New</a>
<a href="/links.html">Links</a> <a href="/current.html">Current</a>
<a href="/search.html">Search</a> <a href="/search.html">Search</a>
<a href="/about.html">Vita</a>
</div> </div>
<div class="nav-controls"> <div class="nav-controls">
<button type="button" class="nav-portal-toggle" aria-label="Toggle portals" aria-expanded="false"> <button type="button" class="nav-portal-toggle" aria-label="Toggle portals" aria-expanded="false">