Personal site for Charles Knight. Jekyll, no JavaScript, no third-party requests.
Content lives in Markdown and YAML; GitHub Actions builds it to static HTML on push. Nothing runs at request time.
The site is three pages: the front page, /about/, and /abaco/. Almost everything you'd
want to change is in _data/ or in one of those page files. You do not need to touch HTML or
CSS to add a job, swap the cover photograph, or change a link.
| To change | Edit |
|---|---|
| The front page — cover photo, paragraph, the three links | index.md |
| The long biography | about.md |
| The organisations shown on About | _data/roles.yml |
| The Charles Kerlee essay | abaco.md |
| Which photo opens a page | portrait: in that page's front matter |
| Email, location, LinkedIn, the nav | _config.yml |
The front page opens with the abaco wordmark and one photograph. The photograph is set in
index.md — cover, cover_w, cover_h, cover_alt, cover_credit — and nothing else on
the site refers to it, so replacing it is a five-line edit plus dropping a file in
assets/img/.
The one there now is a stand-in: a freely-licensed 1989 photograph of Hope Town harbour
from Wikimedia Commons, chosen because it is the right island. It is CC BY-SA 2.0, which is
why cover_credit names the photographer and links the licence — keep a credit of that shape
for anything else pulled from Commons. When Charlie's own print is scanned, that obligation
goes away with it; empty cover_credit and the caption disappears.
Set cover_w and cover_h to the file's real pixel dimensions. They reserve the space while
the image loads, so the page doesn't jump. The image is shown at its own aspect ratio at every
width — nothing is cropped in the browser — so crop it to the shape you want before saving it.
The current one is 2400×1000.
_includes/abaco-mark.html is the wordmark inlined as SVG so it can take fill: currentColor and follow the --brand token into dark mode. assets/img/abaco-wordmark.svg
is the same mark as a standalone file, and assets/img/abaco-wordmark@3x.png is the raster
version the link-preview script uses. All three come from the identity pack; the "tag" lockup
in that pack, which carries a strapline, is deliberately not used.
/writing/ and /ask/ are switched off, at Charlie's request — the articles are on the
Milgrom Daskam & Ellis site and he'd rather not republish them here for now. Nothing has been
deleted. The six articles are in _drafts/, the two pages carry published: false, and the
question bank is still in _data/ask.yml.
To bring them back:
git mv _drafts/*.md _posts/
# delete the `published: false` line from writing.md and ask.md
# add Writing and Ask back to `nav:` in _config.ymlThe layouts, the CSS and the import script are all untouched, so that's the whole job.
Restore the two together — several answers in _data/ask.yml link to /writing/ articles.
Preview the drafts meanwhile with bundle exec jekyll serve --drafts.
Everything below, down to the end of this section, describes those parked pages and applies again once they're back.
Every article is kept in full on this site, including the six that first ran on the Milgrom Daskam & Ellis blog. That's deliberate: if those pages ever come down, nothing here breaks. Search engines are told this site is the canonical copy, and each imported piece still credits and links its original at the foot of the article.
One file per article in _posts/, named YYYY-MM-DD-slug.md, published at
/writing/<slug>/:
---
title: "How to lose a deal in ten days"
date: 2026-08-14
excerpt: >-
One or two sentences. Shown under the title in the article list.
venue: Colorado Real Estate Journal # omit both of these
source_url: https://example.com/… # for something written for this site
---
Body text in Markdown.Leave venue and source_url off and it's simply an original post — no credit line, no
outbound marker. That's the expected shape for anything written from here on.
To archive something published elsewhere, add it to ARTICLES in
tools/import-article.py and run the script; it pulls the page, converts it to Markdown,
and writes the post with front matter filled in. Header comments in that file explain the
one-off dependencies. It overwrites generated posts, so don't hand-edit an imported
article and then re-run it.
In _data/ask.yml, under whichever theme fits:
- q: The question, written the way a host would actually ask it.
id: corporate-transparencyid is required and must be unique across the file. It does two jobs: it's the deep link
(/ask/#corporate-transparency), and it's the URL inside the pre-filled email. Reword a
question freely, but don't change its id — that breaks any link already shared.
Add answer: and the question becomes expandable. Answers accept links:
- q: Has anyone actually read the Corporate Transparency Act?
id: corporate-transparency
answer: >-
Short version, with the caveats: <a href="https://example.com">here</a>.Themes are just list items — add, reorder, or delete them freely.
Every question carries an envelope that opens the visitor's mail client with the message
already written — addressed to site.email, titled Interview question: <the question>, and
with a body quoting the question and linking back to it. Someone who cares about one topic
can start that exact conversation without composing anything.
It's all built in _layouts/ask.html; there's nothing to configure. One detail worth knowing
if you edit that template: the question text is escaped with url_encode and then has +
swapped back to %20. Jekyll's uri_escape is not safe here — it leaves & and #
unescaped, so a question containing "M&A" would silently truncate the email body.
Each page opens with one portrait, set in that page's own front matter:
portrait: /assets/headshots/milgrom-law.png
portrait_alt: Charles Knight in a grey and rust plaid jacket, against a concrete wall.
portrait_w: 600 # the file's real pixel width
portrait_h: 599 # and height — these reserve the space so the page doesn't jumpPhotos are shown whole, at their natural proportions, never cropped to a fixed shape. On About the portrait floats inside the narrative and the text wraps around it; everywhere else it sits beside the page title. Below ~46rem everything stacks to a single column.
There are currently three photographs in assets/headshots/, so /ask/ shares the home
page's. Drop a fourth file in and point ask.md at it to give every page its own.
Current assignments:
| Page | Photo |
|---|---|
| Home | denver-housing-authroity.jpg |
| About | milgrom-law.png |
| Writing | hilltop-ventures.jpg |
| Ask | denver-housing-authroity.jpg (shared with Home) |
| Abaco | no portrait — the page opens straight into the prose |
Drop the file in assets/img/, then use one of two includes. Both need real pixel
dimensions so the page doesn't jump while the image loads.
Inline, inside any Markdown page:
{% include figure.html src="/assets/img/pearl-street.jpg"
alt="Describe what's in the frame, for screen readers."
caption="Old South Pearl Street, September."
width="1600" height="1067" %}Full-bleed band between sections — one per page at most:
{% include band.html src="/assets/img/denver.jpg" alt="…" caption="…"
width="2400" height="1200" %}band does not currently go full-bleed inside an article. There is no breakout rule for
it, so within .prose it renders at the 36rem measure — narrower than figure--wide — and
its caption indents to a different edge than the image. It also crops (object-fit: cover),
which nothing else on the site does. /abaco/ tried it and switched every image to
figure--wide. Until someone adds the breakout CSS, reach for figure with wide="true".
Existing portraits are in assets/headshots/, named for where each one was used.
assets/og-card.png is the 1200×630 image LinkedIn, Slack, and iMessage show when someone
shares the site. It mirrors the front page — wordmark, then the cover photograph — and reads
the same two image files the page does, so swapping the cover photo means regenerating the
card:
python3 tools/make-og-card.py # needs Pillow: pip3 install PillowThe only text on it is the name and the location, in the NAME and PLACE constants at the
top of the script. It carries no headline, because the page doesn't have one; the script warns
on stderr if either line grows too wide for the card's margins.
The favicon is assets/favicon.svg (a CK monogram that inverts with the viewer's theme) and
assets/apple-touch-icon.png — both still his initials rather than the firm's mark, which is
the intended split: the site is his, the wordmark belongs to the LLC.
bundle install
bundle exec jekyll serve
# http://localhost:4000Edits to Markdown, YAML, and CSS rebuild automatically. Changes to _config.yml need a
restart.
Push to main. .github/workflows/pages.yml builds with Jekyll 4 and publishes to GitHub
Pages — live at https://abacollc.com in a minute or two. There is no staging; preview locally
first.
Production is: GitHub Pages (hosting, free) + Cloudflare (DNS and domain registration, on
Chris's personal account) + Google Workspace (his email — independent of the site). The full
architecture, DNS zone, hard constraints (e.g. the repo must stay public or Pages stops
serving), and a troubleshooting runbook live in CLAUDE.md.
_config.yml site metadata, nav, contact details
_data/ roles, ask — the content that isn't prose
_drafts/ the six articles, parked (see "Writing and Ask, parked")
_layouts/ home · about · writing · ask · page · post · default
_includes/ head · nav · footer · page-header · abaco-mark · figure
band · icon-mail · person-jsonld
tools/ make-og-card.py, import-article.py — not part of the build
index.md about.md abaco.md the three live pages
writing.md ask.md parked, published: false
assets/css/site.css the entire stylesheet
assets/fonts/ Fraunces, Source Serif 4, IBM Plex Sans Condensed (woff2, latin subset)
assets/headshots/ existing portraits
assets/img/abaco/ Charles Kerlee's photographs and book pages, for /abaco/
assets/img/ new photography goes here
assets/og-card.png link-preview image, generated by tools/make-og-card.py
Design. The palette is sampled from the photographs — the cool studio backdrop becomes
the page ground, the pocket square becomes the accent, the jacket becomes the secondary. So
portraits sit inside the page instead of on top of it. The structural device is the
question: sections open with one, and /ask/ is nothing but. All colours are CSS variables
at the top of assets/css/site.css, with a dark-mode block right below.
Fonts are self-hosted, so the site makes zero third-party requests and works offline.
Fraunces carries the display type (its WONK axis is what makes the italic in the headline
distinctive); Source Serif 4 is the body; IBM Plex Sans Condensed handles labels and dates.
Structure borrows from Tailwind's Spotlight template — the two-column home page with a sidebar, the soft card panel that scales in on hover, the "Read article →" affordance, the rounded corners. The typography and palette are the site's own.
No JavaScript. The expandable answers on /ask/ are <details> elements, the envelope
buttons are plain mailto: links, and the theme follows the operating system.
Verified: no horizontal overflow at 360–1440px on any page; WCAG AA contrast in both
light and dark; animations disabled under prefers-reduced-motion; no third-party network
requests; all 25 mail links decode to the correct subject, body, and anchor.
- Prose is a first draft. The home page short bio, the About narrative, and every
question in
_data/ask.ymlwere written from public sources (the Milgrom Daskam & Ellis profile and the Denver Housing Authority board page). Charlie should rewrite them in his own voice — particularly the questions, which are the point of the page. - Abaco, LLC now links from the footer to
/abaco/, which explains where the name comes from. If Charlie holds a specific title at the LLC, that still wants adding to_data/roles.yml. - Photography. Beyond the headshots and
/abaco/, there is nothing. Thefigureinclude is ready for whatever gets sourced. Thebandinclude is not — see below.