The identity system for Little Loops Technologies, LLC and its family of properties. One hand draws every loop: shared type, shared paper, shared voice — one accent per property, and every rule stated as something a person or an agent can check. v4.0 ships the assets, closes the ten defects the v3.0 review found, and passes its own harnesses — including the voice-lint, on this very page, by mechanism rather than by exemption.
A hybrid model, OSS-led. The parent and the toolkit share DNA — the double-loop mark. The commercial product will get its own name and accent, but inherits the type system, the paper, and the mechanical-toy conceptual language. The flagship's identity is specified first-class in §03, ahead of the parent's color and type, because the hierarchy is enacted in the document's own order.
The autonomous development toolkit and the flagship of the brand. Speaks to developers in the full poetic register. Its identity — the ll: treatment, its lockup, its refresh spec — is §03.
Commercial offering. Own name, own accent hue, inherited type and voice at a cooler temperature. Launches as “from the makers of little-loops.” Named before its site is designed, against the criteria in §13.
little-loops: lowercase, hyphenated, and set in mono whenever the surrounding sentence could confuse it with the company. In running text where both appear, the company gets the capitals and the toolkit gets the hyphen — that contrast is the rule, not a hazard.Two interlocking loops rendered as a single solid filled form — no dot, no stroke, no cursive connective tissue: the wind-up gesture of the whole brand, resolved as shape rather than as a drawn line. The curve is design-frozen: the approved artwork, stored as canonical path data in /brand/canon. The generator (make-mark.py) is its custodian, not its author — it emits every asset from the frozen curve (sizes, colorways, the favicon derivation, the lockup, and the §02 clearspace table) and never re-derives the geometry. v4.9 replaces the prior cursive-stroke mark with this filled form; the old mark's canon data and every asset it emitted are archived under archive/ll-mark/, not deleted — its own history (the v3.0 bias parameter, the v4.0 regression, the v4.1 fix) stays in the changelog as the record of that mark, not of this one. In notation the mark is written ℓℓ — mono and caption contexts only, where glyph coverage is verified; headings and body call it the double-loop mark, so no surface ever gambles a display face on U+2113.
assets/lockup-ink.svg / lockup-paper.svg · internal gap set by the lockup artwork · ink-to-text --s4 (clearspace-exempt)Datum: loop height is the y-extent of the curve's own bounding box, the artwork's frame padding excluded, as emitted by make-mark.py — 168.7 of the 260 viewBox units. Clearspace is ½ loop height on all sides, against external elements only; lockup-internal spacing is set by the lockup artwork and is exempt. v3.0 stated the rule and then computed its own header example against the SVG bounding box — three demonstrations, three values, none matching. The table below is generator output; nothing in it was hand-computed, and a regenerated mark regenerates it.
| SVG height | Loop height | Clearspace |
|---|---|---|
| 24px | 15.6px | 7.8px |
| 34px (header) | 22.1px | 11.0px |
| 48px | 31.1px | 15.6px |
| 64px | 41.5px | 20.8px |
| 96px | 62.3px | 31.1px |
assets/lockup-ink.svg or lockup-paper.svg, never compose one. (This kit's own HTML wordmarks, fig. 01 and the header included, are schematic stand-ins, displayed under the §04 specimen exemption; they predate the emitted artwork and are not itself an excuse to recompose one downstream.)The toolkit's identity has two registered assets: the shared double-loop mark (in Blueprint or ink) and the ll: prefix treatment — the wordmark the live site already ships, mirroring the /ll: command namespace developers type every day. The prefix is the most ownable thing the brand has. Face ruling, stated once and echoed nowhere in conflict: the ll: treatment is JetBrains Mono 700, always — the §06 scale lends it only its size (--t-h). v3.0's scale table filed it under Archivo; that contradiction is retired.
--d-oss dark); the name takes text color. Never the reverse.| Axis | Live site today | After refresh |
|---|---|---|
| Surface | Dark-leaning presentation | Dark stays the default — the audience lives in dark editors, and flipping the flagship to light-default is a redesign, not an alignment. Light mode ships as the alternative. |
| Palette | Unaligned | Ink surface, Paper text, Blueprint accent, Pass/Signal semantics — the §05 dark token set verbatim. |
| Type | Unaligned | Newsreader display ≥ 28px, Archivo UI, JetBrains Mono code and the ll: wordmark. |
| Copy | Canonical | Unchanged. The site is the voice spec's source, not its subject (§08). |
| Diagrams & tabs | Styled ad hoc | Restyled to the §10 diagram spec. Figure numbering for tabbed panels, decided: a tab group is one figure; its panels are lettered states of it (“fig. 03a Generate · 03b Run · 03c Resume”), so numbers never change when tabs switch. |
| Icons | Unicode arrows (↗), unstyled copy buttons | The §10 set — external-link, copy, and close are drawn in v4.0 precisely because the live site needs them on day one. |
Hierarchy, enacted: this section precedes color, type, and states because the flagship outranks the parent. The parent is a proof; little-loops is the product of the brand.
The parent brand is near-monochrome: warm paper and warm black ink, like a well-made instruction manual. Each property claims exactly one brand accent; accents never mix on the same surface. Separately, two semantic colors — Pass and Signal — carry meaning family-wide and belong to no property. Every ratio below is computed, not estimated, and was independently re-verified for v4.0.
| Token | on Paper | on Paper Deep | on Ink | Text use |
|---|---|---|---|---|
| Ink | 16.4:1 AAA | 14.6:1 AAA | — | All sizes |
| Ink Soft | 10.6:1 AAA | 9.5:1 AAA | — | All sizes |
| Blueprint | 6.6:1 AA | 5.9:1 AA | 2.5:1 fail | Light surfaces only; use --d-oss on Ink |
| Verdigris | 5.8:1 AA | 5.1:1 AA | 2.9:1 fail | Light surfaces only; use --d-product on Ink |
| Pass | 6.3:1 AA | 5.6:1 AA | 2.6:1 fail | Light surfaces only; use --d-ok on Ink |
| Signal | 5.8:1 AA | 5.2:1 AA | 2.8:1 fail | Light surfaces only; use --d-error on Ink |
| Line | 1.4:1 n/a | 1.2:1 n/a | — | Never text; borders only |
| Paper | — | — | 16.4:1 AAA | Text on dark surfaces |
| Paper on Blueprint | 6.6:1 AA | Property tags, filled buttons — new row; v3.0 shipped tags on an unverified pair | ||
| Paper on Verdigris | 5.8:1 AA | Property tags only | ||
| Ink on --d-oss | 6.7:1 AA | Parent tags on Ink surfaces (v4.6): the light fills sit under 3:1 against Ink, so the chip inverts — dark fill, Ink text | ||
| Ink on --d-product | 8.0:1 AAA | Parent tags on Ink surfaces (v4.6) — same inversion | ||
Rule: no accent or semantic color may be used as text on Ink surfaces — the dark-mode counterparts in §05 exist for that. Any text-on-color pair a surface ships must have a row in this matrix; a pair without a row is a defect, not an oversight. When a claim in this table and a design in the wild disagree, recompute; never eyeball.
The parent site at littleloops.tech uses no brand accent at all — ink and paper only, with Blueprint and Verdigris appearing solely as small tags where the properties are listed. Semantic colors are permitted everywhere, including the parent (a form error is a form error). Restraint is the parent's signature — and it is fully specified in §07, because removing color means the interaction language has to work harder, not less.
The audience lives in dark editors and dark terminals. Dark mode is the same system inverted: Ink becomes the surface, Paper becomes the text, and every accent has a lightened counterpart tuned to pass on Ink. The governing rule is stated against the surface, not a component list: any element whose resting background is Ink carries the .dark class — dark panels, dark figure stages, swatch chips, and every code block. Transient state inversions (a hovered button) are states, not surfaces. v3.0 patched the class onto its dark panels and left every code block with an invisible selection; the surface rule closes the category, and it is grep-checkable: a non-state ruleset with background:var(--ink) whose markup lacks .dark fails review.
| Role | Light token | Dark token | Dark ratio on Ink |
|---|---|---|---|
| Surface | --paper #F5F1E8 | --ink #16130F | — |
| Text | --ink | --d-text #F5F1E8 | 16.4:1 AAA |
| Text, soft | --ink-soft | --d-text-soft #C9C2B2 | 10.4:1 AAA |
| Border | --line | --d-line #4A443A | 1.9:1 borders only |
| OSS accent | --oss | --d-oss #8895F2 | 6.7:1 AA |
| Product accent | --product | --d-product #3FBF97 | 8.0:1 AAA |
| Pass | --ok | --d-ok #57B87A | 7.5:1 AAA |
| Signal | --error | --d-error #EE8F55 | 7.7:1 AAA |
| Code comment | — | --code-comment #C9C2B2 | 10.4:1 AAA |
| Focus | --ink outline | --d-text outline | 16.4:1 AAA |
| Selection | ink bg / paper text | paper bg / ink text | 16.4:1 AAA |
:focus-visible, not an element list. --d-line fails the 3:1 non-text minimum and is never a focus indicator.Newsreader for the poetic beat, Archivo for structure and UI, JetBrains Mono for the machine's own voice. All three are open-source (Google Fonts / SIL OFL). Loaded lean: the webfont request carries only the axis ranges this scale uses — Newsreader 300–450 with italics, Archivo 400–900, Mono 400/500/700 — with preconnects to both font origins. v3.0 requested the full 100–900 spectrum of two variable families and used a third of it.
A loop is a small machine you define once and run forever. Describe what to check, what to fix, and when to stop — the rest runs without you. Body copy stays plain, warm, and declarative; the italics live in the display layer.
# two commands to start
pip install little-loops
ll-init| Token | Size | Face | Roles |
|---|---|---|---|
| display / h1 | clamp(44px … 84px) | Newsreader 300–450 | Cover and hero display. |
| display / h2 | clamp(30px … 44px) | Newsreader 450 | Section headings. This row existed in v3.0's CSS and not in its scale — the token-lint exemption now enumerates all three clamp pairs, and a clamp absent from this table fails the lint. |
| display / specimen | clamp(30px … 52px) | Newsreader 300–450 | Type-specimen display. Floor across all clamps is 28px by clamp minimum. |
| --t-pull | 28px · 1.75rem | Newsreader 400 roman | Pull quotes. The Newsreader floor — any Newsreader glyph under 28px is a defect. |
| --t-h | 24px · 1.5rem | Archivo 600–900 | Headings and the schematic wordmark. The ll: treatment borrows only this size; its face is JetBrains Mono 700 by §03. |
| --t-lede | 17px · 1.0625rem | Archivo 400 | Intros and ledes. |
| --t-body | 16px · 1rem | Archivo 400–500 | Body default. |
| --t-code | 14px · 0.875rem | JetBrains Mono / Archivo | Code blocks, buttons, card body, table body, small prose. |
| --t-label | 13px · 0.8125rem | Archivo 800 caps | Section labels (h3), card labels, the header brandline. |
| --t-caption | 12px · 0.75rem | JetBrains Mono / Archivo 600 | Captions, nav, kickers, matrix cells, swatch metadata. |
| --t-micro | 10px · 0.625rem | JetBrains Mono / Archivo 500–600 | Leader indices and lockup sublines. Nothing may be smaller — v3.0's sublines were a 9px calc() the lint couldn't see; the calc is gone, the role has a token, and token-lint v2 flags any calc() on a type token. |
The scale is hand-picked, not ratio-derived — and that is a decision, recorded in §13's decision record. A generator ratio was considered for v4.8 and rejected: retrofitting derived values changes every token, a breaking bump for zero visual gain, and additions are already governed better than a formula would govern them — a new size needs a row in this table and a version bump, and a size computed from another is exactly what the token-lint calc() rule bans. The rem values assume the 16px root this page ships; they are conveniences for builders, and the px column stays canonical.
Removing decoration means interactivity must be legible from behavior alone. Two interaction languages exist: the parent's accent-free language (ink does everything) and the property language (the accent carries links and focus). This document runs the parent language on light surfaces and the dark language inside every .dark element — the buttons and links on this page are the spec running, in both modes. Property-language specimens below are covered by the §04 specimen exemption.
Try it: an ink link ·
Specimen: a Blueprint link · ✓ 12 passed · ✕ config not found — run ll-init
Until v4.8 the §04 matrix carried rows for filled buttons — Paper on Blueprint, Ink on --d-oss — that no component in this kit shipped, and the universal :focus-visible ruling named inputs no spec had drawn. Both gaps close here: an input, its label, a switch, and the primary button, every dimension a token. The switch's interactive box stands on the --touch floor with the track and knob drawn inside it; its on-state pairs are the §04 inversion rows doing their work. One motion ruling travels with it: the knob's travel is an activation transition (--hover-dur, --hover-ease), not a hover one — §09's "nothing moves position" binds hover, and hover still moves nothing. The accented specimens below are property language on a parent surface, covered by the §04 exemption; the dark variants render live in §12's fig. 10.
forrole="switch", aria-checked, and a nameVerification path, stated so it can be walked: tab through this page and every stop shows a 2px ring — ink on paper, Paper on ink. Select text anywhere, including the YAML block in §08 and the code specimen in §06, and the selection is visible. If either fails, the surface rule was broken somewhere; grep for background:var(--ink) and find the element missing .dark.
The voice already exists on little-loops.ai; this codifies it. The metaphor is the wind-up machine: small, complete, self-running. Sentences are short and declarative. Wonder is earned by precision, never by hype. Quotations from the live site are verbatim; where a clause is excerpted, the caption says so — the completeness claim v3.0 made and broke is retired in favor of one the page can keep. v4.4 adds the tagline ruling: the line, its placement, and the candidates it beat, recorded so the ground is not walked twice.
Describe mechanisms, not miracles. Every claim names what actually happens. “AI-powered” is banned; say what the loop does. (Live-site litany, verbatim and complete.)
Declarative fragments are allowed and encouraged in display copy. Body copy uses full sentences but stays under three clauses. (Live-site heading, verbatim and complete.)
The lexicon: wind, run, loop, machine, harness, handoff, done. The banned list lives in the YAML below and only there — a word is banned by a rule, not by a prose list that drifts from it. Spelling ruling: handoff, one word, matching the live docs' “Session Handoff.” (Live-site sentence, verbatim and complete — v3.0 clipped its opening clause while claiming completeness.)
The parent never inflates. Marketing copy avoids a first-person plural that implies a floor of employees that doesn't exist. Little Loops Technologies has multiple founders, so a "we" scoped to them is honest — but a regex can't tell a founder-scoped "we" from a headcount-inflating one, so the rule doesn't try: parent-we flags every "we" and a person confirms the scope, the same treatment unlock-metaphor and handoff-spaced already get below. Legal pages (privacy, terms) require first person and are excluded by path in the YAML — not by an exception in someone's head.
Taglines are property-specific by §01, so this is an assignment, not a slogan. Two lines are decided here. The product’s is deferred on purpose: its register leads with an outcome (above), and an outcome cannot be written before there is a buyer to name it for. The specimens below are mentions under the §04 exemption; the single use on this document is the footer.
| Surface | Treatment | Rule |
|---|---|---|
| Lockup | Beneath the wordmark · Archivo 400 · --t-caption | At lockup size the tagline is Archivo, always — Newsreader under 28px is a defect (§06), and a tagline set at 28px would out-shout the wordmark. Sits one clearspace below the lockup’s lowest ink, never inside the mark’s clearspace (§02). |
| Footer | Sign-off line, under the legal line | Stands beside the §01 attribution line, never instead of it. |
| OG / social | Eyebrow above the Newsreader headline · Ink Soft for parent, accent for properties (§12) | The tagline is the eyebrow or the headline, never both on one card. |
| Nav, buttons, CTAs | Banned | Standing alone as a call to action, “Order from…” reads as a purchase instruction. Adjacent to the name that reading collapses; inside a button it does not. Testable: the line never appears as the sole text of a link or button. |
| Body copy | Once per surface, maximum | The parent runs at low temperature — one poetic line for the whole site. The tagline is that line; a page that prints it twice has spent a budget it does not have. |
A rejected line with no recorded reason comes back. Each row states the ruling and, where the line survives in another role, the role it was reassigned to.
| Candidate | Ruling |
|---|---|
| Determinism in a non-deterministic world | Rejected as tagline; kept as thesis. It promises what the model cannot deliver — the same prompt does not return the same output — and this kit does not ship claims it cannot keep. Retained for the about page and the OG description, where the next sentence can qualify it. Spelling ruling, should it run: nondeterministic, closed, no hyphen — the standard form in computer science, faster to read at display size, and the same class of call as handoff. |
| Small, deterministic machines for a nondeterministic world | Runner-up thesis. “Small” repairs most of the credibility gap: a small machine plausibly is deterministic even when the thing inside it is not. Too long for a lockup; approved for about-page and OG use. |
| Order from the loop | Rejected on the echo. The name already spends loop; the tagline repeating it reads as a stutter, not a rhyme. This rejection is what produced the decided line. |
| Order in a world that drifts | Held in reserve. The best-flowing line of the set, and drift names a failure every reader has watched happen. Reserved for the product, whose register must name the problem before it claims the outcome. |
| Probabilistic parts. Deterministic machines. | Reassigned to technical surfaces. The most precise line considered, and the only survivor that keeps the word — it locates determinism at the machine and never at the model. README opening and conference abstracts, where its vocabulary is native. |
| Small machines that run themselves — and prove it | Rejected on length and on the dash, which survives neither a lockup nor a caption setting. Its second clause is the honest half and stays in the principles above. |
| Ordo ex machina · Ordo ab chao | Rejected. The first is a translation exercise; the second is the Scottish Rite’s 33rd-degree motto, and a borrowed motto carries its lender’s meaning wherever it goes. The English asks nothing of the reader and says more. |
| A jig for a machine that guesses | Not rejected — reassigned to §09. A jig makes every cut identical regardless of whose hand holds the tool: the harness, described without metaphor. Filed as a candidate figure, not as a tagline. |
A banned word is only banned when it is used. The linter parses HTML, not raw text, and skips three containers: code, pre, and anything marked data-mention. That is the whole mechanism — no page-level exemptions, no excluded spec pages. Which puts the burden on honest markup: a code exhibit carries a code element, never mono styling alone, because a linter that parses HTML can only see what the HTML says a thing is. The rule below the specimen is the rule: banned words survive only inside code, pre, or data-mention — and a listing styled as code but not marked as code is a defect in the page, not a gap in the lint. This page passes the lint it defines: every banned word on it sits inside a code element or a data-mention span, and the harness proves it on every version bump. v3.0 failed its own lint and disclosed the failure nowhere in the document; both defects are closed by mechanism, not confession.
# voice-lint.yaml · v4 · runs as a little-loops harness
version: 4
defaults: { scope: all, severity: error, flags: iw } # i = case-insensitive, w = word-boundary
ignore_within: ['code', 'pre', '[data-mention]'] # the use/mention distinction, machine-enforced
rules:
- id: hype-compound
pattern: 'AI[- ]powered'
- id: leverage-verb
pattern: 'leverag(e|es|ed|ing)'
allow: ['leverage ratio']
- id: seamless
pattern: 'seamless(ly)?'
- id: supercharge
pattern: 'supercharg\w*'
allow: ['supercharger'] # the car part is a car part
- id: revolutionize
pattern: 'revolutioniz\w*'
- id: unlock-metaphor
pattern: 'unlock(s|ed|ing)?'
severity: warn # literal vs metaphor needs a human; the loop flags, a person rules
- id: parent-we
pattern: 'we'
scope: parent
severity: warn # founder-scoped "we" is honest, headcount-inflating "we" isn't; a regex can't tell them apart, a person can
exclude_paths: ['/privacy', '/terms', '/legal/*']
- id: handoff-hyphen
pattern: 'hand-off'
fix: 'handoff' # hyphenated form is always wrong; auto-fixable
- id: handoff-spaced
pattern: 'hand off'
severity: warn # legal as a verb phrase, wrong as a noun — a human rules.
# v3.0's prose promised an error no regex can deliver; demoted honestly
# encouraged: wind, run, loop, machine, harness, handoff, doneTwo decorative devices family-wide, and only two. First, the double-loop mark itself, used decoratively — dividers, loading states, low-opacity background geometry. Second, the instruction-manual figure system. Nothing else decorates — no blobs, no gradients, no stock illustration, no photography.
The figure system is the counterweight to a familiar type-and-cream palette, and a counterweight only works if it carries load. It is therefore mandatory wherever exhibits appear: docs diagrams, README screenshots, OG images, slide exhibits, and changelog illustrations all render as numbered figures with mono captions. Numbering restarts per page; a tab group is one figure with lettered panel states (§03); cross-references are namespaced — “docs/loops fig. 03.” A figure without a number and caption is a defect on any family surface.
The divider specimen above is 32px tall — at the boundary, deliberately, so the boundary is visible.
var(--motif-opacity), scale(.94)→scale(1). --draw-dur 900ms, --draw-ease. Triggers on load or first scroll into view, once. Maximum one revealed animation per viewport.--motif-opacity 0.10, never animated, never behind body text under 10:1 effective contrast.prefers-reduced-motion: reduce renders every stroke completed and disables all transitions. Hover transitions elsewhere: --hover-dur 120ms, --hover-ease, opacity and color only — nothing moves position. Activation transitions (a switch knob's travel, §07) may move exactly what they toggle; hover still moves nothing.Single ink strokes, round caps, the mark's own pen — and every icon is drawn in currentColor, so the same SVG is legible on Paper and on Ink without a variant. v3.0 hardcoded light-mode hex into the pass and fail icons, which the §04 matrix itself scores as failing on Ink: the flagship's default surface would have shipped illegible status glyphs. The set also grows from five to eight, because the live site already needs the other three: its external links are Unicode arrows and its copy buttons are unstyled today.
currentColor, never icon fonts, never emoji, never raw Unicode glyphs, never literal hex..icon-ok, .icon-err) and always travel with a word — color is never the sole carrier of meaning. Close is ink; fail is Signal — same geometry, different job, and the color states which.If a build needs a value not listed here, the kit is incomplete — file it as an issue, don't invent the value. This document's own stylesheet passes token-lint v2, and its own navigation works: sticky-header anchor offset is specified below because v3.0 shipped thirteen section links that scrolled their targets under the header.
4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 96 px as --s1…--s9. Section padding: --s9 desktop, --s8 under 720px. Card padding: --s4–--s5. Hand-picked, not ratio-derived — the §06 ruling covers both scales, stated once.--measure 68ch. --measure-narrow 52ch for ledes and asides. Nothing exceeds 68ch.scroll-padding-top to the header's token sum plus --s5 breathing room. An anchor whose heading lands under the header is a defect — testable by clicking every nav link.--r0 0 (default) · --r1 4px (code, inputs) · --r-pill (tags only). Depth is Paper → Paper Deep → Paper Lift plus 1px Line borders. Box shadows are banned family-wide.--bw-hair 1px hairlines · --bw-rule 2px structural rules, buttons, focus · --bw-bar 3px callout bars. Border widths are tokens too.:focus-visible, both modes · reduced motion honored · touch targets ≥ --touch (44px), enforced by min-height on every button.aria-hidden when decorative and carries role="img" with an accessible name when it identifies — this document's own header does it. A linked logo's accessible name is the destination (“little-loops home”), not “logo.”v3.0's every mark was a “schematic stand-in” for artwork that didn't exist; nothing could be built from it without inventing the assets. v4.0 ships them: everything in the table below is a real file in /brand/assets, emitted by make-mark.py, regenerated on every version bump. The kit links its sources; a surface that needs an asset copies a file, never redraws a figure.
Every exhibit before this one exercises a subsystem at a time; a kit whose thesis is checkability owes an integration test. Fig. 10 is a docs settings panel for the flagship, rendered dark because the flagship is (§03), composed entirely from §04–§10 tokens: no value in it exists outside this document. The table after it names where each subsystem appears, so "the system composes" is a claim a reader can walk, not one the kit asserts. Folded from the brand-kit-synth prototype, which mocked a screen the same way — the idea crosses; its values do not.
These defaults govern every loop this project runs. Changes apply on save; a loop already running finishes under the config it started with.
| Subsystem | Where it appears in the figure |
|---|---|
| Color · surfaces | Ink resting background, so the element carries .dark by the §05 surface rule; depth is --d-line hairlines, never a lighter tone — the §05 "depth on Ink is border" ruling, obeyed. |
| Color · accent | One accent, Blueprint's dark variant: the on-state switch tracks and the primary button, with Ink text and knob on --d-oss per the §04 inversion rows (6.7:1). No second accent anywhere. |
| Color · semantic | Pass appears once, in the chrome, as --d-ok — and it travels with a glyph and a word (§10), never color alone. Signal is absent because nothing is wrong; a healthy screen spends no Signal. |
| Type | Title in Archivo 800 at --t-h; body copy at --t-code; setting labels Archivo 700 caps at --t-caption; chrome, input value, and hint in JetBrains Mono. Newsreader is absent — no display role on a settings panel, per the §06 28px boundary. |
| Spacing | Body pads --s6; the grid gaps --s3; rows pad --s3/--s4; the action row stands off by --s5 with a --s4 pad above the hairline. Every gap is a token. |
| Borders & radii | The frame is the 2px rule; internal lines are 1px hairlines; the input rounds --r1; the switch track is the only pill (--r-pill, tags-and-tracks territory); everything else is --r0. No shadows. |
| Interaction | Focus is the universal 2px Paper ring on Ink (§05); selection inverts and stays visible; input, switches, and buttons all stand on the --touch floor. Both buttons and both switch states are §07's spec, running. |
| Motion | The knob's activation travel at --hover-dur / --hover-ease is the only movement; hover changes color only. Reduced motion completes everything instantly (§09). |
| File | What it is | Where it goes |
|---|---|---|
make-mark.py | Custodian of the design-frozen canonical curve (/brand/canon). Emits every file below and the §02 clearspace table; never re-derives the geometry. | Repo root of /brand; also mirrored in the little-loops repo. |
mark-ink.svg / mark-paper.svg | Full double-loop mark, filled in Ink or Paper. | Headers, OG images, GitHub avatar source, print. |
lockup-ink.svg / lockup-paper.svg | The production lockup (v4.7): mark plus wordmark, set from the §06/§08 type tokens, internal gap and clearspace data in mark-data.json's lockup key. Not outlined — <text> in Archivo, so it renders correctly wherever the shared type system is already loaded (§01). | Anywhere §02 requires the lockup: parent header, littleloops.tech, GitHub org avatar. Copy the file; never compose the wordmark yourself. |
favicon-parent.svg | Double-loop glyph, filled Ink, with an internal prefers-color-scheme block flipping to Paper. | littleloops.tech. Ship beside a static PNG/ICO fallback in Ink Soft — the one fill value that reads on both light and dark tab strips — for browsers that ignore SVG media queries. |
favicon-oss.svg | Double-loop glyph, filled Blueprint, flipping to --d-oss. | little-loops.ai and docs. Property favicons take the accent at every size — v3.0's shared-geometry rule made all three properties' tabs pixel-identical at 16px; colorway is now property-specific because differentiation is the favicon's entire job. |
mark-data.json | Parameters, path data, clearspace table — machine-readable for agents. | Consumed by build tooling and this document's own regeneration. |
| Surface | Spec | Notes |
|---|---|---|
| littleloops.tech | Single page: mark, tagline, one-line thesis, the two properties, contact, legal | Tagline “Order from the machine.” under the lockup, in Archivo at --t-caption (§08). Build answers, so the page can be built from this document alone — thesis line: “Little Loops Technologies makes small machines, carefully — an open-source toolkit for autonomous development, and the products built with it.” Contact: hello@littleloops.tech, plain text link. Legal: privacy + terms as sub-paths, excluded from parent-we by the YAML. Ink + paper only, parent interaction language (§07). Dark default, light available (§05), on every parent surface including the legal sub-paths: a reader-run toggle in the footer beside the legal nav — icon-only, §10 sun and moon, choice persisted before first paint, never system-followed; property tags invert to --d-oss and --d-product fills with Ink text (§04); color-scheme dark light; theme-color carries both values; print returns to light. |
| littleloops.tech/brand | This document · dark default · reader-run toggle (§05) | Dark since v4.2, and joined by the registry in v4.6 — still the surface that proves the mechanism end-to-end. It is a working document, and its readers live in dark editors, so dark is the default and light is the persisted alternative; the page never follows the system. The header toggle is icon-only: the §10 sun and moon, the visible glyph naming the mode it switches to. Chrome reads the role layer (§ tokens, v4.2); exhibits that demonstrate light-mode rendering pin it (.lt) in both themes, so a specimen never contradicts its caption. |
| littleloopstechnologies.com | 301 → littleloops.tech | Keep registered for email + legal correspondence credibility. |
| little-loops.ai refresh | Per §03: dark default, §05 tokens, ll: wordmark, §10 diagram restyle, §10 icons for ↗/copy/close | Content and voice stay — palette/type alignment plus diagram framing, per the decided before/after table. |
| docs.little-loops.ai | Same tokens · follows system color-scheme preference | First surface where both token sets ship in full. |
| OG / social images | 1200×630 · Paper field, mark left, Newsreader headline right, fig-numbered when the image is an exhibit | Paper field family-wide — including for dark-default properties; the card renders in feeds, not in the reader's editor theme. Property eyebrows are accent-tinted; parent eyebrow is Ink Soft, and the parent eyebrow default text is the tagline (§08) — eyebrow or headline, never both. |
| GitHub | Org avatar = vertical lockup on Paper; repo card = ll: wordmark from the OG template | README badges use Blueprint. CI status badges use Pass / Signal. |
| Slides / docs | Paper background, Archivo headings, ink tables, mark on title + closing only | Reusable deck template is a fast follow. Figure system is mandatory for exhibits (§09). |
| voice-lint | Ship §08's rules file as a little-loops harness | Parses HTML; honors ignore_within. The brand's most on-brand artifact: a loop that grades copy against the voice spec — including this page. |
| token-lint v2 | Three checks on this file: literal px outside :root (exempt: the 720px breakpoint; clamp() bounds that appear verbatim in the §06 scale table); any calc() multiplying or dividing a type token; literal hex in inline SVG matching a token value | Runs on every version bump. The v3.0 lint passed a 9px calc and an unlisted clamp; v2 of the lint fails both by rule. |
A small founding team doesn't need a committee; it needs a changelog and a canonical URL, so that no person or agent ever builds from a stale copy. Three rules, guardrails, one open legal item, and a table.
make-mark.py re-run so figures and files never diverge. No surface may introduce a value or exception ad hoc — file an issue against the kit; the next version adopts it or names why not. Exemptions exist only where this document states them; an exemption known to a founder but absent from the kit is a defect, not a policy./brand/kit.html, beside /brand/assets and the generator — the repo copy is the source of truth; the URL serves it. Any other copy is a cache.The changelog records when a thing changed; this table records why it is what it is, so a direction already ruled out cannot return as a fresh idea. §08's rejected-candidates table did this first, for taglines; this generalizes it. Started v4.8, folded from the brand-kit-synth prototype's provenance section. Rows marked reconstructed state the reasoning as read back from the system, because it was never minuted at the time — an honest label beats a confident one, and a future version may replace a reconstruction with the real ruling.
| Decision | Ruling |
|---|---|
| Warm paper & ink, near-monochrome parent | Reconstructed. The instruction-manual register: the brand's conceptual language is the mechanical toy (§01), and a manual is printed ink on warm stock. Pure white and pure black are banned because no manual ever shipped on either; the warmth invariant (§05) is this decision restated for dark mode. |
Blueprint #2741D6 for the OSS accent | Reconstructed. The name is the reasoning: a blueprint is the drawing a machine is built from, which is what little-loops hands an agent. The specific hue is bound by the §04 matrix (AA on Paper and Paper Deep) and by --d-oss passing on Ink — a replacement must clear the same rows, which is the only test that matters now. |
| Verdigris held provisional | Decided, §04. Reserved for the product and revisited at naming — an accent chosen before its property has a name would be a decision made on no information. |
| Newsreader / Archivo / JetBrains Mono | Decided, v4.10. Three voices, one hand (§06): a text serif carries the poetic beat, Archivo's geometry matches the wordmark's weight, and the Mono is the machine speaking for itself. The serif seat was Fraunces from v3.0 (reconstructed reasoning, never minuted); v4.10 reseats it with Newsreader, taken from the synth prototype's pairing stage — Fraunces's italic at the scale's light weights turns calligraphic and fights the kit's engineered register, where Newsreader's italic stays calm and editorial. All three SIL OFL — the family's faces must be free to travel with an open-source flagship. |
| Hand-picked scales over ratio-derived | Decided, v4.8. The synth prototype derives its scales from generators (base × ratio); adopting that here was considered and rejected. Retrofitting derived values changes every spacing and type token — a breaking bump for surfaces already built — for zero visual gain, and the lints already govern additions more strictly than a formula would. The idea survives as the §06 rationale note; the values stand. |
| Dark islands by explicit tokens, not a role remap | Decided, v4.8. A .dk class remapping the role layer to dark values — the mirror of .lt — was considered for fig. 10 and refused: every existing dark exhibit already carries explicit --d-* values, and two mechanisms doing one job is how contradictions ship. The screen follows the pattern the darkpanel set. |
| Version | Date | Changes |
|---|---|---|
| v4.16 | 2026-08-31 | The lockup's internal gap becomes part of the artwork's geometry. v4.13 founded the gap on the ink and named it --s4; v4.15 carried that to the header — and carried it as an absolute 16px, which was the last dimension in the lockup still pinned while everything around it scaled. At fig. 01's native 58px render, 16px runs 43% of the mark's ink height; at the header's 34.4px it ran 72%, and the navbar gap read visibly wider than the artwork it abbreviates — the emitted lockup-ink.svg, which scales its internal 16px like any other dimension, already disagreed with the hand-built brandline at every size but native. The ruling: the internal gap is --s4 at the lockup's native scale, and it scales with the render like the mark, the type, and the clearspace table. The header's ink-to-text is now 16 × 34.4/58 = 9.5px, its flex gap calc(--s4 × .593 − 7px) = 2.5px after the frame's padding is given back — the token stays in the expression, so a change to --s4 still propagates. fig. 01 is untouched: at native scale the factor is 1. mark-data.json's gap_token note now states the scaling rule. The .593 factor is the header's render ratio (34.4/58) and its equality with the subline's .593em tracking (v4.12) is coincidence, recorded here so nobody unifies them into one constant. |
| v4.15 | 2026-08-31 | The header brandline joins the ink rules, closing the loose end v4.13 recorded. Both rulings now bind it: the gap is ink-to-text --s4 — the frame's 7px of transparent right padding at this render is given back with a solved calc(), so the visible mark-to-text distance drops from ~23px to 16px — and the mark's ink height equals the brandline's text ink height, computed from the same Archivo metrics as v4.14 but in CSS line-box terms, since the header is HTML, not emitted SVG: cap top of the 13px/1.25 line to the 10px line's baseline is 22.3px of ink, so the frame is 38.9×34.4, derived, never declared. The old 38×34 was close by luck and wrong twice over — its aspect (1.118) didn't even match the artwork's (1.131), a pre-v4.9 remnant sized against the retired stroke mark's frame. Closed with it: the header comment block still described that retired mark — "viewBox 70 units tall, loop 27.2px, clearspace 13.6px" — numbers no asset has carried since the mark was replaced; the comment now states the current geometry and derives it. The brandline's type treatment (label register, 800/.14em over the solved .086em subline) is untouched: the header is the lockup's small-size variant, not a scaled copy — a strict scale-down would put the subline below the --t-micro floor, which is why the variant exists. |
| v4.14 | 2026-08-31 | The mark's size in the lockup is re-founded on the ink, completing what v4.13 started for the gap. The mark's frame height had been a declared 48px, which put its ink at ~31px — between the wordmark's cap height (~17px) and the text block's ink height (~38px), aligned to neither datum: an optical size that was an accident of the artwork's frame padding, chosen by nobody. The ruling: the mark's ink height equals the text block's ink height — cap top of LITTLE LOOPS down to the subline's alphabetic baseline — and their ink tops align. Solved, not centered. make-mark.py derives everything from the webfont's own vertical metrics (Archivo cap height 0.686em, ascender 0.878em, hhea and typo agreeing) and the frozen curve's bbox: text ink 37.61px → frame height 58px, derived, never declared; the wordmark's hanging y is placed so both inks top-align, and both land at 10.98px with bottoms 0.02px apart. mark-data.json records the rule and the metrics under the lockup key; fig. 01's stand-in takes the artwork's own render size (65.6×58) and its gap solve is re-derived for the new scale (the frame's right padding grows to 11.8px with it). The curve, the clearspace table, and every non-lockup asset are untouched. The pattern this closes is now three-for-three: weight, gap, and size were each declared against a convenient box — the font's weight number, the frame's edge, the frame's height — and each is re-founded on what the eye actually measures. A lockup dimension that cannot name its datum is a defect. |
| v4.13 | 2026-08-31 | The lockup's internal gap is re-founded on the ink. The mark's frozen artwork carries transparent frame right of its ink — bbox x-max 241.02 in a 294-unit viewBox, 9.7px at fig. 01's render — and the gap had always been measured from the frame edge, so the declared --s5 read as ~34px of visible space: a token wearing a third more width than its name. The ruling: the lockup's internal gap is ink-to-text, and it is --s4 (16px). make-mark.py now places the wordmark from the ink edge (x1 of the path bbox, scaled to render height) plus the gap; fig. 01's schematic stand-in gives the frame's 9.7px back with a solved calc(), derivation in the comment beside it, the same shape as the subline solves. lockup-ink/paper.svg and mark-data.json re-emitted; canvas narrows with the move. The frame itself is untouched — it is part of the frozen artwork and the clearspace table still keys off it. One loose end recorded rather than hidden: the header brandline's gap, --s4 from the frame edge since v4.3, reads as ~23px ink-to-text at its 34px render under this ruling — it stands unchanged in this version, and this row is the record that it runs looser than the artwork it abbreviates, so any future tightening is a decision, not drift discovered late. |
| v4.12 | 2026-08-31 | The lockup's two lines are balanced: TECHNOLOGIES, LLC now runs exactly the width of LITTLE LOOPS, its tracking solved rather than picked — the treatment the header brandline has carried since v4.3, applied at last to the lockup itself. The solution: natural_s + 16·ls = natural_w + 11·(.10em), Archivo 600 at --t-micro against 700 at --t-h, computed from the webfont's own advance widths; ls = .593em, and both visible runs land on the same 198.74px. The old .3em was a picked value from the two-line era, sized against a LOOPS line that no longer exists — v4.11 moved the wordmark to one line and left the subline tracked for the layout it had left behind. .wordmark small and make-mark.py's SUB_LS both carry the solved value with the derivation in a comment beside each, the same shape as the header's; lockup-ink/paper.svg and mark-data.json are re-emitted. One rule implied since v4.3 is now stated: a lockup subline's tracking is always solved against the line above it — when either line's text, face, weight, or tracking changes, the subline's spacing is re-derived, never inherited. |
| v4.11 | 2026-08-31 | The wordmark is retuned and reset on one line. Two changes, one ruling. First, weight: Archivo 900 drops to 700, and the tracking widens from +0.06em to +0.10em to hold the engineered register at the lower volume — beside the v4.10 Newsreader display, the Black cut read as blocky shout rather than machined plate, and the §06 heading range (Archivo 600–900) already contains the new weight, so no scale row moves. Second, layout: LITTLE LOOPS stands on a single line with TECHNOLOGIES, LLC beneath it — the stacked two-line wordmark survives nowhere; the header brandline had been single-line since v4.3, so fig. 01 and the emitted artwork now agree with the header instead of contradicting it. make-mark.py carries both: L_WEIGHT 900→700, L_LS .06em→.10em, the two-line layout math collapses to one line with the text block vertically centered against the mark, and the canvas allowance widens for the longer run. lockup-ink/paper.svg and mark-data.json's lockup key are re-emitted; the mark's geometry, the clearspace table, and every other asset are untouched — the curve is frozen and this change never touches it. Heavier faces were considered for the seat itself — Barlow 800, Hanken Grotesk 900, JetBrains Mono 700, the prototype's Space Grotesk — and set aside: the defect was the 900 cut's volume, not Archivo, and one sans doing every structural job is worth more than a second family's flavor. |
| v4.10 | 2026-08-31 | The display serif is reseated: Fraunces gives its chair to Newsreader, taken from the synth prototype's font-pairing stage (output/brand-kit-synth-20260831T051836Z.html) — the first of its values to cross, where v4.8 ruled that only its structural ideas do; a ruling scoped to scales and tokens does not bind a typeface judgment. The reason is the italic: the §06 emphasis weights sit at 330–340, and Fraunces's italic at light weights and display optical sizes turns calligraphic — swashy terminals, an unstable rhythm — which fights the engineered register the grid, the mono, and the wordmark all speak. Newsreader's italic at the same weights stays calm and editorial; its roman takes the same seat without ceremony. Nothing else moves: Archivo and JetBrains Mono keep their voices, every size, weight, and tracking value in the §06 scale stands, and the 28px boundary, the italic budget, and the U+2113 glyph rule carry over with only their face renamed. The webfont request swaps families and stays lean — Newsreader 300–450 with italics over its 6–72 optical range, against Fraunces's 9–144. The §13 provenance row for the type trio is rewritten from reconstructed to decided: the trio's reasoning was read back from the system in v4.8; the serif's seat now has a minuted ruling, which is the upgrade that table was built to receive. Space Grotesk was considered alongside — the prototype pairs it with Newsreader — and refused: the italics were the defect, not the sans, and Archivo carries the wordmark's 900-weight geometry, which Space Grotesk does not offer. |
| v4.9 | 2026-08-31 | The mark is replaced. The double-loop mark was a single unbroken cursive stroke — an open, stroked polyline, stroke-width and stroke-linecap carrying the letterform. It is superseded by a new mark: two interlocking loops rendered as one solid filled shape (fill-rule nonzero, no dot, no stroke). The old mark's canonical data and every asset it emitted are archived under archive/ll-mark/, unchanged, rather than deleted — its own history (the v3.0 bias parameter, the v4.0 cycloid regression, the v4.1 fix) documents that mark, not this one. canon/mark-path.txt now holds the new curve; make-mark.py is reworked from a stroke-model to a fill-model generator — bbox math now scans path coordinates by regex instead of parsing a straight-line-only polyline, so it tolerates the new curve's Béziers and closed subpaths, and every emitted asset (mark-*.svg, favicon-*.svg, lockup-*.svg) swaps stroke for fill. The favicon showcase (fig. 03) now reuses the one mark path scaled down rather than a separate favicon-only glyph, which retires along with the stroke it existed to thicken. The mark's aspect ratio changes with it — roughly 1.94:1 (wide) to roughly 1.13:1 (near-square) — so every instance's width/height and the §02 clearspace table (loop height, now the curve's bbox height rather than its stroke-excluded polyline height) are recomputed from the new geometry. The cover's hero animation changes with the render model: a stroke path can be "drawn on" with stroke-dashoffset and pathLength; a filled shape cannot, so the reveal is now a fade + scale-in, and the cover's literal duplicate path (kept only for pathLength on a light-DOM copy) is gone — every instance, cover included, is now a plain <use> of the one #llt-mark def. |
| v4.8 | 2026-08-31 | Four additions folded from the brand-kit-synth prototype (output/brand-kit-synth-20260831T051836Z.html) — a specimen generator, not a family surface: its structural ideas cross; its values do not. First, the composed screen: §12 opens with fig. 10, a docs settings panel built end to end from §04–§10 tokens, dark because the flagship is, followed by a table naming where each subsystem appears — the integration test every prior exhibit only unit-tested. Second, form controls: §07 gains the input, label, switch, and primary-button spec. The §04 matrix had carried filled-button rows (Paper on Blueprint; Ink on --d-oss) for a component that did not exist, and the universal focus ruling named inputs no spec had drawn — both dangling ends now terminate in components. The switch stands its hit box on the --touch floor with the track and knob drawn inside from spacing tokens, and one motion ruling ships with it: knob travel is an activation transition, not a hover one — §09 binds hover, and hover still moves nothing. Third, scale rationale: the §06 note records that both scales are hand-picked and that a generator ratio was considered and rejected (retrofitting derived values is a breaking bump for zero visual gain); rem equivalents join the type table as builder conveniences, px canonical. Fourth, the §13 decision record generalizes §08's rejected-candidates pattern to design decisions, with reconstructed reasoning labeled as such. Hygiene closed with it: the hover ease was the one literal in every transition — --hover-ease tokenizes the keyword, rendering unchanged; and the cover's supersedes line had read v4.4 since that version shipped, three versions stale against the footer, which was correct throughout — the cover now reads v4.7, and both lines move together or the mismatch is a defect. |
| v4.7 | 2026-08-24 | Two issues closed. First: §02 banned setting the wordmark in anything but "the production lockup artwork," and §12 required the tagline sit under the lockup, but no lockup artwork shipped in assets/ — only the mark — leaving a downstream parent nothing to copy without composing a wordmark the kit itself forbade. make-mark.py now emits lockup-ink.svg / lockup-paper.svg: the mark plus wordmark, set from the §06/§08 type tokens (font, weight, tracking, line-height, the --s5 internal gap), with the layout computed from those tokens rather than eyeballed and recorded in mark-data.json's new lockup key, the way the clearspace table already documents the mark. The wordmark is <text>, not outlined — consistent with how this kit's own header and fig. 01 already render it — so a surface with the shared type system loaded (§01) renders it correctly without a font negotiation. Second: the parent-we voice-lint rule banned every "we" on the parent with no allowance, while the §08 prose only asked marketing copy to avoid a first-person plural that implies a floor of employees that doesn't exist — different readings for copy that names the founders. Little Loops Technologies has multiple founders, so a founder-scoped "we" is honest, but a regex can't distinguish it from a headcount-inflating one; parent-we is now severity: warn, the same treatment unlock-metaphor and handoff-spaced already get, so a person rules on scope instead of the lint ruling for them. Closed with it: §13 wrongly called this "a one-founder company" — corrected to name the founders, plural, throughout. And a dangling cross-reference from v4.6 is fixed — the parent-row citation named a §14 that has never existed in this document; it meant §12. |
| v4.6 | 2026-08-24 | The parent flips to dark default. The registry ran light-only from the kit's first publication — one page doesn't need a toggle, the ruling said — and the reasoning was right about the page and wrong about the visitor: the parent is the front door of the family, and the audience §05 was built for arrives from dark editors and dark terminals, so a registry that meets them in light meets them in the wrong register. Every parent surface now ships both modes — the registry and the legal sub-paths alike — dark as the default, light as the persisted alternative, with a reader-run toggle in the footer beside the legal nav: icon-only, the §10 sun and moon, the visible glyph naming the mode it switches to, choice applied before first paint, never system-followed, print returns to light — the v4.2 mechanism, carried over unchanged. One new ruling ships with the flip, because the parent's tags could not cross as-is: the Blueprint and Verdigris chip fills sit at 2.5:1 and 2.9:1 against Ink, under the 3:1 non-text minimum, and Paper text on the dark counterparts computes to 2.4:1 and 2.0:1, worse — so the chip inverts, --d-oss and --d-product fills with Ink text at 6.7:1 and 8.0:1, the same inversion §05 already mandates for text and focus. Two §04 rows record the pairs. The color-scheme meta turns dark light and theme-color carries both values. The §05 mode card and the §12 parent row state the ruling; /brand's row no longer claims the only dark surface on the parent domain. |
| v4.5 | 2026-08-23 | The favicon becomes the name. v4.1 restored the single-loop glyph as its own frozen artwork, but a lone l was never the name: the brand is the double loop, and a tab that reads as a solitary l reads as a letter the company does not have. The favicon glyph is now the frozen mark itself, uniformly scaled into the 32×32 box — scale is one of the parameters the design-freeze already allows — and canon/favicon-path.txt retires with the separate artwork it froze: one canonical curve, and the custodian derives the favicon from it at emit time, so §02's claim that every asset comes from the frozen curve is now literally true. The stroke drops from 4.5 to 3.0 to hold the quarter-of-loop-height ratio the approved single-loop favicon carried (4.5 of 18 then, 3.0 of 12 now): at the smaller loop, 4.5 closed both counters at 16px, caught by §02's rendered-raster check. mark-ink and mark-paper are byte-identical; the clearspace table is unchanged; §02 is retitled — the glyph is the mark wound small, not a sibling — and its don'ts and fig. 03's caption are reworded to match. Colorways, the 24px floor, and the §12 delivery rule stand. |
| v4.4 | 2026-08-23 | The tagline is decided and specified. Parent: “Order from the machine.” Flagship: “Noise in. Order out.” The product line is deferred to its naming (§13), because its register leads with an outcome and there is no buyer yet to name one for. §08 gains the whole ruling — three specimens, the placement table, and the rejected candidates with their reasons, so a line already ruled out cannot return as a fresh idea. Three rules ship with it. The tagline at lockup size is Archivo at --t-caption, never Fraunces, which the §06 28px floor already forbids. The line is banned as the sole text of a link or button, where “Order from…” reads as a purchase instruction — adjacent to the name that reading collapses, alone in a button it does not. And the poetic budget is one line per view: the hero display line and the tagline never share a viewport, so this document carries the hero on the cover and the tagline in the footer, which is now its single use position on the page — every other appearance in §08 is a specimen, a mention under the §04 exemption. The rejected front-runner is recorded rather than erased: “Determinism in a non-deterministic world” is kept as an about-page thesis and refused as a tagline, because the same prompt does not return the same output and §13 binds this kit to claims it can keep. Its spelling is ruled now rather than later — nondeterministic, closed, no hyphen, the same class of call as handoff. §01 records that taglines are assigned, not improvised; §12 puts the line under the parent lockup and in the parent OG eyebrow. The footer sign-off changes from “Small machines, carefully made.” to the tagline; the hero line is unchanged and keeps the cover. Closed with it, by markup rather than by exemption: the §06 and §08 code exhibits were mono-styled divs, which is none of the three containers their own ignore_within names, so every pattern the voice-lint spec quotes — its we, its unlock, its hyphenated handoff — sat in plain prose as far as the linter could see, and the page failed the lint it defines while claiming to pass it. Both listings now carry a code element and are covered by the rules file untouched: no fourth container, no style class exempted, no rule relaxed. Exempting .s-mono was considered and refused — it would exempt a presentation class, making any prose set in mono invisible to the lint, which is the page-level exemption §08 bans wearing a different hat. One CSS line resets the browser default inside the exhibits; they render unchanged. The §08 rule-note now states the markup obligation the mechanism always implied: a listing styled as code but not marked as code is a defect in the page, not a gap in the lint. |
| v4.3 | 2026-08-23 | Dark becomes this page's default mode, and the header toggle goes icon-only. The kit's readers live in dark editors — §05's own premise — so the working document now opens as its audience does: the first-paint script applies dark when no stored choice exists, while stored choice still wins, still persists, and is still never system-followed; print still returns to light, because ink prints on stock (§11). The toggle's semantics stand; its text label becomes two icons — sun and moon — and the visible glyph still names the mode the reader switches to. Sun and moon join the §10 set under its growth rule, drawn on the same grid. The §05 mode card and the §12 surfaces row restate the ruling; light stays fully specified and renders unchanged, and the .lt exhibits still pin it in both themes. The header brandline's subline is tracked to fit: letter-spacing .086em, solved — not picked — so TECHNOLOGIES, LLC runs exactly the width of the LITTLE LOOPS line above it; the lockup's two lines now start and end together, and the mark-to-text gap in the same lockup tightens from --s5 to --s4. And the header itself is rebuilt in two rows. v3.0's single row held all thirteen nav links between the lockup and the toggle and wrapped them midair at every desktop width — a sticky bar two links tall over an offset sized for one, so the page buried its own targets again: the very defect the offset was written to close. Row one is now the lockup and the toggle; row two is the nav, the column's full width, its links standing on the --touch floor by min-height rather than padding, so every target is exactly 44px. The offset is the header's token sum at its tallest state plus --s5 (§11), and widths where the nav fits one row take the difference as clearance. Under the breakpoint the wrapped nav would stand several rows tall, so the header leaves sticky position and joins the document flow: the offset rule binds sticky headers, and an anchor cannot bury under a bar that scrolls away. And the cover's background loop is anchored vertically as it already was horizontally. Its box top stood at --s6 — inside the cover's own top padding, sixty-four pixels above the content line — so the loop's tops floated over the kicker while only its bottom stroke reached the heading; the box top now stands on the same --s9 the cover pads by, and the loop, taller than the display lines, brackets them and centers on the heading. |
| v4.2 | 2026-08-23 | Reader-run dark theme for this page, behind a header toggle. Light stays the default and renders byte-identical: page chrome now reads a role layer (--surface, --surface-deep, --surface-lift, --text, --text-soft, --border, --rule, --state-ok, --state-err) that maps to the §05 dark tokens under [data-theme=dark]; every pair the dark theme produces already has a §04/§05 matrix row. The remap assigns custom properties only — never background: or color: — so the surface rule's grep (background:var(--ink) ⇒ .dark) holds in both themes; ink islands gain a --d-line hairline under the dark theme to keep reading as exhibits. Exhibits that demonstrate light-mode rendering (figs. 01, 03, 04, 05, 07 and the §07 language cards) pin it via .lt, which resets the role layer to light values inside the pinned surface. Kit's own mode ruling added to the §05 mode card; littleloops.tech/brand added to the §12 surfaces table. The page's first script: choice applied before first paint, persisted, never system-followed; print returns to light. |
| v4.1 | 2026-08-21 | Mark geometry regression fixed. v4.0’s generator re-derived the curve from a prolate cycloid and shipped wide, round, symmetric loops in place of the approved tall, right-leaning cursive ll — mathematically tidier, no longer a letterform. The approved curve is restored as canonical and declared design-frozen, stored as sampled path data in /brand/canon; make-mark.py is rewritten as custodian (emits sizes, strokes, colorways, favicon, clearspace table from the frozen curve; never re-derives geometry) and a usage rule now bans regeneration outright. Favicon glyph likewise restored to the approved single-loop cursive ℓ. All assets re-emitted; clearspace table unchanged (identical 56⁄70 viewBox ratio); 16px favicon legibility re-verified against a rendered raster. Bias remains deleted per the v4.0 ruling. Same-day revision: the symbol/use definition clipped every mark instance slightly at the bottom-right (a symbol instantiates a nested viewport at 0,0 in user space, offsetting the −7 −7 viewBox by +7,+7); replaced with bare path defs, which draw in the host svg’s own coordinates. Every other v4.0 change stands. |
| v4.0 | 2026-08-21 | Assets shipped: make-mark.py written and run; mark, favicon, and data files emitted to /brand/assets; every figure in this document renders from the generator's path via a single symbol definition. Bias parameter reverted — four parameters, symmetric mark, favicon and motif consistent with it. Clearspace datum defined (path y-extent, stroke excluded); per-size table generator-emitted; v3.0's three conflicting worked examples retired. Surface rule for dark: Ink background ⇒ .dark, closing invisible selection in code blocks; focus made universal via :focus-visible. Token-lint v2: clamp bounds must appear in the §06 scale (the unlisted h2 clamp is now listed), calc() on type tokens banned (the 9px sublines are now --t-micro), hex banned from inline SVG (everything is currentColor). Voice-lint v4: ignore_within puts use/mention in the machine; hand-off split into an auto-fixable hyphen rule and an honest warn; this page passes its own lint by mechanism. ll: face contradiction resolved (JetBrains Mono 700, §03 rules; §06 lends size only). Icons currentColor, set grown to eight for the live site's real needs, dark specimen shown. Favicon colorways made deliverable (adaptive SVG + static fallback) and property-differentiated. Sticky-header anchor offset implemented and specified. Fonts subset to used axes; preconnect corrected. Kit page carries its own meta/OG. Specimen exemption rewritten to cover what the kit actually exhibits. Clearance guardrail extended to all marks; littleloops.org collision logged as a blocking legal item. Live-site quotes verbatim with excerpts labeled; the clipped §08 sentence restored in full. U+2113 confined to verified faces. Full ISO dates from this version on. Superseded — the generator’s cycloid re-derivation replaced the approved mark geometry; fixed in v4.1. |
| v3.0 | 2026-08 | Every CSS value tokenized (two loopholes found in review); §03 flagship identity and ll: wordmark; dark focus/selection partially implemented; clearspace amended to ½ loop height (datum left undefined); voice-lint with scope/severity/allow (failed on its own page); exhibit exemption named (scoped too narrowly); mark/motif boundary; bias parameter added (reverted in v4.0); iconography drawn (light-mode hex); governance and naming guardrails. Superseded — ten defects found in review. |
| v2.0 | 2026-08 | Computed contrast matrices; semantic Pass/Signal layer; dark token set; interaction-state spec; layout/motion tokens; favicon glyph; pull-quote rule; voice lint list; OSS-led hierarchy declared. Superseded — ten defects found in review. |
| v1.0 | 2026 | Initial system. Superseded — failed its own rules three ways. |