diff --git a/content/CONTROLS.MAP b/content/CONTROLS.MAP
index 6f7808a..4a65e1d 100644
--- a/content/CONTROLS.MAP
+++ b/content/CONTROLS.MAP
@@ -43,61 +43,105 @@
 # hotkey channel (no double dispatch); unbound keys keep their authentic
 # 1995 meaning (see pckey above).
 #
-# ---- WASD CLASSIC (the shipped default) ----------------------------------
+# ---- THE KEYBOARD IS THE BUTTON BOARD ------------------------------------
+# Mirrors contentindings.txt exactly, so the glass (PadRIO) and desktop
+# (btinput) boots feel identical -- the letter and number rows are the MFD
+# button banks laid out where they sit on the panel, and flight lives on the
+# numpad.  G and B are the physical gap between the lower clusters and stay
+# unbound.  A bound key is REMOVED from the typed 1995 hotkey channel, so
+# this board gives those up by design (see pckey above to rebind any back).
 
-# Driving: W/S sweep the throttle lever (sticks, detent at zero); A/D are
-# the turn pedals (spring back); Q/E twist the torso; R/F aim the torso
-# up/down (elevation); X = all stop (recenters twist AND pitch).
-key W     axis Throttle rate 0.7
-key S     axis Throttle rate -0.7
-key Up    axis Throttle rate 0.7
-key Down  axis Throttle rate -0.7
-key A     axis LeftPedal deflect 1
-key D     axis RightPedal deflect 1
-key Left  axis LeftPedal deflect 1
-key Right axis RightPedal deflect 1
-key Q     axis JoystickX deflect -1
-key E     axis JoystickX deflect 1
-key R     axis JoystickY deflect 1
-key F     axis JoystickY deflect -1
-key X     action AllStop
-
-# Fire: the pod joystick's four buttons.
-key D1    button 0x40          # Main trigger (lasers group)
-key Space button 0x40
-key D2    button 0x46          # Middle thumb (PPCs group)
-key D3    button 0x47          # Upper thumb (missiles group)
-key Ctrl  button 0x47
-key D4    button 0x45          # Pinky (4th group)
-key Alt   button 0x3F          # throttle-head = REVERSE THRUST
-
-# --- numpad aim cluster (torso twist/aim -- mirrors bindings.txt so the
-# --- glass (PadRIO) and desktop (btinput) boots feel identical) ---
-key NumPad8 axis JoystickY deflect 1     # torso aim up
-key NumPad2 axis JoystickY deflect -1    # torso aim down
-key NumPad4 axis JoystickX deflect -1    # torso twist left
-key NumPad6 axis JoystickX deflect 1     # torso twist right
-key NumPad7 axis LeftPedal deflect 1     # turn left (alt)
-key NumPad9 axis RightPedal deflect 1    # turn right (alt)
+# Flight: the numpad.  8/2/4/6 stick, 7/9 pedals, 5 all-stop, 0 trigger;
+# Shift/Ctrl walk the throttle lever and it sticks; Alt is reverse thrust.
+key NumPad8 axis JoystickY deflect 1
+key NumPad2 axis JoystickY deflect -1
+key NumPad4 axis JoystickX deflect -1
+key NumPad6 axis JoystickX deflect 1
+key NumPad7 axis LeftPedal deflect 1
+key NumPad9 axis RightPedal deflect 1
 key NumPad5 action AllStop
-key Shift   axis Throttle rate 0.7       # throttle up (alt)
+key Shift   axis Throttle rate 0.7
+key Ctrl    axis Throttle rate -0.7
+key Alt     button 0x3F
 
-# Systems.
-key G     action ConfigHold    # hold + fire keys = regroup the weapon
-key C     action Valve         # coolant valve cycle
-key H     action Flush         # coolant flush (HOLD -- dumps reservoir coolant)
-key M     action ModeCycle     # Basic -> Standard -> Veteran controls
-key N     action DisplayCycle  # secondary schematic: Damage -> Critical -> Heat
-key J     action Mfd1Cycle     # lower-left MFD page: Quad -> Eng detail pages
-key K     action Mfd2Cycle     # upper-center MFD page (engineering)
-key L     action Mfd3Cycle     # lower-right MFD page
-key V     action ViewToggle    # cockpit <-> chase camera (dev)
-key B     action LookBehind    # HOLD = the pod's rear-view button
-key F5    action Generator1
-key F6    action Generator2
-key F7    action Generator3
-key F8    action Generator4
-key F9    action Reconnect
+# The four mappable fire buttons.
+key Space    button 0x40
+key NumPad0  button 0x40
+key NumPad1  button 0x45
+key NumPad3  button 0x46
+key Decimal  button 0x47
+
+# The hat, on the arrows.  0x42 is TORSO CENTER, not a look (streamed
+# mapping: elem 66 -> subsys 17 Torso attr 14; the other three are the
+# mapper's LookBehind/Right/Left).
+key Up    button 0x42
+key Down  button 0x41
+key Left  button 0x44
+key Right button 0x43
+
+# UPPER MFD banks: number row on top, QWERTY beneath.
+# Heat / coolant (1-4, QWER = Condensers 1-6, flush, balance).
+key D1 button 0x2F
+key D2 button 0x2E
+key D3 button 0x2D
+key D4 button 0x2C
+key Q  button 0x2B
+key W  button 0x2A
+key E  button 0x29
+key R  button 0x28
+# Engineering / Mfd2.
+key D5 button 0x27
+key D6 button 0x26
+key D7 button 0x25
+key D8 button 0x24
+key T  button 0x23
+key Y  button 0x22
+key U  button 0x21
+key I  button 0x20
+# Comm / target hotbox.
+key D9 button 0x37
+key D0 button 0x36
+key OemMinus button 0x35
+key Oemplus  button 0x34
+key O button 0x33
+key P button 0x32
+key OemOpenBrackets  button 0x31
+key OemCloseBrackets button 0x30
+
+# LOWER MFD banks: home row on top, the row below beneath.
+key A button 0x0F
+key S button 0x0E
+key D button 0x0D
+key F button 0x0C
+key Z button 0x0B
+key X button 0x0A
+key C button 0x09
+key V button 0x08
+key H button 0x07
+key J button 0x06
+key K button 0x05
+key L button 0x04
+key N button 0x03
+key M button 0x02
+key Oemcomma  button 0x01
+key OemPeriod button 0x00
+
+# The two columns flanking the map.
+key F1  button 0x10
+key F2  button 0x11
+key F3  button 0x12
+key F4  button 0x13
+key F5  button 0x14
+key F6  button 0x15
+key F7  button 0x18
+key F8  button 0x19
+key F9  button 0x1A
+key F10 button 0x1B
+key F11 button 0x1C
+key F12 button 0x1D
+
+# View toggle keeps its own key (V is a board button now).
+key Oemtilde action ViewToggle
 
 # ---- Xbox controller -----------------------------------------------------
 # Left stick steers (the pedals), right stick twists the torso and pushes
diff --git a/context/glass-cockpit.md b/context/glass-cockpit.md
index c7d100c..ae4a6e2 100644
--- a/context/glass-cockpit.md
+++ b/context/glass-cockpit.md
@@ -235,6 +235,53 @@ Bonus confirmation: those legend widgets sit at gauge offsets 537/430/322/215/10
 pitch**, independently corroborating the map legend grid measured from pixels in §Player-tunable
 displays.
 
+## THE KEYBOARD IS THE BUTTON BOARD — new default keymap (2026-07-26) [T2]
+
+Ported from RP412's bindings design (user decision: make it the DEFAULT, not an alternate). The
+letter and number rows are the MFD button banks **laid out where they sit on the panel**, and
+flight moves to the NUMPAD so the board stays free:
+
+| Keys | Bank |
+|---|---|
+| `1 2 3 4` / `Q W E R` | Heat / coolant 0x2F-0x2C / 0x2B-0x28 |
+| `5 6 7 8` / `T Y U I` | Engineering (Mfd2) 0x27-0x24 / 0x23-0x20 |
+| `9 0 - =` / `O P [ ]` | Comm / target hotbox 0x37-0x34 / 0x33-0x30 |
+| `A S D F` / `Z X C V` | Left Weapons (Mfd1) 0x0F-0x0C / 0x0B-0x08 |
+| `H J K L` / `N M , .` | Right Weapons (Mfd3) 0x07-0x04 / 0x03-0x00 |
+| `F1`-`F6` / `F7`-`F12` | the map's two columns 0x10-0x15 / 0x18-0x1D |
+| numpad | 8/2/4/6 stick, 7/9 pedals, 5 all-stop, 0/1/3/. the four fire buttons |
+| `Shift`/`Ctrl` · `Alt` · arrows · `` ` `` | throttle lever · reverse · look/torso-centre · view |
+
+`G` and `B` stay unbound — the physical gap between the lower clusters. **Coverage: 61 of 72
+addresses on the keyboard, and the 11 absent ones are exactly those with no authored function**
+(0x16/0x17/0x1E/0x1F column gaps, 0x38-0x3E intercom/door). All 72 remain clickable.
+
+It lands on BT's addresses better than it deserves to: `1-4`+`QWER` are the ENTIRE coolant system
+(Condensers 1-6, flush, balance), `F6`/`F7` the display/control-mode cycles, `F9`-`F12` Generators
+A-D, `F4` the crouch button.
+
+⚠ **What it costs, deliberately.** A bound key is removed from the authentic 1995 typed-hotkey
+channel to stop double-dispatch, and this board binds nearly everything — so `5` (Quad page), `z`
+(Eng1), `t/y/u/i/o` (pilot select) and `+`/`-` (target zoom) are given up. Unbind a key to get its
+1995 meaning back.
+
+⚠ **New rule: a bindings row WINS over a built-in convenience key** (`PadRIO::KeyHasBinding`).
+`V` and `J/K/L` are board buttons now, so the view-toggle and preset-cycle polls stand down for
+them and the view toggle lives on `` ` `` alone. Without this they would have fired BOTH.
+
+`content/CONTROLS.MAP` was rewritten to mirror the board (90 bindings, 0 parse complaints) so the
+glass/pod/dev boots still feel identical — the 2026-07-21 settlement. An existing `bindings.txt`
+is never overwritten, so a player only meets this board by deleting theirs.
+
+## Hat labels corrected — 0x42 is TORSO CENTER [T1, streamed mapping]
+
+`docs/INPUT_PATH_AUDIT.md` flagged the 0x41-0x44 labels as wrong and it was right. Settled from
+`BT_CTRLMAP_LOG`: **0x42 → subsys 17 (Torso), attrID 14** — not a look at all; the other three go
+to the mapper's look trio (0x44 → attrID 10 LookLeft, 0x43 → 11 LookRight, 0x41 → 12 LookBehind).
+The shipped `.RES` has no "TORSO CENTER" string (its names are LookBehind/Down/Forward/Left/Right),
+so the audit's phrasing was loose but its substance correct. Swept the three UI tables
+(`L4GLASSWIN`, `L4PADPANEL`, `L4VB16`): 0x42 is now "Torso Ctr", 0x41/0x43/0x44 "Look Bk/R/L".
+
 ## RGB keyboard lamp mirror — L4KEYLIGHT (2026-07-26) [T2 live, claimed a real board]
 
 Ported from RP412 (itself vRIO's `KeyboardLampMirror`). Keys bound to a lamp address in
diff --git a/docs/CONTROLS.html b/docs/CONTROLS.html
new file mode 100644
index 0000000..3ef800c
--- /dev/null
+++ b/docs/CONTROLS.html
@@ -0,0 +1,1061 @@
+<title>BattleTech 4.11 — Controls</title>
+
+<style>
+  :root {
+    --void:        #05070a;
+    --void-2:      #080d10;
+    --glass:       #0a1410;
+    --glass-edge:  #16261d;
+    --phosphor:    #40ff40;
+    --phosphor-2:  #8dffa0;
+    --dim:         #189418;
+    --hazard:      #b0302a;
+    --hazard-lit:  #ff5a48;
+    --amber:       #c8a32e;
+    --amber-lit:   #ffd25e;
+    --ink:         #b9cfc0;
+    --ink-quiet:   #6d8478;
+
+    --mono: "Cascadia Mono", Consolas, "SF Mono", ui-monospace, Menlo, monospace;
+    --sans: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI", Roboto, sans-serif;
+
+    --u: 42px;
+    --gap: 5px;
+  }
+
+  * { box-sizing: border-box; }
+
+  body {
+    margin: 0;
+    background:
+      radial-gradient(120% 90% at 50% -10%, #0d1a14 0%, transparent 60%),
+      var(--void);
+    color: var(--ink);
+    font-family: var(--sans);
+    line-height: 1.65;
+    -webkit-font-smoothing: antialiased;
+  }
+
+  body::after {
+    content: "";
+    position: fixed;
+    inset: 0;
+    pointer-events: none;
+    background: radial-gradient(130% 100% at 50% 45%, transparent 55%, rgba(0,0,0,.55) 100%);
+    z-index: 5;
+  }
+
+  .wrap {
+    max-width: 1180px;
+    margin: 0 auto;
+    padding: clamp(28px, 5vw, 64px) clamp(16px, 4vw, 40px) 80px;
+    position: relative;
+    z-index: 1;
+  }
+
+  .masthead { border-bottom: 1px solid var(--glass-edge); padding-bottom: 28px; }
+
+  .eyebrow {
+    font-family: var(--mono);
+    font-size: .72rem;
+    letter-spacing: .28em;
+    text-transform: uppercase;
+    color: var(--dim);
+    margin: 0 0 14px;
+  }
+
+  h1 {
+    font-family: var(--mono);
+    font-size: clamp(1.9rem, 5.2vw, 3.1rem);
+    line-height: 1.05;
+    letter-spacing: .04em;
+    text-transform: uppercase;
+    color: var(--phosphor);
+    text-shadow: 0 0 22px rgba(64,255,64,.28);
+    margin: 0 0 18px;
+    text-wrap: balance;
+  }
+
+  .lede { max-width: 64ch; margin: 0; color: var(--ink); }
+  .lede code { font-family: var(--mono); color: var(--phosphor-2); font-size: .92em; }
+
+  .provenance {
+    margin: 18px 0 0;
+    font-family: var(--mono);
+    font-size: .74rem;
+    letter-spacing: .08em;
+    color: var(--ink-quiet);
+  }
+
+  .glance {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
+    gap: 1px;
+    background: var(--glass-edge);
+    border: 1px solid var(--glass-edge);
+    margin: 34px 0 0;
+  }
+
+  .glance div { background: var(--void-2); padding: 16px 18px; }
+
+  .glance dt {
+    font-family: var(--mono);
+    font-size: .68rem;
+    letter-spacing: .2em;
+    text-transform: uppercase;
+    color: var(--ink-quiet);
+    margin: 0 0 6px;
+  }
+
+  .glance dd { margin: 0; font-family: var(--mono); font-size: 1.02rem; color: var(--phosphor); }
+  .glance dd small { display: block; font-family: var(--sans); font-size: .78rem; color: var(--ink-quiet); letter-spacing: 0; margin-top: 3px; }
+
+  section { margin-top: 64px; }
+
+  h2 {
+    font-family: var(--mono);
+    font-size: .82rem;
+    letter-spacing: .3em;
+    text-transform: uppercase;
+    color: var(--phosphor);
+    margin: 0 0 6px;
+    display: flex;
+    align-items: center;
+    gap: 14px;
+  }
+
+  h2::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, var(--glass-edge), transparent); }
+
+  h3 {
+    font-family: var(--mono);
+    font-size: .78rem;
+    font-weight: 400;
+    letter-spacing: .2em;
+    text-transform: uppercase;
+    color: var(--phosphor);
+    margin: 46px 0 8px;
+  }
+
+  .sub { margin: 0 0 26px; max-width: 64ch; color: var(--ink-quiet); font-size: .93rem; }
+
+  .panel {
+    background: linear-gradient(180deg, rgba(64,255,64,.03), transparent 40%), var(--glass);
+    border: 1px solid var(--glass-edge);
+    padding: clamp(16px, 3vw, 30px);
+    overflow-x: auto;
+  }
+
+  .readout {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: baseline;
+    gap: 8px 22px;
+    border: 1px solid var(--glass-edge);
+    border-top: none;
+    background: var(--void-2);
+    padding: 13px clamp(16px, 3vw, 30px);
+    font-family: var(--mono);
+    min-height: 54px;
+  }
+
+  .readout .rd-name { color: var(--phosphor); font-size: 1rem; letter-spacing: .04em; }
+  .readout .rd-addr { color: var(--amber-lit); font-size: .82rem; border: 1px solid rgba(200,163,46,.35); padding: 1px 8px; }
+  .readout .rd-desc { color: var(--ink-quiet); font-size: .85rem; font-family: var(--sans); }
+  .readout[data-idle="true"] .rd-name { color: var(--ink-quiet); }
+
+  .kb { display: flex; flex-direction: column; gap: 14px; min-width: 940px; position: relative; padding-top: 22px; }
+  .kb-groups { position: absolute; inset: 0; pointer-events: none; }
+  .kb-groups svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
+  .kb-groups path { fill: none; stroke-width: 1; opacity: .85; }
+  .kb-groups path.g-mfd { stroke: #ff9a8c; }
+  .kb-groups path.g-col { stroke: var(--amber-lit); }
+
+  .kb-groups .g-label {
+    position: absolute;
+    transform: translate(0, -100%);
+    font-family: var(--mono);
+    font-size: .56rem;
+    letter-spacing: .16em;
+    text-transform: uppercase;
+    white-space: nowrap;
+    padding-bottom: 3px;
+  }
+  .kb-groups .g-label.g-mfd { color: #ff9a8c; }
+  .kb-groups .g-label.g-col { color: var(--amber-lit); }
+
+  .kb-bank { display: flex; gap: 22px; }
+  .kb-main { display: flex; flex-direction: column; gap: var(--gap); }
+  .kb-row { display: flex; gap: var(--gap); }
+  .kb-row.zone { margin-top: 18px; }
+  .kb-side { display: flex; flex-direction: column; gap: var(--gap); justify-content: flex-end; }
+  .kb-nav { display: flex; flex-direction: column; gap: var(--gap); align-items: center; padding-top: calc(var(--u) + var(--gap)); height: 100%; }
+  .kb-nav .grow { flex: 1 1 auto; min-height: var(--gap); }
+  .kb-pad { display: grid; grid-template-columns: repeat(4, var(--u)); gap: var(--gap); }
+
+  .key {
+    width: var(--u);
+    height: var(--u);
+    flex: none;
+    border: 1px solid #1d2b24;
+    border-bottom-color: #0c1512;
+    background: linear-gradient(180deg, #101a16, #0b120f);
+    color: #4e6a5c;
+    font-family: var(--mono);
+    font-size: .72rem;
+    line-height: 1.1;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    gap: 2px;
+    padding: 2px;
+    cursor: default;
+    transition: transform .08s ease, box-shadow .12s ease, border-color .12s ease;
+  }
+
+  .key span.k-sub { font-size: .53rem; letter-spacing: .04em; text-transform: uppercase; opacity: .85; }
+  .key[data-role] { cursor: pointer; }
+  .key:focus-visible { outline: 2px solid var(--phosphor); outline-offset: 2px; }
+
+  .key[data-role="flight"] { color: var(--phosphor); border-color: #2c6b38; background: linear-gradient(180deg, #10281a, #0a1a11); }
+  .key[data-role="mfd"]    { color: #ff9a8c; border-color: #6b2723; background: linear-gradient(180deg, #2a100e, #1a0a09); }
+  .key[data-role="column"] { color: var(--amber-lit); border-color: #6d5a1c; background: linear-gradient(180deg, #251d08, #171205); }
+
+  .key[data-role]:hover, .key.is-active {
+    transform: translateY(-1px);
+    border-color: currentColor;
+    box-shadow: 0 0 0 1px currentColor, 0 0 18px -4px currentColor;
+  }
+
+  .key.w125 { width: calc(var(--u) * 1.25 + var(--gap) * .25); }
+  .key.w15  { width: calc(var(--u) * 1.5 + var(--gap) * .5); }
+  .key.w175 { width: calc(var(--u) * 1.75 + var(--gap) * .75); }
+  .key.w2   { width: calc(var(--u) * 2 + var(--gap)); }
+  .key.w225 { width: calc(var(--u) * 2.25 + var(--gap) * 1.25); }
+  .key.w275 { width: calc(var(--u) * 2.75 + var(--gap) * 1.75); }
+  .key.w6   { width: calc(var(--u) * 6.25 + var(--gap) * 5.25); }
+  .key.tall { grid-row: span 2; height: calc(var(--u) * 2 + var(--gap)); }
+  .key.pad0 { grid-column: span 2; width: calc(var(--u) * 2 + var(--gap)); }
+
+  .spacer { width: calc(var(--u) * .5); flex: none; }
+
+  .legend { display: flex; flex-wrap: wrap; gap: 10px 26px; margin-top: 20px; font-family: var(--mono); font-size: .74rem; letter-spacing: .06em; }
+  .legend span { display: flex; align-items: center; gap: 8px; color: var(--ink-quiet); }
+  .legend i { width: 13px; height: 13px; border: 1px solid; display: block; }
+  .lg-flight { color: var(--phosphor); border-color: currentColor; background: #10281a; }
+  .lg-mfd { color: #ff9a8c; border-color: currentColor; background: #2a100e; }
+  .lg-col { color: var(--amber-lit); border-color: currentColor; background: #251d08; }
+  .lg-off { color: #4e6a5c; border-color: #1d2b24; background: #101a16; }
+
+  .pad-shell { display: flex; justify-content: center; }
+  svg.pad { width: 100%; max-width: 720px; height: auto; }
+  svg.pad .body { fill: #0c1512; stroke: #1f3329; stroke-width: 2; }
+  svg.pad .ctl { fill: #12211a; stroke: #2c6b38; stroke-width: 1.6; cursor: pointer; transition: fill .12s ease, stroke .12s ease; }
+  svg.pad .ctl:hover, svg.pad .ctl.is-active { fill: #1a3b26; stroke: var(--phosphor); }
+  svg.pad .lbl { font-family: var(--mono); font-size: 11px; fill: var(--phosphor); letter-spacing: .06em; pointer-events: none; }
+  svg.pad .lbl-q { font-family: var(--mono); font-size: 10px; fill: var(--ink-quiet); letter-spacing: .08em; pointer-events: none; text-transform: uppercase; }
+  svg.pad .lead { stroke: #244a33; stroke-width: 1; fill: none; }
+  svg.pad .cap { font-family: var(--mono); font-size: 11px; fill: #cfe8d8; pointer-events: none; }
+
+  .cockpit { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; min-width: 720px; align-items: start; }
+  .mfd { border: 1px solid var(--glass-edge); background: #060c09; display: flex; flex-direction: column; }
+  .mfd-strip { display: flex; gap: 2px; padding: 3px; background: #150a09; }
+  .mfd-strip b {
+    flex: 1;
+    background: linear-gradient(180deg, #b0302a, #7d211d);
+    color: #ffd9d3;
+    font-family: var(--mono);
+    font-size: .62rem;
+    font-weight: 400;
+    text-align: center;
+    padding: 4px 0;
+    letter-spacing: .04em;
+  }
+  .mfd-face { padding: 14px 12px; text-align: center; font-family: var(--mono); }
+  .mfd-face em { display: block; font-style: normal; color: var(--phosphor); font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; }
+  .mfd-face small { display: block; color: var(--ink-quiet); font-size: .68rem; margin-top: 5px; letter-spacing: .06em; }
+  .cockpit-note { grid-column: span 3; margin: 2px 0 0; font-size: .82rem; color: var(--ink-quiet); }
+
+  .mfd-map { min-height: 300px; }
+  .map-body { display: flex; align-items: stretch; flex: 1; }
+  .map-col { display: flex; flex-direction: column; gap: 2px; padding: 3px; background: #150a09; }
+  .map-col b {
+    flex: 1;
+    min-height: 26px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: linear-gradient(90deg, #c8a32e, #8d711b);
+    color: #2a2205;
+    font-family: var(--mono);
+    font-size: .62rem;
+    font-weight: 400;
+    letter-spacing: .04em;
+    padding: 0 7px;
+  }
+  .map-face {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    gap: 6px;
+    padding: 14px 8px;
+    font-family: var(--mono);
+    text-align: center;
+    background: repeating-linear-gradient(0deg, rgba(64,255,64,.05) 0 1px, transparent 1px 22px), #060c09;
+  }
+  .map-face em { font-style: normal; color: var(--phosphor); font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; }
+  .map-face small { color: var(--ink-quiet); font-size: .66rem; letter-spacing: .06em; line-height: 1.5; }
+
+  .tbl-scroll { overflow-x: auto; border: 1px solid var(--glass-edge); }
+  table { width: 100%; border-collapse: collapse; font-size: .87rem; min-width: 480px; }
+  caption { text-align: left; font-family: var(--mono); font-size: .7rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-quiet); padding: 12px 14px; background: var(--void-2); }
+  th, td { text-align: left; padding: 9px 14px; border-top: 1px solid var(--glass-edge); }
+  th { font-family: var(--mono); font-size: .68rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-quiet); font-weight: 400; }
+  td.mono { font-family: var(--mono); color: var(--phosphor); white-space: nowrap; }
+  td.addr { font-family: var(--mono); color: var(--amber-lit); font-variant-numeric: tabular-nums; white-space: nowrap; }
+  tbody tr:hover td { background: rgba(64,255,64,.035); }
+
+  pre {
+    margin: 0;
+    padding: 18px clamp(14px, 3vw, 24px);
+    background: var(--void-2);
+    border: 1px solid var(--glass-edge);
+    overflow-x: auto;
+    font-family: var(--mono);
+    font-size: .82rem;
+    line-height: 1.7;
+    color: var(--ink);
+  }
+  pre b { color: var(--phosphor); font-weight: 400; }
+  pre i { color: var(--ink-quiet); font-style: normal; }
+
+  .two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 22px; }
+
+  .callout {
+    border-left: 2px solid var(--hazard);
+    background: linear-gradient(90deg, rgba(176,48,42,.12), transparent 70%);
+    padding: 14px 18px;
+    margin-top: 26px;
+  }
+  .callout p { margin: 0; font-size: .9rem; }
+  .callout strong { font-family: var(--mono); color: var(--hazard-lit); letter-spacing: .04em; }
+
+  .reach { display: flex; justify-content: center; }
+  .reach-fig { margin: 0; width: min(430px, 100%); }
+  .reach-glass { position: relative; aspect-ratio: 4 / 3; background: var(--glass); border: 1px solid var(--glass-edge); overflow: hidden; }
+  .reach-band {
+    position: absolute;
+    left: 0; right: 0;
+    height: 50%;
+    background: repeating-linear-gradient(-45deg, rgba(176,48,42,.16) 0 5px, transparent 5px 11px);
+    display: flex; align-items: center; justify-content: center;
+    font-family: var(--mono); font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: #e08a7e;
+  }
+  .reach-band.is-top { top: 0; border-bottom: 1px dashed rgba(176,48,42,.6); }
+  .reach-band.is-bot { bottom: 0; }
+  .reach-strip { position: absolute; left: 0; right: 0; height: 8px; display: flex; gap: 2px; z-index: 2; }
+  .reach-strip.is-top { top: 0; }
+  .reach-strip.is-bot { bottom: 0; }
+  .reach-strip i { flex: 1; background: var(--hazard); display: block; }
+  .reach-strip.is-lit i:nth-child(2) { background: var(--hazard-lit); }
+  .reach-fig figcaption { margin-top: 12px; font-size: .84rem; color: var(--ink-quiet); }
+  .reach-fig figcaption b { font-family: var(--mono); font-weight: 400; color: var(--phosphor-2); }
+
+  .pos-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 18px; }
+  .pos { margin: 0; }
+  .pos-box { position: relative; aspect-ratio: 16 / 9; background: var(--void-2); border: 1px solid var(--glass-edge); }
+  .pos-box i, .pos-box u { position: absolute; display: block; }
+  .pos-box i { width: 16.7%; height: 22.2%; background: #2a100e; border: 1px solid #7d3029; }
+  .pos-box u { width: 16.9%; height: 40%; background: #251d08; border: 1px solid var(--amber); }
+  .pos-box i.ul { left: 0; top: 0; }
+  .pos-box i.uc { left: 41.65%; top: 0; }
+  .pos-box i.ur { right: 0; top: 0; }
+  .pos-box i.ll { left: 0; bottom: 0; }
+  .pos-box i.lr { right: 0; bottom: 0; }
+  .pos-box u { left: 41.55%; bottom: 0; }
+  .p-left u { left: 0; bottom: 0; }
+  .p-left i.ll { left: 16.9%; }
+  .p-right u { left: auto; right: 0; bottom: 0; }
+  .p-right i.lr { right: 16.9%; }
+  .p-midleft u { left: 0; bottom: auto; top: 30%; }
+  .p-midright u { left: auto; right: 0; bottom: auto; top: 30%; }
+  .pos figcaption { margin-top: 9px; font-family: var(--mono); font-size: .72rem; letter-spacing: .12em; color: var(--phosphor); }
+  .pos figcaption small { display: block; font-family: var(--sans); font-size: .76rem; letter-spacing: 0; color: var(--ink-quiet); margin-top: 2px; }
+
+  footer {
+    margin-top: 72px;
+    padding-top: 22px;
+    border-top: 1px solid var(--glass-edge);
+    font-family: var(--mono);
+    font-size: .74rem;
+    letter-spacing: .06em;
+    color: var(--ink-quiet);
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px 24px;
+  }
+
+  @media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
+</style>
+
+<div class="wrap">
+
+  <header class="masthead">
+    <p class="eyebrow">Virtual World Entertainment · Tesla II pod · BT 4.11</p>
+    <h1>BattleTech<br>Controls Map</h1>
+    <p class="lede">
+      Every input the pod answers to. <strong>The keyboard is the button
+      board</strong>: the letter and number rows are the MFD banks laid out
+      where they sit on the panel, and flight moved to the number pad so the
+      board stays free. Every one of the 72 buttons is also a mouse target on
+      the cockpit itself.
+    </p>
+    <p class="provenance">Generated from the default <code>bindings.txt</code> · all of it rebindable</p>
+  </header>
+
+  <dl class="glance">
+    <div>
+      <dt>Fly</dt>
+      <dd>Num 8 2 4 6<small>stick · 7/9 pedals · 0 fires · 5 all-stop</small></dd>
+    </div>
+    <div>
+      <dt>Throttle</dt>
+      <dd>Shift / Ctrl<small>walks the lever; it sticks where you leave it</small></dd>
+    </div>
+    <div>
+      <dt>Reverse</dt>
+      <dd>Alt<small>the throttle handle's own button</small></dd>
+    </div>
+    <div>
+      <dt>Control mode</dt>
+      <dd>F7<small>BAS → MID → ADV. You spawn in BASIC</small></dd>
+    </div>
+  </dl>
+
+  <div class="callout">
+    <p>
+      <strong>YOU SPAWN IN BASIC.</strong> In BASIC the stick steers and the
+      pedals do nothing. Press <b>F7</b> once for MID, where the pedals steer
+      and the stick twists the torso — the full pod experience. If "turning
+      doesn't work", this is why, not a bug. (Control mode is not the menu's
+      experience setting; that one changes the simulation, not your controls.)
+    </p>
+  </div>
+
+  <section>
+    <h2>Keyboard</h2>
+    <p class="sub">
+      Colour is the bank each key drives: green flies the mech, red is an MFD
+      button bank, amber is a column beside the map. Dark keys are unbound and
+      free to take. Hover a key to read what it does.
+    </p>
+
+    <div class="panel">
+      <div class="kb" id="kb"></div>
+      <div class="legend">
+        <span><i class="lg-flight"></i> flight &amp; fire</span>
+        <span><i class="lg-mfd"></i> MFD button banks</span>
+        <span><i class="lg-col"></i> map columns</span>
+        <span><i class="lg-off"></i> unbound</span>
+      </div>
+    </div>
+    <div class="readout" id="rd-kb" data-idle="true">
+      <span class="rd-name">Select a key</span>
+      <span class="rd-desc">Hover a key to read what it drives.</span>
+    </div>
+
+    <div class="callout">
+      <p>
+        <strong>COOLANT VALVES BITE.</strong> The valve detents on
+        <b>1 2 3 Q W E</b> run 1 – 5 – 50 (max) – <b>0 (CLOSED)</b> – back to
+        1. One press past max shuts that loop off and everything on it
+        overheats. Boosting one loop starves the others — the supply is
+        shared. <b>4</b> is the flush (hold it), <b>R</b> re-balances every
+        valve.
+      </p>
+    </div>
+  </section>
+
+  <section>
+    <h2>Where the keys live on the panel</h2>
+    <p class="sub">
+      Each MFD carries eight buttons: a four-wide top row and a four-wide row
+      beneath it. The keyboard rows run left to right across the upper
+      displays, then the lower pair — the same order your hand finds them in
+      the pod.
+    </p>
+
+    <div class="panel">
+      <div class="cockpit">
+        <div class="mfd">
+          <div class="mfd-strip"><b>1</b><b>2</b><b>3</b><b>4</b></div>
+          <div class="mfd-face"><em>Heat / coolant</em><small>0x2F – 0x2C<br>0x2B – 0x28</small></div>
+          <div class="mfd-strip"><b>Q</b><b>W</b><b>E</b><b>R</b></div>
+        </div>
+        <div class="mfd">
+          <div class="mfd-strip"><b>5</b><b>6</b><b>7</b><b>8</b></div>
+          <div class="mfd-face"><em>Engineering</em><small>0x27 – 0x24<br>0x23 – 0x20</small></div>
+          <div class="mfd-strip"><b>T</b><b>Y</b><b>U</b><b>I</b></div>
+        </div>
+        <div class="mfd">
+          <div class="mfd-strip"><b>9</b><b>0</b><b>-</b><b>=</b></div>
+          <div class="mfd-face"><em>Comm / hotbox</em><small>0x37 – 0x34<br>0x33 – 0x30</small></div>
+          <div class="mfd-strip"><b>O</b><b>P</b><b>[</b><b>]</b></div>
+        </div>
+
+        <div class="mfd">
+          <div class="mfd-strip"><b>A</b><b>S</b><b>D</b><b>F</b></div>
+          <div class="mfd-face"><em>Left Weapons</em><small>0x0F – 0x0C<br>0x0B – 0x08</small></div>
+          <div class="mfd-strip"><b>Z</b><b>X</b><b>C</b><b>V</b></div>
+        </div>
+        <div class="mfd mfd-map">
+          <div class="map-body">
+            <div class="map-col"><b>F1</b><b>F2</b><b>F3</b><b>F4</b><b>F5</b><b>F6</b></div>
+            <div class="map-face">
+              <em>Map · radar</em>
+              <small>
+                zoom in / out<br>
+                thermal · CROUCH<br>
+                searchlight · display<br>
+                0x10 – 0x15
+              </small>
+            </div>
+            <div class="map-col"><b>F7</b><b>F8</b><b>F9</b><b>F10</b><b>F11</b><b>F12</b></div>
+          </div>
+        </div>
+        <div class="mfd">
+          <div class="mfd-strip"><b>H</b><b>J</b><b>K</b><b>L</b></div>
+          <div class="mfd-face"><em>Right Weapons</em><small>0x07 – 0x04<br>0x03 – 0x00</small></div>
+          <div class="mfd-strip"><b>N</b><b>M</b><b>,</b><b>.</b></div>
+        </div>
+
+        <p class="cockpit-note">
+          G and B stay unbound on purpose: they are the physical gap between
+          the lower clusters. The right-hand map column is control mode (F7)
+          and Generators A–D (F9–F12); F8 is a column slot the pod never
+          wired.
+        </p>
+      </div>
+    </div>
+
+    <div class="tbl-scroll" style="margin-top:26px">
+      <table>
+        <caption>The weapon and engineering displays are page-gated</caption>
+        <tbody>
+          <tr><th>Quad page</th><td>The outer buttons jump straight to a populated Eng page.</td></tr>
+          <tr><th>Eng page</th><td>Generator select A–D, bus mode, coolant toggle, trigger configure, and <b>eject</b>.</td></tr>
+          <tr><th>Jams &amp; fires</th><td>A weapon gone quiet with ammo left has <b>jammed</b> — heat jams ballistics, authentically. Its Eng-page buttons <b>flash</b>. <b>Tap eject</b> to clear the round; <b>hold ~3 s</b> to jettison a burning bay before it detonates. An ammo bay fire gives you about ten seconds and it will kill you.</td></tr>
+        </tbody>
+      </table>
+    </div>
+  </section>
+
+  <section>
+    <h2>Gamepad</h2>
+    <p class="sub">An Xbox-compatible pad drives all five axes and the grip buttons. Hover a control for its RIO address.</p>
+
+    <div class="panel pad-shell">
+      <svg class="pad" viewBox="0 0 700 400" role="img" aria-label="Xbox controller with BattleTech bindings">
+        <rect class="ctl" data-name="LB — Look left" data-addr="0x44" data-desc="Hat left" x="112" y="60" width="86" height="26" rx="13"></rect>
+        <rect class="ctl" data-name="RB — Look right" data-addr="0x43" data-desc="Hat right" x="502" y="60" width="86" height="26" rx="13"></rect>
+        <rect class="ctl" data-name="LT — Left pedal" data-addr="axis" data-desc="Turn left (MID/ADV control mode)" x="132" y="26" width="66" height="24" rx="12"></rect>
+        <rect class="ctl" data-name="RT — Right pedal" data-addr="axis" data-desc="Turn right (MID/ADV control mode)" x="502" y="26" width="66" height="24" rx="12"></rect>
+        <text class="lbl-q" x="132" y="20">LT · left pedal</text>
+        <text class="lbl-q" x="502" y="20">RT · right pedal</text>
+        <text class="lbl-q" x="112" y="102">LB · look left</text>
+        <text class="lbl-q" x="486" y="102">RB · look right</text>
+
+        <path class="body" d="M196 112 h308 c46 0 78 26 92 74 l32 108 c14 48 -10 82 -52 82 c-36 0 -58 -22 -74 -54 l-22 -44 h-180 l-22 44 c-16 32 -38 54 -74 54 c-42 0 -66 -34 -52 -82 l32 -108 c14 -48 46 -74 92 -74 z"></path>
+
+        <circle class="ctl" data-name="Left stick — torso twist / aim" data-addr="axes" data-desc="Pod-faithful: X twists the torso, Y aims it (deadzone 0.24)" cx="248" cy="176" r="34"></circle>
+        <text class="cap" x="222" y="181">STICK</text>
+        <path class="lead" d="M214 176 H150"></path>
+        <text class="lbl-q" x="60" y="180">torso twist / aim</text>
+
+        <g class="ctl" data-name="D-pad — look &amp; torso centre" data-addr="0x41–0x44" data-desc="Up = torso centre, down = look behind, left/right = look">
+          <rect x="286" y="266" width="24" height="66" rx="4"></rect>
+          <rect x="265" y="287" width="66" height="24" rx="4"></rect>
+        </g>
+        <path class="lead" d="M265 299 H180"></path>
+        <text class="lbl-q" x="86" y="303">look · torso centre</text>
+
+        <circle class="ctl" data-name="Right stick Y — Throttle" data-addr="axis" data-desc="Walks the throttle lever; the position sticks" cx="404" cy="266" r="32"></circle>
+        <text class="cap" x="381" y="271">THRTL</text>
+        <path class="lead" d="M436 266 H520"></path>
+        <text class="lbl-q" x="524" y="270">throttle</text>
+
+        <circle class="ctl" data-name="Y — Torso centre" data-addr="0x42" data-desc="Re-centre the torso" cx="470" cy="146" r="20"></circle>
+        <circle class="ctl" data-name="X — Upper thumb" data-addr="0x47" data-desc="Fire group" cx="428" cy="180" r="20"></circle>
+        <circle class="ctl" data-name="B — Reverse thrust" data-addr="0x3F" data-desc="The throttle handle's button" cx="512" cy="180" r="20"></circle>
+        <circle class="ctl" data-name="A — Main trigger" data-addr="0x40" data-desc="Fires the selected weapon group" cx="470" cy="214" r="20"></circle>
+        <text class="lbl" x="465" y="151">Y</text>
+        <text class="lbl" x="423" y="185">X</text>
+        <text class="lbl" x="507" y="185">B</text>
+        <text class="lbl" x="465" y="219">A</text>
+        <path class="lead" d="M490 214 L556 236"></path>
+        <text class="lbl-q" x="560" y="240">A · main trigger</text>
+        <text class="lbl-q" x="560" y="196">X upper · B reverse · Y torso centre</text>
+        <path class="lead" d="M532 180 H556"></path>
+      </svg>
+    </div>
+    <div class="readout" id="rd-pad" data-idle="true">
+      <span class="rd-name">Select a control</span>
+      <span class="rd-desc">Hover the diagram to read its binding.</span>
+    </div>
+
+    <p class="sub" style="margin-top:22px">
+      <b>Flight sticks, HOTAS and pedals:</b> run <code>joyconfig.bat</code>
+      once. A wizard asks you to move each control in turn, writes the
+      bindings, and drops you into the solo menu. It only rewrites its own
+      section of <code>bindings.txt</code>, so hand edits elsewhere survive.
+    </p>
+  </section>
+
+  <section>
+    <h2>The mouse</h2>
+    <p class="sub">
+      Every button on every display is clickable and lights when the game
+      commands its lamp — and each reaches in <em style="font-style:normal;color:var(--phosphor-2)">under the glass</em>
+      beside it, so the picture is the press target. The lit strip at the edge
+      is only the lamp. Right-click latches a button held.
+    </p>
+
+    <div class="panel">
+      <div class="reach">
+        <figure class="reach-fig">
+          <div class="reach-glass">
+            <span class="reach-strip is-top is-lit" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
+            <span class="reach-band is-top">press — top row</span>
+            <span class="reach-band is-bot">press — bottom row</span>
+            <span class="reach-strip is-bot" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
+          </div>
+          <figcaption>
+            One MFD. Four buttons above the glass and four below, each reaching
+            <b>half the pane</b> deep — so the two banks meet in the middle and
+            the whole face answers the mouse. Only a thin strip clears the edge
+            to show its lamp.
+          </figcaption>
+        </figure>
+      </div>
+    </div>
+
+    <h3>Arranging the glass</h3>
+    <p class="sub">
+      The pod bolted its displays down at one size. On a desktop panel there is
+      room to trade viewscreen for instrument, so all six are yours to size —
+      and the map to place. Set these in <code>content\environ.ini</code>,
+      which the game writes on first run with every option documented.
+    </p>
+
+    <div class="pos-grid">
+      <figure class="pos">
+        <div class="pos-box p-center"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
+        <figcaption>CENTER<small>as the pod had it</small></figcaption>
+      </figure>
+      <figure class="pos">
+        <div class="pos-box p-left"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
+        <figcaption>LEFT<small>bottom corner; the MFD slides inboard</small></figcaption>
+      </figure>
+      <figure class="pos">
+        <div class="pos-box p-right"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
+        <figcaption>RIGHT<small>the same, other side</small></figcaption>
+      </figure>
+      <figure class="pos">
+        <div class="pos-box p-midleft"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
+        <figcaption>MIDLEFT<small>halfway up; leaves the bottom row alone</small></figcaption>
+      </figure>
+      <figure class="pos">
+        <div class="pos-box p-midright"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
+        <figcaption>MIDRIGHT<small>halfway up, right</small></figcaption>
+      </figure>
+    </div>
+
+    <div class="tbl-scroll" style="margin-top:26px">
+      <table>
+        <caption>environ.ini — display layout</caption>
+        <tbody>
+          <tr><th>BT_MFD_SCALE</th><td>All five MFDs, as a percentage of pod size. <span style="color:var(--ink-quiet)">25–200, default 100</span></td></tr>
+          <tr><th>BT_MFD_SCALE_<span style="color:var(--phosphor)">xx</span></th><td>Any one on its own — <span class="addr" style="font-family:var(--mono)">UL UC UR LL LR</span>. Unset inherits the line above.</td></tr>
+          <tr><th>BT_RADAR_SCALE</th><td>The map alone.</td></tr>
+          <tr><th>BT_RADAR_POS</th><td class="mono">CENTER · LEFT · RIGHT · MIDLEFT · MIDRIGHT</td></tr>
+          <tr><th>BT_GLASS_PANELS</th><td>1 = break every display into its own desktop window instead of the composited cockpit.</td></tr>
+        </tbody>
+      </table>
+    </div>
+
+    <div class="callout">
+      <p>
+        <strong>Ask for too much and the layout wins.</strong> The surround
+        bands grow to fit whatever you ask for, and a map in a bottom corner
+        pushes the lower MFD beside it inboard — nothing overlaps and nothing
+        comes out stretched.
+      </p>
+    </div>
+
+    <h3>Filling the screen</h3>
+    <p class="sub" style="margin-bottom:0">
+      The cockpit fits itself to whatever window it gets at one uniform scale,
+      so drag it to any shape and nothing distorts — a window that isn't the
+      cockpit's shape just gets black bars. Launch with <code>-fit</code> for
+      borderless over the whole monitor.
+    </p>
+  </section>
+
+  <section>
+    <h2>Rebinding</h2>
+    <p class="sub">
+      <code>content\bindings.txt</code> is written with the full default layout
+      on first run. Change a line, restart. Delete the file to restore these
+      defaults — worth doing after an update, since an existing file is never
+      overwritten.
+    </p>
+
+    <div class="two-col">
+      <pre><i># grammar</i>
+<b>key</b> &lt;KEY&gt;   <b>button</b> &lt;addr&gt;
+<b>key</b> &lt;KEY&gt;   <b>axis</b> &lt;chan&gt; deflect &lt;+|-&gt; &lt;rate&gt;
+<b>key</b> &lt;KEY&gt;   <b>axis</b> &lt;chan&gt; slew    &lt;+|-&gt; &lt;rate&gt;
+<b>key</b> &lt;KEY&gt;   <b>axis</b> &lt;chan&gt; set &lt;value&gt;
+<b>pad</b> &lt;BTN&gt;   <b>button</b> &lt;addr&gt;
+<b>padaxis</b> &lt;SRC&gt; <b>axis</b> &lt;chan&gt; [invert] [slew &lt;r&gt;]
+
+<i># deflect springs back, slew walks and sticks
+# (that is the throttle lever), set jumps</i></pre>
+
+      <pre><i># examples</i>
+<b>key</b> G <b>button</b> 0x0E      <i># regroup weapon 1 on G</i>
+<b>key</b> B <b>button</b> 0x41      <i># look behind on B</i>
+
+<i># put driving back on WASD if you prefer</i>
+<b>key</b> W <b>axis</b> Throttle slew + 0.7
+<b>key</b> S <b>axis</b> Throttle slew - 0.7
+<b>key</b> A <b>axis</b> LeftPedal  deflect + 2.5
+<b>key</b> D <b>axis</b> RightPedal deflect + 2.5</pre>
+    </div>
+
+    <div class="tbl-scroll" style="margin-top:22px">
+      <table>
+        <caption>Reference — channels, addresses, key names</caption>
+        <tbody>
+          <tr><th>Channels</th><td class="mono">Throttle · LeftPedal · RightPedal · JoystickX · JoystickY · Turn</td></tr>
+          <tr><th>Buttons</th><td class="addr">0x00 – 0x47</td></tr>
+          <tr><th>No function</th><td class="addr">0x16 0x17 0x19 0x1E 0x1F 0x38–0x3E <span style="color:var(--ink-quiet);font-family:var(--sans)">— the pod never wired these</span></td></tr>
+          <tr><th>Key names</th><td class="mono">A–Z · 0–9 · F1–F12 · NUMPAD0–9 · NUMPADDOT · UP DOWN LEFT RIGHT · SPACE · LSHIFT LCTRL LALT · MINUS EQUALS COMMA PERIOD LBRACKET RBRACKET BACKTICK</td></tr>
+          <tr><th>Pad</th><td class="mono">A B X Y · DPAD_UP/DOWN/LEFT/RIGHT · LB RB · BACK START · LS RS</td></tr>
+          <tr><th>Pad axes</th><td class="mono">LX LY RX RY LT RT</td></tr>
+        </tbody>
+      </table>
+    </div>
+
+    <div class="callout">
+      <p>
+        <strong>A BOUND KEY LOSES ITS 1995 MEANING.</strong> Binding a key
+        removes it from the authentic typed-hotkey channel so it cannot
+        double-dispatch — and this board binds nearly everything, so the old
+        typed hotkeys (5 = Quad page, z = Eng1, t/y/u/i/o = pilot select, +/−
+        = target zoom) are given up by design. Unbind a key to get its 1995
+        meaning back. Same rule for the built-ins: <b>`</b> is the view
+        toggle, and J/K/L cycle MFD preset pages only while unbound.
+      </p>
+    </div>
+  </section>
+
+  <footer>
+    <span>BattleTech 4.11</span>
+    <span>gitea.mysticmachines.com/VWE/BT411</span>
+    <span>docs/CONTROLS.md · CONTROLS.txt ships with the game</span>
+    <span>RGB keyboards mirror the pod lamps · BT_KEYLIGHT=0 to disable</span>
+  </footer>
+</div>
+
+<script>
+  (function () {
+    "use strict";
+
+    var F = "flight", M = "mfd", C = "column", X = null, SP = "spacer";
+
+    var rows = [
+      [
+        ["Esc", "", X], [SP],
+        ["F1", "MAP+", C, "0x10", "Map zoom in"],
+        ["F2", "MAP-", C, "0x11", "Map zoom out"],
+        ["F3", "IR", C, "0x12", "Thermal sight"],
+        ["F4", "CROUCH", C, "0x13", "Duck — present a smaller target"], [SP],
+        ["F5", "LAMP", C, "0x14", "Searchlight"],
+        ["F6", "DISP", C, "0x15", "Cycle the secondary display"],
+        ["F7", "MODE", C, "0x18", "Cycle control mode: BAS / MID / ADV"],
+        ["F8", "", C, "0x19", "Column slot the pod never wired"], [SP],
+        ["F9", "GEN A", C, "0x1A", "Generator A on/off"],
+        ["F10", "GEN B", C, "0x1B", "Generator B on/off"],
+        ["F11", "GEN C", C, "0x1C", "Generator C on/off"],
+        ["F12", "GEN D", C, "0x1D", "Generator D on/off"]
+      ],
+      [
+        ["`", "VIEW", F, "", "Cockpit / chase camera toggle"],
+        ["1", "VALVE", M, "0x2F", "Condenser 1 valve — detents 1-5-50-CLOSED"],
+        ["2", "VALVE", M, "0x2E", "Condenser 2 valve"],
+        ["3", "VALVE", M, "0x2D", "Condenser 3 valve"],
+        ["4", "FLUSH", M, "0x2C", "Coolant flush — HOLD to dump the reservoir"],
+        ["5", "", M, "0x27", "Engineering · top 1"], ["6", "", M, "0x26", "Engineering · top 2"],
+        ["7", "", M, "0x25", "Engineering · top 3"], ["8", "", M, "0x24", "Engineering · top 4"],
+        ["9", "", M, "0x37", "Comm / hotbox · top 1"], ["0", "", M, "0x36", "Comm / hotbox · top 2"],
+        ["-", "", M, "0x35", "Comm / hotbox · top 3"], ["=", "", M, "0x34", "Comm / hotbox · top 4"],
+        ["Bksp", "", X, "", "", "w2"]
+      ],
+      [
+        ["Tab", "", X, "", "", "w15"],
+        ["Q", "VALVE", M, "0x2B", "Condenser 4 valve"], ["W", "VALVE", M, "0x2A", "Condenser 5 valve"],
+        ["E", "VALVE", M, "0x29", "Condenser 6 valve"], ["R", "BALANCE", M, "0x28", "Balance every coolant valve"],
+        ["T", "", M, "0x23", "Engineering · lower 1"], ["Y", "", M, "0x22", "Engineering · lower 2"],
+        ["U", "", M, "0x21", "Engineering · lower 3"], ["I", "", M, "0x20", "Engineering · lower 4"],
+        ["O", "", M, "0x33", "Comm / hotbox · lower 1"], ["P", "", M, "0x32", "Comm / hotbox · lower 2"],
+        ["[", "", M, "0x31", "Comm / hotbox · lower 3"], ["]", "", M, "0x30", "Comm / hotbox · lower 4"],
+        ["\\", "", X, "", "", "w15"]
+      ],
+      [
+        ["Caps", "", X, "", "", "w175"],
+        ["A", "", M, "0x0F", "Left Weapons · top 1"], ["S", "", M, "0x0E", "Left Weapons · top 2"],
+        ["D", "", M, "0x0D", "Left Weapons · top 3"], ["F", "", M, "0x0C", "Left Weapons · top 4"],
+        ["G", "GAP", X, "", "Panel gap between the lower clusters — free"],
+        ["H", "", M, "0x07", "Right Weapons · top 1"], ["J", "", M, "0x06", "Right Weapons · top 2"],
+        ["K", "", M, "0x05", "Right Weapons · top 3"], ["L", "", M, "0x04", "Right Weapons · top 4"],
+        [";", "", X], ["'", "", X], ["Enter", "", X, "", "", "w225"]
+      ],
+      [
+        ["Shift", "THR +", F, "axis", "Throttle up — walks the lever, position holds", "w225"],
+        ["Z", "", M, "0x0B", "Left Weapons · lower 1"], ["X", "", M, "0x0A", "Left Weapons · lower 2"],
+        ["C", "", M, "0x09", "Left Weapons · lower 3"], ["V", "", M, "0x08", "Left Weapons · lower 4"],
+        ["B", "GAP", X, "", "Panel gap between the lower clusters — free"],
+        ["N", "", M, "0x03", "Right Weapons · lower 1"], ["M", "", M, "0x02", "Right Weapons · lower 2"],
+        [",", "", M, "0x01", "Right Weapons · lower 3"], [".", "", M, "0x00", "Right Weapons · lower 4"],
+        ["/", "", X], ["Shift", "THR +", F, "axis", "Throttle up", "w275"]
+      ],
+      [
+        ["Ctrl", "THR −", F, "axis", "Throttle down — walks the lever, position holds", "w125"],
+        ["Win", "", X, "", "", "w125"],
+        ["Alt", "REV", F, "0x3F", "Reverse thrust — the throttle handle's button", "w125"],
+        ["Space", "TRIGGER", F, "0x40", "Main trigger", "w6"],
+        ["Alt", "REV", F, "0x3F", "Reverse thrust", "w125"],
+        ["Fn", "", X, "", "", "w125"],
+        ["Menu", "", X, "", "", "w125"],
+        ["Ctrl", "THR −", F, "axis", "Throttle down", "w125"]
+      ]
+    ];
+
+    var navRows = [
+      [["Ins", "", X], ["Home", "", X], ["PgUp", "", X]],
+      [["Del", "", X], ["End", "", X], ["PgDn", "", X]],
+      "grow",
+      [["↑", "CENTRE", F, "0x42", "Torso centre"]],
+      [["←", "LOOK", F, "0x44", "Look left"], ["↓", "BEHIND", F, "0x41", "Look behind"], ["→", "LOOK", F, "0x43", "Look right"]]
+    ];
+
+    var padKeys = [
+      ["Num", "", X], ["/", "", X], ["*", "", X], ["−", "", X],
+      ["7", "L PED", F, "axis", "Left pedal — turns in MID/ADV"],
+      ["8", "AIM UP", F, "axis", "Torso aim up"],
+      ["9", "R PED", F, "axis", "Right pedal — turns in MID/ADV"],
+      ["+", "", X, "", "", "tall"],
+      ["4", "TWIST", F, "axis", "Torso twist left"],
+      ["5", "ALL STOP", F, "axis", "Throttle to zero"],
+      ["6", "TWIST", F, "axis", "Torso twist right"],
+      ["1", "PINKY", F, "0x45", "Fire — pinky"],
+      ["2", "AIM DN", F, "axis", "Torso aim down"],
+      ["3", "MIDDLE", F, "0x46", "Fire — middle thumb"],
+      ["Ent", "", X, "", "", "tall"],
+      ["0", "TRIGGER", F, "0x40", "Main trigger", "pad0"],
+      [".", "UPPER", F, "0x47", "Fire — upper thumb"]
+    ];
+
+    var kbEl = document.getElementById("kb");
+    var rdKb = document.getElementById("rd-kb");
+    var rdPad = document.getElementById("rd-pad");
+
+    function makeKey(def) {
+      if (def[0] === SP) {
+        var gap = document.createElement("div");
+        gap.className = "spacer";
+        gap.setAttribute("aria-hidden", "true");
+        return gap;
+      }
+      var cap = def[0], sub = def[1], role = def[2], addr = def[3] || "", desc = def[4] || "", w = def[5];
+      var el = document.createElement(role ? "button" : "div");
+      el.className = "key" + (w ? " " + w : "");
+      if (role) {
+        el.type = "button";
+        el.setAttribute("data-role", role);
+        el.setAttribute("data-name", cap);
+        el.setAttribute("data-addr", addr);
+        el.setAttribute("data-desc", desc);
+      } else if (desc) {
+        el.setAttribute("data-name", cap);
+        el.setAttribute("data-desc", desc);
+        el.title = desc;
+      }
+      var main = document.createElement("span");
+      main.textContent = cap;
+      el.appendChild(main);
+      if (sub) {
+        var s = document.createElement("span");
+        s.className = "k-sub";
+        s.textContent = sub;
+        el.appendChild(s);
+      }
+      return el;
+    }
+
+    var bank = document.createElement("div");
+    bank.className = "kb-bank";
+
+    var main = document.createElement("div");
+    main.className = "kb-main";
+    rows.forEach(function (row, index) {
+      var r = document.createElement("div");
+      r.className = "kb-row" + (index === 1 || index === 3 ? " zone" : "");
+      row.forEach(function (def) { r.appendChild(makeKey(def)); });
+      main.appendChild(r);
+    });
+    bank.appendChild(main);
+
+    var nav = document.createElement("div");
+    nav.className = "kb-side";
+    var navWrap = document.createElement("div");
+    navWrap.className = "kb-nav";
+    navRows.forEach(function (row) {
+      if (row === "grow") {
+        var g = document.createElement("div");
+        g.className = "grow";
+        navWrap.appendChild(g);
+        return;
+      }
+      var r = document.createElement("div");
+      r.className = "kb-row";
+      row.forEach(function (def) { r.appendChild(makeKey(def)); });
+      navWrap.appendChild(r);
+    });
+    nav.appendChild(navWrap);
+    bank.appendChild(nav);
+
+    var side = document.createElement("div");
+    side.className = "kb-side";
+    var pad = document.createElement("div");
+    pad.className = "kb-pad";
+    padKeys.forEach(function (def) { pad.appendChild(makeKey(def)); });
+    side.appendChild(pad);
+    bank.appendChild(side);
+
+    kbEl.appendChild(bank);
+
+    function show(readout, name, addr, desc) {
+      readout.setAttribute("data-idle", "false");
+      readout.innerHTML = "";
+      var n = document.createElement("span");
+      n.className = "rd-name";
+      n.textContent = name;
+      readout.appendChild(n);
+      if (addr) {
+        var a = document.createElement("span");
+        a.className = "rd-addr";
+        a.textContent = addr;
+        readout.appendChild(a);
+      }
+      if (desc) {
+        var d = document.createElement("span");
+        d.className = "rd-desc";
+        d.textContent = desc;
+        readout.appendChild(d);
+      }
+    }
+
+    function bind(root, readout, selector) {
+      if (!root) return;
+      var active = null;
+      function handle(e) {
+        var t = e.target.closest(selector);
+        if (!t || !t.getAttribute("data-name")) return;
+        if (active) active.classList.remove("is-active");
+        active = t;
+        t.classList.add("is-active");
+        show(readout, t.getAttribute("data-name"), t.getAttribute("data-addr"), t.getAttribute("data-desc"));
+      }
+      root.addEventListener("pointerover", handle);
+      root.addEventListener("focusin", handle);
+      root.addEventListener("click", handle);
+    }
+
+    var byCap = {};
+    Array.prototype.forEach.call(main.querySelectorAll(".key"), function (el) {
+      var cap = el.firstChild ? el.firstChild.textContent : "";
+      if (cap && !byCap[cap]) byCap[cap] = el;
+    });
+
+    var groups = [
+      { label: "Map left column", kind: "g-col", top: ["F1", "F6"] },
+      { label: "Map right column", kind: "g-col", top: ["F7", "F12"] },
+      { label: "Heat / coolant", kind: "g-mfd", top: ["1", "4"], bottom: ["Q", "R"] },
+      { label: "Engineering", kind: "g-mfd", top: ["5", "8"], bottom: ["T", "I"] },
+      { label: "Comm / hotbox", kind: "g-mfd", top: ["9", "="], bottom: ["O", "]"] },
+      { label: "Left Weapons", kind: "g-mfd", top: ["A", "F"], bottom: ["Z", "V"] },
+      { label: "Right Weapons", kind: "g-mfd", top: ["H", "L"], bottom: ["N", "."] }
+    ];
+
+    function drawGroups() {
+      var old = kbEl.querySelector(".kb-groups");
+      if (old) old.remove();
+
+      var host = document.createElement("div");
+      host.className = "kb-groups";
+      var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+      host.appendChild(svg);
+
+      var base = kbEl.getBoundingClientRect();
+      var padPx = 3;
+
+      function box(a, b) {
+        if (!a || !b) return null;
+        var ra = a.getBoundingClientRect(), rb = b.getBoundingClientRect();
+        return {
+          l: ra.left - base.left - padPx,
+          r: rb.right - base.left + padPx,
+          t: ra.top - base.top - padPx,
+          b: ra.bottom - base.top + padPx
+        };
+      }
+
+      groups.forEach(function (g) {
+        var top = box(byCap[g.top[0]], byCap[g.top[1]]);
+        if (!top) return;
+        var bot = g.bottom ? box(byCap[g.bottom[0]], byCap[g.bottom[1]]) : null;
+        var d;
+        if (bot) {
+          d = "M" + top.l + " " + top.t + "H" + top.r + "V" + bot.t +
+              "H" + bot.r + "V" + bot.b + "H" + bot.l + "V" + top.b + "H" + top.l + "Z";
+        } else {
+          d = "M" + top.l + " " + top.t + "H" + top.r + "V" + top.b + "H" + top.l + "Z";
+        }
+        var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
+        path.setAttribute("d", d);
+        path.setAttribute("class", g.kind);
+        svg.appendChild(path);
+
+        var label = document.createElement("span");
+        label.className = "g-label " + g.kind;
+        label.textContent = g.label;
+        label.style.left = top.l + "px";
+        label.style.top = top.t + "px";
+        host.appendChild(label);
+      });
+
+      kbEl.appendChild(host);
+    }
+
+    drawGroups();
+    window.addEventListener("resize", drawGroups);
+    if (document.fonts && document.fonts.ready) document.fonts.ready.then(drawGroups);
+
+    bind(kbEl, rdKb, ".key");
+    bind(document.querySelector("svg.pad"), rdPad, ".ctl");
+  })();
+</script>
diff --git a/docs/CONTROLS.md b/docs/CONTROLS.md
index 449f05b..136a058 100644
--- a/docs/CONTROLS.md
+++ b/docs/CONTROLS.md
@@ -9,84 +9,69 @@ Cockpit layout and display sizes live in `content\environ.ini` (likewise self-do
 
 ---
 
-## Keyboard
+## Keyboard — the keyboard IS the button board
+
+The letter and number rows are the MFD button banks, laid out where they sit on the panel.
+Flight lives on the number pad so the board stays free.
+
+### Flight — the number pad
 
 | Key | Action | | Key | Action |
 |---|---|---|---|---|
-| W/S or ↑/↓ | throttle lever | | X or NumPad5 | all stop |
-| A/D or ←/→ | turn pedals | | Alt | reverse thrust |
-| Q/E | torso twist | | Shift | throttle up |
-| R/F | torso aim | | NumPad 8/2/4/6 | torso aim/twist |
-| 1 or Space | main trigger | | 2 | middle thumb |
-| 3 or Ctrl | upper thumb (missiles) | | 4 | pinky |
-| V | external camera | | B | look behind |
-| M | control mode (BAS/MID/ADV) | | N | schematic |
-| J/K/L | MFD preset pages | | H (hold) | coolant flush |
-| C | cycle a coolant valve | | G (hold) + fire | regroup a weapon |
-| − / = | volume down/up | | F5–F9 | generator select / mode |
+| NumPad 8 / 2 | torso aim up / down | | NumPad 7 / 9 | left / right pedal |
+| NumPad 4 / 6 | torso twist left / right | | NumPad 5 | all stop |
+| NumPad 0, Space | main trigger | | NumPad 1 / 3 / . | pinky / middle / upper |
+| Shift / Ctrl | throttle up / down (the lever sticks) | | Alt | reverse thrust |
+| ← ↓ → | look left / behind / right | | ↑ | torso centre |
+| ` | cockpit / chase camera | | − / = *(see below)* | — |
 
-**You spawn in BASIC control mode.** In BASIC the stick steers and the pedal keys do nothing.
-Press **M** once for MID, where the pedals steer and the stick twists the torso — the full
-experience. If "turning doesn't work", this is why.
+**You spawn in BASIC control mode.** In BASIC the stick steers and the pedals do nothing.
+Press **F7** once for MID, where the pedals steer and the stick twists the torso — the full
+pod experience. If "turning doesn't work", this is why.
 
-Numpad aim keys need **NumLock ON**.
+### The panel — letter and number rows
 
-> Control mode (BAS/MID/ADV, the M key) is *not* the menu's experience setting
-> (novice/standard/veteran/expert). That one changes the simulation — heat, damage — not your
-> controls.
+```
+  UPPER DISPLAYS   Heat / coolant      Engineering        Comm / hotbox
+   top row       1   2   3   4  |  5   6   7   8  |  9   0   -   =
+   bottom row    Q   W   E   R  |  T   Y   U   I  |  O   P   [   ]
+                  0x2F..0x2C        0x27..0x24        0x37..0x34
+                  0x2B..0x28        0x23..0x20        0x33..0x30
 
----
+  LOWER DISPLAYS  Left Weapons                        Right Weapons
+   top row       A   S   D   F      (G unbound)     H   J   K   L
+   bottom row    Z   X   C   V      (B unbound)     N   M   ,   .
+                  0x0F..0x0C                          0x07..0x04
+                  0x0B..0x08                          0x03..0x00
 
-## The cockpit panel — all 72 pod buttons
+  COLUMNS beside the map
+   left    F1 zoom+  F2 zoom-  F3 thermal  F4 CROUCH  F5 lamp  F6 display
+   right   F7 control mode  F8 -  F9..F12 Generators A-D
+```
 
-Each display carries the bank of buttons mounted around it in the real cabinet. Click anywhere on
-a display: the buttons reach *under* the glass, so the whole screen is a press target and only the
-lit strip at the edge shows.
+G and B are deliberately unbound: they are the physical gap between the lower clusters.
 
-### Heat / coolant display — the loop controls
-| Button | Action |
-|---|---|
-| `0x28` | **Balance coolant** — equalise every condenser valve |
-| `0x29`–`0x2F` | the six **condenser valves** (Cond 6…1) and the **coolant flush** (`0x2C`, hold) |
+**1 2 3 Q W E are the coolant valves** and the detents run 1 - 5 - 50 (max) - **0 (CLOSED)** -
+back to 1. One press past max shuts that loop off and everything on it overheats; boosting one
+loop starves the others, since the supply is shared. **4** is the flush (hold it) and **R**
+re-balances every valve.
 
-⚠ The valve detents run 1 - 5 - 50 (max) - **0 (CLOSED)** - back to 1. One press past max shuts
-that loop off and everything on it overheats. Boosting one loop starves the others — the supply
-is shared.
+The weapon and engineering displays are **page-gated**: on the Quad page the outer buttons jump
+to a populated Eng page; on an Eng page they drive generator select A-D, bus mode, coolant
+toggle, trigger configure, and **eject** (tap to clear a jam, hold ~3 s to jettison a burning
+ammo bay before it detonates).
 
-### Weapon and engineering displays
-| Bank | Display |
-|---|---|
-| `0x00`–`0x07` | Right Weapons (lower right) |
-| `0x08`–`0x0F` | Left Weapons (lower left) |
-| `0x20`–`0x27` | Engineering (upper centre) |
-| `0x30`–`0x37` | Comm / target hotbox (upper right) — pilot select |
+### What a bound key gives up
 
-Each display's buttons are **page-gated**: on the Quad page the outer buttons jump straight to a
-populated Eng page; on an Eng page they drive the subsystem shown — generator select A–D, bus
-mode, coolant toggle, trigger configure, and **eject** (tap to clear a jam, hold ~3 s to jettison
-a burning ammo bay).
+Binding a key removes it from the authentic 1995 typed-hotkey channel so it cannot
+double-dispatch. This board binds nearly everything, so those hotkeys are given up by design:
+`5` = MFD1 Quad page, `z` = MFD3 Eng1, `t/y/u/i/o` = pilot select, `+`/`-` = target zoom.
+Unbind a key in `bindings.txt` to get its 1995 meaning back. Same rule for the built-ins:
+**`** is the view toggle (V is a board button now), and J/K/L cycle the MFD preset pages only
+while unbound.
 
-### Map / radar — the side columns
-| Button | Action | | Button | Action |
-|---|---|---|---|---|
-| `0x10` | map zoom in | | `0x18` | cycle control mode (BAS/MID/ADV) |
-| `0x11` | map zoom out | | `0x1A`–`0x1D` | generator A–D on/off |
-| `0x12` | thermal / IR sight | | `0x16`,`0x17`,`0x19`,`0x1E`,`0x1F` | no function (as shipped in 1995) |
-| `0x13` | **crouch** | | | |
-| `0x14` | searchlight | | | |
-| `0x15` | cycle display mode | | | |
-
-### Flight controls
-| Button | Action |
-|---|---|
-| `0x3D` | panic / config lamp |
-| `0x3F` | throttle head — reverse thrust |
-| `0x40` | main trigger |
-| `0x41`–`0x44` | hat: behind / torso centre / right / left |
-| `0x45`–`0x47` | pinky, middle thumb, upper thumb — the mappable fire buttons |
-| `0x38`–`0x3C`, `0x3E` | intercom and door lines — no function on a desktop |
-
----
+Addresses with no function at all — the pod never wired them — are `0x16` `0x17` `0x19`
+`0x1E` `0x1F` and `0x38`-`0x3E`.
 
 ## Controllers
 
diff --git a/docs/GLASS_COCKPIT.md b/docs/GLASS_COCKPIT.md
index 608f496..520b6dd 100644
--- a/docs/GLASS_COCKPIT.md
+++ b/docs/GLASS_COCKPIT.md
@@ -682,3 +682,53 @@ each bank surrounds, with the coolant-valve detent warning and the eject/jam pro
 flattens it to ASCII as `CONTROLS.txt` at the zip root (the README's own flattening idiom).
 `players/README.txt` gained pointers to it, to environ.ini, to -fit, and to the RGB mirror.
 Verified: `mkdist.py` writes a 5149-byte pure-ASCII CONTROLS.txt into the zip.
+
+## 2026-07-26 (last) — the keyboard becomes the button board (RP412 bindings port)
+
+User decision: port RP412's bindings design and make it the DEFAULT (not an opt-in alternate),
+plus a BT-flavoured interactive controls page.
+
+**The layout.** Letter and number rows = the MFD banks laid out where they sit on the panel;
+flight to the numpad so the board stays free; F1-F12 = the map's two columns; G and B unbound as
+the physical gap between the lower clusters.  Expressed in BT's OWN grammar (slew/deflect/set,
+the Turn channel) -- the bindings.txt format is a compatibility surface and was not touched.
+
+Coverage audit: **61 of 72 addresses on the keyboard**, and the 11 absent ones are exactly the
+addresses with no authored function (0x16/0x17/0x1E/0x1F, 0x38-0x3E).  0x19 is bound (F8) purely
+for column completeness.  All 72 stay clickable on the panel.
+
+The scheme lands on BT's addresses unreasonably well: 1-4 + QWER are the whole coolant system
+(Condensers 1-6, flush, balance), F6/F7 the display and control-mode cycles, F9-F12 Generators
+A-D, F4 the crouch button reconstructed earlier today.
+
+**The deliberate cost.** A bound key is removed from the 1995 typed-hotkey channel, and this board
+binds nearly everything -- so 5 (Quad), z (Eng1), t/y/u/i/o (pilot select) and +/- (zoom) go.
+Documented in the file header, the markdown and the page.
+
+**New rule: a bindings row WINS over a built-in convenience key.**  `PadRIO::KeyHasBinding` --
+V and J/K/L are board buttons now, so the view-toggle and preset-cycle polls stand down for them
+(they poll GetAsyncKeyState directly and would otherwise have fired BOTH).  View toggle lives on
+BACKTICK alone.
+
+**CONTROLS.MAP mirrored** so glass/pod/dev still feel identical (the 2026-07-21 settlement):
+90 bindings, 0 parse complaints under BT_INPUT_LOG.
+
+### Hat labels: 0x42 is TORSO CENTER [T1]
+INPUT_PATH_AUDIT flagged 0x41-0x44 as mislabelled.  Settled from the streamed mapping
+(BT_CTRLMAP_LOG): `elem 66 -> subsys 17 attrID 14` = the TORSO subsystem, while 0x44/0x43/0x41 go
+to the mapper's look trio (attrID 10/11/12 = LookLeft/LookRight/LookBehind).  The .RES itself has
+no "TORSO CENTER" string -- its names are LookBehind/Down/Forward/Left/Right -- so the audit's
+wording was loose but its substance right.  Swept L4GLASSWIN / L4PADPANEL / L4VB16.
+
+### The controls page
+`docs/CONTROLS.html` -- interactive keyboard (hover any key for its address and meaning, with the
+MFD clusters outlined), pad diagram, panel map, the under-glass press-target figure, the radar
+placement thumbnails, and the environ.ini table.  Modelled on RP412's page, rewritten for BT's
+addresses and hazards (the coolant-valve detent warning, jams and ammo-bay fires, BASIC control
+mode).  mkdist wraps the fragment in a doctype/charset shell and ships it beside CONTROLS.txt.
+
+### Verified
+- bindings.txt regenerates and loads: 74 keys, 0 parse errors; CONTROLS.MAP 90 bindings, 0 errors.
+- 72/72 panel addresses still dispatch on click.
+- surround / exploded / dock / pod / dev: boot + simulate, zero faults.
+- Dist carries CONTROLS.txt + a 46 KB CONTROLS.html with doctype and charset.
diff --git a/engine/MUNGA_L4/L4GLASSWIN.cpp b/engine/MUNGA_L4/L4GLASSWIN.cpp
index db2bfca..065cdad 100644
--- a/engine/MUNGA_L4/L4GLASSWIN.cpp
+++ b/engine/MUNGA_L4/L4GLASSWIN.cpp
@@ -111,8 +111,8 @@ struct NamedButton { int address; const char *name; };
 static const NamedButton namedButtons[] =
 {
 	{ 0x3D, "Panic" }, { 0x3F, "Throttle" },
-	{ 0x40, "Main"  }, { 0x41, "Hat Bk" }, { 0x42, "Hat Up" },
-	{ 0x43, "Hat R" }, { 0x44, "Hat L"  }, { 0x45, "Pinky"  },
+	{ 0x40, "Main"  }, { 0x41, "Look Bk" }, { 0x42, "Torso Ctr" },
+	{ 0x43, "Look R" }, { 0x44, "Look L" }, { 0x45, "Pinky"  },
 	{ 0x46, "Middle"}, { 0x47, "Upper"  },
 };
 enum { NamedButtonCount = sizeof(namedButtons) / sizeof(namedButtons[0]) };
diff --git a/engine/MUNGA_L4/L4PADBINDINGS.cpp b/engine/MUNGA_L4/L4PADBINDINGS.cpp
index 6827a15..08cf9ce 100644
--- a/engine/MUNGA_L4/L4PADBINDINGS.cpp
+++ b/engine/MUNGA_L4/L4PADBINDINGS.cpp
@@ -161,20 +161,35 @@ static int
 // the 12 secondary-panel buttons, 0x00-0x0F the lower aux banks, 0x20-0x37
 // the upper aux banks.  Keypad unit 0 = the pilot's MFD keypad.
 //
-// KEYBOARD RECONCILIATION (2026-07-20, input-coverage audit): the keyboard
-// carries the ~20 core gameplay actions on the SAME keys the desktop
-// CONTROLS.MAP uses (W/S/A/D drive, Q/E twist, R/F elevation, X all-stop,
-// 1-4/Space/Ctrl fire, M/N mode/display cycle, H flush, C valve, B look
-// behind, G configure, V view); FULL pod-address coverage lives on the
-// on-screen panel (every one of the 72 buttons is one click away,
-// right-click = hold latch).  Keys DISPLACED from the previous default
-// (each still reachable by editing bindings.txt or on the panel):
-//   - number row 1-0/-/= -> secondary-panel 0x10-0x1D   (1-4 now fire)
-//   - QWERTY row Q..P    -> pilot MFD keypad unit 0     (dropped)
-//   - arrow keys         -> hat looks 0x41-0x44          (arrows now drive)
-//   - V/C/B              -> fire 0x47/0x46/0x45          (now view/valve/behind)
-//   - LCTRL              -> throttle slew down            (now fire 0x47)
-// The XInput pad section is unchanged.
+// THE KEYBOARD IS THE BUTTON BOARD (2026-07-26, ported from RP412).
+//
+// Every one of the pod's 72 buttons is reachable from the keyboard, laid out
+// WHERE IT SITS ON THE PANEL: the number row is the upper MFDs' top row and
+// QWERTY the row beneath it, running left to right across Heat / Engineering /
+// Comm; the home row and the row below it do the same for the two lower
+// weapon MFDs; F1-F12 are the two columns flanking the map.  G and B stay
+// unbound on purpose -- they are the physical gap between the lower clusters.
+// Flight moves to the NUMPAD (which already carried it as an alternative), so
+// the letter field is free for the board.
+//
+// It lands on BT's own addresses better than it has any right to: 1-4 and
+// QWER are the Heat display's two rows, which is the entire coolant system
+// (Condensers 1-6, the flush and the balance button); F6/F7 are the display
+// and control-mode cycles; F9-F12 are Generators A-D.
+//
+// WHAT THIS COSTS (the deliberate trade).  A bound key is REMOVED from the
+// authentic 1995 typed-hotkey channel to stop double-dispatch, and this board
+// binds nearly the whole field -- so the typed hotkeys go: 5 = MFD1 Quad page,
+// z = MFD3 Eng1, t/y/u/i/o = pilot select 3-7, +/- = target zoom.  They remain
+// reachable on the panel and by editing this file.  The built-in convenience
+// keys yield the same way: V and J/K/L are board buttons now, so PadRIO's
+// view-toggle and preset-cycle polls stand down for them (KeyHasBinding) and
+// the view toggle lives on BACKTICK alone.
+//
+// PREVIOUS DEFAULT (2026-07-20 - 2026-07-26) was the ~20-action CONTROLS.MAP
+// mirror: W/S/A/D drive, Q/E twist, R/F elevation, 1-4/Space/Ctrl fire,
+// M/N/H/C/G systems.  An existing bindings.txt is never overwritten, so a
+// player only meets this board by deleting theirs.
 //###########################################################################
 
 static const char *defaultProfileText =
@@ -192,66 +207,121 @@ static const char *defaultProfileText =
 "#    made for mapping a gamepad stick to turning)\n"
 "# Pad buttons: A B X Y LB RB BACK START LS RS DPAD_*  Axes: LX LY RX RY LT RT\n"
 "#\n"
-"# LAYOUT: the keyboard hosts the core gameplay actions on the desktop\n"
-"# CONTROLS.MAP keys; the on-screen cockpit panel covers EVERY pod button\n"
-"# by click (right-click = press-and-hold latch).  A key bound here is\n"
-"# REMOVED from the authentic 1995 typed-hotkey channel (no double\n"
-"# dispatch); unbound keys keep their 1995 meaning -- 5 = MFD1 Quad page,\n"
-"# z = MFD3 Eng1 page, t/y/u/i/o = pilot select 3-7, + / - = target zoom.\n"
-"# Hardcoded (not rebindable here): ` or V = view toggle, J/K/L = cycle\n"
-"# the Mfd1/Mfd2/Mfd3 preset page.\n"
+"# LAYOUT: THE KEYBOARD IS THE POD'S BUTTON BOARD.  The letter and number\n"
+"# rows are the MFD button banks laid out where they sit on the panel, and\n"
+"# flight lives on the numpad so the board stays free.  Every one of the 72\n"
+"# pod buttons is also one mouse click away on the cockpit itself\n"
+"# (right-click = press-and-hold latch).\n"
+"#\n"
+"# A key bound here is REMOVED from the authentic 1995 typed-hotkey channel\n"
+"# so it cannot double-dispatch -- and this board binds nearly everything,\n"
+"# so those hotkeys (5 = MFD1 Quad, z = MFD3 Eng1, t/y/u/i/o = pilot select,\n"
+"# +/- = target zoom) are given up by design.  Unbind a key here to get its\n"
+"# 1995 meaning back.  Same rule for the built-ins: BACKTICK = view toggle\n"
+"# (V is a board button now), and J/K/L cycle the Mfd1/2/3 preset pages ONLY\n"
+"# while unbound.\n"
 "\n"
-"# --- drive: W/S sweep the throttle lever (sticks; X = all-stop detent),\n"
-"# --- A/D spring the turn pedals, Q/E twist the torso, R/F aim it up/down\n"
-"key W axis Throttle slew + 0.7\n"
-"key S axis Throttle slew - 0.7\n"
-"key UP axis Throttle slew + 0.7\n"
-"key DOWN axis Throttle slew - 0.7\n"
-"key A axis LeftPedal deflect + 2.5\n"
-"key D axis RightPedal deflect + 2.5\n"
-"key LEFT axis LeftPedal deflect + 2.5\n"
-"key RIGHT axis RightPedal deflect + 2.5\n"
-"key Q axis JoystickX deflect - 2.5\n"
-"key E axis JoystickX deflect + 2.5\n"
-"key R axis JoystickY deflect + 2.5\n"
-"key F axis JoystickY deflect - 2.5\n"
-"key X axis Throttle set 0\n"
-"\n"
-"# --- numpad flight cluster (alternative stick/pedals/lever) ---\n"
+"# --- flight: the numpad ------------------------------------------------\n"
+"# 8/2/4/6 stick, 7/9 pedals, 5 all-stop detent, 0 main trigger.\n"
+"# SHIFT/CTRL walk the throttle lever and it STICKS where you leave it;\n"
+"# ALT is the throttle handle's reverse-thrust button.\n"
 "key NUMPAD8 axis JoystickY deflect + 2.5\n"
 "key NUMPAD2 axis JoystickY deflect - 2.5\n"
 "key NUMPAD4 axis JoystickX deflect - 2.5\n"
 "key NUMPAD6 axis JoystickX deflect + 2.5\n"
 "key NUMPAD7 axis LeftPedal deflect + 2.5\n"
 "key NUMPAD9 axis RightPedal deflect + 2.5\n"
-"key LSHIFT axis Throttle slew + 0.7\n"
 "key NUMPAD5 axis Throttle set 0\n"
-"\n"
-"# --- fire groups (the joystick's four mappable buttons) + reverse + look\n"
-"key 1 button 0x40\n"
-"key SPACE button 0x40\n"
-"key 2 button 0x46\n"
-"key 3 button 0x47\n"
-"key LCTRL button 0x47\n"
-"key RCTRL button 0x47\n"
-"key 4 button 0x45\n"
+"key LSHIFT axis Throttle slew + 0.7\n"
+"key RSHIFT axis Throttle slew + 0.7\n"
+"key LCTRL axis Throttle slew - 0.7\n"
+"key RCTRL axis Throttle slew - 0.7\n"
 "key LALT button 0x3F\n"
-"key B button 0x41\n"
+"key RALT button 0x3F\n"
 "\n"
-"# --- systems (pod console buttons) ---\n"
-"key M button 0x18   # cycle control mode (BAS/MID/ADV)\n"
-"key N button 0x15   # cycle secondary schematic (Damage/Critical/Heat)\n"
-"key H button 0x2C   # HOLD = coolant flush (Reservoir InjectCoolant)\n"
-"key C button 0x2F   # cycle Condenser1 coolant valve (MoveValve)\n"
-"key G button 0x0E   # HOLD + fire key = regroup weapon 1 (Mfd1 Quad page)\n"
+"# --- the four mappable fire buttons ------------------------------------\n"
+"key SPACE   button 0x40   # main trigger\n"
+"key NUMPAD0 button 0x40   # main trigger\n"
+"key NUMPAD1 button 0x45   # pinky\n"
+"key NUMPAD3 button 0x46   # middle thumb\n"
+"key NUMPADDOT button 0x47 # upper thumb\n"
 "\n"
-"# --- Mfd2 bank on F5-F8 + F9 (page-gated: Quad page = select the Eng\n"
-"# --- pages, Eng page = generator A-D select / F9 gen-mode toggle) ---\n"
-"key F5 button 0x27\n"
-"key F6 button 0x26\n"
-"key F7 button 0x25\n"
-"key F8 button 0x24\n"
-"key F9 button 0x22\n"
+"# --- the hat: look around, on the arrows -------------------------------\n"
+"key UP    button 0x42   # torso CENTER (the shipped .RES name)\n"
+"key DOWN  button 0x41   # look behind\n"
+"key LEFT  button 0x44   # look left\n"
+"key RIGHT button 0x43   # look right\n"
+"\n"
+"# --- UPPER MFD banks: number row on top, QWERTY beneath ----------------\n"
+"# Heat / coolant (0x28-0x2F) -- 1-4 and QWER ARE the coolant system:\n"
+"#   1/2/3 Condenser 1-3 valve, 4 coolant FLUSH (hold), Q/W/E Condenser\n"
+"#   4-6 valve, R balance coolant.  The valve detents run 1-5-50-CLOSED.\n"
+"key 1 button 0x2F\n"
+"key 2 button 0x2E\n"
+"key 3 button 0x2D\n"
+"key 4 button 0x2C\n"
+"key Q button 0x2B\n"
+"key W button 0x2A\n"
+"key E button 0x29\n"
+"key R button 0x28\n"
+"# Engineering / Mfd2 (0x20-0x27) -- page-gated: Quad page picks an Eng\n"
+"# page, Eng page drives generator select A-D / bus mode / coolant.\n"
+"key 5 button 0x27\n"
+"key 6 button 0x26\n"
+"key 7 button 0x25\n"
+"key 8 button 0x24\n"
+"key T button 0x23\n"
+"key Y button 0x22\n"
+"key U button 0x21\n"
+"key I button 0x20\n"
+"# Comm / target hotbox (0x30-0x37) -- pilot select.\n"
+"key 9 button 0x37\n"
+"key 0 button 0x36\n"
+"key MINUS  button 0x35\n"
+"key EQUALS button 0x34\n"
+"key O button 0x33\n"
+"key P button 0x32\n"
+"key LBRACKET button 0x31\n"
+"key RBRACKET button 0x30\n"
+"\n"
+"# --- LOWER MFD banks: home row on top, the row below beneath -----------\n"
+"# G and B are the PANEL GAP between the two lower clusters -- left\n"
+"# unbound on purpose, so they keep their 1995 typed meaning.\n"
+"# Left Weapons / Mfd1 (0x08-0x0F).\n"
+"key A button 0x0F\n"
+"key S button 0x0E\n"
+"key D button 0x0D\n"
+"key F button 0x0C\n"
+"key Z button 0x0B\n"
+"key X button 0x0A\n"
+"key C button 0x09\n"
+"key V button 0x08\n"
+"# Right Weapons / Mfd3 (0x00-0x07).\n"
+"key H button 0x07\n"
+"key J button 0x06\n"
+"key K button 0x05\n"
+"key L button 0x04\n"
+"key N button 0x03\n"
+"key M button 0x02\n"
+"key COMMA  button 0x01\n"
+"key PERIOD button 0x00\n"
+"\n"
+"# --- the two columns flanking the map, top to bottom -------------------\n"
+"# Left (0x10-0x15): map zoom in/out, thermal IR, CROUCH, searchlight,\n"
+"# cycle secondary display.  Right (0x18-0x1D): cycle control mode\n"
+"# (BAS/MID/ADV), unused, Generator A-D on/off.\n"
+"key F1 button 0x10\n"
+"key F2 button 0x11\n"
+"key F3 button 0x12\n"
+"key F4 button 0x13\n"
+"key F5 button 0x14\n"
+"key F6 button 0x15\n"
+"key F7 button 0x18\n"
+"key F8 button 0x19\n"
+"key F9 button 0x1A\n"
+"key F10 button 0x1B\n"
+"key F11 button 0x1C\n"
+"key F12 button 0x1D\n"
 "\n"
 "# --- XInput pad ---\n"
 "# POD-FAITHFUL (default): stick X = torso twist, triggers = pedals.\n"
diff --git a/engine/MUNGA_L4/L4PADPANEL.cpp b/engine/MUNGA_L4/L4PADPANEL.cpp
index 1c4b7ac..468fbba 100644
--- a/engine/MUNGA_L4/L4PADPANEL.cpp
+++ b/engine/MUNGA_L4/L4PADPANEL.cpp
@@ -96,10 +96,10 @@ static const NamedButton namedButtons[] =
 	{ 0x3D, "Panic" },
 	{ 0x3F, "Throttle" },
 	{ 0x40, "Main" },
-	{ 0x41, "Hat Back" },
-	{ 0x42, "Hat Up" },
-	{ 0x43, "Hat Right" },
-	{ 0x44, "Hat Left" },
+	{ 0x41, "Look Back" },
+	{ 0x42, "Torso Ctr" },
+	{ 0x43, "Look Right" },
+	{ 0x44, "Look Left" },
 	{ 0x45, "Pinky" },
 	{ 0x46, "Middle" },
 	{ 0x47, "Upper" },
diff --git a/engine/MUNGA_L4/L4PADRIO.cpp b/engine/MUNGA_L4/L4PADRIO.cpp
index fabd8d6..13b2c00 100644
--- a/engine/MUNGA_L4/L4PADRIO.cpp
+++ b/engine/MUNGA_L4/L4PADRIO.cpp
@@ -69,6 +69,24 @@ int
 // takes no engine headers (l4keylight.h is scalars only), so it hands us
 // finished lines instead.
 //
+//
+// Does the loaded profile bind this virtual key?  The built-in convenience
+// keys (view toggle, preset cycles) consult this and stand down when the
+// player's bindings claim the key -- see the note at their poll sites.
+//
+Logical
+	PadRIO::KeyHasBinding(int virtualKey) const
+{
+	for (int i = 0; i < bindings.keyBindingCount; ++i)
+	{
+		if (bindings.keyBindings[i].virtualKey == virtualKey)
+		{
+			return True;
+		}
+	}
+	return False;
+}
+
 static void
 	KeyLightLog(const char *line)
 {
@@ -615,11 +633,17 @@ void
 	// message-path-free) and consumed by the game's view-toggle block via
 	// BTPadViewToggleEdge.
 	//
+	// ⚠ A bindings.txt ROW WINS over these built-in convenience keys
+	// (2026-07-26).  The default profile now hands the whole letter field to
+	// the MFD banks -- V is a Left Weapons button, J/K/L are Right Weapons --
+	// and polling them here as well would fire BOTH the button and the
+	// built-in, which is the double-dispatch the typed-channel suppression
+	// exists to prevent.  Bind the key, and the built-in stands down.
 	{
 		static int s_backtickWas = 0;
 		int backtick_held = focused &&
 			(((GetAsyncKeyState(VK_OEM_3) & 0x8000) != 0) ||
-			 ((GetAsyncKeyState('V') & 0x8000) != 0));
+			 (!KeyHasBinding('V') && (GetAsyncKeyState('V') & 0x8000) != 0));
 		if (backtick_held && !s_backtickWas)
 		{
 			extern int gBTPadViewToggleEdges;
@@ -638,7 +662,7 @@ void
 		static const int s_presetKey[3] = { 'J', 'K', 'L' };
 		for (int g = 0; g < 3; ++g)
 		{
-			int held = focused &&
+			int held = focused && !KeyHasBinding(s_presetKey[g]) &&
 				(GetAsyncKeyState(s_presetKey[g]) & 0x8000) != 0;
 			if (held && !s_presetWas[g])
 			{
diff --git a/engine/MUNGA_L4/L4PADRIO.h b/engine/MUNGA_L4/L4PADRIO.h
index b8b84b2..ea0fda4 100644
--- a/engine/MUNGA_L4/L4PADRIO.h
+++ b/engine/MUNGA_L4/L4PADRIO.h
@@ -148,6 +148,12 @@ protected:
 	Logical
 		keyLightActive;
 
+	// True when the loaded bindings claim this virtual key.  The built-in
+	// convenience keys (` / V view toggle, J/K/L preset cycles) yield to an
+	// explicit row rather than double-dispatching with it.
+	Logical
+		KeyHasBinding(int virtualKey) const;
+
 	//
 	// Typed-channel suppression tables (built from the loaded bindings +
 	// the hardcoded view/preset keys; see SuppressKey).
diff --git a/engine/MUNGA_L4/L4VB16.cpp b/engine/MUNGA_L4/L4VB16.cpp
index bc9e1e2..6dc1210 100644
--- a/engine/MUNGA_L4/L4VB16.cpp
+++ b/engine/MUNGA_L4/L4VB16.cpp
@@ -551,8 +551,8 @@ void BTCockpitComputeLayout(int canvasW, int canvasH, BTCockpitLayout *out)
 	{
 		static const char *thrLabel[8] = { 0, 0, 0, 0, 0, "Panic", 0, "Throttle" };  // 0x38..0x3F
 		static const int   thrInert[8] = { 1, 1, 1, 1, 1, 0, 1, 0 };
-		static const char *joyLabel[8] = { "Main", "Hat Bk", "Hat Up", "Hat R",
-										   "Hat L", "Pinky", "Middle", "Upper" };       // 0x40..0x47
+		static const char *joyLabel[8] = { "Main", "Look Bk", "Torso Ctr", "Look R",
+										   "Look L", "Pinky", "Middle", "Upper" };      // 0x40..0x47
 		// These cells carry TEXT, so the surround runs them bigger than the
 		// exploded window's 58x28 -- the caller owns the cell size
 		// (l4riobank.h), only the grid walk is shared.
diff --git a/tools/mkdist.py b/tools/mkdist.py
index 88902be..554f654 100644
--- a/tools/mkdist.py
+++ b/tools/mkdist.py
@@ -135,6 +135,19 @@ def main():
             controls = controls.encode("ascii", "replace").decode("ascii")
             z.writestr(name + "/CONTROLS.txt", controls)
             print("  controls reference -> CONTROLS.txt")
+
+        # The same map as a page, for anyone who would rather look at the
+        # diagrams than read them.  The source is a fragment (no doctype or
+        # <head>), so wrap it: without a doctype the browser drops into quirks
+        # mode, and without a charset the typography arrives as mojibake.
+        if os.path.exists("docs/CONTROLS.html"):
+            page = open("docs/CONTROLS.html", encoding="utf-8").read()
+            page = ('<!doctype html>\n<html lang="en">\n<head>\n'
+                    '<meta charset="utf-8">\n'
+                    '<meta name="viewport" content="width=device-width, initial-scale=1">\n'
+                    '</head>\n<body>\n' + page + '\n</body>\n</html>\n')
+            z.writestr(name + "/CONTROLS.html", page.encode("utf-8"))
+            print("  controls page      -> CONTROLS.html")
         for p in bats:                       # bats at the zip ROOT (next to
             arc = os.path.basename(p)        # content\ + build\, per the guard)
             z.write(p, name + "/" + arc)
