Skip to content

ASCII Design System & Pattern Library

family and icon/tile slot added (2026-07-27); other house kits seeded Scope: Visual composition vocabulary for all screen surfaces — frames, dividers, fills, accents, data display, network maps. Applies to system screens and cartridge UI alike.

Companion documents: character-set.md (glyph bytes — the byte authority for everything below), screen-design-rules.md (row contract), animation.md (the two sanctioned motion styles), animation.md (the two sanctioned animation styles).

Curated 2026-07-26 from a ~610-pattern capture of asciiart.eu (kn86-ascii workbench); the §5 data/sheet family was selected 2026-07-27 in the kn86-ambercalc workbench; artist credits ride inside each sampled piece and are never stripped. Pieces marked (drawn) are original KN-86 work.


A house style is not a pile of art — it is a set of named slots, each filled by one pattern. Screens compose from slots, never from the raw palette. The palette (the workbench) holds alternates; promotion into a slot is a design decision.

SlotPurpose
frame/heroFull-screen ceremony — title, board masthead
frame/panelThe workhorse content border
frame/insetLight sub-panel / card
frame/title-tabLabel embedded in a panel’s top edge
junction/teeHow an interior divider meets the frame edge
divider/majorMulti-row section break
divider/minorOne-row rule
fill/sparse fill/medium fill/denseOne motif at three densities — the amber intensity ramp
accent/setSingle-cell furniture: bullet, selector, gauge ticks, end-caps
icon/tileMulti-cell pictorial mark (7x3 cells) for program / subject identity

Three styles are defined so far. Deck chrome (the runtime’s own voice) is quiet and rectilinear; icebreaker is the first cart house style; wire (§4) is the first program house style. The rule that lets them coexist on one device: chrome is text, house styles are display. Identity moments (titles, mastheads, ceremony) take the house style; dense working panels take chrome.

Straight lines only, drawn from the KN-86 Code Page’s box/block regions (character-set.md §2 — byte constants like KN86_BOX_H 0x80, KN86_BLOCK_FULL 0xA2, shades 0xA5–0xA7).

SlotPiece
frame/hairlineBox-drawing light set (┌─┐│└┘) — dense data screens
frame/emphasisBox-drawing double set (╔═╗║╚╝) — modals, focus
frame/alertBlock frame (█▀▄) — alerts, interrupts
frame/utilityASCII dot-dash box (.--. / '--') — degraded/log surfaces
divider/minor rule; +- tiled variant
divider/major double rule
fill/texture---+---+ grid, ___ ruled lines, .--. brick
accent/set selector (0x0C), marker (0x04), ▲▼ (0x09/0x0A) scroll hints, device icons 0x10–0x13

One vocabulary throughout: the /\ diagonal. Everything below is pure ASCII except the accent (0x04).

frame/hero — ornate diamond (sampled, edge unit repeats every 4 cols)

Section titled “frame/hero — ornate diamond (sampled, edge unit repeats every 4 cols)”
/\ /\ /\ /\ /\ /\ /\ /\
//\\//\\//\\//\\//\\//\\//\\//\\
\\//\\//\\//\\//\\//\\//\\//\\//
//\\ \/ \/ \/ \/ \/ \/ //\\
\\// \\//
\/ \/

frame/panel — tight weave (sampled, 2×2 unit, tiles to any size)

Section titled “frame/panel — tight weave (sampled, 2×2 unit, tiles to any size)”
/\/\/\/\/\/\/\/\/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/
/\ /\
\/ \/
/\/\/\/\/\/\/\/\/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/

frame/inset (drawn) — hairline, <> corners, 1×1 edges

Section titled “frame/inset (drawn) — hairline, <> corners, 1×1 edges”
<>----------------<>
| |
| |
<>----------------<>

frame/title-tab (drawn) — label replaces top-edge weave units

Section titled “frame/title-tab (drawn) — label replaces top-edge weave units”
/\/\[ CONTRACT ]/\/\/\/\/\/\/\/\
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

Label slot must consume a whole number of /\ units (even char count including brackets); pad the label with spaces to fit.

junction/tee (drawn) — interior divider butts the frame with <> caps

Section titled “junction/tee (drawn) — interior divider butts the frame with <> caps”
/\ /\
\/<>--------------<>\/
/\ /\
minor: <><><><><><><><><><> (also >< , <+> , <*> , <<>> , \\// variants)
major: ______________________________________
/\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\
\/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
major-alt (6-col unit):
____ ____ ____ ____ ____ ____
/\ \/\ \/\ \/\ \/\ \/\ \
/ \___\ \___\ \___\ \___\ \___\ \___\
\ / / / / / / / / / / / /
\/___/\/___/\/___/\/___/\/___/\/___/

Fill ramp — one motif, three brightness levels

Section titled “Fill ramp — one motif, three brightness levels”
sparse (dim): medium: dense (bright):
X X X X / \/ \/ \/ \ /__\/__\/__\
/ \/ \/ \/ \ \_/\_/\_/\_/ \ /\ /\ /
\ /\ /\ /\ / / \/ \/ \/ \ \/ \/ \/
X X X X \_/\_/\_/\_/ /\ /\ /\

Density of lit pixels per cell is the deck’s brightness axis (see the light-value ramp in the workbench guide). Use the ramp steps as background states — idle/ambient/active. Animated fills ride the soft-glyph style per animation.md.

bullet: ♦ item selector: ♦> ACCEPT CONTRACT
ticks: |-♦--♦--♦--| end caps: <>------- -------<>
inline: -♦- <*> ><

is 0x04. The icebreaker skin swaps chrome’s selector for ♦> and chrome’s rules for the <> family.

Tiling caveat: the hero’s 4-col unit and panel’s 2-col unit both divide 128; the major-alt’s 6-col unit does not (last repeat truncates — end the row with its cap).

4. Wire house kit (v1) — the recon cluster

Section titled “4. Wire house kit (v1) — the recon cluster”

The shared house style for the recon-cluster programs: bzbx (probe the network) and CONDUIT (ride one host) first, the rest of the toolkit as they land. Implementation: ui/kit-wire.lsp.

This kit invents nothing. It promotes §6’s network-map language ( link, trunk, ├┤┬┴┼ junctions, [host] (store) {ice} nodes, operator) from a map component into house furniture. Deck chrome draws as a rule; the wire kit draws as a wire, so every divider terminates in a junction or a node. Every glyph is already in font.c; every piece rides a renderer that already exists in ui/pattern.lsp or ui/frames.lsp.

One kit, two poses. The shared slots carry both programs. Each pose adds only what its own verb needs: bzbx enumerates (state per target), CONDUIT connects (direction per line). Both put their meaning in the same leading-glyph column (the ui-design-language.md §4 attention channel), which is what makes the two programs read as one toolkit.

SlotPieceRides
frame/panelchrome’s hairline box, deliberatelyui/panel
frame/herodouble rail carrying nodesui/cbox + ui/net-node
junction/tee├───┤, real junctions into the frameui/net-junction
divider/minorlink run with tapsui/net-link-h
divider/majortrunk with junction caps + midspanui/net-link-h
fill/sparse⋅medium⋅densethe link lattice at three densitiesui/pattern-fill
accent/setnode classes + state + directionui/net-node, glyph codes

frame/panel — the quiet box, with honest junctions

Section titled “frame/panel — the quiet box, with honest junctions”

These are dense working surfaces (a ranked capture table, a session transcript), and §1 gives those the quiet frame. The kit shows up where an interior divider meets the frame:

┌───[ CAPTURE ]────────────────────┐
│ │
├──────────────────┬───────────────┤ junction/tee: ├ ─── ┤
│ │ │
└──────────────────────────────────┘

frame/hero — the panel sits on the network

Section titled “frame/hero — the panel sits on the network”
╔═[GW-01]═══╦═══[RELAY-7]═══╦═══[CORE]═══╗
║ ║ ║ ║
║ (VAULT)─────────{ICE} ║
╚════════════════════════════════════════╝

Nodes stamp opaque onto the top rail, so they sit on the trunk. ui/wire-hero takes (col class label) triples; the screen places them, since a declarative panel has no topology to know about.

minor (drops mark branch points): ──────┬───────────┬───────────┬──────
major (trunk, capped, ╬ midspan): ├════════════╬════════════════┤
Section titled “Fill ramp — the link lattice, three densities”
sparse (open field) medium (mapped lattice) dense (saturated grid)
─ ─ ─ ─ ─ ─┼───┼───┼───┼───┼── ═╬═══╬═══╬═══╬═══╬══
│ │ │ │ │ ║ ║ ║ ║ ║

One motif at three densities, per §3’s rule. 4-col unit, so it divides 128 and stays in phase across the full grid. Spaces are transparent, so the sparse step composes over whatever is underneath.

node class: [HOST] host / gateway (STORE) datastore {ICE} countermeasure
state: ● up / responding ○ down / no answer ▒ filtered ♦ revealed
direction: ► outbound (you sent) ◄ inbound (it answered)

Codes: 0x05, 0x06, 0xA6, 0x04, 0x0C, 0x0B. All were already in font.c; only needed a name in ui/glyphs.lsp, alongside the double-junction set ╠╣╦╩╬ (0x91–0x95) the trunk dividers and lattice fill use.

The port strip is bzbx’s motif. Selected pieces #26 and #550 are the same motif at different spacings; #26’s |_||_| unit is 3 cols and does not divide 128, so the kit takes #550’s 2-col |_|_| form and #26 becomes the frame built from it.

port strip (divider): port frame (rails, plain sides):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|_|_|_|_|_|_|_|_|_ |_|_|_|_|_|_|_|_
| |
|_ _ _ _ _ _ _ _|

Open ports hang off a host’s own wire. Closed and filtered ports have no drop; absence is the encoding:

[fin-relay-07]
──┬────┬──────┬──┬──────────┬────────
22 80 443 8080 9100

Target state rides the leading-glyph column, which ui/list already accepts as its leading-glyph-fn. Node-class brackets carry kind; the gutter carries state:

● [fin-relay-07] 10.0.7.14 ssh up / responding
○ [------------] 10.0.7.15 --- down / no answer
▒ [core-ledger ] 10.0.7.31 ftp filtered
♦ [gw-01 ] 10.0.7.1 telnet revealed → DOSSIER

CONDUIT is the client, so its hero is the connection it holds, and session state is the link’s state:

on ♦════════════════════════►[fin-relay-07] solid trunk, arrived
dialing ♦═════════════════····► [fin-relay-07] trailing off, not there yet
auth ♦═════════════════════║ [fin-relay-07] stopped at a wall
closed ♦──────────────────── [fin-relay-07] thin line, nothing at the end

A shell has one visual rhythm: what you sent, and what came back. That is the same leading-glyph column bzbx uses:

● session open: fin-relay-07 (ssh)
► cat /etc/route.conf
◄ gateway 10.0.7.1
♦ finding: routes-to core-ledger
○ session closed

The prompt line is ui/field, which already draws a caret.

panel, divider, and fill take (kit wire). Panel slots: hero, ports, default panel. Divider kinds: major, ports, default minor. fill keeps icebreaker as its default kit, since fill predates the kit attr and existing screens depend on it.

frame/inset and frame/title-tab are inherited from chrome rather than skinned; the hairline box and its title cutout already read correctly at this weight. The pose heroes (ui/wire-hero with rail nodes, ui/wire-link) are called by screens directly, since neither has a meaningful declarative form.

Provenance (kn86-ascii workbench ids): port strip #550, port frame #26, scan-as-map hero #662, host-state gutter #663, ports-as-taps #664 — all selected. Shared frames, dividers, and fill ramp #657–#661, drawn.

A slot for pictorial identity where a single glyph is too small and spending a font byte is unwarranted. A tile is 7 columns x 3 rows of cells (56x24 px), composed entirely from codes that already exist in the Code Page. Because the mark is multi-cell, it needs no new font bytes: the reserved single-cell icon range (0x140x1F, character-set.md §9.2) stays unspent no matter how many tiles get drawn.

First consumer: the PROGS launcher’s ICON view (ADR-0059, progs-launcher.md §5). Set locked 2026-07-27.

  1. 7 x 3 cells, fixed. Every tile in a set is the same size.
  2. Existing glyphs only: box 0x800x90, vertical ramp 0x960x9C, half and full blocks 0xA00xA4, shades 0xA50xA7, symbols 0x040x0F, ASCII. Diagonals (U+2571/2572) do not exist in this font; use ASCII / and \.
  3. Depict the subject’s artifact, the thing it operates on: a grid, a key, a tree, an envelope, a file card, a waveform. One consistent basis is what makes a set cohere; without it the marks read as unrelated doodles.
  4. Watch lit-cell weight across the set. A full-bleed tile (21 of 21 cells lit) reads brighter than a sparse one and pulls the eye. Where a set mixes both, give every tile the same background well so the container carries the rhythm and the interior does the distinguishing.
  5. Spaces are transparent.
  6. The label sits beneath, centred on the tile’s axis. Names longer than the grid pitch wrap to a second centred row.
  7. Selection inverts the label row and flips the label to black. A tile cell owns no other full-pitch band, and selection is inversion everywhere else in the system (§4).
┌┬┬┬┬┬┐ ┌─┬─┬─┐ ┌─────┐ ┌──┐
├┼█┼┼┼┤ │▒│░│▓│ ■═════■ │□□□□□│ │ └──┐
└┴┴┴┴┴┘ └─┴─┴─┘ └─────┘ └─────┘
AMBERCALC bzbx CONDUIT DECK HUB DOSSIER
○─┬─┐ ┌─────┐ ► ▐██ ┌─┬───┐
│ │ │\___/│ λ(▒)█ ► ▐██ │├│▒▒▒│
▀ ▀ └─────┘ ► ▐██ └┴┴───┘
KEYRING kn9 knEmacs knSALK KOMMANDER
┌███┐ ┌─────┐ ▄▄▄▄▄▄▄ ▁▃▅▇▅▃▁
│───│ │ >_ │ ███████ ───────
└───┘ └─────┘ /\/\/\/ ▁▂▃▄▅▆▇
MISSION REPL RIPSAW WAVAMPSTER
BOARD

Each mark is its program’s artifact: the sheet’s grid with an active cell, three shaded applets, two linked hosts, empty loadout slots, a filed folder, a keyring, an envelope, a form with point in it, traffic stopping at a wall, a tree beside a pane, a clipboard, a shell prompt, a saw blade (the name), a level meter.

Known tension in this set. DECK HUB, kn9 and REPL share an identical ┌─────┐ outer frame and differ only across five interior cells, so at 24 px the frame stops carrying information for those three. Lit weight also spans 5 cells (knEmacs) to 21 (seven tiles). Rule 4’s uniform well is the remedy that keeps every mark as drawn.

ui/kv-line returns one dot-leader row as a string (KEY ....... VALUE, value right-aligned to the field) instead of drawing it. ui/kv draws with it; callers that need the row as text — a ui/list-row entry, a selectable readout — share the exact leader arithmetic instead of re-deriving it.

► PLAYING COLD TRANSIT state line — ui/transport-line
▌ PAUSED paused
■ STOPPED stopped
┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐
│ ◄◄ │ │ ► │ │ ▌▌ │ │ ■ │ │ ►► │ button rack — ui/transport-rack
└────┘ └────┘ └────┘ └────┘ └────┘
CONS INFO INFO BACK LINK

Transport state is the identity channel: read it from the leading glyph, never from colour. The rack’s active button inverts (the §4 selection channel), so one glance gives both the binding and the state. Key labels sit under their buttons because the deck has no dedicated transport keys. Buttons are 6 cells wide on a 7-cell pitch, 4 rows tall including the label row.

0:00 ──────────●──────────────── 0:03 ui/needle-track

Progress as a needle (● 0x05) riding a rule (─ 0x80). The quiet counterpart to data/meter: no fill, no density, one row. ui/needle-bar is the bare span; ui/needle-track brackets it with pre-formatted clock strings. Clamped at both ends; max <= 0 parks the needle at the left.

Use the needle when the magnitude is position in a span (playback, scrub, seek) and data/meter when it is level (load, charge, signal). The distinction matters because a needle reads as “where am I” and a filled bar reads as “how much”.

WavAmpster’s visual identity, implemented in ui/kit-knamp.lsp. Selected in the workbench 2026-07-27.

This kit is deliberately light. The selection pass took the decorative pieces and the hardware sprites, and rejected every waveform, staff, and note vocabulary (the sine/pulse/square rules, the carrier-wave borders, the tuner dial). The structural slots stay empty on purpose: WavAmpster’s working panels are deck chrome (ui/panel), and the kit supplies identity only. Two of the three role labels Josh assigned say it outright: frame/decor, divider/flourish.

SlotFilled byFunction
frame/decorcolon band (#20)ui/knamp-decor, ui/knamp-decor-rule
divider/flourish-minorscallop (#603)ui/knamp-flourish-minor
divider/flourish-majorarcs over groove (#605)ui/knamp-flourish-major
accent/tunedtower + play (0x15, 0x0C)ui/knamp-tuned
accent/markoperator mark (#233)ui/knamp-mark
sprite/cassettecompact cassette (#640)ui/knamp-cassette
sprite/boomboxboombox face (#641)ui/knamp-boombox
frame/panel frame/inset frame/title-tab junction/tee fill/*(unfilled — deck chrome serves these)
::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::
:: ::
::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::

The masthead. Two solid colon rows over and under a hollow interior; the title stamps opaque through the interior row. The unit is 2 cells, so any even span stays in phase (128 divides cleanly).

divider/flourish-minor — the scallop (#603)

Section titled “divider/flourish-minor — the scallop (#603)”
.-.-. .-.-. .-.-. .-.-.
/ / \ \ / / \ \ / / \ \ / / \ \
`-' `-`-' `-`-' `-`-' `-

8-cell unit, 3 rows. 128 divides by 8, so a full-width span is in phase.

divider/flourish-major — arcs over a groove (#605)

Section titled “divider/flourish-major — arcs over a groove (#605)”
.--. .--. .--.
:::::.\:::::::::.\:::::::::.\:::
' `--' `--' `--'

10-cell unit, 3 rows. 128 is not a multiple of 10, so a full-width span clips mid-unit at the right edge. Size the span to a multiple of 10 where phase matters; the colon groove row hides the clip well otherwise.

ui/knamp-cassette (21×5) is the library identity piece; ui/knamp-boombox (20×7) is the empty-dial art. Both draw transparently (spaces leave cells untouched), so they compose over a fill.

No fill/* ramp: nothing in the selected pool carried a density motif, and WavAmpster’s panels are chrome-bordered with empty interiors. If a textured background is wanted later, it goes through the workbench first.

Chrome-flavored by default; house styles reskin selector/rules/gauge ticks only.

CONTRACTS AVAILABLE
──────────────────────────
► BREACH THE VAULT 3d
TRACE THE COURIER 1d
SILENT AUDIT 5d
──────────────────────────
3 OPEN · 2 LOCKED

= 0x0C. Right-aligned meta column; hairline rules above/below the scroll region.

NODE CLASS HEAT ICE
─────────────────────────
GW-01 gate ▓▓░ yes
VAULT-2 store ░░░ no

Whitespace columns, no rules — alignment does the work at 8×8. A ▓░ cell is a micro-meter; any column may be one.

OPERATOR ......... KESTREL
CREDITS .......... 12,405
REPUTATION ....... TRUSTED

Three forms, all built on glyphs that already exist (character-set.md §9):

INTEGRITY [██████████ ] 66% bracketed bar — 0xA2 fill, blank empty (ui/meter)
TRACE █████▊░░░ 78% fine bar — sub-cell eighths, §9.5 recipe
HEAT ▓▓▓▓▓░░░░░ HIGH unbracketed ambient strip
CALIBRATE |-♦--♦--♦--| icebreaker tick-rail skin

The fine-bar recipe (full blocks + one partial 0xA8–0xAE glyph + shade padding) gives a 16-cell bar 128 distinct positions — smooth enough to animate during a run.

NET LOAD ▂▃▅▇█▆▃▁▂▄▆▇▅▃▂▁ bar sparkline — vertical ramp 0x96–0x9D, §9.5 recipe
PING 1h ░░▒▒▓█▓▒░░▒▓██▓▒ shade-run alternative (4 levels)
QUIET .:-=+*#*+=-:. pure-ASCII density run (7 levels)

One char per sample, scroll left, newest sample rightmost (bold via invert attribute). Empty buckets render as space, not the zero-bar.

The furniture a columnar spreadsheet surface needs and the slots above have no place for. Implemented in ui/sheet.lsp; selected slot by slot in the kn86-ascii sibling workbench (kn86-ambercalc, port 7781) on 2026-07-27, where every candidate was the real screen with exactly one thing changed. AmberCalc is the first consumer; Black Ledger’s register lens is the next.

┌─[ AMBERCALC ]────────────────────────────────────────────────┐
│@ ■1 ►2 ¤3 4 5 │ <- inverted
│ 1│ 2087-03-11 Shell:Cay:~ $12,405 cleared TX-8801 │
│ 2│ 2087-03-14 Shell:Cay:~ ($3,200) held TX-8814 │
│ 3│ 2087-03-19 Trust:Car:~ ($900) cleared TX-8822 │
│ 4│ ────────── │
│ 5│ TOTAL ♦$8,305 │ <- cursor row
│ │ ══════════ │
│ EMPTY WORLD │
│ ┌─[ @5$3 ]─────────────────────────────┐ │
│ │ (sum (range @1$3 @3$3))█ │ │
│ └──────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
SlotPieceBytes
sheet/header-bandone unbroken inverted slab over gutter + columns, carrying grid column numbers so a scrolled window reads $6 $7 rather than renumberinginversion
sheet/gutterrow number flush right against a rule, so 9 and 10 line up 0x81
sheet/cell-cursorinverted cell and inverted row number — both ends of a crosshair, no ink betweeninversion
sheet/col-rulenone: whitespace and alignment do the work
sheet/type-sigila distinct glyph per column type; :acct is a real badge, not a blank¤ 0x0E, 0x07, 0x0C, #
sheet/numeric-aligncurrency right-aligns to the column edge
sheet/negativeaccounting parens — at 8×8 a leading - is one lit row and reads as noise( )
sheet/total-rulesingle rule above an aggregate, double below 0x80, 0x8B
sheet/formula-marka formula’s value leads with the diamond 0x04
sheet/composerframed strip, coordinate as a title tab, block caret — entry reads as a mode┌┐└┘│─, 0xA2
sheet/col-widthfixed pitch, truncation marked so the operator knows the value continues~ 0x7E
sheet/overflow-tickerthe resting cell reads itself out
sheet/column-viewportthe window follows the cursor; cues where populated grid continues◄►▲▼ 0x0B/0x0C/0x09/0x0A

Geometry is an argument, never a constant. Sheet components take a pixel origin plus a scale, with widths in characters — unlike the rest of the kit, which is cell-addressed at scale 1. A sheet is a mixed-scale surface (AmberCalc draws its body at scale 2 so 16px glyphs survive a downscaled recording) and cell-at-scale-1 cannot express that.

The ticker holds at the head for 4 frames, scrolls one character per frame to the tail, holds, then snaps back — at 250 ms/frame, the 4 chars/sec the CIPHER-LINE OLED driver already uses for its own row overflow (hosts/device/firmware/pico2/src/oled_layout.c, compute_ticker_window). One ticker rule on both displays. A value that fits is returned whole and never moves; the ticker is armed only by overflow, which is that driver’s ticker_active rule. Drawing it is animation.md’s glyph-cycle style (a per-cell glyph choice per frame), not soft-glyph — one cell row, so ~11 glyph writes a frame on the idle timer.

The viewport moves only when the cursor would leave it: stepping off the left/top pulls the window to the cursor, stepping off the right/bottom pulls it so the cursor is the last visible line. Edge cues key off the populated extent of the grid, never off (cell-cols) / (cell-rows-usable) — the grid is addressable to 128×73, so cueing against the maximum promises “more this way” on every row of an empty sheet.

Two glyph traps, both of which draw a blank cell if you walk into them: there is no ellipsis anywhere in the Code Page (U+2026 is unmapped), and 0xFA — CP437’s · — is all-zeros in font.c. Hence ~ as the truncation marker. Build any glyph ≥ 0x80 from its code via (char->string CODE), which is byte-exact; a literal in Lisp source is stored as multi-byte UTF-8 and render_text draws each byte as a separate glyph.

Palette alternates (judged, not promoted): a per-column-chip header, underlined names, and name-over-coordinate for the header band; ruler ticks in the gutter; hairline column rules; OLED-rule wrap and ping-pong for the ticker; freeze-first-column and page-by-screenful for the viewport.

Node class is encoded by bracket shape — readable at any density, no legend needed after first exposure:

[GW-01] host / gateway {ICE} countermeasure
(VAULT) datastore ♦ operator position

Trunk style (orthogonal, CP437 box edges — the generatable form):

[GW-01]──┬──[RELAY-7]══[CORE]
│ │
(VAULT)────{ICE}───┘

── link, ══ trunk/backbone, ┬ ┘ ├ junctions. Generated topologies (World Engine) snap nodes to a coarse grid and route edges Manhattan-style so this renders with pure cell writes.

Freeform style (authored screens only — diagonals allowed):

[GW-01]
/ \
{fw-1} (VAULT)
| |
[RELAY-7]---{ICE!}
|

Animated edges: for live topology (pulse traveling a link during a trace), draw the same grid-snapped layout with render/half-block-line strokes instead of box glyphs — the maps-doc overlay approach; node vocabulary unchanged. Animate the lookup, not the pixels.

The 256×64 auxiliary display carries its own slot decisions, settled 2026-07-27 in the kn86-aux workbench against the real oled.c render path and the real kn86_font. It is deck chrome only, because the panel is the runtime’s own voice, so cart house styles do not reach it.

The panel renders design-system surfaces on a 32-column × 8-row grid at a strict 8×8 cell. The 8×16 tier (four logical rows, each glyph sampled twice vertically) is not used for these surfaces.

Two reasons, both observed on glass:

  • A two-row frame or a pair of rules costs 25% of the panel at 8×8 and 50% at 8×16. Only 8×8 leaves room for chrome and four lines of content.
  • The icons (§7.2) are 8×8 art. On the 8×16 tier every glyph row is drawn twice, so the battery nub doubles and the signal bars smear. Icons only read at 8×8.

This supersedes the four-row logical carving in ADR-0015 §2 for design-system surfaces. The row semantics are unchanged (status, CIPHER current, CIPHER echo, contextual); they now occupy named cell rows (§7.3) instead of four 16-pixel bands. ADR-0015’s panel geometry (32 cells × 8 rows at 8×8 native) is unchanged; only the carving moves.

0x100x13, defined in character-set.md §9.2 and reserved there for inline UI markers. No new art was drawn.

ByteIconUse on the panel
0x10CARTRIDGELoaded cart, before its name
0x11BATTERYCharge readout, before the gauge
0x12SIGNALLink quality, before the level
0x13PADLOCKMarks an encrypted value (the seed line)

Icons replace their words (BAT: SIG: CART:), which frees ~9 of 32 cells. That saving is what buys the mission timer a place on the strip.

Cell rowContentOwner
0Status strip (§7.4)runtime
1divider/minor: (0x80) tiled full widthruntime
2blank
3CIPHER current utteranceCIPHER engine
4CIPHER previous echoCIPHER engine
5blank
6divider/minorruntime
7Contextual line (the oled_row4.c claim stack)claimant
B[███▊] S4 C ICE T+12:04 TERM
────────────────────────────────
> ICE TRACE CLEAN.
LINK STABLE. FLOOR LOW.
────────────────────────────────
L 4F2A910C LAB 00:17

Icon placeholders above: B = 0x11 BATTERY, S = 0x12 SIGNAL, C = 0x10 CARTRIDGE, L = 0x13 PADLOCK. Each occupies one cell; the icons have no Unicode equivalent, so they are written by byte throughout this section.

SlotPiece
aux/geometry32×8 at 8×8
aux/divider/minor (0x80) tiled full width, cell rows 1 and 6
aux/icon/set0x100x13 (§7.2)
aux/icon/battery0x11 + a bracketed bar: B[███▊]
aux/meterBracketed bar + exact percent: TRACE [██████▍░░░] 66%
aux/row4/missionTrace gauge + countdown, right-aligned: TRACE [███▎░░░░] 42% T-01:38
aux/hero/clockHH:MM:SS at scale 4, full width, nothing else
aux/hero/splashKINOSHITA at scale 2 over <>--- KN-86 // DECKLINE ---<>

A fill needs a boundary glyph at this scale. A bare run of full blocks (0xA2) reads as one lit slab. At any charge above ~75% the level stops being legible, because there is no edge to measure it against. Every gauge on this panel is bracketed:

B[███▊] battery, bracketed reads
B ███▊ battery, bare slab
TRACE [██████▍░░░] 66% reads

The bracket also does the labelling work that the freed cells paid for: [ and ] cost 2 cells and remove any doubt about where the gauge starts and ends.

Gauges use the fine-bar recipe from character-set.md §9.5 (full blocks 0xA2 plus one partial 0xA80xAE plus light-shade 0xA5 padding), so a 4-cell bar has 32 distinct positions and slides smoothly rather than stepping.

Modes claim the whole panel through the oled/ mode manager and paint with the pixel tier, so they are not bound to the cell grid.

  • Clock. HH:MM:SS at scale 4. Eight glyphs × 32 px spans exactly 256 px. No furniture: a full-screen mode is ceremony, and chrome only crowds it.
  • Boot splash. KINOSHITA at scale 2, with <>--- KN-86 // DECKLINE ---<> beneath. The <> caps are the accent family from §3, the one place the panel borrows a house gesture, used here because the splash is an identity moment.

The workbench holds curated palettes awaiting slot promotion: depthcharge (waves/sonar), blackledger (ledger rules, columns, medallions), neongrid (skyline, traffic, arrows), snake (serpentine, scales), tunnel-rat (mice, tunnels), attract (fractals + the fireworks/starfield/galaxy animation recipes). Each becomes a §3-style section when its cart enters design.

First-party programs take kits too, and they share rather than one-per-program: the deck would stop reading as one device at thirteen vocabularies. §4’s wire kit covers the whole recon cluster (bzbx, CONDUIT, and RIPSAW / Kommander / Keyring / kn9 as they enter design), with each program adding only an accent pose over the shared slots.

Sampled pieces: asciiart.eu, captured 2026-07-26; embedded artist initials preserved (e.g. gfj/98 in the cross-stitch sampler), credit fields in the workbench db. Drawn pieces: original. The workbench (kn86-ascii, port 7797) remains the palette browser and the place new candidates get judged before entering this document.