Let the candidate choose the editor indent width
The code editor always indented by four spaces (INDENT in web/editor.js), while 130 of the 151 JavaScript starters indent by two, so the first Tab or Enter inside a JavaScript starter function produced a body at a different width from the line above it, and a candidate who writes two-space or tab-indented code had no way to change it.
The lobby's editor options gain an Indentation select with 2 spaces, 4 spaces (the default) and Tab. The choice is saved in the browser and forwarded to the interview in a new editorIndent URL parameter. The interview nests by it until the buffer has an indented block of its own, then by the width that block uses (bufferIndent in web/indent-guides.js, which shares detectWidth's voting), so a four-space Python starter stays four-space under a two-space or tab choice instead of raising IndentationError or TabError; a space-indented line also nests with spaces under the tab unit. indentSelection and indentNewline take the unit as an argument, so Tab, Shift+Tab and auto-indent on Enter use it. Shift+Tab takes back one level: a single tab, or up to one unit's width of spaces, where the tab unit counts as four columns to match tab-size: 4, so four-space starter code still outdents by one level in one press.
Four spaces leaves no URL parameter and no stored value, so existing interview URLs behave as before, and the 66 existing tests in tests/browser/editor.test.js pass unmodified. The task page is unchanged, as agreed in #589: it honors none of the lobby's editor options today. The interview start payload pinned in tests/web/contract.rs does not change, since this is a display preference neither Jim nor the report needs.
Validation: new cases in tests/browser/editor.test.js (each unit for Tab, block indent and outdent, Enter after { and a Python :), tests/browser/editor-options.test.js (round-trip, unknown values fall back to four, storage), tests/browser/lobby.test.js (the select survives a reload, is disabled for the whiteboard and reaches the interview URL) tests/browser/editor-pipeline.test.js (the real interview page with editorIndent=2 and editorIndent=tab, and a four-space Python starter under each) and tests/browser/indent-guides.test.js (bufferIndent returns null with no block, and the width or a tab otherwise). Restoring the old editor.js, removing the unit arguments in web/interview.js, dropping bufferIndent, or dropping the space guard in indentNewline each fails a new test. ./scripts/test.sh on macOS passes everything this change touches; the lanes that fail (cargo-test's connection-drain timeout under load, task-authoring's timing ratio, run-budget, interactive-review-hook's /private/var path, and six git-hooks checks needing shellcheck, ruff or network) fail the same way on clean main here. Not run: a manual check in a live interview.
Close #589
Summary by cubic
Lets candidates pick 2 spaces, 4 spaces, or Tab for the editor's indentation in the lobby, instead of the fixed four spaces. The choice is saved in the browser and forwarded to the interview via an editorIndent URL parameter, and drives Tab, Shift+Tab, and auto-indent after Enter. Once the buffer has an indented block, nesting follows that block's own width, so a four-space Python starter stays four-space under a two-space or Tab choice rather than raising an IndentationError or TabError. Shift+Tab takes back one level, counting a Tab as four columns so four-space starter code still outdents correctly. Four spaces remains the default and leaves no URL parameter, so existing interview URLs and the task page behave exactly as before. Closes #589.