{
  "id": "v1-tm-3233",
  "long_id": "v1.terminal.blog.s03.p02.t03.u03",
  "name": "Terminal, a personal site as a shell session",
  "era": {
    "slug": "terminal",
    "code": "tm",
    "name": "Terminal",
    "years": [
      2015,
      2026
    ],
    "description": "The text-terminal look of 2015 to 2026: a monospace face for everything, bright text on a dark screen, boxes and rules drawn with borders like character frames, prompts and cursors, status lines and key hints instead of buttons with chrome."
  },
  "archetype": "blog",
  "mode": "mix",
  "viewport": {
    "width": 1280,
    "fluid": false
  },
  "summary": "This is a personal site or weblog written as a shell session, common from 2015 to 2026 among programmers who wanted a home page that looked like where they work. Every block of the page is the output of a command typed at a prompt above it: the introduction answers `whoami`, the posts are a long directory listing with modes, sizes and dates, and the page ends at an empty prompt with a block cursor. One monospace face sets everything, frames are one-cell lines with their titles cut in, and an editor's status line is stuck to the foot of the window.",
  "url": "https://chrono.skin/s/v1-tm-3233",
  "preview_url": "https://chrono.skin/s/v1-tm-3233/specimen.html",
  "files": {
    "STYLE.md": "# Terminal, a personal site as a shell session\n\n## Summary\n\nThis is a personal site or weblog written as a shell session, common from 2015 to 2026 among programmers who wanted a home page that looked like where they work. Every block of the page is the output of a command typed at a prompt above it: the introduction answers `whoami`, the posts are a long directory listing with modes, sizes and dates, and the page ends at an empty prompt with a block cursor. One monospace face sets everything, frames are one-cell lines with their titles cut in, and an editor's status line is stuck to the foot of the window.\n\n## Layout\n\n- The page is designed for a 1280px window and does not stretch: `.ds-session` is one centred column of at most `--size-page` (920px) on `--color-canvas`, with `3ch` of side padding, at least as tall as the window. It holds the header, the views and the footer.\n- `.ds-nav` is the first line of the session, with a rule below: the `.ds-brand`, then `.ds-nav__list` whose `.ds-nav__link` items are written as paths under the home directory (`~/posts`; the stylesheet adds the `~/`), and a muted `.ds-nav__host` at the right. The current link is inverse video.\n- The page is a rhythm of prompt and output: a `.ds-prompt` line, then what it prints in `.ds-out` (or a component with its own margin), `--space-4` (36px) before the next prompt. Nothing is boxed unless the command would print a box.\n- The home view is `.ds-cols`: the session at the left and a `--size-side` (230px) column at the right. The session runs four commands: `whoami` prints the `.ds-hero`, `wc` the `.ds-stats` line, `ls -lt` the `.ds-list` of posts, `ls projects/` the `.ds-grid`, two cells across; it ends at an empty prompt with the `.ds-cursor`. The right column is the `.ds-sidebar` (always at the right): framed blocks of tags and links and one panel.\n- Inner pages have no hero. They open with the `.ds-breadcrumb` as a path from `~`, then `.ds-page-header` (the post view has only the path and the typed `cat` line, because the text starts with its own title). The archive is one column: prompt, tabs written as options, the table in `.ds-tabs__body`, pagination, then the empty state under its own prompt. A post is `.ds-cols` with the text at the left and the sidebar at the right. About and contact are `.ds-cols--even`, two halves each under its own prompt.\n- Views. The specimen is one small site with five views, one showing at a time; header and footer are written once around them. `home` is the session described above. `posts` is the archive. `post` is one post: notices, running text with a listing, related links, pagination, beside its figures, badges and contents. `about` is a text beside a table and a panel. `contact` is the form with its error and four buttons, beside the dialog that asks before a draft is removed. A view is `<section class=\"ds-view\" id=\"...\">`; links between views are plain `href=\"#id\"`.\n- Spacing scale, in quarters and multiples of one 24px text line: `--space-1` 6px is the vertical padding of buttons, inputs, tabs and listing rows; `--space-2` 12px the gap between a prompt and its output and the padding in a frame; `--space-3` 24px the gap between paragraphs and under the header; `--space-4` 36px the gap between one command's output and the next prompt; `--space-5` 48px the room kept above the status line.\n- Fixed sizes: `--size-label` 120px the label column of a form, `--size-field` 480px a text input, `--size-date` 110px the date column of the listing, `--size-dialog` 420px a dialog.\n- Below 720px there is one column: the sidebar goes under the session, the grid is one cell across, the listing keeps its date and name and drops mode and size, form labels sit above their fields, and table cells marked `.ds-table__extra` are left out. The status line stays stuck to the foot. The page never scrolls sideways; only a `.ds-code` block does, inside itself.\n\n## Typography and colour roles\n\nThis style combines the parts of different styles of one era. Where a note below names a value that differs from `tokens.css`, the tokens are right.\n\n- One face for everything: `--font-body`, `--font-heading`, `--font-ui` and `--font-mono` are the same monospace stack (\"SF Mono\", \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, monospace). Weights are `--weight-body` 400, bold and `--weight-ui` 700, and `--weight-display` 800 for the hero title. The stack starts with SF Mono and Cascadia Code, the faces terminal programs ship with. Nothing is tracked: `--heading-tracking` and `--display-tracking` are 0.\n- Sizes stay close to one size, as on a screen of character cells: `--text-base` 14px with `--line-body` 1.45 is all running text, `--text-ui` 13px navigation, buttons, inputs, tabs and pagination, `--text-small` 12px meta lines, hints, badges, code blocks and the status line, `--text-large` 16px the hero's lead and the brand name. Headings are told apart by weight and colour more than by size: `--text-display` 32px is the hero title only, `--text-h1` 20px a page title and the first heading of running text, `--text-h2` 16px section rules and second headings, `--text-h3` 14px frame titles, grid titles and third headings. `--heading-transform` is none, `--ui-transform` is none.\n- A modern sixteen-colour scheme on blue-grey, with each colour given a role: cyan for links, blue for quiet links, bars and heavy rules, magenta for the primary button, the prompt path, frame titles and tags, yellow for the accent, focus and warnings, green and red for status.\n- The screen: `--color-page` #181825 is the window, `--color-canvas` #1e1e2e the sheet the text stands on, `--color-surface` #24253a the inside of frames and table rows, `--color-surface-alt` #2a2b40 alternate rows, code blocks and the install line, `--color-surface-strong` #45475a group rows and inline code. `--color-text` #bac2de is running text, `--color-text-muted` #9399b2 dates, counts, hints and the brackets of fields, `--color-heading` #cdd6f4 headings, typed commands and figures, `--color-heading-alt` #cba6f7 frame titles, third headings and tags.\n- Links: `--color-link` #89dceb, `--color-link-quiet` #89b4fa for navigation, breadcrumb, tabs and sidebar links, `--color-link-visited` #b4befe, `--color-link-hover` #f5c2e7, `--color-link-active` #f9e2af. `--link-decoration` is underline for links in text and titles, `--link-decoration-quiet` none for navigation, and every link takes `--link-decoration-hover` (underline) when hovered. A link may also be marked by brackets (`.ds-link--bracket`).\n- Bars: `--color-bar` #89b4fa with `--color-bar-text` #11111b is the head row of a table and, where a layout has one, the menu line and the labels of the key bar. `--color-bar-alt` #313244 with `--color-bar-alt-text` #cdd6f4 is the status line at the foot. `--color-inverse` #cdd6f4 with `--color-inverse-text` #1e1e2e is inverse video: the current navigation link, tab, page number and sidebar link, the selected table row, a text selection.\n- Lines: `--color-border` #585b70 at `--border-width` 2px `--border-style` solid draws every frame and rule; `--color-border-strong` #89b4fa at `--border-width-strong` 4px is the rule under a page header, the left edge of a notice and the frame of a dialog; `--color-border-muted` #3b3d54 parts rows inside a frame and boxes the empty state.\n- Accents: `--color-accent` #f9e2af with `--color-accent-text` #1e1e2e is the count badge; `--color-accent-alt` #cba6f7 is the prompt sign and flag letters. `--color-fill-1` to `--color-fill-4` (#1e2a3a, #2a2238, #2e2a24, #1f2e2a) are the four grid cells. Status words are text in brackets: `--color-success` #a6e3a1, `--color-warning` #f9e2af, `--color-danger` #f38ba8; meters use the first two.\n- Controls: the primary button is `--color-button` #cba6f7 with `--color-button-text` #11111b; the secondary button is `--fill-button-secondary` with `--color-button-secondary-text` #cdd6f4 and a frame in `--color-border`; fields are `--fill-input` with `--color-input-text` #cdd6f4 and a line in `--color-input-border` #585b70; disabled is `--color-disabled` #313244 with `--color-disabled-text` #6c7086. The notice is `--color-notice` #1e2a3a with `--color-notice-text` #b4d8f0, the error notice `--color-danger-surface` #302030 with `--color-danger`. `--color-overlay` #313244 is the block a dialog stands on; `--color-focus` #f9e2af is the focus outline.\n- Surface: Lines are 2px and solid, the heavy rule 4px. `--fill-panel` is `--color-surface`; `--fill-button-secondary` and `--fill-input` are their own colours; `--fill-button-hover` lightens the button (60% of it over white). Shadows are hard lines and blocks only: `--shadow-control-hover` is a 2px ring in `--color-focus`, `--shadow-control-pressed` a 2px inner line in `--color-shadow`, and `--shadow-dialog` drops a block of `--color-shadow` 8px down and right, the way a text-mode dialog throws its shadow one cell away; `--shadow-panel`, `--shadow-control` and `--shadow-text` are none. All four radii are 0 and `--backdrop-blur` is 0px. `--focus-ring` is 2px solid var(--color-focus); `--transition` is none: states change at once. The only motion is the blinking `.ds-cursor`, and only under `prefers-reduced-motion: no-preference`.\n- The era's rule for which text stands on which fill (all three layouts keep it, so any palette works with any layout): `--color-text`, `--color-text-muted`, the two heading colours, every link colour and the three status colours stand on `--fill-page`, `--color-canvas`, the three surfaces, `--fill-panel` and the four fills, and on nothing else. `--color-bar-text` only on `--fill-bar`; `--color-bar-alt-text` only on `--fill-bar-alt`; `--color-inverse-text` only on `--fill-inverse`; `--color-accent-text` only on `--fill-accent`; `--color-button-text` and `--color-button-hover-text` only on `--fill-button` and its hover; `--color-notice-text` only on `--color-notice`; `--color-danger` also on `--color-danger-surface`; `--color-disabled-text` on `--color-disabled`. Two tokens must read in two places, because a surface set may leave a control unfilled: `--color-button-secondary-text` on `--fill-button-secondary` and on the canvas and surfaces, `--color-input-text` on `--fill-input` and on the canvas and surfaces. Where the navigation stands on `--fill-bar`, its current item is a hole in the bar: `--color-canvas` with `--color-heading`. Nothing but a button, a badge or a key label stands on a bar or an inverse block.\n- One-off sizes written with `calc()` or `ch`: a frame title is lifted by half of `--text-h3` times `--line-body` so that the frame's top line runs through it; the brand tile is one text line square (`--text-base` times `--line-body`); horizontal paddings and column gaps are whole character cells (`1ch`, `2ch`, `3ch`, `4ch`); the checkbox is `2ch` square.\n\n## Components\n\n- `.ds-page` on `<body>`: page fill and base type, no margin. `.ds-session` is the centred column; `.ds-cols` (`--even`) are its column pairs, `.ds-main` a column that may shrink, `.ds-out` the output of one command.\n- `.ds-nav`: the header line. `.ds-nav__list` holds `.ds-nav__link` items written as paths, and `.ds-nav__host` is the muted text at the right. The stylesheet marks the link whose `href` names the view that is showing (one selector per view, and the home link when the address names none); on a page of its own give the link `is-current`.\n- `.ds-hero`: what `whoami` prints: `.ds-hero__title` (`--text-display`), `.ds-hero__lead` at most 62 characters wide, and a `.ds-buttons` row. No box, no fill, left-aligned, under its prompt.\n- `.ds-footer`: an editor's status line on `--fill-bar-alt`, stuck to the foot of the window: `.ds-footer__mode` (a bold word on `--fill-bar`), then `.ds-footer__seg` segments, the last pushed right with `.ds-footer__seg--right`; links in it are `.ds-footer__link`, key hints `.ds-key`.\n- `.ds-brand`: the site's mark and name, a link to the home view, first thing in the header. `.ds-brand__mark` is a tile one text line square, dressed like the primary button (`--fill-button`, `--color-button-text`, the button's frame, `--radius-control`, `--shadow-control`); `.ds-brand__name` is bold at `--text-large`. The name `chronoskin` and the mark are placeholders for the installing project's own name and logo.\n- `.ds-breadcrumb`: a path: `.ds-breadcrumb__link` items with `.ds-breadcrumb__sep` (`/`) between them, ending in a bold `.ds-breadcrumb__current`.\n- `.ds-page-header`: the head of an inner page: `.ds-page-header__main` with `.ds-page-header__title` (`--text-h1`) and one muted `.ds-page-header__text` line; `.ds-page-header__action` at the right holds one button. A rule of `--border-width-strong` closes it.\n- `.ds-prose`: running text, at most 78 characters wide: h1, h2, h3, paragraphs, lists whose bullet is a typed dash, `code` on `--color-surface-strong`, `strong` in the heading colour. Headings keep their marks, as in the source file: the stylesheet writes `#`, `##` or `###` before them in the muted colour. Anything typed literally goes in `.ds-code`, a `<pre>` at `--text-small` on `--color-surface-alt` in a frame; it scrolls sideways inside itself.\n- `.ds-link`: text link with `is-visited`, `is-hover`, `is-active`, `is-focus`. `.ds-link--quiet` for meta and tool links, `.ds-link--bracket` for a link written as a word in brackets (`[copy]`, `[get]`). `.ds-links` sets several in a wrapping line.\n- `.ds-button`: a `<button>`, or an `<a>` when it only leads to another view. It is a word after a `>` sign, which the stylesheet adds. The primary one is a block of `--fill-button`; `.ds-button--secondary` is a frame in `--color-border`; `.ds-button--danger` is the destructive action in `--color-danger` on `--color-danger-surface`; `disabled` or `is-disabled` is greyed. `is-hover`, `is-active` and `is-focus` show the states. Group with `.ds-buttons`.\n- `.ds-form`: rows (`.ds-form__row`) of a `.ds-form__label` ending in a colon and a `.ds-form__field`. Controls take `.ds-form__input` (`--wide` for the full width, `--auto` for selects). `is-invalid` plus a bold `.ds-form__error` marks a validation error, `.ds-form__hint` is help text, `.ds-form__check` holds a `.ds-form__checkbox` (one empty cell, a filled block when set), and `.ds-form__actions` lines the buttons up with the fields.\n- `.ds-table`: aligned columns under a head row on `--fill-bar`. `.ds-table__num` right-aligns a numeric column, `.ds-table__code` is a bold first column, `.ds-table__date` a muted one, `tr.is-alt` an alternate row, `.ds-table__group` a group row, `tr.is-selected` the row under the cursor in inverse video, `tr.is-unread` a bold row, `.ds-table__title` the link in a row, `.ds-table__extra` cells a phone leaves out. Rows stand on the canvas, parted by faint rules; only alternate rows are tinted.\n- `.ds-list`: a long directory listing. Each `.ds-list__item` is a muted `.ds-list__perm` (the mode), a right-aligned `.ds-list__size`, a `.ds-list__date` and a `.ds-list__main` with the file name as a bold `.ds-list__title` link and a small `.ds-list__meta` line under it. `.ds-list__total` is the muted first line (`total 38`). No rules between rows.\n- `.ds-panel`: a frame with its `.ds-panel__title` cut into the top line, and a `.ds-panel__body` of `.ds-panel__text` paragraphs. The title row draws the top line itself (a one-cell rule, the title, a rule to the corner), so the frame works on any fill and with any border style; a panel always has a title.\n- `.ds-stat`: one figure of a line that a counting command prints: a bold `.ds-stat__value` at `--text-large` and its `.ds-stat__label` after it. `.ds-stats` is the line: the figures side by side, `4ch` apart, with no boxes.\n- `.ds-grid`: four `.ds-grid__cell` boxes (two across, one on a phone), each a frame on `--color-fill-1` (`--2`, `--3`, `--4` select the other fills), with a `.ds-grid__title`, `.ds-grid__text` and an optional muted `.ds-grid__label`.\n- `.ds-tabs`: a row of words on a rule; each `.ds-tabs__tab` is a link, the current one a `<span class=\"ds-tabs__tab is-current\">` in inverse video. Each tab is written as an option, after two dashes. Content goes in `.ds-tabs__body`.\n- `.ds-badge`: a count: bold `--text-small` on `--fill-accent`. `.ds-badge--tag` is a word after a `#` in `--color-heading-alt`. Status labels are `.ds-badge--success`, `.ds-badge--warning` and `.ds-badge--danger`: a word in brackets in the status colour, with no fill and no frame. `.ds-badges` sets several in a line.\n- `.ds-sidebar`: the right column of the home and post views. Each `.ds-sidebar__block` is a frame with a `.ds-sidebar__title` cut into its top line and a `.ds-sidebar__list` of `.ds-sidebar__link` items (`is-current` in inverse video) with optional `.ds-sidebar__count`; `.ds-sidebar__text` is a small muted paragraph.\n- `.ds-notice`: a sentence in a frame of `--color-border-strong` with a heavy left edge, starting with a bold `.ds-notice__title`. `.ds-notice--error` uses `--color-danger` on `--color-danger-surface`. Stack several in `.ds-notices`.\n- `.ds-pagination`: a line of `.ds-pagination__link` items: previous, numbers, next. The current page is a `<span>` with `is-current` in inverse video; `is-disabled` is muted.\n- `.ds-dialog`: a frame in `--color-border-strong`, `--size-dialog` wide, with `--shadow-dialog`. It sits in the page flow on `.ds-dialog__backdrop`, a flat block of `--color-overlay`; it has a `.ds-dialog__title`, a `.ds-dialog__body`, `.ds-dialog__text` and `.ds-dialog__actions`. No dimming of the page, no blur.\n- `.ds-empty`: a faint frame with a centred muted `.ds-empty__text` sentence and one secondary button.\n- Text-mode pieces: `.ds-prompt` is a typed command line (`.ds-prompt__sign` the `$`, `.ds-prompt__path` the directory); `.ds-cursor` is the block cursor, a `<span>` holding one no-break space; `.ds-key` is a key hint whose `.ds-key__cap` is set in brackets (`[q] quit`); `.ds-meter` is a progress bar typed in block characters, the filled part in `.ds-meter__on` (`--warning` for the amber one); `.ds-section__title` is a heading followed by a rule to the right edge; `.ds-note` is a small muted line under a block.\n\n## Never\n\n- `font-families <= 1`: one monospace face sets everything, headings and buttons included.\n- `font-size <= 32px`: the largest text is the hero title; everything else is within a few pixels of the body size.\n- `font-size >= 12px`: small text is the meta size; nothing is set smaller.\n- `letter-spacing <= 0px`: the grid of the typeface is never stretched.\n- `line-height <= 1.6`: lines sit as close as a terminal sets them.\n- `border-radius <= 0px`: a character cell has no round corner; every box, button and field is square.\n- `box-shadow-blur <= 0px`: nothing is soft and nothing floats; where a set has a shadow at all it is a hard offset block.\n- `gradient-fills <= 0%`: every fill is one flat colour.\n- `border-width <= 4px`: lines are one weight, with one heavier rule.\n- `transition = none`: states change at once, as a screen redraws.\n- `text-shadow = none`: text is flat.\n\n## Extending\n\nDerive a new component from the nearest one in the specimen. A boxed thing copies `.ds-panel`: a frame of `--border-width` in `--color-border` with its title cut into the top line and `--space-2` and `2ch` of padding. A row type copies a `.ds-table` row or a `.ds-list__item`. A set of choices copies `.ds-tabs`, and whatever is current or selected is inverse video (`--fill-inverse` with `--color-inverse-text`). A status is a word in brackets in its status colour; a quantity is a `.ds-meter` of block characters; an action is a word dressed as `.ds-button`, with its key given as a `.ds-key`. Use only the tokens of `tokens.css` and the `--size-*` and `--space-*` tokens of `components.css`, write horizontal steps in `ch`, and pair each text token with its own fill as listed above. When in doubt do less: text before a box, a rule before a fill, a typed character before an icon, and never a radius, a soft shadow, a gradient, a picture or a second typeface.\n",
    "components.css": "/* Layout tokens (structure part). A shell session: one narrow column, a\n   24px text line as the unit, a status line stuck to the foot of the window. */\n:root {\n  --size-page: 920px;\n  --size-side: 230px;\n  --size-label: 120px;\n  --size-field: 480px;\n  --size-dialog: 420px;\n  --size-date: 110px;\n  --space-1: 6px;\n  --space-2: 12px;\n  --space-3: 24px;\n  --space-4: 36px;\n  --space-5: 48px;\n}\n\n/* ---------- shell: one face, one size, a screen of text ---------- */\n.ds-page {\n  margin: 0;\n  padding: 0;\n  background: var(--fill-page);\n  color: var(--color-text);\n  font-family: var(--font-body);\n  font-size: var(--text-base);\n  font-weight: var(--weight-body);\n  line-height: var(--line-body);\n}\n/* Selection is inverse video. */\n.ds-page ::selection {\n  background: var(--fill-inverse);\n  color: var(--color-inverse-text);\n}\n/* ---------- views: one screen of the example site shows at a time ---------- */\n.ds-view { display: none; }\n.ds-view:target, .ds-page:not(:has(.ds-view:target)) .ds-view--home { display: block; }\n.ds-view:target { scroll-margin-top: 100vh; }\n\n/* ---------- frames: a box of 1-cell lines with its title cut into the top edge.\n   The top edge is drawn by the title row (a short rule, the title, a long rule),\n   so it works on any fill and with any border style. ---------- */\n.ds-panel,\n.ds-dialog,\n.ds-sidebar__block {\n  position: relative;\n  display: flow-root;\n  box-sizing: border-box;\n  min-width: 0;\n  margin: calc(var(--text-h3) * var(--line-body) * 0.5) 0 0;\n  border: var(--border-width) var(--border-style) var(--color-border);\n  border-top: 0;\n  border-radius: var(--radius-panel);\n  background: var(--fill-panel);\n  box-shadow: var(--shadow-panel);\n}\n.ds-panel__title,\n.ds-dialog__title,\n.ds-sidebar__title {\n  display: flex;\n  align-items: center;\n  gap: 1ch;\n  margin: calc(var(--text-h3) * var(--line-body) * -0.5) calc(var(--border-width) * -1) 0;\n  padding: 0;\n  overflow: hidden;\n  white-space: nowrap;\n  color: var(--color-heading-alt);\n  font-family: var(--font-heading);\n  font-size: var(--text-h3);\n  font-weight: var(--weight-heading);\n  line-height: var(--line-body);\n  letter-spacing: var(--heading-tracking);\n  text-transform: var(--heading-transform);\n}\n.ds-panel__title::before,\n.ds-dialog__title::before,\n.ds-sidebar__title::before {\n  content: \"\";\n  flex: none;\n  width: 1ch;\n  border-top: var(--border-width) var(--border-style) var(--color-border);\n}\n.ds-panel__title::after,\n.ds-dialog__title::after,\n.ds-sidebar__title::after {\n  content: \"\";\n  flex: 1;\n  min-width: 1ch;\n  border-top: var(--border-width) var(--border-style) var(--color-border);\n}\n.ds-panel__body {\n  padding: var(--space-2) 2ch var(--space-3);\n}\n.ds-panel__text {\n  margin: 0 0 var(--space-2);\n}\n.ds-panel__text:last-child {\n  margin-bottom: 0;\n}\n\n/* ---------- a section rule: the name, then a line to the right edge ---------- */\n.ds-section {\n  margin: 0 0 var(--space-4);\n}\n.ds-section__title {\n  display: flex;\n  align-items: center;\n  gap: 1ch;\n  margin: var(--space-3) 0 var(--space-2);\n  color: var(--color-heading);\n  font-family: var(--font-heading);\n  font-size: var(--text-h2);\n  font-weight: var(--weight-heading);\n  line-height: var(--line-heading);\n  letter-spacing: var(--heading-tracking);\n  text-transform: var(--heading-transform);\n}\n.ds-section__title:first-child {\n  margin-top: 0;\n}\n.ds-section__title::after {\n  content: \"\";\n  flex: 1;\n  border-top: var(--border-width) var(--border-style) var(--color-border);\n}\n.ds-note:last-child {\n  margin-bottom: 0;\n}\n.ds-note {\n  margin: var(--space-2) 0 var(--space-3);\n  color: var(--color-text-muted);\n  font-size: var(--text-small);\n}\n\n/* ---------- prompt, cursor, key hints, meters ---------- */\n.ds-prompt {\n  margin: 0 0 var(--space-2);\n  color: var(--color-heading);\n  font-family: var(--font-mono);\n  font-weight: var(--weight-bold);\n  overflow-wrap: anywhere;\n}\n.ds-prompt__sign {\n  color: var(--color-accent-alt);\n}\n.ds-prompt__path {\n  color: var(--color-heading-alt);\n}\n/* The block cursor: one cell of inverse video. It holds a no-break space. */\n.ds-cursor {\n  background: currentColor;\n}\n@media (prefers-reduced-motion: no-preference) {\n  .ds-cursor {\n    animation: ds-blink 1.2s steps(1) infinite;\n  }\n}\n@keyframes ds-blink {\n  0% { opacity: 1; }\n  50% { opacity: 0; }\n  100% { opacity: 0; }\n}\n.ds-key {\n  white-space: nowrap;\n}\n.ds-key__cap {\n  font-weight: var(--weight-bold);\n}\n.ds-key__cap::before {\n  content: \"[\";\n}\n.ds-key__cap::after {\n  content: \"]\";\n}\n/* A progress bar typed in block characters. */\n.ds-meter {\n  color: var(--color-border);\n  white-space: nowrap;\n}\n.ds-meter__on {\n  color: var(--color-success);\n}\n.ds-meter__on--warning {\n  color: var(--color-warning);\n}\n.ds-code {\n  box-sizing: border-box;\n  max-width: 100%;\n  margin: 0 0 var(--space-3);\n  padding: var(--space-2) 2ch;\n  overflow-x: auto;\n  border: var(--border-width) var(--border-style) var(--color-border);\n  border-radius: var(--radius-panel);\n  background: var(--color-surface-alt);\n  color: var(--color-text);\n  font-family: var(--font-mono);\n  font-size: var(--text-small);\n  line-height: var(--line-body);\n  white-space: pre;\n}\n.ds-code:last-child {\n  margin-bottom: 0;\n}\n\n/* ---------- links ---------- */\n.ds-link {\n  color: var(--color-link);\n  text-decoration: var(--link-decoration);\n}\n.ds-link:visited,\n.ds-link.is-visited {\n  color: var(--color-link-visited);\n}\n.ds-link:hover,\n.ds-link.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-link:active,\n.ds-link.is-active {\n  color: var(--color-link-active);\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-link:focus-visible,\n.ds-link.is-focus {\n  outline: var(--focus-ring);\n}\n.ds-link--quiet,\n.ds-link--quiet:visited,\n.ds-link--quiet.is-visited {\n  color: var(--color-link-quiet);\n  text-decoration: var(--link-decoration-quiet);\n}\n.ds-link--quiet:hover,\n.ds-link--quiet.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n/* A link marked by brackets instead of an underline: [copy]. */\n.ds-link--bracket,\n.ds-link--bracket:visited {\n  color: var(--color-link);\n  text-decoration: none;\n  white-space: nowrap;\n}\n.ds-link--bracket::before {\n  content: \"[\";\n}\n.ds-link--bracket::after {\n  content: \"]\";\n}\n.ds-links {\n  display: flex;\n  flex-wrap: wrap;\n  gap: var(--space-1) 3ch;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n}\n\n/* ---------- brand: the mark on a one-line tile dressed like the primary button ---------- */\n.ds-brand {\n  display: inline-flex;\n  align-items: center;\n  gap: 1ch;\n  color: var(--color-heading);\n  text-decoration: none;\n}\n.ds-brand__mark {\n  flex: none;\n  display: block;\n  box-sizing: border-box;\n  width: calc(var(--text-base) * var(--line-body));\n  height: calc(var(--text-base) * var(--line-body));\n  border: var(--border-width) var(--border-style) var(--color-button);\n  border-radius: var(--radius-control);\n  background: var(--fill-button);\n  box-shadow: var(--shadow-control);\n  color: var(--color-button-text);\n}\n.ds-brand__mark svg {\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n.ds-brand__name {\n  font-family: var(--font-heading);\n  font-size: var(--text-large);\n  font-weight: var(--weight-display);\n  line-height: var(--line-heading);\n  letter-spacing: var(--display-tracking);\n  text-shadow: var(--shadow-text);\n}\n.ds-brand:hover .ds-brand__name,\n.ds-brand.is-hover .ds-brand__name {\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-brand:focus-visible,\n.ds-brand.is-focus {\n  outline: var(--focus-ring);\n}\n\n/* ---------- breadcrumb: a path ---------- */\n.ds-breadcrumb {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0 1ch;\n  margin: 0 0 var(--space-2);\n  padding: 0;\n  list-style: none;\n}\n.ds-breadcrumb__link {\n  color: var(--color-link-quiet);\n  text-decoration: var(--link-decoration-quiet);\n}\n.ds-breadcrumb__link:hover,\n.ds-breadcrumb__link.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-breadcrumb__sep {\n  color: var(--color-text-muted);\n}\n.ds-breadcrumb__current {\n  color: var(--color-heading);\n  font-weight: var(--weight-bold);\n}\n\n/* ---------- page header: title, one line, an action at the right, a heavy rule ---------- */\n.ds-page-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: flex-end;\n  gap: var(--space-2) 2ch;\n  margin: 0 0 var(--space-3);\n  padding: 0 0 var(--space-2);\n  border-bottom: var(--border-width-strong) var(--border-style) var(--color-border-strong);\n}\n.ds-page-header__main {\n  min-width: 0;\n}\n.ds-page-header__title {\n  margin: 0;\n  color: var(--color-heading);\n  font-family: var(--font-heading);\n  font-size: var(--text-h1);\n  font-weight: var(--weight-heading);\n  line-height: var(--line-heading);\n  letter-spacing: var(--heading-tracking);\n  text-transform: var(--heading-transform);\n  text-shadow: var(--shadow-text);\n}\n.ds-page-header__text {\n  margin: var(--space-1) 0 0;\n  color: var(--color-text-muted);\n}\n.ds-page-header__action {\n  flex: none;\n}\n\n/* ---------- stats ---------- */\n.ds-stats {\n  display: grid;\n  grid-template-columns: repeat(4, minmax(0, 1fr));\n  margin: 0;\n  padding: 0;\n  list-style: none;\n}\n.ds-stat {\n  min-width: 0;\n}\n.ds-stat__value {\n  color: var(--color-heading);\n  font-family: var(--font-heading);\n  font-weight: var(--weight-display);\n}\n.ds-stat__label {\n  color: var(--color-text-muted);\n}\n\n/* ---------- prose ---------- */\n.ds-prose {\n  max-width: 78ch;\n}\n.ds-prose h1,\n.ds-prose h2,\n.ds-prose h3 {\n  margin: 0 0 var(--space-2);\n  color: var(--color-heading);\n  font-family: var(--font-heading);\n  font-weight: var(--weight-heading);\n  line-height: var(--line-heading);\n  letter-spacing: var(--heading-tracking);\n  text-transform: var(--heading-transform);\n}\n.ds-prose h1 {\n  font-size: var(--text-h1);\n}\n.ds-prose h2 {\n  margin-top: var(--space-4);\n  font-size: var(--text-h2);\n}\n.ds-prose h3 {\n  margin-top: var(--space-3);\n  font-size: var(--text-h3);\n  color: var(--color-heading-alt);\n}\n.ds-prose p {\n  margin: 0 0 var(--space-3);\n}\n.ds-prose ul,\n.ds-prose ol {\n  margin: 0 0 var(--space-3);\n  padding: 0 0 0 3ch;\n}\n.ds-prose ul {\n  list-style-type: \"- \";\n  padding-left: 2ch;\n}\n.ds-prose code {\n  padding: 0 0.5ch;\n  background: var(--color-surface-strong);\n  color: var(--color-heading);\n  font-family: var(--font-mono);\n}\n.ds-prose strong {\n  color: var(--color-heading);\n  font-weight: var(--weight-bold);\n}\n.ds-prose > :first-child {\n  margin-top: 0;\n}\n.ds-prose > :last-child {\n  margin-bottom: 0;\n}\n\n/* ---------- buttons: a word in brackets, the primary one in inverse video ---------- */\n.ds-buttons {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: var(--space-2) 2ch;\n}\n.ds-button {\n  display: inline-block;\n  box-sizing: border-box;\n  margin: 0;\n  padding: var(--space-1) 1ch;\n  border: var(--border-width) var(--border-style) var(--color-button);\n  border-radius: var(--radius-control);\n  background: var(--fill-button);\n  box-shadow: var(--shadow-control);\n  color: var(--color-button-text);\n  font-family: var(--font-ui);\n  font-size: var(--text-ui);\n  font-weight: var(--weight-ui);\n  line-height: var(--line-body);\n  text-transform: var(--ui-transform);\n  text-decoration: none;\n  white-space: nowrap;\n  transition: var(--transition);\n  cursor: pointer;\n}\n.ds-button::before {\n  content: \"\";\n}\n.ds-button::after {\n  content: \"\";\n}\n.ds-button:hover,\n.ds-button.is-hover {\n  background: var(--fill-button-hover);\n  box-shadow: var(--shadow-control-hover);\n  color: var(--color-button-hover-text);\n}\n.ds-button:active,\n.ds-button.is-active {\n  box-shadow: var(--shadow-control-pressed);\n}\n.ds-button:focus-visible,\n.ds-button.is-focus {\n  outline: var(--focus-ring);\n}\n.ds-button--secondary,\n.ds-button--secondary:hover,\n.ds-button--secondary.is-hover {\n  border-color: var(--color-border);\n  background: var(--fill-button-secondary);\n  color: var(--color-button-secondary-text);\n}\n.ds-button--secondary:hover,\n.ds-button--secondary.is-hover {\n  border-color: var(--color-border-strong);\n}\n.ds-button--danger,\n.ds-button--danger:hover,\n.ds-button--danger.is-hover {\n  border-color: var(--color-danger);\n  background: var(--color-danger-surface);\n  color: var(--color-danger);\n}\n.ds-button:disabled,\n.ds-button.is-disabled {\n  border-color: var(--color-border-muted);\n  background: var(--color-disabled);\n  box-shadow: none;\n  color: var(--color-disabled-text);\n  cursor: default;\n}\n\n/* ---------- form ---------- */\n.ds-form {\n  margin: 0;\n}\n.ds-form__row {\n  display: grid;\n  grid-template-columns: var(--size-label) minmax(0, 1fr);\n  gap: 0 2ch;\n  align-items: start;\n  margin: 0 0 var(--space-2);\n}\n.ds-form__label {\n  padding: var(--space-1) 0;\n  color: var(--color-heading);\n}\n.ds-form__field {\n  min-width: 0;\n}\n.ds-form__input {\n  box-sizing: border-box;\n  width: 100%;\n  max-width: var(--size-field);\n  margin: 0;\n  padding: var(--space-1) 1ch;\n  border: var(--border-width) var(--border-style) var(--color-input-border);\n  border-radius: var(--radius-control);\n  background: var(--fill-input);\n  box-shadow: var(--shadow-control);\n  color: var(--color-input-text);\n  font-family: var(--font-ui);\n  font-size: var(--text-ui);\n  line-height: var(--line-body);\n  transition: var(--transition);\n}\n.ds-form__input--wide {\n  max-width: none;\n}\n.ds-form__input--auto {\n  width: auto;\n}\n.ds-form__input:focus-visible,\n.ds-form__input.is-focus {\n  outline: var(--focus-ring);\n}\n.ds-form__input:disabled,\n.ds-form__input.is-disabled {\n  background: var(--color-disabled);\n  color: var(--color-disabled-text);\n}\n.ds-form__input.is-invalid {\n  border-color: var(--color-danger);\n}\n.ds-form__error {\n  margin: var(--space-1) 0 0;\n  color: var(--color-danger);\n  font-weight: var(--weight-bold);\n}\n.ds-form__hint {\n  margin: var(--space-1) 0 0;\n  color: var(--color-text-muted);\n  font-size: var(--text-small);\n}\n.ds-form__check {\n  display: flex;\n  align-items: center;\n  gap: 1ch;\n  padding: var(--space-1) 0;\n}\n/* A checkbox is one cell: an empty box, or a filled block when set. */\n.ds-form__checkbox {\n  appearance: none;\n  flex: none;\n  box-sizing: border-box;\n  width: 2ch;\n  height: 2ch;\n  margin: 0;\n  border: var(--border-width) var(--border-style) var(--color-input-border);\n  border-radius: var(--radius-control);\n  background: var(--fill-input);\n}\n.ds-form__checkbox:checked {\n  border-style: solid;\n  background: var(--color-input-text);\n}\n.ds-form__checkbox:focus-visible {\n  outline: var(--focus-ring);\n}\n.ds-form__actions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: var(--space-2) 2ch;\n  margin: var(--space-3) 0 0;\n  padding-left: calc(var(--size-label) + 2ch);\n}\n\n/* ---------- table: aligned columns under a head line in the bar colours ---------- */\n.ds-table {\n  width: 100%;\n  border-collapse: collapse;\n  border-radius: var(--radius-panel);\n}\n.ds-table th,\n.ds-table td {\n  padding: var(--space-1) 1ch;\n  border-style: var(--border-style);\n  border-color: var(--color-border-muted);\n  border-width: 0 0 var(--border-width);\n  text-align: left;\n  vertical-align: top;\n}\n.ds-table th {\n  background: var(--fill-bar);\n  color: var(--color-bar-text);\n  border-color: var(--color-border);\n  font-family: var(--font-heading);\n  font-weight: var(--weight-bold);\n  text-transform: var(--heading-transform);\n  white-space: nowrap;\n}\n.ds-table td {\n  background: var(--color-surface);\n}\n.ds-table tr.is-alt td,\n.ds-table__row--alt td {\n  background: var(--color-surface-alt);\n}\n.ds-table th.ds-table__num,\n.ds-table td.ds-table__num {\n  text-align: right;\n  white-space: nowrap;\n}\n.ds-table td.ds-table__code {\n  color: var(--color-heading);\n  font-family: var(--font-mono);\n  font-weight: var(--weight-bold);\n  white-space: nowrap;\n}\n.ds-table td.ds-table__date {\n  color: var(--color-text-muted);\n  white-space: nowrap;\n}\n.ds-table__group td {\n  background: var(--color-surface-strong);\n  color: var(--color-heading-alt);\n  font-weight: var(--weight-bold);\n}\n/* The row under the cursor: inverse video. */\n.ds-table tr.is-selected td {\n  background: var(--fill-inverse);\n  color: var(--color-inverse-text);\n}\n.ds-table tr.is-selected .ds-table__title {\n  color: var(--color-inverse-text);\n}\n.ds-table tr.is-unread td {\n  font-weight: var(--weight-bold);\n}\n.ds-table__title {\n  color: var(--color-link);\n  text-decoration: var(--link-decoration);\n}\n.ds-table__title:visited,\n.ds-table__title.is-visited {\n  color: var(--color-link-visited);\n}\n.ds-table__title:hover,\n.ds-table__title.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n\n/* ---------- list ---------- */\n.ds-list {\n  margin: 0;\n  padding: 0;\n  list-style: none;\n}\n.ds-list__item {\n  display: grid;\n  grid-template-columns: var(--size-date) minmax(0, 1fr);\n  gap: 0 2ch;\n  padding: var(--space-2) 0;\n  border-bottom: var(--border-width) var(--border-style) var(--color-border-muted);\n}\n.ds-list__item:first-child {\n  padding-top: 0;\n}\n.ds-list__item:last-child {\n  padding-bottom: 0;\n  border-bottom: 0;\n}\n.ds-list__date {\n  color: var(--color-text-muted);\n  white-space: nowrap;\n}\n.ds-list__main {\n  min-width: 0;\n}\n.ds-list__title {\n  color: var(--color-link);\n  font-weight: var(--weight-bold);\n  text-decoration: var(--link-decoration);\n}\n.ds-list__title:visited,\n.ds-list__title.is-visited {\n  color: var(--color-link-visited);\n}\n.ds-list__title:hover,\n.ds-list__title.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-list__excerpt {\n  margin: 0;\n}\n.ds-list__meta {\n  margin: 0;\n  color: var(--color-text-muted);\n  font-size: var(--text-small);\n}\n\n/* ---------- grid: four framed cells on the block fills ---------- */\n.ds-grid {\n  display: grid;\n  grid-template-columns: repeat(4, minmax(0, 1fr));\n  gap: var(--space-2) 2ch;\n}\n.ds-grid__cell {\n  min-width: 0;\n  padding: var(--space-2) 2ch var(--space-3);\n  border: var(--border-width) var(--border-style) var(--color-border);\n  border-radius: var(--radius-page);\n  background: var(--color-fill-1);\n}\n.ds-grid__cell--2 {\n  background: var(--color-fill-2);\n}\n.ds-grid__cell--3 {\n  background: var(--color-fill-3);\n}\n.ds-grid__cell--4 {\n  background: var(--color-fill-4);\n}\n.ds-grid__label {\n  margin: 0;\n  color: var(--color-text-muted);\n  font-size: var(--text-small);\n}\n.ds-grid__title {\n  margin: 0 0 var(--space-1);\n  color: var(--color-heading);\n  font-family: var(--font-heading);\n  font-size: var(--text-h3);\n  font-weight: var(--weight-heading);\n  line-height: var(--line-heading);\n  letter-spacing: var(--heading-tracking);\n  text-transform: var(--heading-transform);\n}\n.ds-grid__text {\n  margin: 0;\n}\n\n/* ---------- tabs: words on a rule, the current one in inverse video ---------- */\n.ds-tabs {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0 1ch;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  border-bottom: var(--border-width) var(--border-style) var(--color-border);\n  font-family: var(--font-ui);\n  font-size: var(--text-ui);\n  font-weight: var(--weight-ui);\n  text-transform: var(--ui-transform);\n}\n.ds-tabs__tab {\n  display: block;\n  padding: var(--space-1) 1ch;\n  color: var(--color-link-quiet);\n  text-decoration: var(--link-decoration-quiet);\n}\n.ds-tabs__tab:hover,\n.ds-tabs__tab.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-tabs__tab:focus-visible,\n.ds-tabs__tab.is-focus {\n  outline: var(--focus-ring);\n}\n.ds-tabs__tab.is-current {\n  background: var(--fill-inverse);\n  color: var(--color-inverse-text);\n  font-weight: var(--weight-bold);\n  text-decoration: none;\n}\n.ds-tabs__body {\n  padding: var(--space-2) 0 var(--space-3);\n}\n\n/* ---------- badge: a count on the accent fill; a tag; a status word in brackets ---------- */\n.ds-prose + .ds-links {\n  margin-top: var(--space-3);\n}\n.ds-badges {\n  margin: 0 0 var(--space-2);\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: var(--space-1) 2ch;\n}\n.ds-badge {\n  display: inline-block;\n  padding: 0 1ch;\n  border-radius: var(--radius-pill);\n  background: var(--fill-accent);\n  color: var(--color-accent-text);\n  font-family: var(--font-ui);\n  font-size: var(--text-small);\n  font-weight: var(--weight-bold);\n  line-height: var(--line-body);\n  white-space: nowrap;\n}\n.ds-badge--tag,\n.ds-badge--success,\n.ds-badge--warning,\n.ds-badge--danger {\n  padding: 0;\n  background: transparent;\n  font-weight: var(--weight-body);\n}\n.ds-badge--tag {\n  color: var(--color-heading-alt);\n}\n.ds-badge--tag::before {\n  content: \"#\";\n}\n.ds-badge--success::before,\n.ds-badge--warning::before,\n.ds-badge--danger::before {\n  content: \"[\";\n}\n.ds-badge--success::after,\n.ds-badge--warning::after,\n.ds-badge--danger::after {\n  content: \"]\";\n}\n.ds-badge--success {\n  color: var(--color-success);\n}\n.ds-badge--warning {\n  color: var(--color-warning);\n}\n.ds-badge--danger {\n  color: var(--color-danger);\n}\n\n/* ---------- sidebar: framed blocks of links ---------- */\n.ds-sidebar {\n  display: grid;\n  gap: var(--space-2);\n  align-content: start;\n  min-width: 0;\n}\n.ds-sidebar__list {\n  margin: 0;\n  padding: var(--space-1) 1ch var(--space-2);\n  list-style: none;\n}\n.ds-sidebar__list li {\n  display: flex;\n  justify-content: space-between;\n  gap: 1ch;\n}\n.ds-sidebar__link {\n  flex: 1;\n  min-width: 0;\n  padding: 0 1ch;\n  color: var(--color-link-quiet);\n  text-decoration: var(--link-decoration-quiet);\n}\n.ds-sidebar__link:hover,\n.ds-sidebar__link.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-sidebar__link.is-current {\n  background: var(--fill-inverse);\n  color: var(--color-inverse-text);\n  font-weight: var(--weight-bold);\n  text-decoration: none;\n}\n.ds-sidebar__count {\n  flex: none;\n  padding: 0 1ch 0 0;\n  color: var(--color-text-muted);\n}\n.ds-sidebar__text {\n  margin: 0;\n  padding: var(--space-1) 2ch var(--space-2);\n  color: var(--color-text-muted);\n  font-size: var(--text-small);\n}\n\n/* ---------- notice: a box with a heavy left edge ---------- */\n.ds-notices {\n  display: grid;\n  gap: var(--space-2);\n  margin: 0 0 var(--space-3);\n}\n.ds-notice {\n  margin: 0;\n  padding: var(--space-2) 2ch;\n  border: var(--border-width) var(--border-style) var(--color-border-strong);\n  border-left-width: var(--border-width-strong);\n  border-radius: var(--radius-panel);\n  background: var(--color-notice);\n  color: var(--color-notice-text);\n}\n.ds-notice__title {\n  font-weight: var(--weight-bold);\n  text-transform: var(--heading-transform);\n}\n.ds-notice--error {\n  border-color: var(--color-danger);\n  background: var(--color-danger-surface);\n  color: var(--color-danger);\n}\n\n/* ---------- pagination ---------- */\n.ds-pagination {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: var(--space-1) 1ch;\n  margin: var(--space-3) 0 0;\n  padding: 0;\n  list-style: none;\n  font-family: var(--font-ui);\n  font-size: var(--text-ui);\n}\n.ds-pagination__link {\n  display: block;\n  padding: 0 1ch;\n  color: var(--color-link);\n  text-decoration: var(--link-decoration-quiet);\n}\n.ds-pagination__link:hover,\n.ds-pagination__link.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-pagination__link:focus-visible,\n.ds-pagination__link.is-focus {\n  outline: var(--focus-ring);\n}\n.ds-pagination__link.is-current {\n  background: var(--fill-inverse);\n  color: var(--color-inverse-text);\n  font-weight: var(--weight-bold);\n  text-decoration: none;\n}\n.ds-pagination__link.is-disabled {\n  color: var(--color-text-muted);\n  text-decoration: none;\n}\n\n/* ---------- dialog: a framed box on a block of the overlay colour, in the page flow ---------- */\n.ds-dialog__backdrop {\n  box-sizing: border-box;\n  padding: var(--space-3) 2ch;\n  border-radius: var(--radius-panel);\n  background: var(--color-overlay);\n}\n.ds-dialog {\n  max-width: var(--size-dialog);\n  margin-left: auto;\n  margin-right: auto;\n  border-color: var(--color-border-strong);\n  box-shadow: var(--shadow-dialog);\n}\n.ds-dialog__title::before,\n.ds-dialog__title::after {\n  border-color: var(--color-border-strong);\n}\n.ds-dialog__body {\n  padding: var(--space-2) 2ch var(--space-3);\n}\n.ds-dialog__text {\n  margin: 0 0 var(--space-3);\n}\n.ds-dialog__actions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: var(--space-2) 2ch;\n}\n\n/* ---------- empty ---------- */\n.ds-empty {\n  padding: var(--space-3) 2ch;\n  border: var(--border-width) var(--border-style) var(--color-border-muted);\n  border-radius: var(--radius-panel);\n  text-align: center;\n}\n.ds-empty__text {\n  margin: 0 0 var(--space-2);\n  color: var(--color-text-muted);\n}\n.ds-note code,\n.ds-notice code,\n.ds-panel__text code {\n  color: var(--color-heading);\n  font-family: var(--font-mono);\n}\n.ds-notice code {\n  color: inherit;\n  font-weight: var(--weight-bold);\n}\n\n/* ---------- layout 3: a shell session: a prompt, its output, the next prompt ---------- */\n.ds-session {\n  display: flex;\n  flex-direction: column;\n  box-sizing: border-box;\n  max-width: var(--size-page);\n  min-height: 100vh;\n  margin: 0 auto;\n  padding: var(--space-3) 3ch 0;\n  border-radius: var(--radius-page);\n  background: var(--color-canvas);\n}\n.ds-cols {\n  display: grid;\n  grid-template-columns: minmax(0, 1fr) var(--size-side);\n  gap: var(--space-3) 4ch;\n  align-items: start;\n}\n.ds-cols--even {\n  grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n.ds-main {\n  min-width: 0;\n}\n.ds-out {\n  margin: 0 0 var(--space-4);\n}\n.ds-session > .ds-view {\n  padding-bottom: var(--space-5);\n}\n.ds-button::before {\n  content: \"> \";\n}\n\n/* nav: the first line of the session: who, then the directories of the site */\n.ds-nav {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: var(--space-1) 3ch;\n  margin: 0 0 var(--space-3);\n  padding: 0 0 var(--space-2);\n  border-bottom: var(--border-width) var(--border-style) var(--color-border);\n  font-family: var(--font-ui);\n  font-size: var(--text-ui);\n  font-weight: var(--weight-ui);\n}\n.ds-nav__list {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0 2ch;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n}\n.ds-nav__link {\n  display: block;\n  padding: 0 1ch 0 0;\n  color: var(--color-link-quiet);\n  text-decoration: var(--link-decoration-quiet);\n  text-transform: var(--ui-transform);\n}\n.ds-nav__link::before {\n  content: \"~/\";\n  padding: 0 0 0 1ch;\n}\n.ds-nav__link:hover,\n.ds-nav__link.is-hover {\n  color: var(--color-link-hover);\n  text-decoration: var(--link-decoration-hover);\n}\n.ds-nav__link:focus-visible,\n.ds-nav__link.is-focus {\n  outline: var(--focus-ring);\n}\n.ds-nav__link.is-current,\n.ds-page:not(:has(.ds-view:target)) .ds-nav__link[href=\"#home\"],\n.ds-page:has(#home:target) .ds-nav__link[href=\"#home\"],\n.ds-page:has(#posts:target) .ds-nav__link[href=\"#posts\"],\n.ds-page:has(#post:target) .ds-nav__link[href=\"#post\"],\n.ds-page:has(#about:target) .ds-nav__link[href=\"#about\"],\n.ds-page:has(#contact:target) .ds-nav__link[href=\"#contact\"] {\n  background: var(--fill-inverse);\n  color: var(--color-inverse-text);\n  font-weight: var(--weight-bold);\n  text-decoration: none;\n}\n.ds-nav__host {\n  margin: 0 0 0 auto;\n  color: var(--color-text-muted);\n}\n\n/* hero: what whoami prints */\n.ds-hero {\n  margin: 0 0 var(--space-4);\n}\n.ds-hero__title {\n  margin: 0 0 var(--space-2);\n  color: var(--color-heading);\n  font-family: var(--font-heading);\n  font-size: var(--text-display);\n  font-weight: var(--weight-display);\n  line-height: var(--line-display);\n  letter-spacing: var(--display-tracking);\n  text-transform: var(--heading-transform);\n  text-shadow: var(--shadow-text);\n}\n.ds-hero__lead {\n  max-width: 62ch;\n  margin: 0 0 var(--space-3);\n  font-size: var(--text-large);\n}\n\n/* stats: what wc prints: figures and their names on one line */\n.ds-stats {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0 4ch;\n}\n.ds-stat__value {\n  font-size: var(--text-large);\n  text-shadow: var(--shadow-text);\n}\n\n/* list: a long directory listing: mode, size, date, name */\n.ds-list__item {\n  grid-template-columns: 10ch 5ch var(--size-date) minmax(0, 1fr);\n  padding: var(--space-1) 0;\n  border-bottom: 0;\n}\n.ds-list__item:first-child {\n  padding-top: var(--space-1);\n}\n.ds-list__perm {\n  color: var(--color-text-muted);\n  white-space: nowrap;\n}\n.ds-list__size {\n  text-align: right;\n  white-space: nowrap;\n}\n.ds-list__total {\n  margin: 0;\n  color: var(--color-text-muted);\n}\n.ds-grid {\n  grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n/* headings in running text keep their marks, as in the source file */\n.ds-prose h1::before {\n  content: \"# \";\n  color: var(--color-text-muted);\n}\n.ds-prose h2::before {\n  content: \"## \";\n  color: var(--color-text-muted);\n}\n.ds-prose h3::before {\n  content: \"### \";\n  color: var(--color-text-muted);\n}\n.ds-tabs__tab::before {\n  content: \"--\";\n}\n.ds-table td {\n  background: transparent;\n}\n.ds-table tr.is-alt td,\n.ds-table__row--alt td {\n  background: var(--color-surface-alt);\n}\n\n/* footer: the status line of an editor, stuck to the foot of the window */\n.ds-footer {\n  position: sticky;\n  bottom: 0;\n  display: flex;\n  flex-wrap: wrap;\n  margin: auto -3ch 0;\n  background: var(--fill-bar-alt);\n  color: var(--color-bar-alt-text);\n  font-size: var(--text-small);\n}\n.ds-footer__mode {\n  padding: 0 1ch;\n  background: var(--fill-bar);\n  color: var(--color-bar-text);\n  font-weight: var(--weight-bold);\n  text-shadow: var(--shadow-text);\n}\n.ds-footer__seg {\n  margin: 0;\n  padding: 0 1ch;\n}\n.ds-footer__seg--right {\n  margin-left: auto;\n}\n.ds-footer__link {\n  color: var(--color-bar-alt-text);\n  text-decoration: var(--link-decoration-quiet);\n}\n.ds-footer__link:hover,\n.ds-footer__link.is-hover {\n  text-decoration: var(--link-decoration-hover);\n}\n\n/* ---------- narrow windows: one column, nothing scrolls sideways but code ---------- */\n@media (max-width: 720px) {\n  .ds-page-header {\n    flex-direction: column;\n    align-items: flex-start;\n  }\n  .ds-stats,\n  .ds-grid {\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n  }\n  .ds-form__row {\n    grid-template-columns: minmax(0, 1fr);\n  }\n  .ds-form__label {\n    padding: 0;\n  }\n  .ds-form__actions {\n    padding-left: 0;\n  }\n  .ds-table__extra {\n    display: none;\n  }\n  .ds-dialog__backdrop {\n    padding: var(--space-3) 1ch;\n  }\n  .ds-button {\n    white-space: normal;\n  }\n  .ds-table td.ds-table__code {\n    white-space: normal;\n    overflow-wrap: anywhere;\n  }\n\n  .ds-session {\n    padding: var(--space-2) 2ch 0;\n  }\n  .ds-footer {\n    margin: auto -2ch 0;\n  }\n  .ds-cols,\n  .ds-cols--even {\n    grid-template-columns: minmax(0, 1fr);\n  }\n  .ds-nav__host {\n    display: none;\n  }\n  .ds-hero__title {\n    font-size: var(--text-h1);\n  }\n  .ds-stats {\n    display: flex;\n  }\n  .ds-grid {\n    grid-template-columns: minmax(0, 1fr);\n  }\n  .ds-list__item {\n    grid-template-columns: var(--size-date) minmax(0, 1fr);\n  }\n  .ds-list__perm,\n  .ds-list__size {\n    display: none;\n  }\n}\n",
    "specimen.html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>chronoskin: notes on small tools and old machines. Terminal, 2015 to 2026: specimen</title>\n<link rel=\"stylesheet\" href=\"tokens.css\">\n<link rel=\"stylesheet\" href=\"components.css\">\n</head>\n<body class=\"ds-page\" data-component=\"shell\">\n<div class=\"ds-session\">\n\n<header class=\"ds-nav\" data-component=\"nav\">\n  <a class=\"ds-brand\" data-component=\"brand\" href=\"#home\"><span class=\"ds-brand__mark\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.6\" stroke-linecap=\"square\" aria-hidden=\"true\"><path d=\"M12 3a9 9 0 1 1-9 9\"/><path d=\"M12 17a5 5 0 1 1 5-5\"/><circle cx=\"12\" cy=\"12\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\"/></svg></span><span class=\"ds-brand__name\">chronoskin</span></a>\n  <ul class=\"ds-nav__list\">\n    <li><a class=\"ds-nav__link\" href=\"#home\">home</a></li>\n    <li><a class=\"ds-nav__link\" href=\"#posts\">posts</a></li>\n    <li><a class=\"ds-nav__link\" href=\"#post\">latest</a></li>\n    <li><a class=\"ds-nav__link\" href=\"#about\">about</a></li>\n    <li><a class=\"ds-nav__link\" href=\"#contact\">contact</a></li>\n  </ul>\n  <p class=\"ds-nav__host\">guest@tty1</p>\n</header>\n\n<!-- View 1, the home page: a session of four commands and what they print. -->\n<section class=\"ds-view ds-view--home\" id=\"home\">\n  <div class=\"ds-cols\">\n    <div class=\"ds-main\">\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> whoami</p>\n      <section class=\"ds-hero\" data-component=\"hero\">\n        <h1 class=\"ds-hero__title\">Notes on small tools and old machines.</h1>\n        <p class=\"ds-hero__lead\">I write programs that fit in one file and keep computers running longer than their makers planned. This is where the notes go.</p>\n        <div class=\"ds-buttons\">\n          <a class=\"ds-button\" href=\"#post\">read the latest post</a>\n          <a class=\"ds-button ds-button--secondary is-hover\" href=\"#posts\">ls posts/</a>\n        </div>\n      </section>\n\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> wc posts/*.md | tail -1</p>\n      <div class=\"ds-out\">\n        <ul class=\"ds-stats\" data-component=\"stats\">\n          <li class=\"ds-stat\"><span class=\"ds-stat__value\">38</span> <span class=\"ds-stat__label\">posts</span></li>\n          <li class=\"ds-stat\"><span class=\"ds-stat__value\">61,204</span> <span class=\"ds-stat__label\">words</span></li>\n          <li class=\"ds-stat\"><span class=\"ds-stat__value\">7</span> <span class=\"ds-stat__label\">years</span></li>\n          <li class=\"ds-stat\"><span class=\"ds-stat__value\">0</span> <span class=\"ds-stat__label\">trackers</span></li>\n        </ul>\n      </div>\n\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> ls -lt posts/ | head -5</p>\n      <div class=\"ds-out\">\n        <p class=\"ds-list__total\">total 38</p>\n        <ul class=\"ds-list\" data-component=\"list\">\n          <li class=\"ds-list__item\">\n            <span class=\"ds-list__perm\">-rw-r--r--</span><span class=\"ds-list__size\">9.4K</span><span class=\"ds-list__date\">Mar 02 2026</span>\n            <div class=\"ds-list__main\">\n              <a class=\"ds-list__title\" href=\"#post\">a-status-line-in-forty-lines.md</a> <span class=\"ds-badge\">new</span>\n              <p class=\"ds-list__meta\">A status line for the shell, without a framework. 1,480 words</p>\n            </div>\n          </li>\n          <li class=\"ds-list__item\">\n            <span class=\"ds-list__perm\">-rw-r--r--</span><span class=\"ds-list__size\">6.1K</span><span class=\"ds-list__date\">Feb 11 2026</span>\n            <div class=\"ds-list__main\">\n              <a class=\"ds-list__title\" href=\"#post\">the-laptop-from-2011.md</a>\n              <p class=\"ds-list__meta\">A new battery, a small disk and one more year. 960 words</p>\n            </div>\n          </li>\n          <li class=\"ds-list__item\">\n            <span class=\"ds-list__perm\">-rw-r--r--</span><span class=\"ds-list__size\">12K</span><span class=\"ds-list__date\">Jan 20 2026</span>\n            <div class=\"ds-list__main\">\n              <a class=\"ds-list__title is-visited\" href=\"#post\">make-is-enough.md</a>\n              <p class=\"ds-list__meta\">Twelve lines that replaced a build system. 1,910 words</p>\n            </div>\n          </li>\n          <li class=\"ds-list__item\">\n            <span class=\"ds-list__perm\">-rw-r--r--</span><span class=\"ds-list__size\">4.0K</span><span class=\"ds-list__date\">Dec 03 2025</span>\n            <div class=\"ds-list__main\">\n              <a class=\"ds-list__title is-visited\" href=\"#post\">plain-text-accounts.md</a>\n              <p class=\"ds-list__meta\">A year of bookkeeping in one file. 640 words</p>\n            </div>\n          </li>\n        </ul>\n        <p class=\"ds-note\"><a class=\"ds-link\" href=\"#posts\">34 more in posts/</a></p>\n      </div>\n\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> ls projects/</p>\n      <div class=\"ds-out\">\n        <div class=\"ds-grid\" data-component=\"grid\">\n          <div class=\"ds-grid__cell\">\n            <h3 class=\"ds-grid__title\">tally/</h3>\n            <p class=\"ds-grid__text\">Counts things in text files. 300 lines of C.</p>\n            <p class=\"ds-grid__label\">C | since 2020</p>\n          </div>\n          <div class=\"ds-grid__cell ds-grid__cell--2\">\n            <h3 class=\"ds-grid__title\">lantern/</h3>\n            <p class=\"ds-grid__text\">A status line for any shell, in forty lines.</p>\n            <p class=\"ds-grid__label\">shell | since 2026</p>\n          </div>\n          <div class=\"ds-grid__cell ds-grid__cell--3\">\n            <h3 class=\"ds-grid__title\">ledgerline/</h3>\n            <p class=\"ds-grid__text\">Plain-text accounts and the script that sums them.</p>\n            <p class=\"ds-grid__label\">awk | since 2024</p>\n          </div>\n          <div class=\"ds-grid__cell ds-grid__cell--4\">\n            <h3 class=\"ds-grid__title\">dotfiles/</h3>\n            <p class=\"ds-grid__text\">What I carry from machine to machine.</p>\n            <p class=\"ds-grid__label\">mixed | since 2019</p>\n          </div>\n        </div>\n      </div>\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> <span class=\"ds-cursor\">&nbsp;</span></p>\n    </div>\n\n    <aside class=\"ds-sidebar\" data-component=\"sidebar\">\n      <div class=\"ds-sidebar__block\">\n        <h2 class=\"ds-sidebar__title\">tags</h2>\n        <ul class=\"ds-sidebar__list\">\n          <li><a class=\"ds-sidebar__link\" href=\"#posts\">shell</a><span class=\"ds-sidebar__count\">14</span></li>\n          <li><a class=\"ds-sidebar__link\" href=\"#posts\">hardware</a><span class=\"ds-sidebar__count\">9</span></li>\n          <li><a class=\"ds-sidebar__link is-hover\" href=\"#posts\">make</a><span class=\"ds-sidebar__count\">6</span></li>\n          <li><a class=\"ds-sidebar__link\" href=\"#posts\">plain-text</a><span class=\"ds-sidebar__count\">5</span></li>\n          <li><a class=\"ds-sidebar__link\" href=\"#posts\">c</a><span class=\"ds-sidebar__count\">4</span></li>\n        </ul>\n      </div>\n      <div class=\"ds-sidebar__block\">\n        <h2 class=\"ds-sidebar__title\">elsewhere</h2>\n        <ul class=\"ds-sidebar__list\">\n          <li><a class=\"ds-sidebar__link\" href=\"#posts\">feed.xml</a></li>\n          <li><a class=\"ds-sidebar__link\" href=\"#about\">about.txt</a></li>\n          <li><a class=\"ds-sidebar__link\" href=\"#contact\">mail</a></li>\n        </ul>\n      </div>\n      <div class=\"ds-panel\" data-component=\"panel\">\n        <h2 class=\"ds-panel__title\">now.txt</h2>\n        <div class=\"ds-panel__body\">\n          <p class=\"ds-panel__text\">Rewriting tally so it reads from a pipe.</p>\n          <p class=\"ds-panel__text\">progress <span class=\"ds-meter\"><span class=\"ds-meter__on\">██████</span>░░░░</span></p>\n          <p class=\"ds-panel__text\"><a class=\"ds-link\" href=\"#about\">more about this site</a></p>\n        </div>\n      </div>\n    </aside>\n  </div>\n</section>\n\n<!-- View 2, the archive: every post as a line of a listing. -->\n<section class=\"ds-view\" id=\"posts\">\n  <ol class=\"ds-breadcrumb\">\n    <li><a class=\"ds-breadcrumb__link\" href=\"#home\">~</a></li>\n    <li class=\"ds-breadcrumb__sep\">/</li>\n    <li class=\"ds-breadcrumb__current\">posts</li>\n  </ol>\n  <div class=\"ds-page-header\" data-component=\"page-header\">\n    <div class=\"ds-page-header__main\">\n      <h1 class=\"ds-page-header__title\">posts/</h1>\n      <p class=\"ds-page-header__text\">38 files, 2019 to 2026, newest first.</p>\n    </div>\n    <div class=\"ds-page-header__action\"><a class=\"ds-button ds-button--secondary\" href=\"#posts\">feed.xml</a></div>\n  </div>\n  <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~/posts</span> <span class=\"ds-prompt__sign\">$</span> ls -lt --year 2026</p>\n  <ul class=\"ds-tabs\" data-component=\"tabs\">\n    <li><a class=\"ds-tabs__tab\" href=\"#posts\">all</a></li>\n    <li><span class=\"ds-tabs__tab is-current\">year 2026</span></li>\n    <li><a class=\"ds-tabs__tab\" href=\"#posts\">year 2025</a></li>\n    <li><a class=\"ds-tabs__tab\" href=\"#posts\">tag shell</a></li>\n    <li><a class=\"ds-tabs__tab\" href=\"#posts\">tag hardware</a></li>\n  </ul>\n  <div class=\"ds-tabs__body\">\n    <table class=\"ds-table\" data-component=\"table\">\n      <thead>\n        <tr><th>date</th><th>file</th><th class=\"ds-table__extra\">tag</th><th class=\"ds-table__num\">words</th><th class=\"ds-table__num ds-table__extra\">min</th></tr>\n      </thead>\n      <tbody>\n        <tr><td class=\"ds-table__date\">Mar 02</td><td><a class=\"ds-table__title\" href=\"#post\">a-status-line-in-forty-lines.md</a></td><td class=\"ds-table__extra\"><span class=\"ds-badge ds-badge--tag\">shell</span></td><td class=\"ds-table__num\">1,480</td><td class=\"ds-table__num ds-table__extra\">7</td></tr>\n        <tr class=\"is-alt\"><td class=\"ds-table__date\">Feb 11</td><td><a class=\"ds-table__title\" href=\"#post\">the-laptop-from-2011.md</a></td><td class=\"ds-table__extra\"><span class=\"ds-badge ds-badge--tag\">hardware</span></td><td class=\"ds-table__num\">960</td><td class=\"ds-table__num ds-table__extra\">5</td></tr>\n        <tr><td class=\"ds-table__date\">Jan 20</td><td><a class=\"ds-table__title is-visited\" href=\"#post\">make-is-enough.md</a></td><td class=\"ds-table__extra\"><span class=\"ds-badge ds-badge--tag\">make</span></td><td class=\"ds-table__num\">1,910</td><td class=\"ds-table__num ds-table__extra\">9</td></tr>\n        <tr class=\"is-alt\"><td class=\"ds-table__date\">Jan 04</td><td><a class=\"ds-table__title is-visited\" href=\"#post\">what-i-kept-from-2025.md</a></td><td class=\"ds-table__extra\"><span class=\"ds-badge ds-badge--tag\">plain-text</span></td><td class=\"ds-table__num\">720</td><td class=\"ds-table__num ds-table__extra\">4</td></tr>\n        <tr class=\"ds-table__group\"><td colspan=\"5\">2025, last three of eleven</td></tr>\n        <tr><td class=\"ds-table__date\">Dec 03</td><td><a class=\"ds-table__title is-visited\" href=\"#post\">plain-text-accounts.md</a></td><td class=\"ds-table__extra\"><span class=\"ds-badge ds-badge--tag\">plain-text</span></td><td class=\"ds-table__num\">640</td><td class=\"ds-table__num ds-table__extra\">3</td></tr>\n        <tr class=\"is-alt\"><td class=\"ds-table__date\">Nov 15</td><td><a class=\"ds-table__title\" href=\"#post\">a-serial-console-for-the-router.md</a></td><td class=\"ds-table__extra\"><span class=\"ds-badge ds-badge--tag\">hardware</span></td><td class=\"ds-table__num\">2,230</td><td class=\"ds-table__num ds-table__extra\">11</td></tr>\n        <tr><td class=\"ds-table__date\">Oct 28</td><td><a class=\"ds-table__title\" href=\"#post\">sixteen-colours-are-plenty.md</a></td><td class=\"ds-table__extra\"><span class=\"ds-badge ds-badge--tag\">shell</span></td><td class=\"ds-table__num\">1,105</td><td class=\"ds-table__num ds-table__extra\">5</td></tr>\n      </tbody>\n    </table>\n    <ul class=\"ds-pagination\" data-component=\"pagination\">\n      <li><span class=\"ds-pagination__link is-disabled\">&lt; newer</span></li>\n      <li><span class=\"ds-pagination__link is-current\">1</span></li>\n      <li><a class=\"ds-pagination__link\" href=\"#posts\">2</a></li>\n      <li><a class=\"ds-pagination__link\" href=\"#posts\">3</a></li>\n      <li><a class=\"ds-pagination__link\" href=\"#posts\">4</a></li>\n      <li><a class=\"ds-pagination__link\" href=\"#posts\">older &gt;</a></li>\n    </ul>\n  </div>\n  <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~/posts</span> <span class=\"ds-prompt__sign\">$</span> ls drafts/</p>\n  <div class=\"ds-empty\" data-component=\"empty\">\n    <p class=\"ds-empty__text\">ls: drafts/ is empty. Nothing is half written at the moment.</p>\n    <a class=\"ds-button ds-button--secondary\" href=\"#contact\">suggest a topic</a>\n  </div>\n</section>\n\n<!-- View 3, one post. -->\n<section class=\"ds-view\" id=\"post\">\n  <ol class=\"ds-breadcrumb\" data-component=\"breadcrumb\">\n    <li><a class=\"ds-breadcrumb__link\" href=\"#home\">~</a></li>\n    <li class=\"ds-breadcrumb__sep\">/</li>\n    <li><a class=\"ds-breadcrumb__link\" href=\"#posts\">posts</a></li>\n    <li class=\"ds-breadcrumb__sep\">/</li>\n    <li class=\"ds-breadcrumb__current\">a-status-line-in-forty-lines.md</li>\n  </ol>\n  <div class=\"ds-cols\">\n    <div class=\"ds-main\">\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~/posts</span> <span class=\"ds-prompt__sign\">$</span> cat a-status-line-in-forty-lines.md</p>\n      <div class=\"ds-notices\" data-component=\"notice\">\n        <p class=\"ds-notice\"><span class=\"ds-notice__title\">updated Mar 05:</span> the script now also works in a shell without arrays.</p>\n        <p class=\"ds-notice ds-notice--error\"><span class=\"ds-notice__title\">broken:</span> the version in the first listing fails when the path holds a space. Use the second.</p>\n      </div>\n      <article class=\"ds-prose\" data-component=\"typography\">\n        <h1>A status line in forty lines</h1>\n        <p>Every few years I install a prompt framework, admire it for a week and remove it when the shell takes half a second to start. This time I wrote down what I <strong>actually read</strong> in a prompt. It was three things.</p>\n        <h2>What a prompt has to say</h2>\n        <ul>\n          <li>Where I am, shortened to the last two directories.</li>\n          <li>Whether the last command failed, as its exit code.</li>\n          <li>The branch, when there is one, and nothing when there is none.</li>\n        </ul>\n        <p>That is one call to <code>pwd</code>, one read of <code>$?</code> and one small file under <code>.git</code>. No process has to start.</p>\n        <h3>The whole of it</h3>\n<pre class=\"ds-code\">lantern() {\n  code=$?\n  dir=${PWD#\"${PWD%/*/*}/\"}\n  [ $code -ne 0 ] &amp;&amp; err=\" [$code]\" || err=\"\"\n  printf '%s%s $ ' \"$dir\" \"$err\"\n}</pre>\n        <p>The remaining thirty lines read the branch and choose colours. They are in the repository.</p>\n      </article>\n      <h2 class=\"ds-section__title\">related</h2>\n      <ul class=\"ds-links\" data-component=\"links\">\n        <li><a class=\"ds-link\" href=\"#post\">sixteen-colours-are-plenty.md</a></li>\n        <li><a class=\"ds-link is-visited\" href=\"#post\">make-is-enough.md (visited)</a></li>\n        <li><a class=\"ds-link is-hover\" href=\"#post\">the lantern repository (hover)</a></li>\n        <li><a class=\"ds-link is-active\" href=\"#post\">raw source (active)</a></li>\n      </ul>\n      <ul class=\"ds-pagination\">\n        <li><a class=\"ds-pagination__link\" href=\"#post\">&lt; the-laptop-from-2011.md</a></li>\n        <li><span class=\"ds-pagination__link is-disabled\">newest &gt;</span></li>\n      </ul>\n    </div>\n    <aside class=\"ds-sidebar\">\n      <div class=\"ds-sidebar__block\">\n        <h2 class=\"ds-sidebar__title\">stat</h2>\n        <p class=\"ds-sidebar__text\">Mar 02 2026, 9.4K<br>1,480 words, 7 minutes<br>changed Mar 05</p>\n        <p class=\"ds-sidebar__text ds-badges\" data-component=\"badge\"><span class=\"ds-badge\">3</span> <span class=\"ds-badge ds-badge--tag\">shell</span> <span class=\"ds-badge ds-badge--success\">tested</span> <span class=\"ds-badge ds-badge--warning\">bash 3</span> <span class=\"ds-badge ds-badge--danger\">not csh</span></p>\n      </div>\n      <div class=\"ds-sidebar__block\">\n        <h2 class=\"ds-sidebar__title\">in this post</h2>\n        <ul class=\"ds-sidebar__list\">\n          <li><a class=\"ds-sidebar__link is-current\" href=\"#post\">What a prompt has to say</a></li>\n          <li><a class=\"ds-sidebar__link\" href=\"#post\">The whole of it</a></li>\n          <li><a class=\"ds-sidebar__link\" href=\"#post\">Colours</a></li>\n        </ul>\n      </div>\n    </aside>\n  </div>\n</section>\n\n<!-- View 4, about. -->\n<section class=\"ds-view\" id=\"about\">\n  <ol class=\"ds-breadcrumb\">\n    <li><a class=\"ds-breadcrumb__link\" href=\"#home\">~</a></li>\n    <li class=\"ds-breadcrumb__sep\">/</li>\n    <li class=\"ds-breadcrumb__current\">about.txt</li>\n  </ol>\n  <div class=\"ds-page-header\">\n    <div class=\"ds-page-header__main\">\n      <h1 class=\"ds-page-header__title\">about.txt</h1>\n      <p class=\"ds-page-header__text\">Who writes this, on what, and how the pages are made.</p>\n    </div>\n    <div class=\"ds-page-header__action\"><a class=\"ds-button\" href=\"#contact\">write to me</a></div>\n  </div>\n  <div class=\"ds-cols ds-cols--even\">\n    <div class=\"ds-main\">\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> cat about.txt</p>\n      <div class=\"ds-prose\">\n        <p>I repair and program computers for a small workshop. In the evenings I write the tools I wished I had during the day, and then I write down how they work, because in two years I will have forgotten.</p>\n        <h2>What you will find here</h2>\n        <ul>\n          <li>Short programs, explained line by line.</li>\n          <li>Repairs of machines that were meant to be thrown away.</li>\n          <li>Opinions about plain text, held loosely.</li>\n        </ul>\n        <p>Everything here may be copied. A link back is kind but not required.</p>\n      </div>\n    </div>\n    <div class=\"ds-main\">\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> column -t uses.tsv</p>\n      <table class=\"ds-table\">\n        <thead><tr><th>what</th><th>which</th><th class=\"ds-table__num\">since</th></tr></thead>\n        <tbody>\n          <tr><td>machine</td><td>a laptop from 2011</td><td class=\"ds-table__num\">2019</td></tr>\n          <tr class=\"is-alt\"><td>editor</td><td>the one that was installed</td><td class=\"ds-table__num\">2012</td></tr>\n          <tr><td>shell</td><td>plain sh, with lantern</td><td class=\"ds-table__num\">2026</td></tr>\n          <tr class=\"is-alt\"><td>site</td><td>make, awk and 60 lines of sh</td><td class=\"ds-table__num\">2020</td></tr>\n        </tbody>\n      </table>\n      <div class=\"ds-panel\">\n        <h2 class=\"ds-panel__title\">colophon</h2>\n        <div class=\"ds-panel__body\">\n          <p class=\"ds-panel__text\">Set in whatever monospace your system has. No scripts, no fonts to fetch, no pictures. The whole site is 412K.</p>\n        </div>\n      </div>\n    </div>\n  </div>\n</section>\n\n<!-- View 5, contact: a form, and the question before it is sent. -->\n<section class=\"ds-view\" id=\"contact\">\n  <ol class=\"ds-breadcrumb\">\n    <li><a class=\"ds-breadcrumb__link\" href=\"#home\">~</a></li>\n    <li class=\"ds-breadcrumb__sep\">/</li>\n    <li class=\"ds-breadcrumb__current\">contact</li>\n  </ol>\n  <div class=\"ds-page-header\">\n    <div class=\"ds-page-header__main\">\n      <h1 class=\"ds-page-header__title\">contact</h1>\n      <p class=\"ds-page-header__text\">A correction, a question or a machine that will not start.</p>\n    </div>\n    <div class=\"ds-page-header__action\"><a class=\"ds-button ds-button--secondary\" href=\"#about\">about.txt</a></div>\n  </div>\n  <div class=\"ds-cols ds-cols--even\">\n    <div class=\"ds-main\">\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> mail -s \"hello\" author</p>\n      <form class=\"ds-form\" data-component=\"form\" action=\"#contact\">\n        <div class=\"ds-form__row\">\n          <label class=\"ds-form__label\" for=\"m-name\">name:</label>\n          <div class=\"ds-form__field\"><input class=\"ds-form__input\" id=\"m-name\" type=\"text\" value=\"A. Reader\"></div>\n        </div>\n        <div class=\"ds-form__row\">\n          <label class=\"ds-form__label\" for=\"m-mail\">reply-to:</label>\n          <div class=\"ds-form__field\">\n            <input class=\"ds-form__input is-invalid\" id=\"m-mail\" type=\"text\" value=\"reader at example\">\n            <p class=\"ds-form__error\">mail: \"reader at example\" is not an address.</p>\n          </div>\n        </div>\n        <div class=\"ds-form__row\">\n          <label class=\"ds-form__label\" for=\"m-topic\">about:</label>\n          <div class=\"ds-form__field\">\n            <select class=\"ds-form__input ds-form__input--auto\" id=\"m-topic\"><option>a post</option><option>a program</option><option>a repair</option><option>something else</option></select>\n          </div>\n        </div>\n        <div class=\"ds-form__row\">\n          <label class=\"ds-form__label\" for=\"m-text\">message:</label>\n          <div class=\"ds-form__field\">\n            <textarea class=\"ds-form__input\" id=\"m-text\" rows=\"5\">The second listing in the status line post has a stray quote on line 3.</textarea>\n            <p class=\"ds-form__hint\">Plain text. End with a line holding a single dot, or press send.</p>\n          </div>\n        </div>\n        <div class=\"ds-form__row\">\n          <span class=\"ds-form__label\">copy:</span>\n          <div class=\"ds-form__field\">\n            <label class=\"ds-form__check\"><input class=\"ds-form__checkbox\" type=\"checkbox\" checked> send a copy to me</label>\n          </div>\n        </div>\n        <div class=\"ds-form__actions\" data-component=\"buttons\">\n          <button class=\"ds-button\" type=\"button\">send</button>\n          <button class=\"ds-button ds-button--secondary\" type=\"button\">preview</button>\n          <button class=\"ds-button ds-button--danger\" type=\"button\">clear</button>\n          <button class=\"ds-button\" type=\"button\" disabled>attach</button>\n        </div>\n      </form>\n    </div>\n    <div class=\"ds-main\">\n      <p class=\"ds-prompt\"><span class=\"ds-prompt__path\">~</span> <span class=\"ds-prompt__sign\">$</span> rm draft.txt</p>\n      <div class=\"ds-dialog__backdrop\">\n        <div class=\"ds-dialog\" data-component=\"dialog\" role=\"dialog\" aria-labelledby=\"dlg-3\">\n          <h2 class=\"ds-dialog__title\" id=\"dlg-3\">rm: remove draft.txt?</h2>\n          <div class=\"ds-dialog__body\">\n            <p class=\"ds-dialog__text\">The message has not been sent. Clearing the form throws away 14 words.</p>\n            <div class=\"ds-dialog__actions\">\n              <button class=\"ds-button ds-button--danger\" type=\"button\">clear it</button>\n              <button class=\"ds-button ds-button--secondary\" type=\"button\">keep writing</button>\n            </div>\n          </div>\n        </div>\n      </div>\n      <p class=\"ds-note\">Answers usually take a few days. The address is never passed on and the form sets no cookie.</p>\n    </div>\n  </div>\n</section>\n\n<footer class=\"ds-footer\" data-component=\"footer\">\n  <span class=\"ds-footer__mode\">NORMAL</span>\n  <p class=\"ds-footer__seg\">chronoskin | <a class=\"ds-footer__link\" href=\"#posts\">posts/</a> | <a class=\"ds-footer__link\" href=\"#about\">about.txt</a></p>\n  <p class=\"ds-footer__seg ds-footer__seg--right\"><span class=\"ds-key\"><span class=\"ds-key__cap\">j</span> older</span> <span class=\"ds-key\"><span class=\"ds-key__cap\">k</span> newer</span> <span class=\"ds-key\"><span class=\"ds-key__cap\">q</span> home</span> | utf-8 | 2019 to 2026</p>\n</footer>\n\n</div>\n</body>\n</html>\n",
    "style.json": "{\n  \"id\": \"v1.terminal.blog.s03.p02.t03.u03\",\n  \"library\": \"2026.10.07-2111\",\n  \"era\": {\n    \"slug\": \"terminal\",\n    \"years\": [\n      2015,\n      2026\n    ]\n  },\n  \"archetype\": \"blog\",\n  \"mode\": \"mix\",\n  \"markup\": \"modern\",\n  \"viewport\": {\n    \"width\": 1280,\n    \"fluid\": false\n  },\n  \"source\": \"https://chrono.skin/s/v1-tm-3233\",\n  \"short\": \"v1-tm-3233\"\n}\n",
    "tokens.css": "/* @part palette */\n:root {\n  --color-page: #efe6c8;\n  --color-canvas: #fbf5dc;\n  --color-surface: #fbf5dc;\n  --color-surface-alt: #f1e9c9;\n  --color-surface-strong: #e4d9b0;\n  --color-bar: #3b2a1a;\n  --color-bar-text: #fbf5dc;\n  --color-bar-alt: #e4d9b0;\n  --color-bar-alt-text: #3b2a1a;\n  --color-inverse: #3b2a1a;\n  --color-inverse-text: #fbf5dc;\n  --color-text: #3b2a1a;\n  --color-text-muted: #7a6748;\n  --color-heading: #1f140a;\n  --color-heading-alt: #9a4a00;\n  --color-link: #a64b00;\n  --color-link-quiet: #3b2a1a;\n  --color-link-visited: #6b4a6e;\n  --color-link-hover: #1f140a;\n  --color-link-active: #c2410c;\n  --color-border: #8c7a58;\n  --color-border-strong: #3b2a1a;\n  --color-border-muted: #cfc39c;\n  --color-accent: #c2410c;\n  --color-accent-text: #fffbdc;\n  --color-accent-alt: #1a6fb0;\n  --color-fill-1: #fbf5dc;\n  --color-fill-2: #f1e9c9;\n  --color-fill-3: #e4d9b0;\n  --color-fill-4: #f1e9c9;\n  --color-button: #3b2a1a;\n  --color-button-text: #fbf5dc;\n  --color-button-hover-text: #ffffff;\n  --color-button-secondary: #f1e9c9;\n  --color-button-secondary-text: #3b2a1a;\n  --color-input: #fffdf0;\n  --color-input-text: #1f140a;\n  --color-input-border: #8c7a58;\n  --color-disabled: #e4d9b0;\n  --color-disabled-text: #9c8c6a;\n  --color-danger: #b3123f;\n  --color-danger-surface: #f8e1d4;\n  --color-success: #3d7a1c;\n  --color-warning: #a86400;\n  --color-notice: #f1e9c9;\n  --color-notice-text: #3b2a1a;\n  --color-focus: #c2410c;\n  --color-shadow: #8c7a58;\n  --color-overlay: #e4d9b0;\n}\n\n/* @part type */\n:root {\n  --font-body: \"SF Mono\", \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, monospace;\n  --font-heading: \"SF Mono\", \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, monospace;\n  --font-ui: \"SF Mono\", \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, monospace;\n  --font-mono: \"SF Mono\", \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, monospace;\n  --text-base: 14px;\n  --text-small: 12px;\n  --text-ui: 13px;\n  --text-large: 16px;\n  --text-display: 32px;\n  --text-h1: 20px;\n  --text-h2: 16px;\n  --text-h3: 14px;\n  --line-body: 1.45;\n  --line-heading: 1.3;\n  --line-display: 1.15;\n  --weight-body: 400;\n  --weight-bold: 700;\n  --weight-heading: 700;\n  --weight-display: 800;\n  --weight-ui: 700;\n  --heading-transform: none;\n  --heading-tracking: 0;\n  --display-tracking: 0;\n  --ui-transform: none;\n  --link-decoration: underline;\n  --link-decoration-hover: underline;\n  --link-decoration-quiet: none;\n}\n\n/* @part surface */\n:root {\n  --border-width: 2px;\n  --border-width-strong: 4px;\n  --border-style: solid;\n  --radius-control: 0;\n  --radius-panel: 0;\n  --radius-pill: 0;\n  --radius-page: 0;\n  --shadow-panel: none;\n  --shadow-control: none;\n  --shadow-control-hover: 0 0 0 2px var(--color-focus);\n  --shadow-control-pressed: inset 0 0 0 2px var(--color-shadow);\n  --shadow-dialog: 8px 8px 0 var(--color-shadow);\n  --shadow-text: none;\n  --fill-page: var(--color-page);\n  --fill-bar: var(--color-bar);\n  --fill-bar-alt: var(--color-bar-alt);\n  --fill-inverse: var(--color-inverse);\n  --fill-accent: var(--color-accent);\n  --fill-panel: var(--color-surface);\n  --fill-button: var(--color-button);\n  --fill-button-hover: color-mix(in srgb, var(--color-button) 60%, white);\n  --fill-button-secondary: var(--color-button-secondary);\n  --fill-input: var(--color-input);\n  --focus-ring: 2px solid var(--color-focus);\n  --transition: none;\n  --backdrop-blur: 0px;\n}\n"
  }
}
