/* Local handbook study: Riforma, a white field, and a ruled editorial grid. */
.workspace-page { --ink: #0a0a0a; --line: #d3d3d3; --quiet: #626262; background: #fff; color: var(--ink); font-family: var(--sans); }
.workspace-shell { display: grid; grid-template-columns: 156px minmax(0, 1fr); gap: 60px; max-width: 1600px; margin: 0 auto; padding: 32px 80px 60px; }
.workspace-page .wordmark, .workspace-page .handbook-back, .workspace-page .nav-contact { left: max(80px, calc((100vw - 1600px) / 2 + 80px)); }
.workspace-main { min-width: 0; max-width: 1100px; }
.workspace-page a { text-decoration: underline; text-underline-offset: 3px; }
.workspace-page .sidebar a { text-decoration: none; }
.workspace-page button, .workspace-page input { font: inherit; }
.workspace-page button { border: 1px solid var(--line); background: #fff; padding: 8px 11px; cursor: pointer; color: inherit; font: 11px var(--mono); }
.workspace-page button:hover:not(:disabled) { background: var(--ink); color: #fff; border-color: var(--ink); }
.workspace-page button:disabled { color: #777; cursor: default; }
.workspace-page :focus-visible { outline: 2px solid #0a0a0a; outline-offset: 3px; }
.workspace-page kbd { font: 10px var(--mono); color: var(--quiet); }
.workspace-intro { margin-bottom: 30px; max-width: 65ch; }
.eyebrow { font: 10px var(--mono); color: var(--quiet); margin-bottom: 12px; }
.workspace-intro h1 { font-size: 24px; font-weight: 500; line-height: 1.25; }
.workspace-deck { font-size: 15px; font-weight: 300; line-height: 1.5; margin-top: 10px; color: var(--quiet); }
.entry-byline { font: 11px var(--mono); margin-top: 14px; color: var(--quiet); }
.session-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; padding: 12px 0; border-block: 1px solid var(--ink); }
.workspace-page .button-primary { background: var(--ink); color: #fff; border-color: var(--ink); }
.workspace-page .button-primary:disabled { background: #fff; color: var(--quiet); border-color: var(--line); }
.button-primary span { margin-left: 8px; }
.session-indicators { display: flex; flex-wrap: wrap; gap: 16px; font: 10px var(--mono); }
.session-indicators > span { display: flex; align-items: center; gap: 6px; }
.session-indicators strong { color: var(--quiet); font-weight: 400; }
.status-dot { width: 6px; height: 6px; border: 1px solid #888; border-radius: 50%; }
.status-dot[data-state="ready"] { background: var(--ink); border-color: var(--ink); }
.status-dot[data-state="error"] { background: #a33a2d; }
.session-actions { display: flex; gap: 6px; margin-left: auto; }
.session-actions kbd { margin-left: 6px; }
.session-message { color: var(--quiet); font-size: 12px; padding: 10px 0 26px; }
.session-message[data-error="true"] { color: #a33a2d; }
.workspace-grid { display: grid; grid-template-columns: minmax(0, 65ch) minmax(190px, 1fr); gap: 32px; align-items: start; }
.notebook, .instrument-column { min-width: 0; }
.reading-toc { border-bottom: 1px solid var(--ink); padding-bottom: 17px; margin-bottom: 26px; display: flex; flex-wrap: wrap; column-gap: 16px; row-gap: 5px; font-size: 12px; }
.reading-toc > span { flex-basis: 100%; font: 10px var(--mono); color: var(--quiet); margin-bottom: 5px; }
.prose { font: 400 15px/1.6 var(--sans); max-width: 65ch; margin-bottom: 16px; }
.prose code { font: 12px var(--mono); background: #f5f5f5; padding: 1px 3px; }
.reading-link { display: flex; flex-wrap: wrap; gap: 12px 22px; font-size: 11px; margin: 20px 0 36px; }
.prose-section { margin-top: 32px; scroll-margin-top: 24px; }
.prose-section h2 { border-top: 1px solid var(--ink); padding-top: 14px; margin-bottom: 18px; font: 500 15px var(--sans); }
.section-heading { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 12px 0; border-top: 1px solid var(--ink); }
.section-heading h2 { font: 11px var(--mono); margin: 0; padding: 0; border: 0; }
.workspace-page .text-button { border: 0; padding: 0; font: 11px var(--sans); text-decoration: underline; text-underline-offset: 3px; }
.code-cell { border-block: 1px solid var(--line); margin: 24px 0; background: #fff; }
.cell-header { display: flex; align-items: center; gap: 10px; padding: 10px 0; }
.cell-number { font: 10px var(--mono); color: var(--quiet); }
.cell-header h3 { font: 400 12px var(--sans); flex: 1; }
.cell-description { font: 11px/1.5 var(--sans); color: var(--quiet); margin-bottom: 8px; }
.cell-footer { display: flex; justify-content: space-between; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 0; }
.workspace-page .cell-evaluate { border-color: var(--ink); }
.cell-saved { font: 9px var(--mono); color: var(--quiet); }
.workspace-page .cell-reset { border: 0; padding: 4px 0; font: 10px var(--sans); text-decoration: underline; text-underline-offset: 3px; }
/* --mono-fixed, not --mono: the editor is code, and the reading toggle does
   not reach into it. See the token block in style.css. */
.CodeMirror { font: 12px/1.6 var(--mono-fixed); height: auto; background: #fafafa; color: var(--ink); border-left: 2px solid var(--ink); }
.CodeMirror-scroll { min-height: 70px; max-height: 360px; }
.CodeMirror-lines { padding: 12px 0; }
.CodeMirror-gutters { background: #fafafa; border: 0; }
.CodeMirror-linenumber { color: #767676; padding: 0 8px; font-size: 10px; }
.cm-s-default .cm-comment { color: #696969; font-style: italic; }
.cm-s-default .cm-keyword { color: #0a0a0a; font-weight: 700; }
.cm-s-default .cm-number, .cm-s-default .cm-string, .cm-s-default .cm-string-2, .cm-s-default .cm-variable-2 { color: #444; }
.cm-s-default .cm-type { color: #0a0a0a; font-weight: 700; }
.instrument-column { position: sticky; top: 24px; }
.instrument { border-block: 1px solid var(--ink); margin: 24px 0; }
.instrument > .section-heading { border-top: 0; }
.instrument-number { font: 10px var(--mono); color: var(--quiet); }
.instrument-body { padding-bottom: 15px; }
.scope-heading, .meter-caption { display: flex; justify-content: space-between; gap: 10px; font: 10px var(--mono); color: var(--quiet); padding: 10px 0; }
/* ── A handbook entry running a live session ─────────────────────────
   Session controls and examples sit in the text; the monitoring stack
   lives in the sidebar, so the measure is not squeezed by a column.

   A live entry keeps the shell's own geometry. Widening the sidebar to 380px
   to fit the monitor moved the text 134px right of every other page and cut
   100px off the measure — and the monitor is hidden until the reader reaches
   the live section, so most of the page was simply an empty gutter. The
   monitor is sized on its own below instead, which is what it needed: it is
   fixed, so it never occupied a grid track in the first place. */
.entry-live { --ink: #0a0a0a; --line: #d3d3d3; --quiet: #626262; }
.entry-session .handbook-entry { grid-template-columns: minmax(0, 1fr); }
.entry-session .xref-note { display: none; }
.entry-live button, .entry-live input { font: inherit; }
.entry-live button { border: 1px solid var(--line); background: #fff; padding: 8px 11px; cursor: pointer; color: inherit; font: 11px var(--mono); }
.entry-live button:hover:not(:disabled) { background: var(--ink); color: #fff; border-color: var(--ink); }
.entry-live button:disabled { color: #777; cursor: default; }
.entry-live :focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.entry-live kbd { font: 10px var(--mono); color: var(--quiet); }
.entry-live .button-primary { background: var(--ink); color: #fff; border-color: var(--ink); }
.entry-live .button-primary:disabled { background: #fff; color: var(--quiet); border-color: var(--line); }
.entry-live .text-button { border: 0; padding: 0; font: 11px var(--sans); text-decoration: underline; text-underline-offset: 3px; }
.entry-live .cell-evaluate { border-color: var(--ink); }
.entry-live .cell-reset { border: 0; padding: 4px 0; font: 10px var(--sans); text-decoration: underline; text-underline-offset: 3px; }
.entry-live input[type="range"] { appearance: none; -webkit-appearance: none; width: 100%; height: 24px; background: transparent; cursor: pointer; border-radius: 0; }
.entry-live input[type="range"]::-webkit-slider-runnable-track { height: 1px; background: #888; }
.entry-live input[type="range"]::-webkit-slider-thumb { appearance: none; width: 10px; height: 20px; background: var(--ink); border: 1px solid #fff; border-radius: 0; margin-top: -10px; }
.entry-live input[type="range"]::-moz-range-track { height: 1px; background: #888; }
.entry-live input[type="range"]::-moz-range-thumb { width: 8px; height: 18px; background: var(--ink); border: 1px solid #fff; border-radius: 0; }
.entry-live input:disabled { opacity: .4; cursor: default; }
.entry-live .release-button { grid-column: 1 / -1; justify-self: start; }
.entry-live .code-cell { background: #fff; }
/* The instrument is the builder's counterpart: same field, same rule. */
.entry-live .instrument {
  border: 1px solid #d4d8dc; border-left: 3px solid #2a6496;
  background: #f6f8fa; padding: 0 1.25rem; margin: 0 0 1.5rem;
}
.entry-live .instrument > .section-heading { border-top: 0; padding-top: 14px; }
.entry-live .instrument .instrument-body { padding-bottom: 18px; }
/* Each control is its own panel, on the builder's canvas field and rule.
   Compact: a laboratory control, not a consumer fader. */
.entry-live .instrument .parameter { background: none; border: 0; padding: 0; }
.entry-live .instrument .parameter > .parameter-field {
  background: var(--bg, #fff);
  border: 1px solid #d4d8dc;
  padding: 6px 8px 7px;
  display: block;
  margin-bottom: 0;
}
.entry-live .instrument .parameter-head {
  display: flex; justify-content: space-between; flex-wrap: wrap;
  gap: 4px; align-items: baseline; margin-bottom: 1px; font-size: 11px;
}
.entry-live .instrument .parameter output { font-size: 9px; }
.entry-live .instrument .parameter p { margin-top: 5px; font-size: 9px; }
.entry-live .instrument input[type="range"] { height: 13px; }
.entry-live .instrument input[type="range"]::-webkit-slider-thumb {
  width: 6px; height: 11px; margin-top: -5px;
}
.entry-live .instrument input[type="range"]::-moz-range-thumb { width: 5px; height: 11px; }
/* stretch, so a control without a caption still matches its neighbours */
.entry-live #instrument-controls { gap: 8px; align-items: stretch; }

/* ── The monitor, along the foot of the window ───────────────────────
   It used to run down the left margin, under the handbook mark, and it did not
   fit: 200px wide between 901 and 1440px, which is narrower than the lines
   scsynth prints, and 738px of content in a 593px box, so the stack scrolled
   internally and the scope was clipped by the bottom of the viewport. Below
   1100px it was worse than cramped — `position: static` put it in the nav
   flow, so it scrolled off the top of the page the moment the reader reached
   the cells it belongs to.

   Full bleed, so it reads as a rule under the whole page rather than a panel
   floating in one corner, and one line deep: the session's state, the last
   thing the language said, and the controls. Everything else — the transcript
   behind that last line, the waveform behind that figure — is a thing the
   reader opens, because the entry underneath is what the page is for. */
.sidebar-monitor {
  --ink: #0a0a0a; --line: #d3d3d3; --quiet: #626262;
  position: fixed;
  inset: auto 0 0 0;
  z-index: 30;
  padding: 0 var(--frame-pad-x) env(safe-area-inset-bottom, 0px);
  background: #fff;
  border-top: 1px solid var(--ink);
  box-shadow: 0 -2px 10px rgba(10, 10, 10, 0.07);
}
/* The one line. Nothing in it may wrap: a bar that reflows to two rows as the
   post tail lengthens is a bar that moves the page under the reader. */
.sidebar-monitor .session-bar {
  flex-direction: row; flex-wrap: nowrap; align-items: center; gap: 16px;
  padding: 7px 0; border-block: 0;
}
.sidebar-monitor .session-indicators { flex: 0 0 auto; flex-wrap: nowrap; gap: 16px; white-space: nowrap; }
.sidebar-monitor .session-actions { flex: 0 0 auto; margin-left: auto; gap: 6px; align-items: center; }
.sidebar-monitor #start-session { flex: 0 0 auto; white-space: nowrap; }
.sidebar-monitor .section-heading { border-top: 0; padding: 0 0 5px; }
/* The instruction line is prose about what to do next, not state: it belongs
   with the transcript it explains. */
.sidebar-monitor .session-message {
  font-size: 11px; line-height: 1.45; color: var(--quiet);
  padding: 0 0 8px; margin: 0;
}
.sidebar-monitor .session-message[data-error="true"] { color: #a33a2d; }

/* The last line of the transcript, standing in for all of it. Flexible so it
   takes whatever the fixed items leave, truncated so it can never take more. */
.sidebar-monitor .post-tail {
  flex: 1 1 0; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 10px var(--mono); color: var(--quiet);
}
.sidebar-monitor .post-tail:empty { visibility: hidden; }
/* An error outranks it: both want the middle, and only one of them is news. */
.sidebar-monitor[data-alert="true"] .post-tail { display: none; }

/* ── Folded, which is how the dock sits unless asked otherwise ───────
   Written as :not([data-fold="open"]) rather than [data-fold="folded"] so the
   folded size is what the stylesheet says before the script has set the
   attribute at all: unfolded-then-folded would be a visible jump on load. */
.sidebar-monitor:not([data-fold="open"]) .post-section,
.sidebar-monitor:not([data-fold="open"]) .session-message { display: none; }

/* ── Unfolded: the transcript, under the same bar ────────────────── */
.sidebar-monitor[data-fold="open"] .post-section { padding-bottom: 11px; }
.sidebar-monitor[data-fold="open"] #post-output { height: clamp(96px, 17vh, 180px); }
.sidebar-monitor[data-fold="open"] .session-bar { border-bottom: 1px solid var(--line); margin-bottom: 9px; }
.sidebar-monitor #session-fold, .sidebar-monitor #toggle-scope { padding: 5px 8px; white-space: nowrap; }
.sidebar-monitor .session-fold-caret { display: inline-block; margin-left: 5px; }
.sidebar-monitor[data-fold="open"] .session-fold-caret { transform: rotate(180deg); }
/* Pressed state for the two things that open something. */
.sidebar-monitor [aria-expanded="true"] { background: var(--ink); color: #fff; border-color: var(--ink); }
#scope-peak { font-variant-numeric: tabular-nums; opacity: .72; margin-left: 5px; }

/* ── The audio window ────────────────────────────────────────────────
   Opened from the bar, moved by its head, and remembered where it was left.
   Fixed rather than absolute so dragging it is not also a bet on the scroll
   position, and clamped on resize so it cannot be left off the screen. */
.audio-window {
  --ink: #0a0a0a; --line: #d3d3d3; --quiet: #626262;
  position: fixed;
  z-index: 31;
  width: 340px;
  background: #fff;
  border: 1px solid var(--ink);
  box-shadow: 3px 3px 0 rgba(10, 10, 10, 0.1);
  padding: 0 12px 11px;
}
.audio-window[hidden] { display: none; }
.audio-window-head {
  display: flex; align-items: baseline; gap: 10px;
  padding: 8px 0 7px; margin-bottom: 2px;
  border-bottom: 1px solid var(--line);
  cursor: grab; touch-action: none;
  /* The head is a handle; dragging it should not select its own label. */
  user-select: none; -webkit-user-select: none;
}
.audio-window[data-dragging="true"] .audio-window-head { cursor: grabbing; }
.audio-window-head h2 { font: 400 11px var(--mono); margin: 0; }
.audio-window-head #sample-rate { font: 10px var(--mono); color: var(--quiet); margin-left: auto; }
.audio-window .audio-window-close {
  border: 0; background: none; padding: 2px 4px; margin-right: -4px;
  font: 11px var(--mono); cursor: pointer; color: var(--quiet);
}
.audio-window .audio-window-close:hover { background: var(--ink); color: #fff; }
.audio-window #audio-scope { display: block; width: 100%; height: 118px; margin-top: 9px; border: 1px solid var(--line); }
.audio-window .meter-caption { display: flex; justify-content: space-between; font: 10px var(--mono); color: var(--quiet); padding: 5px 0 0; }
.audio-window .output-strip {
  display: grid; grid-template-columns: auto 1fr auto auto; gap: 10px;
  align-items: center; padding: 8px 0 0; border-top: 0; font: 10px var(--mono);
}
.audio-window input[type="range"] { appearance: none; -webkit-appearance: none; width: 100%; height: 22px; background: transparent; cursor: pointer; }
.audio-window input[type="range"]::-webkit-slider-runnable-track { height: 1px; background: #888; }
.audio-window input[type="range"]::-webkit-slider-thumb { appearance: none; width: 9px; height: 18px; background: var(--ink); border: 1px solid #fff; margin-top: -9px; }
.audio-window input[type="range"]::-moz-range-track { height: 1px; background: #888; }
.audio-window input[type="range"]::-moz-range-thumb { width: 8px; height: 16px; background: var(--ink); border: 1px solid #fff; }
.audio-window button { border: 1px solid var(--line); background: #fff; padding: 5px 8px; cursor: pointer; color: inherit; font: 10px var(--mono); }
.audio-window button[aria-pressed="true"] { background: var(--ink); color: #fff; border-color: var(--ink); }
.audio-window :focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* ── An error says so in the bar, and in the rule above it ───────────
   Folded, the transcript is not on screen at all: a failure that only printed
   there would be a failure reported nowhere. The line that failed takes the
   middle of the bar, ahead of the post tail, and the dock's top rule — the one
   edge of this thing visible from anywhere on the page — turns with it.
   Cleared by the next evaluation, because the next thing you run is the answer
   to whether it is still broken. */
.sidebar-monitor .session-alert {
  flex: 1 1 0; min-width: 0; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 10px var(--mono); color: #a33a2d;
}
.sidebar-monitor .session-alert[hidden] { display: none; }
.sidebar-monitor[data-alert="true"] { border-top: 3px solid #a33a2d; }
.sidebar-monitor[data-alert="true"] .session-alert::before {
  content: "✕"; margin-right: 6px; font-weight: 700;
}
/* Room to scroll the whole document clear of the dock. On the frame rather
   than on .content: the footer is .content's sibling, so padding there pushes
   the colophon down without ever letting it out from under the bar — and the
   reading switch lives in the colophon. The height is published by
   sc-workspace.mjs, which measures the thing itself; the fallback is roughly
   what it comes to at a desktop width. */
body.entry-session:not(.workspace-page) .frame {
  padding-bottom: calc(var(--monitor-height, 270px) + var(--frame-pad-bottom));
}
.sidebar-monitor .button-primary { background: var(--ink); color: #fff; border-color: var(--ink); }
.sidebar-monitor .button-primary:disabled { background: #fff; color: var(--quiet); border-color: var(--line); }
.sidebar-monitor button:disabled { color: #777; cursor: default; }
.sidebar-monitor .text-button { border: 0; padding: 4px 0; font: 10px var(--sans); text-decoration: underline; text-underline-offset: 3px; }
.sidebar-monitor .text-button:hover { background: none; color: inherit; }
.sidebar-monitor button { border: 1px solid var(--line); background: #fff; padding: 6px 9px; cursor: pointer; color: inherit; font: 10px var(--mono); }
.sidebar-monitor button:hover:not(:disabled) { background: var(--ink); color: #fff; border-color: var(--ink); }
.sidebar-monitor input[type="range"] { appearance: none; -webkit-appearance: none; width: 100%; height: 20px; background: transparent; cursor: pointer; }
.sidebar-monitor input[type="range"]::-webkit-slider-runnable-track { height: 1px; background: #888; }
.sidebar-monitor input[type="range"]::-webkit-slider-thumb { appearance: none; width: 9px; height: 18px; background: var(--ink); border: 1px solid #fff; margin-top: -9px; }
.sidebar-monitor input[type="range"]::-moz-range-track { height: 1px; background: #888; }
.sidebar-monitor input[type="range"]::-moz-range-thumb { width: 8px; height: 16px; background: var(--ink); border: 1px solid #fff; }
/* One line has a width below which something has to give. The keyboard hint
   goes first — it names a shortcut for a keyboard that may not be there — and
   then the post tail, which is the only item in the bar that can be read
   somewhere else. The row itself never wraps. */
@media (max-width: 1000px) {
  .sidebar-monitor .session-bar { gap: 12px; }
  .sidebar-monitor .session-actions kbd { display: none; }
}
@media (max-width: 820px) {
  .sidebar-monitor .post-tail { display: none; }
}
@media (max-width: 860px) {
  .audio-window { width: min(340px, calc(100vw - 2 * var(--frame-pad-x))); }
}

/* Below 700px base.njk does not load the session scripts at all: a phone reads
   the entry, and the session is a page of its own reached through the door.
   The monitor would be real chrome reporting on nothing, nailed across the
   foot of every phone entry, so it goes with the rest of the live apparatus.
   It was previously hidden here by accident rather than by rule — the default
   was `visibility: hidden` and only the script ever lifted it. */
@media (max-width: 700px) {
  .sidebar-monitor { display: none; }
  body.entry-session:not(.workspace-page) .frame { padding-bottom: var(--frame-pad-bottom); }
}

/* The scope stays in the aside, under the post window, at all times. */
.scope-section { margin-top: 20px; }
.scope-section .section-heading span { font: 10px var(--mono); color: var(--quiet); }
.scope-section #audio-scope { margin-top: 2px; }
#audio-scope { display: block; width: 100%; height: 130px; background: #fff; border: 1px solid var(--line); }
.meter-caption { padding: 7px 0 15px; }
.instrument-instruction, .binding-note { font: 11px/1.6 var(--sans); color: var(--quiet); }
.instrument-instruction code { font: 10px var(--mono); }
.binding-note { margin-top: 12px; }
#instrument-controls { border: 0; margin-top: 18px; min-width: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
.parameter > div { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 4px; align-items: baseline; margin-bottom: 8px; font: 12px var(--sans); }
.parameter output { font: 10px var(--mono); }
.parameter p { font: 10px var(--sans); color: var(--quiet); margin-top: 4px; }
.workspace-page input[type="range"] { appearance: none; -webkit-appearance: none; width: 100%; height: 24px; background: transparent; cursor: pointer; border-radius: 0; }
.workspace-page input[type="range"]::-webkit-slider-runnable-track { height: 1px; background: #888; }
.workspace-page input[type="range"]::-webkit-slider-thumb { appearance: none; width: 10px; height: 20px; background: var(--ink); border: 1px solid #fff; border-radius: 0; margin-top: -10px; }
.workspace-page input[type="range"]::-moz-range-track { height: 1px; background: #888; }
.workspace-page input[type="range"]::-moz-range-thumb { width: 8px; height: 18px; background: var(--ink); border: 1px solid #fff; border-radius: 0; }
.workspace-page input:disabled { opacity: .4; cursor: default; }
.workspace-page .release-button { grid-column: 1 / -1; justify-self: start; }
.output-strip { display: grid; grid-template-columns: auto 1fr auto auto; gap: 12px; align-items: center; padding: 12px 0; border-top: 1px solid var(--line); font: 10px var(--mono); }
.output-strip button[aria-pressed="true"] { background: var(--ink); color: #fff; }
/* Same field as the code cells: input and output read as one pair. */
#post-output { background: #fafafa; color: var(--ink); border: 0; border-left: 2px solid var(--ink); padding: 13px 14px; height: 280px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font: 10px/1.6 var(--mono); }
.workspace-about { font: 11px/1.6 var(--sans); margin-top: 18px; color: var(--quiet); }
.workspace-about summary { cursor: pointer; }
.workspace-about p { margin: 9px 0; }
.workspace-footer { display: flex; justify-content: space-between; gap: 20px; padding-top: 20px; margin-top: 44px; border-top: 1px solid var(--ink); font: 10px var(--mono); color: var(--quiet); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.skip-workspace { position: absolute; top: -100px; left: 10px; padding: 10px; background: white; z-index: 10; }
.skip-workspace:focus { top: 10px; }
@media (max-width: 1200px) {
  .workspace-shell { padding-inline: 32px; grid-template-columns: 136px minmax(0, 1fr); gap: 32px; }
  .workspace-page .wordmark, .workspace-page .handbook-back, .workspace-page .nav-contact { left: 32px; }
  .workspace-grid { grid-template-columns: minmax(0, 1fr) 185px; gap: 24px; }
  .session-actions kbd { display: none; }
}
@media (max-width: 900px) {
  .workspace-shell { display: block; padding: 24px; }
  .workspace-page .sidebar { display: flex; flex-direction: row; align-items: center; flex-wrap: wrap; gap: 24px; margin-bottom: 30px; }
  .workspace-page .wordmark, .workspace-page .handbook-back, .workspace-page .nav-contact { position: static; margin: 0; }
  /* Relative keeps normal flow but anchors the contact card; in a top bar it opens downward. */
  .workspace-page .nav-contact { position: relative; margin-left: auto; }
  .workspace-page .contact-card { top: calc(100% + 10px); bottom: auto; left: auto; right: 0; }
  .workspace-grid { grid-template-columns: minmax(0, 1fr) 200px; }
}
@media (max-width: 650px) {
  .workspace-shell { padding: 20px 16px 35px; }
  .workspace-page .sidebar { gap: 20px; }
  .workspace-page .wm-grid { font-size: 1rem; }
  .workspace-page .nav-contact { display: none; }
  .workspace-grid { grid-template-columns: minmax(0, 1fr); }
  .instrument-column { position: static; }
  .session-bar { gap: 12px; }
  .session-indicators { flex: 1; gap: 8px; flex-direction: column; }
  .session-actions { margin-left: 0; }
  .workspace-page button { min-height: 38px; }
  .workspace-page .text-button { padding: 6px 0; }
  #instrument-controls { grid-template-columns: 1fr; }
  .workspace-footer { font-size: 9px; }
}
/* Whether a cell sounds, stated before it is run. The dot carries no meaning
   on its own; the words do, so this still reads without colour or shape. */
.cell-sound {
  font: 9px var(--mono);
  color: var(--quiet);
  letter-spacing: 0.02em;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.cell-sound::before {
  content: "";
  width: 6px;
  height: 6px;
  border: 1px solid var(--quiet);
  border-radius: 50%;
}
.cell-sound[data-sounds="true"]::before { background: var(--ink); border-color: var(--ink); }
/* A draft that has fallen behind the published example: say so plainly. */
.cell-saved[data-stale="true"] {
  color: #a33a2d;
  font-size: 10px;
}

/* What just ran, flashed briefly so evaluating one line inside a long cell
   is visible rather than silent. */
.entry-live .cell-ran, .workspace-page .cell-ran { background: rgba(0, 0, 0, 0.07); }
.cell-keys { color: var(--quiet); font: 10px var(--mono); margin-left: auto; padding: 0 1rem; }
.cell-keys kbd { font: inherit; border: 1px solid var(--line); padding: 1px 4px; border-radius: 2px; }
@media (max-width: 700px) { .cell-keys { display: none; } }

/* ── Cells on phones ───────────────────────────────────────
   Wrapped lines come from CodeMirror's lineWrapping; the rules here stop the
   pieces that sit outside the wrapped text — the hidden input field, the
   horizontal scroller — from setting a width the page then has to scroll to. */
@media (max-width: 700px) {
  .CodeMirror { border-left-width: 3px; font-size: 13px; }
  .CodeMirror-scroll { max-height: 60vh; overflow-x: hidden !important; }
  .CodeMirror-sizer { max-width: 100%; }
  .CodeMirror-hscrollbar { display: none; }
  /* CodeMirror parks its input field at the caret with a nominal 1000px width.
     Off-screen on a desktop column, a page-widening overhang at 390px. */
  .CodeMirror textarea { max-width: 100%; }

  .cell-footer .cell-evaluate,
  .cell-header .cell-evaluate { min-height: 40px; }
}

/* The editor uses the same four tones as the static listings, so a cell and a
   printed example do not look like two different languages. */
.cm-s-default .cm-comment { color: var(--code-comment); font-style: italic; }
.cm-s-default .cm-string, .cm-s-default .cm-string-2 { color: var(--code-string); }
.cm-s-default .cm-number { color: var(--code-number); }
.cm-s-default .cm-keyword { color: var(--code-class); font-weight: 700; }
.cm-s-default .cm-type { color: var(--code-class); font-weight: 600; }
.cm-s-default .cm-variable-2 { color: var(--code-number); }
/* A pointer back up the page to the builder, which stays live throughout. */
.jump-up { white-space: nowrap; }
/* A cell of separate calls has no single thing to run, so it says so where
   the Evaluate button would otherwise sit. */
.cell-linewise { font: 11px var(--mono); color: var(--quiet); border: 1px dashed var(--line); padding: 8px 11px; }

/* ─────────────────────────────────────────────────────────────────────────
   The split: an entry read on a phone against an entry run on a desktop.

   One set of markup serves both. Which half is shown is decided here, in a
   media query, so it is settled before first paint; whether the session
   scripts are fetched at all is decided in base.njk, because CSS can only
   hide what the browser has already downloaded.
   ───────────────────────────────────────────────────────────────────────── */

/* Wide: the apparatus runs, the static stand-ins and the door stay out. */
@media (min-width: 701px) {
  .session-static,
  .session-door { display: none; }
}

/* Narrow: the apparatus is shown as it stands, and the door says where it
   runs. The code itself is not hidden in either case: a cell is a static
   listing until the workspace script replaces it. */
@media (max-width: 700px) {
  .session-live { display: none; }
  .session-static { margin: 1.25rem 0; }
  .session-static figcaption {
    font: 11px/1.5 var(--sans);
    color: var(--quiet);
    margin-top: 0.5rem;
  }
}

/* The door. Phone only, at the head of the executable stretch. */
.session-door {
  margin: 1.5rem 0 2rem;
  border: 1px solid var(--ink, #0a0a0a);
  padding: 14px 15px 15px;
}
.session-door-open {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: 44px;
  padding: 10px 13px;
  background: #0a0a0a;
  color: #fff;
  font: 0.78rem var(--mono);
  text-decoration: none;
}
.session-door-arrow { font-size: 1.05rem; line-height: 1; }
.session-door-note {
  margin: 11px 0 0;
  font: 11px/1.55 var(--sans);
  color: #626262;
}

/* A static listing stands where an editor would. Capping its height at the
   editor's keeps the swap from moving the page on a desktop. */
.cell-source {
  margin: 0;
  overflow: auto;
  background: #fafafa;
  border-left: 2px solid #0a0a0a;
  padding: 12px 13px;
  font: 12px/1.6 var(--mono);
  max-height: 360px;
}
@media (max-width: 700px) {
  /* On a phone the listing is the presentation, not a placeholder for one. */
  .cell-source { max-height: 70vh; font-size: 12.5px; }
}

/* ─────────────────────────────────────────────────────────────────────────
   The workspace page.
   ───────────────────────────────────────────────────────────────────────── */

.workspace {
  max-width: 760px;
  margin: 0 auto;
  padding: 18px 16px calc(28px + var(--dock-height, 56px));
  /* A grid item will not shrink below its content, and an editor's content is
     as wide as its longest line. Without this the page is dragged sideways. */
  min-width: 0;
}
.workspace .CodeMirror { max-width: 100%; }
.workspace .CodeMirror-sizer { max-width: 100% !important; }
.workspace-return { margin-bottom: 18px; }
.workspace-return a {
  font: 11px var(--mono);
  text-decoration: none;
  display: inline-flex;
  gap: 0.45rem;
  min-height: 44px;
  align-items: center;
}
.workspace-return a:hover { text-decoration: underline; }
.workspace-head { border-bottom: 1px solid var(--line, #d3d3d3); padding-bottom: 14px; margin-bottom: 20px; }
.workspace-head h1 { font: 400 1.05rem var(--sans); margin: 0 0 6px; }
.workspace-sub { font: 11px/1.55 var(--sans); color: var(--quiet, #626262); margin: 0; max-width: 54ch; }
.workspace-foot { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--line, #d3d3d3); }
.workspace-foot a { font: 11px var(--mono); }

/* The workspace runs the apparatus at every width: it is the page you opened
   in order to use it. */
.workspace .session-live { display: block; }

/* Four tables two-up even on a phone: here they are instruments being read
   against each other, not figures being studied one at a time. */
@media (max-width: 650px) {
  .workspace .control-tables { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .workspace .control-table canvas { height: 56px; }
}
/* The builder is left to stack. Its panels carry a label and a preset menu
   side by side, which needs more width than half a phone, so forcing them
   two-up drags the page sideways. */

/* ─────────────────────────────────────────────────────────────────────────
   The dock: the monitor, kept within reach of the cell that wrote to it.
   ───────────────────────────────────────────────────────────────────────── */

.session-dock {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 20;
  background: #fff;
  border-top: 1px solid #0a0a0a;
  /* Clear of the iOS home indicator, which otherwise sits on the dock bar. */
  padding-bottom: env(safe-area-inset-bottom, 0);
}
.session-dock-bar { display: flex; align-items: stretch; gap: 0; }
/* The one control that must never be hunted for. It leaves the bar once the
   session is up, handing the room back to the status and the last line. */
.session-dock-bar #start-session {
  flex: 0 0 auto;
  min-height: 48px;
  border: 0 !important;
  border-right: 1px solid #0a0a0a !important;
  padding: 0 14px;
  font: 11px var(--mono);
  white-space: nowrap;
}
.session-dock[data-session="ready"] .session-dock-bar #start-session { display: none; }
.session-dock-toggle {
  flex: 1;
  display: flex;
  align-items: center;
  text-align: left;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: 48px;
  padding: 0 15px;
  border: 0 !important;
  background: #fff;
  font: 11px var(--mono);
}
.session-dock-head { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; flex: 1; text-align: left; }
.session-dock-status { display: flex; gap: 1rem; align-items: center; }
/* The language's last word, held to one line. It is a tail, not a transcript:
   the transcript is in the panel below. */
.session-dock-last {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 10px var(--mono);
  color: var(--quiet, #626262);
}
.session-dock-last:empty { display: none; }
.session-dock-status span { display: inline-flex; align-items: center; gap: 0.35rem; }
.session-dock-caret { transition: transform 0.15s; }
.session-dock[data-state="open"] .session-dock-caret { transform: rotate(180deg); }

.session-dock-panel { display: none; border-top: 1px solid var(--line, #d3d3d3); }
.session-dock[data-state="open"] .session-dock-panel { display: block; }

.session-dock-tabs { display: flex; border-bottom: 1px solid var(--line, #d3d3d3); }
.session-dock-tabs button {
  flex: 1;
  min-height: 44px;
  border: 0 !important;
  background: #fff;
  font: 10px var(--mono);
  letter-spacing: 0.03em;
  text-transform: lowercase;
}
.session-dock-tabs button[aria-selected="true"] { background: #0a0a0a; color: #fff; }

.session-dock-view { padding: 12px 15px 15px; }
.session-dock-view #post-output {
  height: clamp(110px, 26vh, 260px);
  margin: 0;
  overflow: auto;
}
.session-dock-view #audio-scope { width: 100%; display: block; }
.session-dock .session-bar { border: 0; padding: 0; flex-direction: column; align-items: stretch; gap: 10px; }
.session-dock .session-message { font: 11px/1.5 var(--sans); color: var(--quiet, #626262); margin: 10px 0 0; }
.session-dock .section-heading { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.session-dock .section-heading h2 { font: 400 11px var(--mono); margin: 0; }

/* On a desktop the dock is a strip rather than a sheet: there is room, and the
   workspace is the page, so it does not need to hide. */
@media (min-width: 701px) {
  .session-dock { left: auto; right: 0; width: min(420px, 38vw); border-left: 1px solid #0a0a0a; }
  .session-dock-view #post-output { height: clamp(140px, 30vh, 320px); }
}

/* A `1fr` track is `minmax(auto, 1fr)`, so it cannot shrink below the
   min-content of what sits in it, and an editor's min-content is its longest
   unbroken line. Entries never hit this because their prose wraps; the
   workspace does, so its content column is told it may shrink and the
   editors are held to the width that remains. */
@media (max-width: 900px) {
  .workspace-page .content { min-width: 0; }
}

/* A line-wise cell is divided into one box per statement, each with its own
   button. They read as one run rather than six unrelated cells: tighter
   spacing, and a rule only above the first. */
.code-cell--step { margin: 10px 0; border-top: 0; }
.code-cell--step:first-of-type { margin-top: 24px; border-top: 1px solid var(--line, #d3d3d3); }
.cell-step-note { margin-bottom: 8px; }
@media (max-width: 700px) {
  /* Each statement is short, so the editor should not reserve a cell's height. */
  .code-cell--step .CodeMirror-scroll { min-height: 44px; }
  .code-cell--step .cell-source { max-height: 40vh; }
  /* The key hints are for a keyboard that is not there. */
  .code-cell--step .cell-keys { display: none; }
}


/* The runtime frame is hidden machinery until iOS withholds audio, at which
   point it has to be tappable: it is the document that owns the AudioContext,
   and the gesture only counts there. Shown as a band above the dock. */
#runtime-host[data-unlock] {
  position: fixed;
  left: 0; right: 0;
  bottom: calc(48px + env(safe-area-inset-bottom, 0px));
  z-index: 25;
  height: 56px;
  border-top: 1px solid #0a0a0a;
  background: #fff;
}
#runtime-host[data-unlock] iframe { width: 100%; height: 100%; border: 0; display: block; }
/* On an entry the bar below it is the monitor, which is taller than the
   workspace dock's collapsed 48px and measures itself. */
body.entry-session:not(.workspace-page) #runtime-host[data-unlock] {
  bottom: var(--monitor-height, 270px);
}

/* Instrument sliders on the workspace. The entry draws these at 13px with a
   5px thumb, which is right for a pointer and unusable with a finger, and in
   any case those rules are scoped to .entry-live and never reached this page.
   Here they are sized for a touch: a 44px row to land in, a thumb wide enough
   to see under a fingertip, and touch-action none so the drag belongs to the
   slider rather than being taken by the page as a scroll. */
.workspace input[type="range"],
.session-dock input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 44px;
  margin: 0;
  background: transparent;
  border-radius: 0;
  cursor: pointer;
  touch-action: none;
}
.workspace input[type="range"]::-webkit-slider-runnable-track,
.session-dock input[type="range"]::-webkit-slider-runnable-track {
  /* Thicker than the entry's hairline: at arm's length a 1px rule does not
     read as something you can take hold of. */
  height: 3px;
  background: #c4c4c4;
}
.workspace input[type="range"]::-webkit-slider-thumb,
.session-dock input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  /* Wide enough to be caught rather than aimed at. A 14px thumb cleared the
     44px row height and was still fiddly in practice: the row is what the
     finger lands in, the thumb is what it has to find. */
  width: 24px;
  height: 32px;
  margin-top: -16px;
  background: #0a0a0a;
  border: 1px solid #fff;
  border-radius: 0;
}
.workspace input[type="range"]::-moz-range-track,
.session-dock input[type="range"]::-moz-range-track { height: 1px; background: #888; }
.workspace input[type="range"]::-moz-range-thumb,
.session-dock input[type="range"]::-moz-range-thumb {
  width: 12px; height: 26px; background: #0a0a0a; border: 1px solid #fff; border-radius: 0;
}
.workspace input[type="range"]:disabled::-webkit-slider-thumb { background: #999; }
/* The parameter rows need room for the taller control. */
/* Room between rows so a thumb aimed at one slider does not land on its
   neighbour, and the whole row reads as one control. */
.workspace .parameter { margin-bottom: 14px; }
.workspace .parameter p { margin: 0; font: 10px var(--mono); color: var(--quiet, #626262); }
.workspace .parameter-head { font: 11px var(--mono); }
.workspace .parameter-head { display: flex; justify-content: space-between; align-items: baseline; }

/* Said at the top because the control that matters is at the bottom. A reader
   arriving here has a page of code in front of them and no reason to look at
   the foot of the screen, so the one prerequisite is stated where they are,
   and removed once it has been met. */
.session-note {
  margin: 0 0 22px;
  padding: 11px 13px;
  border-left: 2px solid #0a0a0a;
  background: #f5f5f5;
  font: 11px/1.55 var(--sans);
  color: #333;
}
.session-note strong { font-weight: 500; }
body[data-session="ready"] .session-note { display: none; }
