diff --git a/.gitignore b/.gitignore index 08dea64..2529e62 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,28 @@ _site/ _cache/ .DS_Store .env -# Defense-in-depth: catch any stray .env / .env.* anywhere in the tree -# (the auto-snapshot in the Makefile stages content/ on every build). +# Defense-in-depth: the auto-snapshot in `make build` stages content/ +# 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 +**/*.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 *~ @@ -58,6 +76,9 @@ data/semantic-meta.json # IGNORE.txt is for the local build and need not be synced. 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 # live under static/ and ship with the site; the pipeline itself is # kept locally for regeneration but not version-controlled here. diff --git a/Makefile b/Makefile index bafec16..07e2389 100644 --- a/Makefile +++ b/Makefile @@ -16,17 +16,12 @@ build: # either reuses it (no new content/ changes) or appends another # snapshot on top, so failures don't disappear from the log. # - # Pathspec is explicit (not `git add content/`) so a stray .env, - # credential file, or other non-content artifact dropped under - # content/ is NOT auto-staged. The :(glob) magic prefix makes `**` - # match across path components (git default fnmatch does not). - # Add new extensions here if a new asset type is introduced. - @git add ':(glob)content/**/*.md' ':(glob)content/**/*.html' ':(glob)content/**/*.bib' \ - ':(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 add content/` respects .gitignore, which excludes credential- + # shaped patterns (.env, *.key, *.pem, id_rsa*, credentials*, etc.) + # so a stray secret dropped under content/ is NOT auto-staged. To + # intentionally commit a normally-ignored file, use `git add -f` + # manually before running `make build`. + @git add content/ @git diff --cached --quiet || git commit -m "auto: $$(date -u +%Y-%m-%dT%H:%M:%SZ) [skip ci]" @mkdir -p data @date +%s > data/build-start.txt diff --git a/build/Now.hs b/build/Now.hs new file mode 100644 index 0000000..2381b42 --- /dev/null +++ b/build/Now.hs @@ -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 + [ "" + , escapeHtml (titleCaseWords s) + , "" + ] + +-- | 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 + [ "
  • " + , "" + , renderStatusChip (neStatus e) + , "" + , "
    " + , "
    " + , renderTitle (neLink e) (neTitle e) + , "" + , "
    " + , maybe "" (\n -> "

    " ++ escapeHtml n ++ "

    ") (neNote e) + , "
    " + , "
  • " + ] + +renderShippedEntry :: NowShipped -> String +renderShippedEntry s = concat + [ "
  • " + , "" + , renderStatusChip "shipped" + , "" + , "
    " + , "
    " + , renderTitle (nsLink s) (nsTitle s) + , "" + , "
    " + , maybe "" (\n -> "

    " ++ escapeHtml n ++ "

    ") (nsNote s) + , "
    " + , "
  • " + ] + +renderTitle :: Maybe String -> String -> String +renderTitle mu title = case mu of + Just url -> "" ++ escapeHtml title ++ "" + Nothing -> "" ++ escapeHtml title ++ "" + +renderSection :: String -> [NowEntry] -> String +renderSection sec es = concat + [ "
    " + , "

    " + , escapeHtml (titleCaseWords sec) + , "

    " + , "" + , "
    " + ] + +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 + [ "
    " + , "

    Recently Shipped

    " + , "" + , "
    " + ] + 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 diff --git a/build/Site.hs b/build/Site.hs index c06e8d4..79ae90a 100644 --- a/build/Site.hs +++ b/build/Site.hs @@ -27,6 +27,7 @@ import Compilers (essayCompiler, postCompiler, pageCompiler, poetryCompiler, fi compositionCompiler, sidecarCompiler) import Catalog (musicCatalogCtx) import Commonplace (commonplaceCtx) +import Now (nowCtx) import Contexts (siteCtx, essayCtx, postCtx, pageCtx, poetryCtx, fictionCtx, compositionCtx, contentKindField, recentFirstByDisplay, tagLinksFieldExcludingTopSegment) @@ -206,6 +207,10 @@ rules = do -- with dependency tracking by the commonplace page compiler. 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 -- --------------------------------------------------------------------------- @@ -261,6 +266,19 @@ rules = do >>= loadAndApplyTemplate "templates/default.html" commonplaceCtx >>= 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 route $ constRoute "colophon.html" compile $ essayCompiler @@ -272,6 +290,7 @@ rules = do .&&. complement "content/index.md" .&&. complement "content/commonplace.md" .&&. complement "content/colophon.md" + .&&. complement "content/current.md" .&&. complement "content/library.md") $ do route $ gsubRoute "content/" (const "") `composeRoutes` setExtension "html" diff --git a/data/now.yaml b/data/now.yaml new file mode 100644 index 0000000..7ceda9d --- /dev/null +++ b/data/now.yaml @@ -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 2026–2027." + + - 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." diff --git a/levineuwirth.cabal b/levineuwirth.cabal index 77eef3a..4183bcc 100644 --- a/levineuwirth.cabal +++ b/levineuwirth.cabal @@ -16,6 +16,7 @@ executable site Authors Catalog Commonplace + Now Backlinks Dingbat SimilarLinks diff --git a/static/css/now.css b/static/css/now.css new file mode 100644 index 0000000..7a38aca --- /dev/null +++ b/static/css/now.css @@ -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; + } +} diff --git a/templates/current.html b/templates/current.html new file mode 100644 index 0000000..7a366ae --- /dev/null +++ b/templates/current.html @@ -0,0 +1,15 @@ +
    +
    +

    $title$

    +

    + Last updated + + $if(now-last-updated-relative)$$now-last-updated-relative$$endif$ +

    +
    + + $if(body)$
    $body$
    $endif$ + + $now-entries-html$ + $now-shipped-html$ +
    diff --git a/templates/partials/head.html b/templates/partials/head.html index acf7622..2cdd472 100644 --- a/templates/partials/head.html +++ b/templates/partials/head.html @@ -40,6 +40,8 @@ $if(list-page)$$endif$ $if(memento-mori)$$endif$ $if(catalog)$$endif$ $if(commonplace)$$endif$ +$if(now)$$endif$ +$if(now)$$endif$ $if(build)$$endif$ $if(reading)$$endif$ $if(composition)$$endif$ diff --git a/templates/partials/nav.html b/templates/partials/nav.html index a60e6f2..4d1526e 100644 --- a/templates/partials/nav.html +++ b/templates/partials/nav.html @@ -6,10 +6,12 @@