RELEASE
planning/2.8.5/designs/FO3/FO3_PIPBOY_DESIGN.md
sha256 33ec1d2614aed87c · 20304 bytes ·
original held in the private archive
# FO3 PIP-BOY 3000 — The True Recreation (Design of Record)
> **Design document only — nothing here ships.** Fifth pass, and the real one: the owner
> ruled *"why not make it actually laid out like the pipboy? I want a genuine recreation.
> down to the panels fitting into the UI as well."* This document is rewritten around that
> brief. Canonical mockup: `planning/mockups/fo3/fo3-pipboy-true.html`. All prior passes
> (`fo3-pipboy-mockup.html`, `fo3-body-{a,b,c}-*.html`, `fo3-body-final*.html`) are history —
> useful only as the record of what didn't work and why. Grounded in `dev` @ `709a96c`.
> Next: Opus audits this against the real code; Sonnet builds it. NV IS UNTOUCHED THROUGHOUT.
---
## 1. The two ideas everything hangs on
**The orientation split.** The Pip-Boy screen is landscape (~4:3); a phone is portrait.
Every compromise in passes 1–4 came from fighting that. So we stop:
- **LANDSCAPE = the true Pip-Boy** — the primary FO3 view, a genuine recreation. Deciding
viewport: **780×360** (the owner's phone rotated).
- **PORTRAIT = the LEGACY VIEW** — the existing flat/plain layout, unchanged. No squashed
compromise object exists anymore.
- Rotating is diegetic — you turn your wrist to look at a Pip-Boy. Pure
`@media (orientation: …)` CSS; no JS branching.
**The thesis: NV puts the machinery ON THE GLASS; FO3 puts it IN THE CASING.** NV's screen
is full of instruments (faders, weigh bridge, dials). The real Pip-Boy screen is **austere**
— flat phosphor text, boxed outlines, dash-connected rails, `▲▼` arrows, plain-text actions
with symbols. All the tactility lives in the body: the lamps, the knob, the gauge, the
toggle. FO3's on-screen controls get **simpler**, not more elaborate — faster to build AND
more faithful, and it is what makes the two skins different *machines* rather than different
paint.
---
## 2. The two measurements (taken first, headless, 780×360)
| Question | Measured answer | What it forced |
|---|---|---|
| Is 360px of height enough for hood + bar + glass with a top strip, a sub-tab rail, AND a list-and-detail split? | **Yes.** Glass inner: **634×262px**. Top strip 23 + sub-rail 32 → **content region 207px tall** — comfortably 6 list rows + a full detail pane with actions. | Hood thinned to **24px**; lamp bar to **52px** (domes 24px, stamped labels above); fins to 9px; the gauge and knob moved **off the bottom entirely onto the left rail** (86px wide — width is the cheap axis in landscape). The `▲▼` steppers had to go **horizontal** (`[▼][val][▲]`, each arrow 28×28) — the classic stacked-arrow stepper is untappable in a 28px row and failed the probe at 14px. |
| Does the Vault Boy figure fit with six labelled limb boxes? | **Yes — 193px tall rendered, no compact pose needed.** Landscape is the gift: the limb boxes flank the figure (3 left, 3 right) with leader lines, and the vitals column sits beside them. | Nothing cut. The figure + 6 boxes + HP/RAD/effects all fit the 207px content region with room. |
Verified at capture: zero overflow, zero page scroll, every tap ≥28px, 16px inputs, portrait
correctly hands off to the legacy card.
---
## 3. The structure — 11/6/6 boards onto 5/5/5 rails
Three domed lamps (the only nav on the bar), each opening a 5-sub-tab rail **inside the
glass**. The groupings are more authentic than the current stack, not a compromise of it:
**STATS lamp (OPERATOR, hotkey 1) — 11 boards → 5 sub-tabs**
| Sub-tab | Absorbs | Layout | Note |
|---|---|---|---|
| STATUS | Vital Telemetry + Skeletal Harness + Status Effects | **full-bleed: the Vault Boy figure** | three boards BECOME the thing they were imitating — HP, rads, limb boxes, effect chips arranged around the figure |
| SPECIAL | S.P.E.C.I.A.L. Tuning | full-bleed 7-row list | inline meters + boxed value + `▲▼` per row (tap parity) |
| SKILLS | Skill VU Array + Skill Books + Magazines | list + detail | inline `▲▼` per skill row; books/mags = the read-tracker sub-list with `[X]` toggles |
| PERKS | Perk Loadout + Traits | list + detail | `[+ ADD PERK]` boxed autocomplete (16px), `REMOVE ▢` per row |
| GENERAL | Position & Clock + Faction Standing + Karma | list + detail | FO3's real General tab IS the karma/rep screen; faction rows carry inline fame/infamy `▲▼` |
**ITEMS lamp (OPERATIONS, hotkey 2) — 6 boards → 5 sub-tabs**
| Sub-tab | Absorbs | Layout |
|---|---|---|
| MANIFEST | Cargo Manifest (+ ammo) — the Weapons/Apparel/Aid/Misc/Ammo drawers become the **filter row inside the tab** (they already are the drawers) | list + detail + actions |
| CRAFT | Field Fabrication | list + detail, `CRAFT △` |
| BARTER | Barter Uplink | full-bleed form (a calculator isn't a list) |
| SQUAD | Squad Roster | list + detail |
| CURIO | Bobblehead Archive | tracker rows, inline `[X]` |
**The Weigh Bridge needs no tab** — carry weight rides the top strip (`Wg 142/230`), exactly
where the real device puts it.
**DATA lamp (DATABANK, hotkey 3) — 6 boards → 5 sub-tabs**
MAP (cartography, **full-bleed** — a map is not a list) · QUESTS (Directive Registry) ·
NOTES (Field Notes) · LOG (Campaign Chronicle + Service Tally, read-only as today) ·
QUERY (Catalog Query).
**The other subsystems are CONTROLS ON THE BODY** (kept from pass 4 — the breakthrough):
| Subsystem | Control | Placement | Active state |
|---|---|---|---|
| UPLINK [4] | the big black ribbed **KNOB** | left rail, bottom | needle swings to the amber 98.3 detent, ring glows, **all three lamps go dark** — you're on the radio |
| CHASSIS [5] | the white-face red-needle **GAUGE** | left rail, top | phosphor ring around the dial |
| SETTINGS [6] | the **flick TOGGLE** | right column, under the arc rail | lever drops to the amber position |
**DIR IS CUT FROM FO3** — a directory listing is a desk-terminal concept; a Pip-Boy has
none. The a11y promise holds without it: three lamps → sub-tab is never more than 2 taps
deep, and **hotkey [0] still opens the flat directory** (behavior preserved, invisible
affordance; the modal itself is shared app furniture).
Every control carries its real name stamped beside it (clarity outranks flavor); every
subsystem is one tap; hotkeys [1]–[6]/[0] route through `selectSubsystem()` unchanged; the
scattered controls remain one `role=tablist` (display:contents — a knob that selects a view
is still a tab); the 5-sub-tab rails are a second-level `role=tablist` inside the glass.
---
## 4. The screen anatomy & the FO3 control language
Every tab, same skeleton: **top strip** (boxed phosphor segments — view name + LVL, HP, Wg,
RAD, Caps) · **content** · **bottom sub-tab rail** (dash-connected, active boxed).
**List-and-detail where the content IS a list; full-bleed where it isn't** (STATUS figure,
MAP, BARTER form, SPECIAL's 7 rows).
The control language is deliberately SMALL — four primitives, reused everywhere, never
bespoke per board:
1. **The stepper** — `[▼] [boxed value] [▲]`, horizontal, every arrow 28×28 (measured
constraint, §2).
2. **The boxed field** — 16px text input in a phosphor box (search, add-perk, notes,
location).
3. **The toggle** — `[X] / [ ]` inline on tracker rows (books read, curios acquired,
tactics).
4. **The action** — plain text + symbol in the detail pane: `EQUIP ✕ · USE ◯ · CRAFT △ ·
DROP ▢` (straight off the reference's own `Equip ✕ / Drop ◯ / Repair △`).
Selected list rows get the thin boxed outline + `►` cursor. That is the entire vocabulary.
---
## 5. The Vault Boy figure
Original inline SVG (zero assets, ~20 shapes): standing figure in the FO3 outline style —
phosphor stroke, round head, hair swoosh, dot eyes, the grin, tapered torso with belt, one
arm down, **one arm up in a thumbs-up** — plus five limb condition boxes (HEAD, L.ARM,
R.ARM, L.LEG, R.LEG) with leader lines, `[████] OK / [▒░░░] CRPL` bars, crippled state red
and blinking (plain `@keyframes`, reduced-motion-neutralized). **The boxes are buttons** —
tap toggles the limb state, same one tap as today's harness. The SVG strokes use the
phosphor token, so it re-tints with the optic and is themeable per game off the same shapes.
---
## 6. ⚠ THE EDIT AUDIT — how editing survives on all 15 sub-tabs
The invariant: *if you can change it today, you can change it in the Pip-Boy — in no more
taps than now.* Verdict per sub-tab (**✓ parity** = equal taps; **⚑ FLAG** = honestly
flagged, never silently downgraded):
| # | Sub-tab | Edits | Verdict |
|---|---|---|---|
| 1 | STATUS | HP/RAD steppers inline; limb boxes ARE toggle buttons; effects chips + `+ ADD` | **✓ parity** |
| 2 | SPECIAL | inline `▲▼` on each of the 7 rows (no select step) | **✓ parity** |
| 3 | SKILLS | inline `▲▼` per skill row; read-tracker `[X]` inline | **✓ parity** |
| 4 | PERKS | add via boxed autocomplete; `REMOVE ▢` per row | **✓ parity** |
| 5 | GENERAL | location/clock boxed fields; faction rows inline fame/infamy `▲▼`; karma stepper | **✓ parity** (11 faction rows carry the steppers inline, not in detail) |
| 6 | MANIFEST | actions in detail: `EQUIP ✕ / USE ◯ / qty ▲▼ / DROP ▢` | **⚑ FLAG: +1 tap on the FIRST action per item** (today's inline row buttons act without selecting; here you select the row, then act — equal from the second action on, and rapid qty runs are equal). Mitigation if the owner wants strict parity: a compact inline EQP glyph on each row alongside the detail actions. Owner call. |
| 7 | CRAFT | select recipe → `CRAFT △` | **✓ parity** (today is select-then-craft too) |
| 8 | BARTER | boxed value fields, full-bleed form | **✓ parity** |
| 9 | SQUAD | affinity `▲▼`, tactics `[X]`, `DISMISS ▢` in detail | **⚑ FLAG: same +1-first-action class as MANIFEST** |
| 10 | CURIO | `[X]` acquired toggles inline on tracker rows | **✓ parity** |
| 11 | MAP | full-bleed; tap nodes = TRAVEL / MARK SURVEYED as today | **✓ parity** |
| 12 | QUESTS | `+ ADD` boxed input; status CYCLE in detail | **⚑ FLAG: cycle is +1 on first action per quest** (same class) |
| 13 | NOTES | list + detail edit box | **✓ parity** |
| 14 | LOG | read-only | **✓ (read-only today too — the one legitimately read-only tab)** |
| 15 | QUERY | boxed search input + results | **✓ parity** |
**One flag class, three tabs** (MANIFEST/SQUAD/QUESTS): the master-detail select step costs
one extra tap on the *first* action per item, equal thereafter. The reference itself blesses
detail-pane actions, and the common rapid case (qty runs) is at parity — but it is a real
deviation from the letter of the rule, so it is flagged, with the inline-primary-glyph
mitigation priced in. **No tab goes read-only.**
---
## 7. ⭐ THE TEMPTATION LIST — every place this design wanted to fork per-game
*(Discipline 4 — this list IS the spec for the future N-game abstraction, written from
evidence. Each entry: what tempted a fork, and how FO3 resolves it without one.)*
1. **The nav skeleton itself** (NV: 7-keycap row · FO3: 3 lamps + knob + gauge + toggle) →
both markup blocks exist in the shared DOM, display-gated by `[data-game]`. *Future
abstraction: a per-game "nav layout" partial selected by `identity.machine`.*
2. **Where the live status text lives** (NV: bezel telemetry LCD · FO3: in-glass top strip)
→ ONE writer (`_bezelTelemetryText()`), two projection targets, CSS decides which shows.
3. **Which stats ride the strip** (FO3 adds `Wg` because the Weigh Bridge folded into it) →
an `identity.statusStrip` field list, not code.
4. **Sub-tab rails / board grouping** (FO3 groups 23 boards onto 15 rails; NV shows boards
as a stacked page) → a per-game *board-grouping map* in identity (`rails: {stats:[…]}`);
the boards themselves are shared.
5. **Orientation behavior** (FO3: landscape-true/portrait-legacy · NV: agnostic) → an
`identity.orientation` flag driving which layout the orientation query activates.
6. **The control skin** (NV faders/steppers · FO3 `▲▼` boxes) → one shared handler set;
per-game **control-kit CSS classes**; the temptation is per-board bespoke controls — the
4-primitive language (§4) is the fence.
7. **The figure** (FO3 Vault Boy · NV zone plate · a future game's silhouette) → per-game
inline SVG blocks in shared markup, display-gated; strokes on the phosphor token.
8. **List-row treatment** (selected-row box + `►` vs NV's tracker rows) → control-kit CSS.
9. **The DIR affordance existing at all** (cut on FO3) → `identity.nav.hasDirectoryKey`;
hotkey [0] stays wired app-wide regardless.
10. **Sounds** (rotary detent, dead-lamp thunk vs key click) → `identity.audio` facets
(already data).
11. **Motion textures** (DETENT-ROLL vs phosphor smear on the same SWEEP verb) → already
`identity.motionTexture` + `[data-game]` keyframes.
12. **Boot flavor, empty-state voice, Overseer framing, cursor** → already identity data
(shipped stubs; refresh per this design).
13. **Hotkey glyph presentation** (NV etched `[1]` on keycaps · FO3 stamped labels, no
visible numbers on lamps) → aria-labels keep the hotkey text; visual glyphs are CSS
content from identity strings, never literals.
14. **The wire/accent color and its *usage sparsity*** (NV amber = ambient wire · FO3 amber
= exactly one lit lamp + tuned knob) → the token is per-game (`--bezel-wire`); the
discipline of WHERE it's spent is per-skin CSS.
15. **Per-game panel *presence*** (FO3 hides nothing NV shows — but a future game might) →
already `GAME_DEFS` capability flags (hasTraits, hasMagazines…); the rails map (item 4)
must consume them so a rail drops absent boards.
**Owner real-device landscape feedback pass, round 2 (room reclaim + Vault Boy) adds these:**
16. **The Vault Boy figure as a duplicate limb-toggle control.** Tempted to give the figure
its own tappable limb hit-areas (closer to the reference's own clickable body). Resolved:
the figure is purely decorative (`aria-hidden`); the REAL toggle stays the five existing
`toggleLimb()` buttons, CSS-flowed to flank the figure via `order` + `display:contents`
on their wrapper divs — one control, one id, one state path (Protocol 22), never a second
interactive surface duplicating the same five booleans.
17. **The sub-tab-rail-overlays-content fix as a JS scroll-position hack.** Tempted to patch
the bug by having JS clamp scroll position or toggle a class when content got "too close"
to the rail. Resolved: the real defect was structural (`position:sticky` sharing a scroll
container with taller board content) — the fix is a plain-layout wrapper div
(`#fo3BoardScroll`) plus three flex `order` values, zero JS, zero new state to keep in
sync with scroll events.
18. **The KARMA CENTER real-label leak** (literal `(FO3 — karmaCenterDisplay)` text on
screen — the internal element id AND a hardcoded game literal, both violations at once)
→ the plainest possible fix: swap it for a real-world term like every other real-label on
the page. *Future abstraction: a lint-style guard that a `.real-label` span's text can
never match an `id="..."` pattern would catch this class of leak before it ships.*
19. **Reclaiming header/strip space as a JS visibility toggle keyed on subsystem.** Tempted
to write a small `_fo3HideChromeFor(subsystem)` helper that JS-toggles the casing-top /
carrier-strip / Director-Uplink-header visibility per subsystem. Resolved: all three are
unconditional `display:none` under `[data-game='FO3']` landscape — no subsystem-keyed
branching needed, because none of the three ever belonged on the Pip-Boy screen in ANY
subsystem, so a static rule is both simpler and strictly correct.
**We deliberately do NOT build this framework now** — FO3 is built concretely; the
abstraction is extracted later from two real machines. The four disciplines hold meanwhile:
never hardcode `FO3` anywhere; **the FO3 skin gets its own numbered CSS file** (slot ~12,
e.g. `12-fo3-pipboy.css` — a third game ADDS a file, never edits one); every flavor string
in `identity`; this list stays maintained.
---
## 8. Identity data (refresh of the shipped FO3 stubs)
Palette: phosphor `green3 #4fb05a` (shipped default; figure/strips/rails all ride the optic
token) · **the amber `#ffa030`** — spent ONLY on the lit lamp, the tuned knob, the toggle's
active lever, the UPLINK lamp dot, and warm accents (`UNEQUIP`, RAD readouts). Shell:
weathered olive castings (`#4a5132 → #22270f`). Motion: SWEEP texture = `detent-roll`
(vertical hold slip, overlay-only). Audio: `humFreq 55`, `navClick: rotary-detent`, a dull
*dead-lamp thunk* for lamp presses. Boot: the Vault-Tec POST (authored in pass 3, unchanged
— it ends `CARRIER ACQUIRED — GALAXY NEWS RADIO 98.3 MHz` / `WELCOME BACK, VAULT DWELLER`).
Serial flavor: `UNIT PB3K-101-77 · VAULT-TEC ISSUE — VAULT 101` + the scratch *"happy 10th
birthday, kiddo — love, dad"* (desktop casing has room; landscape phone may carry it on the
left rail as a micro-etch — owner call). Cursor: `pipboy-reticle`. Overseer: `OVERSEER
UPLINK / GNR RELAY · 98.3 MHz` (shipped stub, kept).
---
## 9. Risks & worries (the Opus audit's first stops)
- **The rails are new INFORMATION ARCHITECTURE, not just chrome.** Grouping 23 boards onto
15 sub-tabs means per-game *composition* of shared boards. The clean mechanism: boards
keep their one shared markup; FO3's rails show/hide them per sub-tab via the grouping map
(temptation item 4). This is the unit's real engineering risk — panel visibility today is
tab-based (`data-tab`); the sub-tab layer adds a second axis. Must not fork the render
pipeline (Protocol 22/23).
- **Landscape-primary is new for the app.** The shipped app has no orientation-specific
layout; the desktop-gate media queries key on width+hover. The FO3 landscape layout must
gate on `(orientation: landscape)` scoped under `[data-game='FO3']` without disturbing
NV or the desktop shell. Real-device verify (Protocol 10/29-class caution): browser chrome
in landscape (URL bars, notches/safe-areas) eats height — test with `100dvh` on the real
phone, and PWA standalone mode is the true target (no browser chrome).
- **The edit-flag class (§6)** needs the owner's explicit sign-off on the +1-first-action
tap for MANIFEST/SQUAD/QUESTS, or the inline-glyph mitigation ships with it.
- **Second-level tablist a11y**: lamps tablist + sub-rail tablist must be distinct,
labelled, and axe-clean; the scattered-controls `display:contents` tablist needs the same
verification flagged in pass 4.
- **The counter-rotated glass** (±0.7° here) — same real-device crispness check as pass 4;
retreat is 0° with lighting + arm carrying the read.
- **Scope**: this is the largest FO3 unit yet (new IA + skin + figure + control kit). Cut
order if hot: the cant → the arm → gauge sway/roller dressing → SKILLS/PERKS/GENERAL ship
as re-skinned stacks before full rail-composition lands (the rails can arrive per-tab).
---
## 10. Artifacts & open questions
**Canonical mockup:** `planning/mockups/fo3/fo3-pipboy-true.html` — landscape 780×360
primary; desktop landscape centered; portrait shows the legacy handoff card. Fully built:
STATS>STATUS (figure, editable limbs/HP/RAD), STATS>SPECIAL (inline-stepper editing),
ITEMS>MANIFEST (drawers + list + detail + actions); other sub-tabs are austere placeholders
naming their real boards. Params `?tab=…&sub=…`, hotkeys [1]–[6]/[0]. Screenshots:
`fo3-true-780x360-{status,special,items,uplink}.png`, `fo3-true-desktop-status.png`,
`fo3-true-portrait-legacy.png`. *(All `fo3-body-*` and `fo3-pipboy-mockup.html` files =
history.)*
**Open questions for the owner:**
1. The §6 flag class — accept +1 first-action tap on MANIFEST/SQUAD/QUESTS, or ship the
inline primary-action glyph on rows?
2. The birthday scratch's landscape home (left-rail micro-etch vs desktop-only).
3. Lamp sub-labels (OPERATOR/OPERATIONS/DATABANK) — keep visible under the domes (current),
or aria-only once learned?
4. The cant (−0.7°) — keep pending real-device crispness, or drop?
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).