/* The workspace shell, from the UI handoff pack.
 *
 * Tokens, grid values and the three view presets are the pack's own
 * (`starter/layout-shell.css` and section 3 of the spec), kept to its
 * numbers so the four reference screens can be checked against rather than
 * argued about.
 *
 * TWO DELIBERATE DEPARTURES, both asked for directly:
 *
 *   - The primary rail carries Robotics, Scratch, CODE and LAB, then More.
 *     The pack says to delete the text-code workspace and put everything
 *     else behind More. Keeping Code is what stops every saved MicroPython
 *     and C++ project becoming unopenable, and Lab is a place a learner
 *     goes often enough to deserve one click.
 *   - The device actions — connect, run, save to robot, stop — are in the
 *     toolbar. The reference screens do not show them, and an IDE for a
 *     robot that cannot be told to run it is not finished.
 */

/* The shell's own names for colours the whole app already has.
 *
 * They used to be a second palette written out in hex, and a light one:
 * `--xp-surface: #ffffff` in a `:root` that this app themes DARK by
 * default. Every rule using them painted a light chrome onto a dark page,
 * and no `[data-theme]` block existed to correct it because there was
 * nothing to correct — the values were not wrong, they were unconditional.
 *
 * Pointed at the global tokens instead of duplicated per theme: one source
 * of truth means a dark mode that cannot be half-finished, and the names
 * stay so the six hundred lines below did not have to change. Brand colours
 * stay literal — orange is orange in both themes. */
:root {
  --xp-orange: #ff5a1f;
  /* Only ever used as TEXT — four `color:` rules and no fill. #ee4d12 is
     3.48:1 on the light chrome, so the active simulator tab was the least
     readable label in the workspace. Darkened on light, lifted on dark. */
  --xp-orange-strong: #a83309;
  --xp-purple: #7449f0;
  --xp-text: var(--ink);
  --xp-muted: var(--muted);
  --xp-surface: var(--panel);
  --xp-canvas: var(--surface);
  --xp-warm: var(--surface-2);
  --xp-border: var(--line);
  --xp-success: #25b45b;
  --xp-radius-sm: 8px;
  --xp-radius-md: 12px;
  --xp-shadow-drawer: 0 16px 34px rgb(0 0 0 / 0.45);
  --xp-transition: 180ms cubic-bezier(.2, .8, .2, 1);
}

/* On light, the shadow returns to the warm, shallow one the pack drew. */
:root[data-theme="light"] {
  --xp-shadow-drawer: 0 16px 34px rgb(39 28 20 / 0.12);
}

:root[data-theme="dark"] {
  --xp-orange-strong: #ff8654;
}

/* ------------------------------------------------------------ the shell */

/* An id in the selector, because `#workspace-view` already claims this
 * element as a flex column and an id beats a class. The same trap caught
 * the Blockly canvas earlier in this redesign: the shell's rules have to
 * outrank the page's own. */
#workspace-view.xp-workspace {
  /* The pack says 140. 168 was needed while the longest category was called
     "Machine Learning" and was cut in half at anything less. It is "AI" now,
     so the longest label left is "Variables" and the rail can come in to
     132 — 36px of canvas back, and under the pack's own figure. */
  --palette-rail-width: 132px;
  /* Folded: wide enough for the badge and its tap target, and nothing else.
     Folding it away entirely took the categories with it, so getting a block
     meant unfolding first and the fold was worth less than it cost. */
  --palette-folded-width: 56px;
  /* THE TWO SEAMS, as properties a drag can move — see workspace-split.js.
   *
   * The palette's width goes through `--palette-open-width` rather than
   * being set directly, and that indirection is load-bearing: a dragged
   * width is an inline style, inline beats everything, and setting
   * `--palette-rail-width` from script would win over the folded rule
   * below as well. The rail would then refuse to fold, which is a worse
   * bug than not being resizable. */
  --palette-open-width: 132px;
  --palette-rail-width: var(--palette-open-width);
  --sim-width: clamp(420px, 35vw, 560px);
  position: relative;
  display: grid;
  /* The canvas takes what is left, down to nothing.
   *
   * It used to insist on 520px, which on a 1000px window made the three
   * columns add up to more than the window and the grid simply overflowed
   * — the simulator hung off the right-hand edge. What actually protects
   * the canvas is the drag's own limit (CANVAS_LEAST), which is measured
   * against the window rather than guessed at once here. */
  grid-template-columns:
    var(--palette-rail-width)
    minmax(0, 1fr)
    var(--sim-width);
  min-height: 0;
  height: 100%;
  overflow: hidden;
  transition: grid-template-columns var(--xp-transition);
}

/* Not while a finger is on the seam. A transition is right when the focus
 * tabs change the layout and wrong when the column is chasing a pointer:
 * it arrives half a second late and the drag feels broken. */
#workspace-view.xp-workspace[data-dragging="true"] {
  transition: none;
}

#workspace-view.xp-workspace[data-view="blocks-focus"] {
  --palette-open-width: 132px;
  grid-template-columns:
    var(--palette-rail-width) minmax(0, 1fr) 88px;
}

#workspace-view.xp-workspace[data-view="simulator-focus"] {
  --palette-open-width: 68px;
  grid-template-columns:
    var(--palette-rail-width)
    clamp(280px, 28vw, 360px) minmax(560px, 1fr);
}

/* Folded by the handle on its seam: the words go, the icons stay. Written
 * through the same variable the three views use, and placed after them so it
 * wins in all three. */
#workspace-view.xp-workspace[data-palette="folded"] {
  --palette-rail-width: var(--palette-folded-width);
}
/* A folded rail has no seam to take hold of. */
#workspace-view.xp-workspace[data-palette="folded"] .xp-grip-palette,
#workspace-view.xp-workspace[data-language="text"] .xp-grip-palette {
  display: none;
}
#workspace-view.xp-shell[data-palette="folded"] .xp-cat-label { display: none; }
#workspace-view.xp-shell[data-palette="folded"] .xp-palette-rail {
  padding-inline: 6px;
}
#workspace-view.xp-shell[data-palette="folded"] .xp-palette-rail button {
  justify-content: center;
  padding-inline: 4px;
}

/* A text project has no drawers, so the palette column collapses and the
 * editor takes the whole of the canvas side. The simulator beside it is the
 * same one a blocks project gets — the MicroPython is read back into the
 * same IR — so nothing else about the shell changes. */
#workspace-view.xp-workspace[data-language="text"] {
  --palette-open-width: 0px;
  --palette-rail-width: 0px;
  --palette-folded-width: 0px;
}
#workspace-view.xp-shell[data-language="text"] > #editor-shell {
  grid-column: 2;
  min-width: 0;
  min-height: 0;
  height: 100%;
  margin: 0;
  border: 0;
  border-radius: 0;
}
/* Both seam handles belong to the palette, which this project has not got. */
#workspace-view.xp-shell[data-language="text"] > .xp-fold-blocks { display: none; }

/* A learner who has asked for less movement gets none. */
@media (prefers-reduced-motion: reduce) {
  #workspace-view.xp-workspace { transition: none; }
}

/* The rail of device buttons that stood in column one is gone; see
 * `.action-bar` below, which lays the same buttons across the foot of the
 * canvas. The columns are the palette, the canvas and the dock. */

/* ---------------------------------------------- the view-mode switcher */

.xp-view-switch {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--xp-border);
  border-radius: 999px;
  background: var(--xp-warm);
}
.xp-view-switch[hidden] { display: none; }
.xp-view-switch button {
  min-height: 34px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--xp-muted);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}
.xp-view-switch button[aria-pressed="true"] {
  background: var(--xp-surface);
  color: var(--xp-orange-strong);
  box-shadow: 0 1px 3px rgb(39 28 20 / .14);
}


/* ------------------------------------------- the shell, over the old page
 *
 * `#workspace-view` was a centred column with page padding. As the shell it
 * is a full-bleed four-column grid instead: rail, palette, programming
 * canvas, simulator. Applied by a class the script adds, so a page that has
 * not reached this code is unchanged rather than half-converted.
 */
#workspace-view.xp-shell {
  max-width: none;
  margin: 0;
  padding: 0;
  min-height: 0;
  /* Whatever is left under the header, measured rather than guessed. The
     guess was 76px; the header is 87 with the project name in it, so the
     shell hung 11px past the bottom of the window and clipped whatever sat
     at the foot of the canvas — which is now the robot's controls. The
     variable is written from script, off the header's own height. */
  height: var(--xp-shell-height, calc(100vh - var(--xp-chrome-height, 76px)));
  gap: 0;
}

/* Everything that is not one of the two rails shares the remaining columns.
 *
 * The exclusions are load-bearing. This selector carries an id and two
 * classes, so it outranks `.xp-palette-rail { grid-column: 1 }` — the rail
 * was spanning columns two to four and the canvas and simulator were
 * squeezed into a strip at the bottom of the window. A catch-all has to
 * name everything it is not meant to catch.
 *
 * EVERY EXCLUSION IS A CLASS, AND THAT IS NOT A STYLE CHOICE. `:not()` takes
 * the specificity of its most specific argument, so excluding `#design-view`
 * by id — which this rule did — put a second id into a selector that already
 * had one. That made the catch-all outrank
 * `#workspace-view.xp-shell > #simulate-view { grid-column: 3 }`, so the
 * canvas and the simulator both claimed columns two to the end, and a grid
 * given two children in the same columns puts them on separate ROWS. The
 * simulator appeared underneath the editor instead of beside it, in every
 * project type at once. Name a thing here by its class. */
#workspace-view.xp-shell
  > :not(.xp-palette-rail):not(.xp-drawer-head):not(.xp-fold):not(.xp-grip):not(.design-view):not(.code-editor-view) {
  grid-column: 2 / -1;
  min-width: 0;
  min-height: 0;
}

/* The 3D workbench takes the whole shell, column one included.
 *
 * It brings its own browser column, so leaving column one to the block
 * palette put a strip of Motion and Loops beside a CAD viewport — drawers
 * for a canvas that is not on screen, in a project that has no blocks in it
 * at all. Named in the exclusion above rather than fought with specificity,
 * because that catch-all's own comment says a catch-all has to name
 * everything it is not meant to catch. */
#workspace-view.xp-shell > #design-view {
  grid-column: 1 / -1;
  min-width: 0;
  min-height: 0;
}

/* And the code workspace, for the same reason: it brings its own file
 * column, so leaving column one to the block palette put a 132px strip of
 * Motion and Loops to the left of a file tree, in a project with no blocks
 * in it. Excluded from the catch-all above by CLASS rather than by id —
 * see the note there for what happens otherwise. */
#workspace-view.xp-shell > #code-editor-view {
  grid-column: 1 / -1;
  min-width: 0;
  min-height: 0;
}
#workspace-view.xp-shell > [hidden] { display: none; }

/* ------------------------------------------------------ the category rail
 *
 * The rail is not the drawer. It stays compact and visible; the blocks live
 * in a drawer that opens OVER the canvas, so choosing a category never costs
 * the programming area any width. */

#workspace-view.xp-shell > .xp-palette-rail {
  grid-column: 1;
  padding: 10px 8px;
  border-right: 1px solid var(--xp-border);
  background: var(--xp-surface);
  overflow-y: auto;
  min-width: 0;
}
.xp-palette-rail[hidden] { display: none; }

.xp-palette-rail button {
  display: flex;
  width: 100%;
  min-height: 44px;
  align-items: center;
  gap: 9px;
  margin-bottom: 2px;
  padding: 0 8px;
  border: 0;
  border-radius: var(--xp-radius-sm);
  background: transparent;
  color: var(--xp-text);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
.xp-palette-rail button:hover { background: var(--xp-warm); }
/* A tint and a heavier label, not a solid colour bar: the row is a label
 * with a swatch, and a wall of colour makes the words hard to read. */
.xp-palette-rail button[aria-selected="true"] {
  /* The theme's tint, not a fixed cream: in dark mode the cream sat under
   * light text and the selected category's name could not be read. */
  background: var(--xp-warm);
  box-shadow: inset 3px 0 0 var(--xp-orange);
  font-weight: 650;
}
.xp-palette-rail button:focus-visible {
  outline: 2px solid var(--xp-orange);
  outline-offset: -2px;
}
/* A drawn badge rather than a coloured dot. A dot says only "this category
 * is purple", which is no help to a learner who has not yet learnt what
 * purple means here; a picture of a wheel says Motion before the word is
 * read. The colour is still in it, so nothing is lost. */
.xp-cat-icon {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
}
/* The five with no supplied file, built to match: the catalogue's own glyph
 * in white, centred on a badge of the category's colour. Masked rather than
 * drawn as an image, because the glyphs are white on nothing and a white
 * shape on a white rail is an empty circle. */
.xp-cat-icon-drawn { position: relative; }
.xp-cat-icon-drawn::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #fff;
  -webkit-mask: var(--glyph) center / 14px 14px no-repeat;
  mask: var(--glyph) center / 14px 14px no-repeat;
}
.xp-cat-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Icons only when the simulator has the room. */
#workspace-view.xp-workspace[data-view="simulator-focus"] .xp-palette-rail {
  padding-inline: 7px;
}
#workspace-view.xp-workspace[data-view="simulator-focus"] .xp-palette-rail button {
  justify-content: center;
  padding-inline: 4px;
}
#workspace-view.xp-workspace[data-view="simulator-focus"] .xp-cat-label { display: none; }

/* Blockly's own category column is replaced by the rail above, but its
 * flyout is kept: the blocks in the drawer have to be real ones a learner
 * can drag, and reimplementing that gesture is how a palette goes subtly
 * wrong. */
/* The class is `blocklyToolbox`, not `blocklyToolboxDiv` — the latter
 * matched nothing and Blockly's own coloured category pills sat in a second
 * column beside the new rail, two lists of the same thing. */
#workspace-view.xp-shell .blocklyToolbox { display: none !important; }

/* ------------------------------------------------------------- the drawer */

/* 44px, and `DRAWER_HEAD_HEIGHT` in app.js says so too: the flyout is given
 * exactly this much headroom so its first block starts below the title
 * rather than behind it. Change one and change the other. */
.xp-drawer-head {
  position: fixed;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 44px;
  padding: 0 8px 0 14px;
  border-bottom: 1px solid var(--xp-border);
  background: var(--xp-surface);
  pointer-events: auto;
}
.xp-drawer-head[hidden] { display: none; }
.xp-drawer-head h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 650;
  color: var(--xp-text);
}
.xp-drawer-close {
  display: grid;
  width: 32px;
  height: 32px;
  place-items: center;
  border: 0;
  border-radius: var(--xp-radius-sm);
  background: transparent;
  color: var(--xp-muted);
  cursor: pointer;
}
.xp-drawer-close:hover { background: var(--xp-warm); }
.xp-drawer-close svg { width: 20px; height: 20px; }

/* The flyout itself, dressed as the drawer the pack describes. */
#workspace-view.xp-shell .blocklyToolboxFlyout .blocklyFlyoutBackground {
  fill: var(--xp-surface);
  fill-opacity: 1;
}

/* ------------------------------------------------------ canvas and dock
 *
 * In Robotics and Scratch the programming canvas and the simulator are on
 * screen together — that is what Split means, and the focus presets only
 * change how much each gets. Code and Lab are their own full-width views.
 */
#workspace-view.xp-shell > #blockly-canvas {
  grid-column: 2;
  min-width: 0;
  min-height: 0;
  height: 100%;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: var(--xp-canvas);
}
#workspace-view.xp-shell > #simulate-view {
  grid-column: 3;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  height: 100%;
  padding: 0;
  border-left: 1px solid var(--xp-border);
  background: var(--xp-surface);
  overflow: hidden;
}
#workspace-view.xp-shell > #simulate-view[hidden] { display: none; }

/* Robot / Board */
.xp-sim-tabs {
  display: flex;
  flex: 0 0 auto;
  gap: 4px;
  padding: 10px 12px 0;
}
.xp-sim-tabs button {
  flex: 1 1 0;
  min-height: 40px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--xp-muted);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.xp-sim-tabs button[aria-selected="true"] {
  border-bottom-color: var(--xp-orange);
  color: var(--xp-orange-strong);
}
.xp-sim-tabs button:focus-visible {
  outline: 2px solid var(--xp-orange);
  outline-offset: -2px;
}
/* Only Robotics has two things to show. */
#workspace-view.xp-shell[data-mode="scratch"] .xp-sim-tabs { display: none; }

/* The dock's own contents scroll, never the page. */
#workspace-view.xp-shell > #simulate-view > .sim-stage,
#workspace-view.xp-shell > #simulate-view > .circuit-stage,
#workspace-view.xp-shell > #simulate-view > .stage-view {
  grid-column: auto;
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}
#workspace-view.xp-shell > #simulate-view > .sim-devices {
  grid-column: auto;
  flex: 0 0 auto;
  max-height: 40%;
  overflow: auto;
}

/* ------------------------------------------------------- what is folded away
 *
 * Everything under the buttons — the readings, the robot's own buttons,
 * what the challenge asks and the log — is a disclosure now, and all of
 * them start shut.
 *
 * Asked for in these words: "the place that shows the sensor reading and
 * all that, you don't need all of them, all of them can be hidden — we
 * only click on that if we really need them, so we can have enough space.
 * The simulation side should be clean."
 *
 * They are shut rather than gone: a distance reading is exactly what you
 * want halfway through debugging a sensor, and a learner who opens one
 * finds it open next time.
 */
.sim-fold { border-top: 1px solid var(--xp-border); }
.sim-fold:first-of-type { border-top: 0; }
.sim-fold > summary {
  list-style: none;
  cursor: pointer;
  padding: 9px 14px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--xp-muted);
}
.sim-fold > summary::-webkit-details-marker { display: none; }
/* A caret of our own, so it turns with the disclosure in every browser. */
.sim-fold > summary::before {
  content: "";
  width: 0;
  height: 0;
  flex: 0 0 auto;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform .15s ease;
}
.sim-fold[open] > summary::before { transform: rotate(90deg); }
.sim-fold > summary:hover { color: var(--xp-text); }
.sim-fold[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) {
  .sim-fold > summary::before { transition: none; }
}

/* ------------------------------------------------------------- telemetry
 *
 * Cards rather than a definition list. Three show in Split and all seven in
 * Simulator Focus: a 504px dock with seven cards in it is seven cards
 * nobody can read, and the pack asks for the short set in Split and the
 * full set when the simulator has the room. */
.xp-telemetry-heading {
  margin: 14px 14px 8px;
  font-size: 15px;
  font-weight: 650;
  color: var(--xp-text);
}
.xp-telemetry {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  padding: 0 14px 14px;
}
.xp-card {
  min-width: 0;
  padding: 10px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  background: var(--xp-surface);
}
.xp-card-label {
  margin: 0 0 6px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--xp-muted);
}
.xp-card-value {
  margin: 0;
  font-size: 15px;
  font-weight: 650;
  color: var(--xp-text);
  overflow-wrap: anywhere;
}
/* The four fuller cards wait until the simulator is the main thing. */
#workspace-view.xp-shell:not([data-view="simulator-focus"]) .xp-card-wide {
  display: none;
}
#workspace-view.xp-shell[data-view="simulator-focus"] .xp-telemetry {
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

/* ----------------------------------------------- the dock, collapsed
 *
 * In Blocks Focus the simulator becomes a rail that says what it is and
 * offers a way back. The whole rail is the button — the pack asks for
 * clicking any part of it to expand — so there is no small chevron to hunt
 * for. */
.xp-sim-peek {
  display: none;
  width: 100%;
  height: 100%;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 16px 4px;
  border: 0;
  background: transparent;
  color: var(--xp-muted);
  font: inherit;
  cursor: pointer;
}
.xp-sim-peek-title {
  writing-mode: vertical-rl;
  font-size: 13px;
  font-weight: 650;
  letter-spacing: .02em;
}
.xp-sim-peek-chevron { font-size: 13px; }
.xp-sim-peek:hover { background: var(--xp-warm); }
.xp-sim-peek:focus-visible {
  outline: 2px solid var(--xp-orange);
  outline-offset: -3px;
}

/* Collapsed: the rail is all there is. */
#workspace-view.xp-shell[data-view="blocks-focus"] .xp-sim-peek { display: flex; }
/* The handle is not part of the panel's contents — it is how you get the
 * panel back — so it stays when everything else in the column goes. */
#workspace-view.xp-shell[data-view="blocks-focus"] > #simulate-view
  > :not(.xp-sim-peek):not(.xp-fold-sim) {
  display: none;
}

/* --------------------------------------------------- the Scratch inspector */

.xp-stage-head {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  position: relative;
  min-height: 44px;
  padding: 0 12px;
}
.xp-stage-head h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 650;
  color: var(--xp-text);
}
.xp-icon-button {
  position: absolute;
  right: 10px;
  display: grid;
  width: 40px;
  height: 40px;
  place-items: center;
  border: 0;
  border-radius: var(--xp-radius-sm);
  background: transparent;
  color: var(--xp-muted);
  cursor: pointer;
}
.xp-icon-button:hover { background: var(--xp-warm); }
.xp-icon-button svg { width: 18px; height: 18px; }

/* The stage keeps 4:3 and never stretches. */
#workspace-view.xp-shell .stage-canvas-host {
  flex: 0 0 auto;
  aspect-ratio: 4 / 3;
  margin: 0 12px;
  overflow: hidden;
  border-radius: var(--xp-radius-md);
}

.xp-inspector-tabs {
  display: flex;
  flex: 0 0 auto;
  margin: 12px 12px 0;
  border-bottom: 1px solid var(--xp-border);
}
.xp-inspector-tabs button {
  flex: 1 1 0;
  min-height: 40px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--xp-muted);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.xp-inspector-tabs button[aria-selected="true"] {
  border-bottom-color: var(--xp-orange);
  color: var(--xp-orange-strong);
}

.xp-inspector { flex: 0 1 auto; padding: 12px; overflow-y: auto; min-height: 0; }
.xp-inspector[hidden] { display: none; }

/* One row of five, and the last row in the inspector, so no bottom margin
   pushing the thumbnails down. */
.xp-field-row { display: flex; gap: 8px; margin-bottom: 8px; }
.xp-field-row:last-of-type { margin-bottom: 0; }
.xp-field { display: flex; flex: 1 1 0; min-width: 0; flex-direction: column; gap: 4px; }
.xp-field-name { flex: 2 1 0; }
/* A name is longer than its box and that is fine — it is being edited, not
   read. The numbers stay legible, which is what could not be given up. */
.xp-field-name input { text-overflow: ellipsis; }

/* Five fields, one line, at any width the panel can be.
 *
 * As a flex row this wrapped: the four numbers and the name came to more
 * than the 320px the panel gives them and Direction dropped to a line of
 * its own — which is the line merging the rows was meant to remove. Widths
 * that add up are a calculation that stops being true the next time the
 * column changes.
 *
 * A grid cannot wrap. The name gets 1.6 shares because it holds a word and
 * the others hold two or three digits, and `minmax(0, …)` lets them shrink
 * below their content rather than pushing the row wider. */
.xp-field span { font-size: 11.5px; font-weight: 600; color: var(--xp-muted); }
.xp-field input {
  min-width: 0;
  min-height: 36px;
  padding: 0 9px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-sm);
  background: var(--xp-surface);
  color: var(--xp-text);
  font: inherit;
  font-size: 14px;
}
.xp-field input:focus-visible {
  outline: 2px solid var(--xp-orange);
  outline-offset: -1px;
}

.xp-thumbs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(76px, 1fr));
  gap: 8px;
  margin-top: 4px;
}
.xp-thumb {
  position: relative;
  display: grid;
  aspect-ratio: 1;
  place-items: center;
  padding: 6px;
  border: 2px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  background: var(--xp-surface);
  cursor: pointer;
}
.xp-thumb[aria-selected="true"] { border-color: var(--xp-orange); }
.xp-thumb img { max-width: 100%; max-height: 100%; object-fit: contain; }
.xp-thumb-name {
  position: absolute;
  bottom: 2px;
  max-width: 100%;
  overflow: hidden;
  font-size: 10px;
  color: var(--xp-muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The delete badge only on the chosen one: a grid of crosses invites a
 * learner to clear the stage by accident. */
.xp-thumb-drop {
  position: absolute;
  top: -7px;
  right: -7px;
  display: none;
  width: 22px;
  height: 22px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--xp-orange);
  /* Black on orange, 6.09:1 — white is 3.12:1. */
  color: #12100e;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.xp-thumb[aria-selected="true"] .xp-thumb-drop { display: grid; }
/* And on the one the keyboard is on, so its Remove is a Tab away rather
   than something only a pointer can reveal. Still one cross at a time. */
.xp-thumb:focus-within .xp-thumb-drop { display: grid; }
.xp-thumb-add { border-style: dashed; color: var(--xp-muted); font-size: 22px; }

.xp-stage-controls {
  display: flex;
  flex: 0 0 auto;
  gap: 8px;
  padding: 10px 12px 12px;
}
.xp-run-green, .xp-stop-red, .xp-stage-button {
  display: inline-flex;
  flex: 1 1 0;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  background: var(--xp-surface);
  color: var(--xp-text);
  font: inherit;
  font-size: 14px;
  font-weight: 650;
  cursor: pointer;
}
.xp-run-green {
  border-color: var(--xp-success);
  background: var(--xp-success);
  /* White on this green is 2.71:1; black is 7.01:1. Run is the button a
     learner presses most, and it was the least readable one in the row. */
  color: #12100e;
}
.xp-stop-red { border-color: #c62d20; background: #c62d20; color: #fff; }
.xp-run-green svg, .xp-stop-red svg, .xp-stage-button svg { width: 17px; height: 17px; }

/* The name plate and the run HUD swap sides with the rail.
 *
 * Everywhere else the tools are down the RIGHT edge and these two sit top
 * left, out of their way. Simulator Focus moves the rail to the left for
 * the room to label it — and left both of them underneath it: the plate
 * saying which level you are on was drawn behind the Home button, and the
 * clock landed in the middle of the column. They move too, so the corner
 * they occupy is always the one the rail is not in. */
#workspace-view.xp-shell[data-view="simulator-focus"] .sim-where,
#workspace-view.xp-shell[data-view="simulator-focus"] .sim-hud {
  left: auto;
  right: 14px;
}

#workspace-view.xp-shell[data-view="simulator-focus"] .sim-where {
  align-items: flex-end;
  text-align: right;
}

/* --------------------------------------------- the viewport's edge tools
 *
 * Down the left edge of the viewport in Simulator Focus, with words beside
 * the glyphs. A column of unlabelled icons is a quiz; the pack names all
 * five, so they are named. Elsewhere the tools stay as they were — there is
 * no room for words beside a 504px dock. */
#workspace-view.xp-shell[data-view="simulator-focus"] .sim-rail {
  left: 14px;
  right: auto;
  top: 14px;
  bottom: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  background: var(--xp-surface);
  box-shadow: 0 4px 14px rgb(39 28 20 / .08);
}
#workspace-view.xp-shell[data-view="simulator-focus"] .rail-button {
  display: flex;
  width: auto;
  min-width: 64px;
  min-height: 52px;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  border-radius: var(--xp-radius-sm);
}
.rail-word { display: none; font-size: 10.5px; font-weight: 600; }
#workspace-view.xp-shell[data-view="simulator-focus"] .rail-word { display: block; }

/* The controls row: Run, Step, Reset, then what the run happens IN. */
#workspace-view.xp-shell[data-view="simulator-focus"] .sim-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: end;
  padding: 12px 14px;
}

/* ------------------------------- the device chip, now in the header
 *
 * These lived in a 64px bar of their own. Two short facts — which board, and
 * whether it is connected — did not earn a row across the whole window, so
 * they sit in the header beside the focus switch and the workspace keeps the
 * height. */
.xp-device {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 40px;
  padding: 0 10px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-sm);
  background: var(--xp-surface);
  color: var(--xp-text);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.xp-device[hidden] { display: none; }
.xp-device:hover { background: var(--xp-warm); }
.xp-device-chip {
  display: grid;
  width: 24px;
  height: 24px;
  place-items: center;
  border-radius: 6px;
  background: #1f2937;
  color: #7bd88f;
}
.xp-device-chip svg { width: 15px; height: 15px; }
.xp-device-caret { color: var(--xp-muted); font-size: 11px; }

.xp-connection {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-size: 14px;
  white-space: nowrap;
  color: var(--xp-muted);
}
.xp-connection[hidden] { display: none; }
.xp-connection i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #c9ced4;
}
.xp-connection[data-connected="true"] i { background: var(--xp-success); }
.xp-connection[data-connected="true"] { color: var(--xp-text); }

/* The action bar stops floating once it is in the header. */
#project-bar .action-bar {
  position: static;
  display: flex;
  margin: 0;
  padding: 0;
  border: 0;
  box-shadow: none;
  background: none;
}


/* ------------------------------------- the header, when the screen is small
 *
 * The header now carries the title, the save state, the board, the
 * connection, the focus switch and six device buttons. That fits at 1440
 * and does not at 1180, so the least important words fold away first: the
 * long halves of the focus tabs, then the connection wording (the dot stays,
 * and the title attribute keeps the words a hover away), then the board's
 * name (its chip stays). Nothing a learner can only reach by reading is ever
 * the thing that goes. */
.project-bar { min-width: 0; }
#workspace-title.project-bar-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 1400px) {
  .focus-toggle .xp-long { display: none; }
}
@media (max-width: 1280px) {
  .xp-connection .xp-long { display: none; }
  .xp-connection { gap: 0; }
}
@media (max-width: 1140px) {
  #xp-device-name, .xp-device-caret { display: none; }
  .xp-device { padding: 0 8px; }
}

/* A window narrower than the three columns want.
 *
 * Split asks for 132 + 520 + 420 = 1072px, and the shell clips what does not
 * fit — so on a 1000px laptop the right edge of the simulator was simply cut
 * off. The canvas gives way rather than the simulator, because the simulator
 * has a fixed thing to show and the canvas scrolls: `minmax(0, 1fr)` lets it
 * shrink instead of forcing the overflow onto its neighbour. */
@media (max-width: 1140px) {
  #workspace-view.xp-workspace {
    /* Through the open-width property, so a dragged rail still folds and a
       narrow window still gets its narrower default. */
    --palette-open-width: 112px;
    /* AND STILL THROUGH `--sim-width`. Declaring the columns again here
       with a literal `clamp()` in the third track is how a drag came to
       move the handle, set the property, remember the number and change
       nothing at all: the property was being written and this rule was
       what the browser actually read. Every place that lays out these
       three columns has to read the same two properties. */
    --sim-width: clamp(300px, 38vw, 420px);
    grid-template-columns:
      var(--palette-rail-width)
      minmax(0, 1fr)
      var(--sim-width);
  }
  #workspace-view.xp-workspace[data-view="simulator-focus"] {
    grid-template-columns:
      var(--palette-rail-width)
      clamp(200px, 24vw, 300px)
      minmax(0, 1fr);
  }

  /* Scratch gets a wider right-hand column than robotics does.
   *
   * The stage is locked to 4:3 and sized by the width it is given, so on a
   * tablet `clamp(300px, 38vw, 420px)` capped it at about 275px across and
   * 206px tall — with six hundred pixels of empty panel underneath it. The
   * height was never the constraint and no amount of trimming rows was
   * going to fix it; the width was.
   *
   * Robotics is left alone deliberately. Its panel holds telemetry, buttons
   * and a log that are read as text, and they do not get better by being
   * wider — the 3D view does not have a fixed shape to honour. A stage
   * does, and it is the thing a learner is watching. */
  #workspace-view.xp-workspace[data-mode="scratch"] {
    /* Its own default, still through the property, so the Stage's seam can
       be dragged like every other one. */
    --sim-width: clamp(340px, 48vw, 520px);
    grid-template-columns:
      var(--palette-rail-width)
      minmax(0, 1fr)
      var(--sim-width);
  }
}

/* ------------------------------------- the robot controls, under the canvas
 *
 * Connect, run, save to the robot, stop, the code view and the serial
 * monitor. They were a column down the left of the workspace, in a rail of
 * their own, and that rail cost 68px of width on every screen — Scratch and
 * AI projects included, which have no robot for any of these buttons to act
 * on and so showed an empty 68px strip beside the palette.
 *
 * They lie across the foot of the canvas instead, where the program they act
 * on is. Nothing here is new: the same six buttons, the same listeners, laid
 * out in a row.
 *
 * The catch-all above gives the bar `grid-column: 2 / -1`, and an absolutely
 * positioned child of a grid is placed inside its GRID AREA — so `left` is
 * measured from the canvas's left edge, not the window's, and the bar tracks
 * the palette's fold without being told about it.
 *
 * Bottom-LEFT, because Blockly puts its zoom controls and its bin at the
 * bottom right of the canvas and a bar over them would cover both. */
#workspace-view.xp-shell > .action-bar {
  position: absolute;
  z-index: 30;
  right: auto;
  bottom: 14px;
  left: 16px;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
}
#workspace-view.xp-shell > .action-bar[hidden] { display: none; }
#workspace-view.xp-shell > .action-bar .action-group {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  padding: 6px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  background: var(--xp-surface);
  box-shadow: var(--xp-shadow-drawer);
}
/* One size for all of them, connect included. Run, save and stop drawn
 * smaller made connect look like the important one, and it is not: it is the
 * one you press once at the start. */
#workspace-view.xp-shell > .action-bar .action-button {
  width: 40px;
  height: 40px;
  min-height: 0;
  flex-direction: row;
  gap: 0;
  padding: 0;
}
#workspace-view.xp-shell > .action-bar .action-button svg {
  width: 20px;
  height: 20px;
}
/* READY, in the shell: the larger tiles the ready colours are drawn on
 * (styles.css, "READY"). Here rather than there because the shell sizes
 * every action button through the workspace's id, which a rule in
 * styles.css cannot outrank. Still one size for all six, connect included. */
#workspace-view.xp-shell > .action-bar[data-ready="true"] .action-group {
  gap: 8px;
  padding: 8px;
}
#workspace-view.xp-shell > .action-bar[data-ready="true"] .action-button {
  width: 46px;
  height: 46px;
  border-radius: 14px;
}
#workspace-view.xp-shell > .action-bar[data-ready="true"] .action-button svg {
  width: 22px;
  height: 22px;
}

/* The popover opens upwards, out of a bar that sits near the bottom edge. */
#workspace-view.xp-shell > .action-bar .connect-popover {
  right: auto;
  left: 0;
  bottom: calc(100% + 10px);
}

/* ------------------------------------------------ the handles on the seams
 *
 * A tab on the line between two panels, which folds the panel it belongs to
 * away and brings it back. The chevron points where the seam will move, so
 * the handle says what it will do before it is pressed rather than after.
 *
 * They ride the edge they belong to rather than being placed by arithmetic:
 * the simulator's handle is a child of the simulator, so it follows the
 * column through all three views without being told; the palette's is placed
 * in script because the drawer that opens over the canvas changes where that
 * seam actually is. */
.xp-fold {
  position: absolute;
  z-index: 30;
  display: grid;
  width: 22px;
  height: 54px;
  place-items: center;
  padding: 0;
  border: 1px solid var(--xp-border);
  border-radius: 8px;
  background: var(--xp-surface);
  color: var(--xp-muted);
  font: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 6px rgb(16 24 40 / .06);
}
.xp-fold:hover { background: var(--xp-warm); color: var(--xp-text); }
.xp-fold:focus-visible {
  outline: 2px solid var(--xp-orange);
  outline-offset: 2px;
}
.xp-fold[hidden] { display: none; }

/* The simulator's handle sits on its left edge, half over the seam. */
.xp-fold-sim {
  top: 50%;
  left: -11px;
  transform: translateY(-50%);
}

/* The palette's handle sits where the rail ends — written as the two column
 * widths, so it tracks the fold and all three views without being placed by
 * hand, and slides with the same transition the columns use. Positioning it
 * from script against the open drawer's edge looked right until the drawer
 * shut and left the handle stranded mid-canvas.
 *
 * It is exempt from the catch-all above, and has to be: an absolutely
 * positioned child of a grid is placed inside its GRID AREA, so with
 * `grid-column: 2` the offsets below would be measured from the canvas's
 * left edge instead of the window's and the handle would sit a palette's
 * width too far right. */
.xp-fold-blocks {
  top: 50%;
  left: calc(var(--palette-rail-width) - 11px);
  transform: translateY(-50%);
  transition: left var(--xp-transition);
}
@media (prefers-reduced-motion: reduce) {
  .xp-fold-blocks { transition: none; }
}

/* --------------------------------------- the dock, sized for what it shows
 *
 * The simulator was half the column and the two panels under it were the
 * other half: 414px of robot, 123px of controls, 237px of numbers. Watching
 * the robot is the reason the panel exists, so it gets roughly two thirds
 * and the rest is made to fit in what is left.
 *
 * Nothing is hidden to achieve it. Everything that was there is still there,
 * in a tighter setting of the same type — because a control a learner
 * cannot find is worse than one that is small. */
#workspace-view.xp-shell .sim-controls {
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 12px;
}
#workspace-view.xp-shell .sim-controls .primary-button,
#workspace-view.xp-shell .sim-controls .secondary-button {
  min-height: 32px;
  padding: 0 12px;
  font-size: 13px;
}
#workspace-view.xp-shell .sim-field select {
  font-size: 12px;
  padding: 4px 6px;
}
#workspace-view.xp-shell .sim-check { font-size: 12px; }
/* The readout shares the run row rather than claiming one of its own: it
 * is usually a few words, and a line to itself cost the field 25px. */
#workspace-view.xp-shell .sim-readout {
  flex: 1 1 60px;
  min-width: 0;
  margin: 0;
  font-size: 11.5px;
}

/* The telemetry heading was a section title with a section's spacing, for
 * one word above three small cards. */
#workspace-view.xp-shell .xp-telemetry-heading {
  margin: 10px 12px 6px;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--xp-muted);
}
#workspace-view.xp-shell .xp-telemetry {
  gap: 6px;
  padding: 0 12px 10px;
}
#workspace-view.xp-shell .xp-card { padding: 7px 8px; }
#workspace-view.xp-shell .xp-card-label {
  margin: 0 0 2px;
  font-size: 10.5px;
}
#workspace-view.xp-shell .xp-card-value { font-size: 13.5px; }

/* And the dock's own shares: the field takes what is left after two panels
 * that are now told how much they may have. */
#workspace-view.xp-shell > #simulate-view > .sim-stage {
  flex: 1 1 auto;
  min-height: 0;
}
#workspace-view.xp-shell > #simulate-view > .sim-devices {
  flex: 0 0 auto;
  max-height: 34%;
  padding: 0;
  overflow-y: auto;
}

/* Setup: a small disclosure in the run row, and a sheet that opens over the
 * field rather than under it. */
#workspace-view.xp-shell .sim-setup-toggle {
  min-height: 32px;
  padding: 0 10px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-sm);
  background: var(--xp-surface);
  color: var(--xp-text);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}
#workspace-view.xp-shell .sim-setup-toggle:hover { background: var(--xp-warm); }
#workspace-view.xp-shell .sim-setup-toggle[aria-expanded="true"] {
  border-color: var(--xp-orange);
  color: var(--xp-orange-strong);
}
#workspace-view.xp-shell .sim-setup {
  position: absolute;
  right: 12px;
  bottom: calc(100% + 8px);
  z-index: 25;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  width: min(360px, calc(100% - 24px));
  padding: 12px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  background: var(--xp-surface);
  box-shadow: var(--xp-shadow-drawer);
}
#workspace-view.xp-shell .sim-setup[hidden] { display: none; }
#workspace-view.xp-shell .sim-controls { position: relative; }

/* Robot / Board: two words, which did not need a 50px band. */
#workspace-view.xp-shell .xp-sim-tabs { padding: 6px 12px; }
#workspace-view.xp-shell .xp-sim-tabs button {
  min-height: 30px;
  font-size: 13px;
}

/* ------------------------------ the Stage, sized for what it shows
 *
 * The same rebalance the robot's dock got, for the same reason. The stage
 * was 41% of the column and the inspector under it 41%: a learner watching
 * their sprite had less room than the boxes describing it. Nothing here is
 * hidden — the same fields, set tighter. */
#workspace-view.xp-shell .xp-inspector-tabs { margin: 8px 12px 0; }
#workspace-view.xp-shell .xp-inspector-tabs button {
  min-height: 32px;
  font-size: 13px;
}
#workspace-view.xp-shell .xp-inspector { padding: 8px 12px; }
#workspace-view.xp-shell .xp-field-row { gap: 6px; margin-bottom: 6px; }
#workspace-view.xp-shell .xp-field { gap: 2px; }
#workspace-view.xp-shell .xp-field span { font-size: 10.5px; }
#workspace-view.xp-shell .xp-field input {
  min-height: 28px;
  padding: 0 7px;
  font-size: 13px;
}
/* Five short fields on one wrapped line rather than two rows of their own.
 * `display: contents` on the rows lets the fields inside them share the
 * inspector's own flex line, so the markup keeps its grouping and the panel
 * stops spending 84px on ten characters of number. */
/* Five fields on one line, then the sprites underneath.
 *
 * The row element is dissolved with `display: contents` — that is what
 * makes this rule the one that decides the layout, and why setting a grid
 * on `.xp-field-row` did nothing at all: there is no row box to set it on.
 *
 * A grid, because as a wrapping flex row the five came to more than the
 * 320px the panel gives them and Direction dropped to a line of its own —
 * the exact line that merging the two rows was meant to remove. Widths
 * that happen to add up are a sum that stops being true the next time this
 * column changes width; five columns cannot wrap at any width.
 *
 * The name gets 1.6 shares because it holds a word and the rest hold two
 * or three digits. `minmax(0, …)` lets them shrink under their content
 * rather than pushing the row wider than the panel. */
#workspace-view.xp-shell #xp-inspect-sprite {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(4, minmax(0, 1fr));
  align-items: start;
  gap: 6px;
}
#workspace-view.xp-shell #xp-inspect-sprite .xp-field-row { display: contents; }
#workspace-view.xp-shell #xp-inspect-sprite .xp-field { min-width: 0; }
/* The sprites sit below the fields, across all five columns. */
#workspace-view.xp-shell #xp-inspect-sprite .xp-thumbs { grid-column: 1 / -1; }

/* The inspector takes the height the stage cannot use, and scrolls if the
 * sprites outrun it. That height used to be grey apron; it now shows more
 * sprites, which is the only thing in this panel that wants more room. */
#workspace-view.xp-shell > #simulate-view #xp-inspect-sprite,
#workspace-view.xp-shell > #simulate-view #xp-inspect-backdrop {
  flex: 1 1 auto;
  overflow-y: auto;
  align-content: flex-start;
}
#workspace-view.xp-shell .xp-thumbs {
  grid-template-columns: repeat(auto-fill, minmax(52px, 1fr));
  gap: 6px;
}
#workspace-view.xp-shell .xp-stage-controls { gap: 6px; padding: 8px 12px 10px; }
#workspace-view.xp-shell .xp-run-green,
#workspace-view.xp-shell .xp-stop-red,
#workspace-view.xp-shell .xp-stage-button {
  min-height: 32px;
  font-size: 12.5px;
}
/* The stage is 480x360 and is never stretched, so in this column it is the
 * WIDTH that limits it, not the height: handing the host more rows just
 * painted more grey apron above and below the picture. Shaped 4:3 instead,
 * so the host is exactly the stage — and the height that would have been
 * apron goes to the sprite thumbnails, where it shows more sprites. */
#workspace-view.xp-shell #stage-canvas {
  flex: 0 1 auto;
  width: 100%;
  aspect-ratio: 4 / 3;
  min-height: 0;
}
#workspace-view.xp-shell .stage-view { gap: 6px; padding: 6px 12px 10px; }

/* ---------------------------------- the electronics bench, and its handle
 *
 * Vertically the bench was already most of the panel. Its problem was
 * sideways: the shelf of parts held 216px of a 503px column, so the board
 * being wired had less room than the drawer of things to wire to it. */
#workspace-view.xp-shell .parts-library { flex-basis: 168px; }
#workspace-view.xp-shell .circuit-says {
  max-height: 44px;
  padding: 6px 10px;
  font-size: 12px;
  overflow-y: auto;
}
/* With nothing to report it takes no room at all. */
#workspace-view.xp-shell .circuit-says:empty { display: none; }

/* The shelf's handle predates the seam handles and looked like neither of
 * them. One shape for "fold this away", wherever it appears. Which way the
 * chevron points is already decided in styles.css from aria-expanded; this
 * only changes how it is drawn. */
#workspace-view.xp-shell .parts-handle {
  display: grid;
  width: 22px;
  height: 54px;
  place-items: center;
  border: 1px solid var(--xp-border);
  border-radius: 8px;
  background: var(--xp-surface);
  color: var(--xp-muted);
  box-shadow: 0 2px 6px rgb(16 24 40 / .06);
}
#workspace-view.xp-shell .parts-handle::before { font-size: 13px; line-height: 1; }
#workspace-view.xp-shell .parts-handle:hover {
  background: var(--xp-warm);
  color: var(--xp-text);
}

/* --------------------------------------------------------- the sprite picker */
.xp-picker {
  width: min(520px, 92vw);
  padding: 0;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  background: var(--xp-surface);
  color: var(--xp-text);
}
.xp-picker::backdrop { background: rgb(16 24 40 / .38); }
.xp-picker-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  border-bottom: 1px solid var(--xp-border);
}
.xp-picker-head h2 { margin: 0; font-size: 15px; font-weight: 650; }
.xp-picker-head .xp-icon-button { position: static; }
.xp-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 10px;
  max-height: 60vh;
  padding: 14px;
  overflow-y: auto;
}
.xp-pick {
  display: grid;
  gap: 6px;
  justify-items: center;
  padding: 10px 6px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  background: var(--xp-surface);
  color: var(--xp-text);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}
.xp-pick:hover { border-color: var(--xp-orange); background: var(--xp-warm); }
.xp-pick:focus-visible { outline: 2px solid var(--xp-orange); outline-offset: 2px; }
.xp-pick img { width: 56px; height: 56px; object-fit: contain; }

/* ================================ the electronics bench, stacked
 *
 * Board on top, parts underneath — the same shape as the robot's dock and
 * the Stage, and what the reference screens show. It was a workbench laid
 * out sideways: the shelf took a third of a column that was already the
 * narrower half of the window, so the board being wired had less room than
 * the drawer of things to wire to it. Stacked, the board gets the full width
 * of the panel and the parts get a shelf across the bottom, where three
 * across becomes five and a learner can see the whole set.
 *
 * The order is set here rather than in the markup: the shelf still belongs
 * BEFORE the bench for anyone reading the page in order — it is the list you
 * choose from — and reversing it visually is a layout decision, not a change
 * to what the page says. */
#workspace-view.xp-shell .circuit-stage {
  flex-direction: column;
  gap: 8px;
  padding: 8px 12px 12px;
}
#workspace-view.xp-shell .circuit-work {
  order: 1;
  flex: 1 1 auto;
  padding-left: 0;
}
#workspace-view.xp-shell .parts-handle {
  order: 2;
  width: 54px;
  height: 20px;
  align-self: center;
}
/* The chevrons in styles.css point left and right; stacked, they have to
 * point down (fold) and up (bring back). */
#workspace-view.xp-shell .parts-handle::before { content: "\276F"; transform: rotate(90deg); }
#workspace-view.xp-shell .parts-handle[aria-expanded="false"]::before {
  content: "\276F";
  transform: rotate(-90deg);
}
#workspace-view.xp-shell .parts-library {
  order: 3;
  flex: 0 0 auto;
  max-height: 34%;
  padding: 0;
  border-right: 0;
  border-top: 1px solid var(--xp-border);
}
#workspace-view.xp-shell .parts-head { padding: 8px 2px 0; }
/* Smaller cards, more of them. A part is recognised by its picture, and at
 * 84px a shelf of thirty-six parts was four rows deep with the bench pushed
 * up above it. */
#workspace-view.xp-shell .parts-shelf {
  grid-template-columns: repeat(auto-fill, minmax(62px, 1fr));
  gap: 6px;
  padding: 0 2px 4px;
}
#workspace-view.xp-shell .part-card {
  gap: 4px;
  padding: 6px 4px 5px;
  border-color: var(--xp-border);
  border-radius: var(--xp-radius-sm);
  background: var(--xp-surface);
  font-size: 10.5px;
  line-height: 1.15;
}
#workspace-view.xp-shell .part-card:hover {
  border-color: var(--xp-orange);
  background: var(--xp-warm);
}
#workspace-view.xp-shell .part-card svg,
#workspace-view.xp-shell .part-card img { width: 26px; height: 26px; }

/* Run and Reset lead; what you are holding, and Clear, follow. The holding
 * label takes the middle so Clear sits at the far end, away from Run — the
 * two are one slip apart and only one of them is reversible. */
#workspace-view.xp-shell .circuit-tools { gap: 6px; min-height: 34px; }
#workspace-view.xp-shell .circuit-tools .primary-button,
#workspace-view.xp-shell .circuit-tools .secondary-button {
  min-height: 30px;
  padding: 0 12px;
  font-size: 12.5px;
}
#workspace-view.xp-shell .circuit-holding {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Which set of parts, and the search for one, on the same line. Stacked they
 * cost two rows of a shelf that is only a third of the panel — and neither
 * of them needs the full width. */
#workspace-view.xp-shell .parts-head {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 8px 2px 6px;
}
#workspace-view.xp-shell .parts-drawer { flex: 0 0 44%; min-width: 0; }
#workspace-view.xp-shell .parts-search {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
}
#workspace-view.xp-shell .parts-search input { padding: 6px 9px; font-size: 13px; }

/* A backdrop is 4:3 and a sprite is a figure, so the two pickers are not the
 * same grid: at the sprite's 96px a market scene is a smudge. */
.xp-picker-grid.xp-picker-wide {
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.xp-picker-wide .xp-pick img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 6px;
}


/* ------------------------------------------- room for the stage itself

   The panel beside a Scratch project is a stage and the things that
   describe it, and it had drifted into being the other way round: two rows
   of fields, a permanent line of instruction and a row of buttons took
   more of the height than the stage they referred to. On a tablet the
   stage was the smaller half of its own panel.

   The stage takes the space that is left over rather than a share of it,
   and everything else takes exactly what it needs. */
.stage-view {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/* Everything that describes the stage takes what it needs and no more. */
.stage-view > .xp-inspector-tabs,
.stage-view > .xp-inspector,
.stage-view > .xp-stage-controls,
.stage-view > .stage-says { flex: 0 0 auto; }
/* The stage itself is NOT given `flex: 1`. It is locked to 4:3 forty lines
   up and sized by its width, which is right — a stage that stretched would
   show a different shape from the one a learner's project is drawn for.
   What it gains is the height the two field rows and the permanent hint
   used to take, which on a tablet is about a fifth of the panel. */

/* Where a sprite speaks. Empty most of the time, and empty it should cost
   nothing — it was seeded with an instruction, which made two permanent
   lines out of a thing that is usually silent. */
.stage-says:empty { display: none; margin: 0; }

/* ---------------------------------------------------------------- the seams
 *
 * The lines between the palette, the canvas and the simulator, made to move.
 * The Code Editor has had this since it was built; everywhere else the
 * widths were decided once here, and a learner on a small laptop had no way
 * to give the blocks any more room than the designer had guessed.
 *
 * Positioned from the column widths rather than placed by hand, the same
 * way `.xp-fold-blocks` is and for the same reason: an absolutely positioned
 * child of a grid is laid out inside its grid AREA, so a seam given a
 * column would be measured from that column's edge instead of the shell's.
 * They are named in the catch-all's exclusion list above so they never get
 * a column at all.
 */

.xp-grip {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 25;                 /* under .xp-fold, which is 30 and sits on it */
  width: 9px;
  cursor: col-resize;
  background: transparent;
  /* The grab area is nine pixels; the line that lights up is two. A target
     you can actually hit, without a nine-pixel bar of colour down the
     middle of the workspace. */
  transition: background 0.12s ease;
  touch-action: none;          /* or a tablet scrolls the page instead */
}

.xp-grip::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 50%;
  width: 2px;
  transform: translateX(-50%);
  border-radius: 2px;
  background: transparent;
  transition: background 0.12s ease;
}

.xp-grip:hover::after,
.xp-grip:focus-visible::after,
.xp-grip[data-dragging="true"]::after {
  background: var(--xp-orange);
}

.xp-grip:focus-visible {
  outline: 2px solid var(--xp-orange);
  outline-offset: -2px;
}

/* Between the palette and the canvas, and between the canvas and the
   simulator. Written as the column widths so both track the fold, the three
   views and a drag without anything having to reposition them. */
.xp-grip-palette {
  left: calc(var(--palette-rail-width) - 4px);
  transition: left var(--xp-transition), background 0.12s ease;
}
.xp-grip-sim {
  right: calc(var(--sim-width) - 4px);
  transition: right var(--xp-transition), background 0.12s ease;
}
#workspace-view.xp-workspace[data-dragging="true"] .xp-grip {
  transition: background 0.12s ease;
}
@media (prefers-reduced-motion: reduce) {
  .xp-grip { transition: background 0.12s ease; }
}

/* The two focus views name their own columns, so their seams are not the
   shell's to move. A grip that moved a property nothing was reading would
   be a handle that does nothing, which is worse than no handle. */
#workspace-view.xp-workspace[data-view="blocks-focus"] .xp-grip,
#workspace-view.xp-workspace[data-view="simulator-focus"] .xp-grip-sim {
  display: none;
}

/* And a workspace with no simulator column has no seam beside it. */
#workspace-view.xp-shell > #simulate-view[hidden] ~ .xp-grip-sim,
#workspace-view.xp-shell[data-editor-view="design"] .xp-grip,
#workspace-view.xp-shell[data-editor-view="editor"] .xp-grip {
  display: none;
}

/* A finger needs more to aim at than a pointer does. */
@media (pointer: coarse) {
  .xp-grip { width: 18px; }
  .xp-grip-palette { left: calc(var(--palette-rail-width) - 9px); }
  .xp-grip-sim { right: calc(var(--sim-width) - 9px); }
}

/* --------------------------------------------- one project, one simulator
 *
 * Robot and Board are two renderings of one program, and offering both is
 * right when the project is simply "a robot" — you may want to look at the
 * board that drives it. It is not right when the learner has said which of
 * the two they are building: somebody who chose Electronics and named their
 * circuit did not ask to be shown a robot on a field, and the tab that
 * offers it is a way to end up somewhere confusing by accident.
 *
 * `openedAs` in app.js already writes the project's type here and already
 * picks the right simulator. Until now nothing read the attribute, so the
 * tabs stood there in both. */
#workspace-view.xp-shell[data-project-type="robotics"] .xp-sim-tabs,
#workspace-view.xp-shell[data-project-type="electronics"] .xp-sim-tabs {
  display: none;
}


/* ORDER MATTERS HERE, and it caught me out once.
 *
 * This block was first written above the 1140px rules, which re-declare
 * `grid-template-columns` — and 375px is also under 1140, so those won and
 * the phone kept a 300px simulator column beside a 19px canvas. Same
 * specificity, later wins; it lives at the end of the file for that reason
 * and should stay there.
 */

/* ---------------------------------- the simulator on a small screen
 *
 * The picture is the point. The dock (telemetry, buttons, challenge, log)
 * is one switch in the run row and starts hidden on a phone; the mission
 * name and the run's numbers over the picture shrink to one small line;
 * the readout under the buttons goes, because the numbers are already on
 * the picture. Run, Step and Reset stay exactly as they are. */
#workspace-view.xp-shell > #simulate-view[data-dock="hidden"] > .sim-devices { display: none; }
#workspace-view.xp-shell .sim-dock-toggle[aria-expanded="true"] { border-color: var(--xp-orange); color: var(--xp-orange-strong); }
@media (max-width: 1140px), (max-height: 720px) {
  #workspace-view.xp-shell .sim-controls { gap: 6px; padding: 6px 10px; }
  #workspace-view.xp-shell .sim-controls .sim-readout { display: none; }
  #workspace-view.xp-shell .sim-controls .primary-button.compact,
  #workspace-view.xp-shell .sim-controls .secondary-button.compact { min-height: 34px; padding: 0 14px; }
  #workspace-view.xp-shell .sim-setup-toggle { margin-left: auto; min-height: 28px; padding: 0 8px; font-size: 11.5px; }
  #workspace-view.xp-shell .sim-setup-toggle + .sim-setup-toggle { margin-left: 0; }
  /* Where you are and how the run is going: one small line each, top-left. */
  #workspace-view.xp-shell .sim-where { top: 6px; left: 6px; gap: 0; padding: 3px 8px; }
  #workspace-view.xp-shell .sim-where-campaign { display: none; }
  #workspace-view.xp-shell .sim-where-level { font-size: 11px; line-height: 1.2; }
  #workspace-view.xp-shell .sim-where:not([hidden]) ~ .sim-hud { top: 30px; }
  #workspace-view.xp-shell .sim-hud { top: 6px; left: 6px; gap: 4px; }
  #workspace-view.xp-shell .hud-cell { padding: 2px 7px; font-size: 11px; gap: 4px; }
  #workspace-view.xp-shell .hud-icon { font-size: 11px; }
  /* The view tools: glyphs only, in a short column at the top right, so
     they neither cover the mission line nor run down over Run. */
  #workspace-view.xp-shell[data-view="simulator-focus"] .sim-rail { left: auto; right: 8px; top: 8px; gap: 2px; padding: 3px; }
  #workspace-view.xp-shell[data-view="simulator-focus"] .rail-button { min-width: 34px; width: 34px; min-height: 34px; height: 34px; gap: 0; }
  #workspace-view.xp-shell[data-view="simulator-focus"] .rail-word { display: none; }
}

/* ------------------------------------------------------- a phone
 *
 * WHAT WAS ACTUALLY HAPPENING AT 375px. The three columns are 132 for the
 * palette, whatever is left for the canvas, and 420–560 for the simulator.
 * On a phone that adds up to more than the screen, so the middle track —
 * the one told it may shrink to nothing — did exactly that:
 *
 *     grid-template-columns: 112px 0px 300px
 *
 * The block canvas was ZERO PIXELS WIDE. Not cramped: absent. The app
 * opened, the project loaded, the toolbox was there, and there was nowhere
 * to put a block. Nothing below 1140px had ever been designed, and the
 * Android build is the same page in a WebView.
 *
 * The rule here is one thing at a time, which is the only honest layout for
 * a 375px screen: the rail folds to its icons, the canvas takes everything
 * else, and the simulator appears only when the focus tabs ask for it —
 * which they already do, and which is why "split" cannot mean two columns
 * here and quietly becomes "blocks".
 *
 * The seams go too. A drag handle between two columns is a mouse idea; on a
 * phone it is 18px of canvas that swallows a fingertip.
 */
@media (max-width: 700px) {
  /* The header ran to 575px in a 375px window — the view tabs and the
     project menu simply hung off the side, taking the page's horizontal
     scroll with them. It wraps now rather than overflowing. */
  .topbar { flex-wrap: wrap; row-gap: 6px; }
  #project-bar { flex-wrap: wrap; row-gap: 6px; }

  #workspace-view.xp-workspace,
  #workspace-view.xp-workspace[data-view="blocks-focus"],
  #workspace-view.xp-workspace[data-view="simulator-focus"] {
    /* Folded, and not draggable back open: there is no room for the words
       and the icons are the whole of what a phone can show. */
    --palette-open-width: var(--palette-folded-width);
    --palette-rail-width: var(--palette-folded-width);
  }

  /* Blocks, and "split", which on a phone is blocks. */
  #workspace-view.xp-workspace {
    grid-template-columns: var(--palette-rail-width) minmax(0, 1fr) 0;
  }
  #workspace-view.xp-workspace:not([data-view="simulator-focus"]) #simulate-view {
    display: none;
  }

  /* The simulator, when it is what was asked for: all of the screen. The
     palette goes with the canvas — there is nothing to drag a block onto. */
  #workspace-view.xp-workspace[data-view="simulator-focus"] {
    grid-template-columns: 0 0 minmax(0, 1fr);
  }
  #workspace-view.xp-workspace[data-view="simulator-focus"] #blockly-canvas,
  #workspace-view.xp-workspace[data-view="simulator-focus"] #xp-palette-rail {
    display: none;
  }

  /* A seam is a mouse idea. */
  #workspace-view.xp-workspace .xp-grip { display: none; }
}

/* ------------------------------------------------------------------ */
/* THE COSTUMES AND SOUNDS TABS, AND THE PAINT AND SOUND EDITORS.       */
/* Built by public/stage/*.js. The strips reuse .xp-thumb so a costume  */
/* looks like a sprite tile; the editors are dialogs like the pickers. */
/* ------------------------------------------------------------------ */
.xp-inspector-media { display: grid; gap: 10px; align-content: start; overflow: auto; }
.xp-media-bar { display: flex; flex-wrap: wrap; gap: 6px; }
.xp-media-add {
  padding: 6px 10px;
  border: 1px solid var(--xp-border);
  border-radius: 999px;
  background: var(--xp-surface);
  color: var(--xp-text);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.xp-media-add:hover { border-color: var(--xp-orange); color: var(--xp-orange); }
.xp-media-tools { display: flex; flex-wrap: wrap; gap: 6px; align-items: end; }
.xp-media-tools button, .xp-sound-actions button, .xp-paint-actions button, .xp-paint-tool {
  padding: 6px 10px;
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-sm);
  background: var(--xp-surface);
  color: var(--xp-text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.xp-media-tools button:disabled { opacity: .45; cursor: default; }
.xp-media-thumbs .xp-thumb img { background: repeating-conic-gradient(rgba(127,127,127,.12) 0 25%, transparent 0 50%) 0 0 / 12px 12px; }
.xp-sound-thumb { grid-template-rows: 1fr auto; }
.xp-sound-thumb-play {
  width: 36px; height: 36px;
  border: 0; border-radius: 50%;
  background: #cf63cf; color: #fff;
  font-size: 14px; cursor: pointer;
}
.xp-sound-thumb-length { font-size: 11px; color: var(--xp-muted); }
.xp-sound-tile { display: grid; justify-items: center; gap: 6px; }
.xp-sound-editor { display: grid; gap: 8px; }
.xp-sound-head { display: flex; gap: 8px; align-items: end; }
.xp-sound-length { font-size: 12px; color: var(--xp-muted); }
.xp-sound-wave-wrap { position: relative; }
.xp-sound-wave { display: block; width: 100%; height: 96px; border: 1px solid var(--xp-border); border-radius: var(--xp-radius-sm); background: var(--xp-canvas); }
.xp-sound-trim { position: absolute; left: 0; right: 0; width: 100%; margin: 0; pointer-events: none; appearance: none; background: transparent; height: 14px; }
.xp-sound-trim-from { top: -4px; }
.xp-sound-trim-to { bottom: -4px; }
.xp-sound-trim::-webkit-slider-thumb { pointer-events: auto; appearance: none; width: 12px; height: 18px; border-radius: 4px; background: var(--xp-orange); cursor: ew-resize; }
.xp-sound-trim::-moz-range-thumb { pointer-events: auto; width: 12px; height: 18px; border-radius: 4px; background: var(--xp-orange); border: 0; cursor: ew-resize; }
.xp-sound-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.xp-sound-none { margin: 0; font-size: 13px; color: var(--xp-muted); }
.xp-picker-search { flex: 1 1 auto; margin: 0 10px; padding: 6px 10px; border: 1px solid var(--xp-border); border-radius: 999px; background: var(--xp-surface); color: var(--xp-text); font: inherit; }

/* the paint editor */
.xp-paint { width: min(96vw, 980px); }
.xp-paint-name { display: flex; align-items: center; gap: 6px; margin-left: auto; margin-right: 8px; font-size: 13px; }
.xp-paint-name input { padding: 5px 8px; border: 1px solid var(--xp-border); border-radius: var(--xp-radius-sm); background: var(--xp-surface); color: var(--xp-text); font: inherit; }
.xp-paint-body { display: grid; grid-template-columns: 112px 1fr 180px; gap: 10px; padding: 10px 14px; }
.xp-paint-tools { display: grid; gap: 6px; align-content: start; }
.xp-paint-tool[aria-pressed="true"] { border-color: var(--xp-orange); background: var(--xp-orange); color: #fff; }
.xp-paint-stage { min-height: 320px; max-height: 62vh; overflow: auto; border: 1px solid var(--xp-border); border-radius: var(--xp-radius-md); background: repeating-conic-gradient(rgba(127,127,127,.12) 0 25%, transparent 0 50%) 0 0 / 16px 16px; }
.xp-paint-scroll { position: relative; display: inline-block; margin: 12px; }
.xp-paint-canvas, .xp-paint-overlay { display: block; image-rendering: auto; }
.xp-paint-overlay { position: absolute; inset: 0; touch-action: none; cursor: crosshair; }
.xp-paint-side { display: grid; gap: 10px; align-content: start; }
.xp-paint-palette { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; }
.xp-paint-swatch { aspect-ratio: 1; border: 2px solid transparent; border-radius: 6px; cursor: pointer; }
.xp-paint-swatch[aria-checked="true"] { border-color: var(--xp-text); }
.xp-paint-row { display: grid; gap: 4px; font-size: 12px; color: var(--xp-muted); }
.xp-paint-row input[type="text"] { padding: 5px 8px; border: 1px solid var(--xp-border); border-radius: var(--xp-radius-sm); background: var(--xp-surface); color: var(--xp-text); font: inherit; }
.xp-paint-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.xp-paint-foot { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--xp-border); }
.xp-paint-hint { flex: 1 1 auto; font-size: 12px; color: var(--xp-muted); }
@media (max-width: 720px) {
  .xp-paint-body { grid-template-columns: 1fr; }
  .xp-paint-tools { grid-template-columns: repeat(4, 1fr); }
}

/* the recorder and the camera */
.xp-record-body, .xp-camera-body { display: grid; gap: 10px; padding: 14px; }
.xp-record-meter { height: 12px; border-radius: 999px; background: var(--xp-warm); overflow: hidden; }
.xp-record-meter span { display: block; height: 100%; width: 0; background: #cf63cf; transition: width 60ms linear; }
.xp-record-clock { margin: 0; font-size: 22px; font-weight: 700; }
.xp-record-say, .xp-record-opt { margin: 0; font-size: 13px; color: var(--xp-muted); }
.xp-camera-body video { width: min(90vw, 480px); border-radius: var(--xp-radius-md); background: #000; }


/* ------------------------------------------ Code, Costumes, Sounds tabs
 *
 * A Stage project's editor has three tabs above the palette, the way
 * Scratch has them. The bar takes a row of its own across the palette and
 * canvas columns; the stage panel spans both rows. Costumes and Sounds
 * swap the block canvas, its rail and its docks for the selected sprite's
 * media; the blocks are untouched underneath and come back on Code. */
#workspace-view.xp-shell[data-editor-tabs="shown"] {
  grid-template-rows: auto minmax(0, 1fr);
}
#workspace-view.xp-shell > #xp-editor-tabs {
  grid-column: 1 / 3;
  grid-row: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px 0;
  border-bottom: 1px solid var(--xp-border);
  background: var(--xp-surface);
  min-width: 0;
}
#workspace-view.xp-shell > #xp-editor-tabs[hidden] { display: none; }
#workspace-view.xp-shell[data-editor-tabs="shown"] > .xp-palette-rail,
#workspace-view.xp-shell[data-editor-tabs="shown"] > #blockly-canvas,
#workspace-view.xp-shell[data-editor-tabs="shown"] > #stage-docks,
#workspace-view.xp-shell[data-editor-tabs="shown"] > #xp-editor-media { grid-row: 2; }
#workspace-view.xp-shell[data-editor-tabs="shown"] > #simulate-view { grid-row: 1 / -1; }
.xp-editor-tabs button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 16px;
  border: 0;
  border-radius: 10px 10px 0 0;
  background: transparent;
  color: var(--xp-muted);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
.xp-editor-tabs button:hover { color: var(--xp-text); background: var(--xp-warm); }
.xp-editor-tabs button[aria-selected="true"] {
  color: var(--xp-orange-strong);
  background: var(--xp-warm);
  box-shadow: inset 0 -2px 0 var(--xp-orange);
}
.xp-editor-tabs button:focus-visible { outline: 3px solid var(--xp-orange); outline-offset: -3px; }
.xp-editor-count { font-weight: 600; color: var(--xp-muted); }
.xp-editor-tabs button[aria-selected="true"] .xp-editor-count { color: var(--xp-orange-strong); }
.xp-editor-count:empty { display: none; }
/* The way back to Multilingual Dialogue or the Story Studio, at the far end of the tabs. */
.xp-editor-tabs-gap { flex: 1; }
.xp-editor-tabs .xp-editor-back {
  border-radius: 999px;
  min-height: 32px;
  margin-bottom: 4px;
  padding: 0 14px 0 10px;
  border: 1px solid var(--xp-border);
  color: var(--xp-orange-strong);
  background: var(--xp-surface);
}
.xp-editor-tabs .xp-editor-back::before {
  content: "";
  width: 8px;
  height: 8px;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  margin-right: 2px;
}
.xp-editor-tabs .xp-editor-back:hover { background: var(--xp-warm); }
.xp-editor-tabs .xp-editor-back[hidden] { display: none; }

#workspace-view.xp-shell > #xp-editor-media {
  grid-column: 1 / 3;
  min-width: 0;
  min-height: 0;
  overflow: auto;
  padding: 12px 16px;
  background: var(--xp-canvas);
}
#workspace-view.xp-shell > #xp-editor-media[hidden] { display: none; }
#workspace-view.xp-shell > #xp-editor-media > .xp-inspector-media { padding: 0; max-width: 1100px; }
#workspace-view.xp-shell > #xp-editor-media > .xp-inspector-media[hidden] { display: none; }
/* The Costume and Sound editors are full workspace pages
 * (xplore_scratch_editors_v3.0): while one is open it takes every column,
 * the stage and its seam step aside, and the page does its own scrolling. */
#workspace-view.xp-shell[data-editor-tabs="shown"]:is([data-editor-tab="costumes"], [data-editor-tab="sounds"]) > :is(#xp-editor-tabs, #xp-editor-media) {
  grid-column: 1 / -1;
}
#workspace-view.xp-shell[data-editor-tabs="shown"]:is([data-editor-tab="costumes"], [data-editor-tab="sounds"]) > :is(#simulate-view, .xp-grip) {
  display: none;
}
#workspace-view.xp-shell[data-editor-tabs="shown"]:is([data-editor-tab="costumes"], [data-editor-tab="sounds"]) > #xp-editor-media {
  padding: 0;
  overflow: hidden;
}
#workspace-view.xp-shell > #xp-editor-media > .xp-inspector-media.xs-host { max-width: none; height: 100%; }
/* While a media tab is up, the block canvas and everything that belongs
 * to it are off screen; the blocks stay as they were. */
#workspace-view.xp-shell[data-editor-tab]:not([data-editor-tab="code"])[data-editor-tabs="shown"] > #blockly-canvas,
#workspace-view.xp-shell[data-editor-tab]:not([data-editor-tab="code"])[data-editor-tabs="shown"] > .xp-palette-rail,
#workspace-view.xp-shell[data-editor-tab]:not([data-editor-tab="code"])[data-editor-tabs="shown"] > #xp-drawer-head,
#workspace-view.xp-shell[data-editor-tab]:not([data-editor-tab="code"])[data-editor-tabs="shown"] > #xp-fold-blocks,
#workspace-view.xp-shell[data-editor-tab]:not([data-editor-tab="code"])[data-editor-tabs="shown"] > #stage-docks { display: none; }

/* The block the simulator is running, or stopped at (FR-SIM-003). Blockly's
 * own highlight is a faint emboss nobody could find on a busy canvas. */
.blocklySvg .blocklyHighlighted > .blocklyPath {
  stroke: #f15f24;
  stroke-width: 4px;
  filter: drop-shadow(0 0 6px rgb(241 95 36 / 0.55));
}

/* The AI Coach pane. */
.coach-panel { display: flex; flex-direction: column; gap: 10px; overflow: auto; padding: 8px 2px; }
.coach-privacy, .coach-policy, .coach-status, .coach-empty { margin: 0; font-size: 0.85rem; color: var(--xp-muted, var(--muted)); }
.coach-policy { color: var(--xp-orange-strong, #a83309); font-weight: 600; }
.coach-asks { display: flex; flex-wrap: wrap; gap: 8px; }
.coach-rung { opacity: 0.7; font-weight: 400; }
.coach-answers { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.coach-answer { border: 1px solid var(--xp-border, var(--line)); border-radius: 10px; padding: 10px 12px; }
.coach-answer-kind { margin: 0 0 4px; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--xp-muted, var(--muted)); }
.coach-answer-text { margin: 0; white-space: pre-wrap; line-height: 1.5; }
.coach-caution { margin: 6px 0 0; font-size: 0.8rem; color: var(--xp-orange-strong, #a83309); }
