/*
 * The tool page.
 *
 * Loaded only where a tool is mounted, so the reading column every other page
 * gets can be widened to a workspace here without touching the rest of the site.
 * The `:has` test is the whole mechanism: a page that contains a tool is a page
 * whose main column is not a measure of prose.
 */
/*
 * A tool page's main column is a workspace, not a measure of prose.
 *
 * The width is set with border-box, because otherwise the padding sits outside
 * the maximum and the page is two rem wider than the screen it is on — which is
 * exactly the sideways scroll this is here to avoid.
 */
/*
 * The app frame.
 *
 * A tool page is not a page of prose and must not read like one: it is a window that
 * fills what is left of the screen under the header, split into panes by hairlines,
 * with the panes themselves scrolling and the page not scrolling at all. That is the
 * shape the live site has, and it is what makes a tool feel like an instrument rather
 * than an article with a widget in it.
 *
 * The frame is one grid: the explorer, and the workspace beside it. Inside the
 * explorer, the head is fixed and the tree scrolls; inside the workspace, the
 * content scrolls as one column.
 */
main.tool-page {
  box-sizing: border-box;
  inline-size: 100%;
  max-inline-size: none;
  padding: 0;
  block-size: calc(100dvh - var(--nav-height));
  overflow: hidden;
  /* The handle is placed in this box, and the width it sets is this variable. */
  position: relative;
  --tool-rail: 340px;
}
/*
 * The resize handle, on the seam between the explorer and the tool.
 *
 * Eight pixels wide and mostly invisible, because a divider that announces itself is a
 * divider that gets in the way: it colours on hover and while dragging, and takes the
 * keyboard's arrow keys as well as a pointer.
 */
.tool-resizer {
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(var(--tool-rail) - 4px);
  inline-size: 8px;
  z-index: 5;
  cursor: col-resize;
  touch-action: none;
  background: transparent;
  transition: background 120ms ease;
}
.tool-resizer:hover,
.tool-resizer:focus-visible,
.tool-resizer[data-hover="true"] { background: var(--accent-dim); }
.tool-resizer:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
main.tool-page .tool {
  block-size: 100%;
  min-block-size: 0;
  /* The frame's two rows: everything, and the status line under it. */
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
}
/*
 * The status row: what the tool is doing, in words, announced politely.
 *
 * It is the live site's own row — a hairline above it, the surface behind it, small
 * dim text with the detail pushed to the far end — and it is a live region because a
 * reader who cannot see it still has to be told whether their cartridge was read.
 */
.tool-status {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-block-size: 28px;
  padding: 0.2rem 0.75rem;
  border-block-start: 1px solid var(--border);
  background: color-mix(in oklab, var(--surface) 70%, transparent);
  font-size: 0.89rem;
  color: var(--dim);
}
.tool-status-note { margin-inline-start: auto; color: var(--faint); }
/*
 * The parts of a body, each drawn on its own geometry.
 *
 * They are shown as a grid of separate pictures rather than assembled into a figure,
 * because the placement that would assemble them is not read: a picture of a jumble
 * would be a worse lie than twenty pictures of parts.
 */
.tool-parts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 0.75rem;
  align-items: end;
}
.tool-part {
  display: grid;
  gap: 0.35rem;
  justify-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.5rem;
  min-inline-size: 0;
}
.tool-part figcaption {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--faint);
  text-align: center;
}
main.tool-page .tool-shell {
  grid-template-columns: var(--tool-rail) minmax(0, 1fr);
  /* One row, exactly as tall as the frame: without this the row grows to its content
     and neither pane ever scrolls, which is the whole point of the frame. */
  grid-template-rows: minmax(0, 1fr);
  gap: 0;
  block-size: 100%;
  min-block-size: 0;
  /* The panes fill the row rather than sitting at the top of it: an item aligned to
     the start takes its content's height, which is how a pane stops being a pane. */
  align-items: stretch;
}
/* The explorer is a pane, not a card: square, hairline on its inner edge, translucent
   so the page's wash shows through, and a grid of its own so its tree scrolls while
   its heading stays put. */
main.tool-page #explorer {
  position: static;
  max-block-size: none;
  border: 0;
  border-inline-end: 1px solid var(--border);
  border-radius: 0;
  background: color-mix(in oklab, var(--surface) 55%, transparent);
  display: grid;
  /* Heading, then the list that scrolls, then the drop zone pinned under it. The rows
     have no gap: the heading's own hairline and the drop zone's are the divisions, and
     the shell's gap is inherited from `.tool-shell` otherwise, which would leave a
     strip of page between the tree and the box under it. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 0;
  overflow: hidden;
  padding: 0;
}
main.tool-page #tool-explorer {
  min-block-size: 0;
  overflow-y: auto;
  padding: 0.9rem;
}
main.tool-page #tool-drop {
  border-block-start: 1px solid var(--border);
  padding: 0.9rem;
}
/*
 * The explorer's head: the name at one end, the actions at the other, a hairline
 * under it.
 *
 * It is the live site's own row, and the shape matters: a heading that does not look
 * like a paragraph that happens to be bold. The name is a heading and is labelled by
 * the tree, so the two say the same thing once — the row itself is a header and
 * carries no role of its own.
 */
main.tool-page .tool-explorer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  block-size: 31px;
  padding: 0 0.4rem 0 0.6rem;
  border-block-end: 1px solid var(--border);
}
main.tool-page .tool-explorer-head h2 {
  font-size: 0.79rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}
main.tool-page .tool-explorer-actions { display: flex; align-items: center; gap: 0.15rem; }
/*
 * An icon-only button: a mark that is a control.
 *
 * It has no text, so its name is its `aria-label` — which is also its tooltip, so the
 * two cannot drift apart — and it keeps a transparent border so its box does not move
 * when the hover colour arrives.
 */
main.tool-page .tool-icon-button {
  font: inherit;
  display: grid;
  place-items: center;
  inline-size: 32px;
  block-size: 32px;
  padding: 0;
  color: var(--dim);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
main.tool-page .tool-icon-button:hover { color: var(--text); background: var(--elevated); }
main.tool-page .tool-icon-button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
main.tool-page .tool-icon-button svg { inline-size: 14px; block-size: 14px; }
/*
 * The tree of cartridges.
 *
 * The live site's explorer, drawn with this site's own variables: a cartridge is its
 * own node with the file's name, a chip saying what the cartridge is and a close
 * button; under it, a row per region with the number of rows that region holds at the
 * far end, in the monospaced column the tables use. The name is trimmed rather than
 * wrapped, because a cartridge file name is long and a rail is narrow, and the whole
 * name is in the `title` attribute.
 */
main.tool-page .tool-tree {
  list-style: none;
  margin: 0;
  padding: 0.35rem 0.5rem 0.6rem;
}
main.tool-page .tool-tree-group {
  list-style: none;
  margin: 0;
  padding: 0 0 0 14px;
}
/* A closed cartridge's regions are out of the document as far as a reader is
   concerned: hidden, and out of the tree's own walk for the same reason. */
main.tool-page .tool-tree-cartridge-item[aria-expanded="false"] > .tool-tree-group { display: none; }
main.tool-page .tool-tree-item { margin: 1px 0; }
main.tool-page .tool-tree-button {
  font: inherit;
  display: block;
  inline-size: 100%;
  padding: 0;
  text-align: start;
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}
main.tool-page .tool-tree-button:focus { outline: none; }
main.tool-page .tool-tree-item[tabindex="0"] > .tool-tree-button:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
main.tool-page .tool-tree-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-block-size: 32px;
  padding: 0 0.3rem 0 0.15rem;
  border-radius: 6px;
  color: var(--dim);
  /* The live explorer's 13px on a 16px root, brought over as a rem so this site's own
     root decides the size. A tree read at 11px is a tree a reader leans into. */
  font-size: 0.93rem;
  white-space: nowrap;
}
main.tool-page .tool-tree-button:hover .tool-tree-row,
main.tool-page .tool-tree-button:focus-visible .tool-tree-row { background: var(--elevated); color: var(--text); }
main.tool-page .tool-tree-item[aria-selected="true"] > .tool-tree-button > .tool-tree-row {
  background: var(--accent-dim);
  color: var(--text);
}
main.tool-page .tool-tree-row svg { inline-size: 13px; block-size: 13px; flex: none; color: var(--faint); }
main.tool-page .tool-tree-cartridge .tool-tree-row { color: var(--text); font-size: 0.95rem; font-weight: 500; }
/* The accent belongs to the cartridge's own mark, not to every svg in the row: matching all of them painted
   the close button orange, which reads as a warning on a control that only closes a tab. */
main.tool-page .tool-tree-cartridge .tool-tree-row .tool-tree-mark { color: var(--accent); }
/* The twisty turns a quarter turn when the node is open, and fills the row's height
   so the whole mark is a hit target rather than the twelve pixels it draws. It needs the row's own
   specificity to say so: `main.tool-page .tool-tree-row svg` sets 13px on every icon, and a one-class
   selector loses to it, so the chevron stayed 13px tall and sat 9.5px above the row's midline. */
main.tool-page .tool-tree-row svg.tool-tree-twisty {
  align-self: stretch;
  box-sizing: content-box;
  block-size: auto;
  padding-inline: 0.28rem;
  margin-inline: -0.28rem;
  transition: transform 120ms ease;
  overflow: hidden;
}
main.tool-page .tool-tree-cartridge-item[aria-expanded="true"] .tool-tree-twisty { transform: rotate(90deg); }
main.tool-page .tool-tree-label {
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The chip: the cartridge's game, in the pill the live site uses for a fact of this
   shape. Uppercased and letterspaced, with the line height of a small label. */
main.tool-page .tool-tree-badge {
  flex: none;
  font-family: var(--sans);
  font-size: 9.5px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  /* --border-strong, not --border-input: the input grey is bright enough that the pill read as a different
     kind of thing from the switch and the export controls beside it, which is the "odd border" a reader sees
     without being able to name it. */
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 3.5px 6px;
}
/* The chip is the first thing to give way, because it repeats what the row already says and the cartridge
   summary prints it in full: at 276px of rail it took 99px of a 228px row and left the name 29px, which is
   `W…`. The name is the row's subject; the chip is a note about it. */
@media (max-width: 1500px) {
  main.tool-page .tool-tree-badge { display: none; }
}
main.tool-page .tool-tree-count {
  flex: none;
  margin-inline-start: auto;
  font-family: var(--mono);
  font-size: 0.79rem;
  font-weight: 400;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}
/* The close button is quiet until the row is under the pointer: a control that is
   always shouting in a list of six cartridges is noise, and it is still reachable by
   keyboard, where the row's own focus ring covers it. */
main.tool-page .tool-tree-close {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: 26px;
  block-size: 26px;
  padding: 0;
  color: color-mix(in oklab, var(--faint) 70%, transparent);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
main.tool-page .tool-tree-close svg { inline-size: 12px; block-size: 12px; }
main.tool-page .tool-tree-row:hover .tool-tree-close,
main.tool-page .tool-tree-close:focus-visible { color: var(--text); background: var(--surface); }
main.tool-page .tool-tree-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
main.tool-page .tool-tree-empty {
  padding: 0.5rem;
  font-size: 0.93rem;
  line-height: 1.5;
  color: var(--faint);
}
main.tool-page .tool-view-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-block-size: 31px;
}
main.tool-page .tool-view-head h2 { min-inline-size: 0; }
main.tool-page .tool-content {
  min-block-size: 0;
  overflow-y: auto;
  padding: 1rem;
  gap: 0.75rem;
  /* One row, the pane's own height, and the workspace takes all of it. */
  grid-template-rows: minmax(0, 1fr);
}
/*
 * The welcome: what the tool is, in the area it will work in.
 *
 * This is the previous build's welcome view, and it is not a page above the tool — it is the tool's
 * own area before a cartridge is open, distributed over the height. The hero, the table of games it
 * reads and the work it does sit here; the module's prose is the last block rather than a page of its
 * own. The moment a cartridge lands the chassis replaces all of it, so a modder gets the whole window
 * and a reader arriving for the first time gets the tool's face.
 */
main.tool-page .tool-welcome {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  flex: 0 0 auto;
  min-block-size: 0;
}
main.tool-page .tool-welcome .tool-intro { padding-block-start: 0.4rem; }
main.tool-page .tool-welcome .tool-doc { max-inline-size: none; }
/* The prose about the tool, behind its own summary: it is a page's worth of reading, and a reader
   who has not opened a cartridge yet is looking at the tool rather than at the text about it. */
main.tool-page .tool-welcome .tool-guide {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
main.tool-page .tool-welcome .tool-guide-summary {
  padding: 0.55rem 0.85rem;
  cursor: pointer;
  font-size: 0.88rem;
  color: var(--dim);
}
main.tool-page .tool-welcome .tool-guide-summary:hover { color: var(--text); }
main.tool-page .tool-welcome .tool-guide[open] .tool-guide-summary { border-block-end: 1px solid var(--border); }
main.tool-page .tool-welcome .tool-guide-body { padding: 0.85rem; max-block-size: 40vh; overflow-y: auto; }
/* The stage and the layout column are panes too, for the same reason. */
main.tool-page .tool-workspace { gap: 0; min-block-size: 0; align-items: stretch; }
main.tool-page .tool-workspace .tool-col-center {
  display: flex;
  flex-direction: column;
  min-block-size: 0;
  min-inline-size: 0;
}
main.tool-page .tool-workspace .tool-col-center > .tool-stage { flex: 1 1 auto; }
main.tool-page .tool-workspace .tool-col {
  border: 0;
  border-radius: 0;
  background: none;
  border-inline-start: 1px solid var(--border);
  padding-inline: 1.25rem;
}
main.tool-page .tool-workspace .tool-col:first-child { border-inline-start: 0; padding-inline-start: 0; }
@media (max-width: 1100px) {
  main.tool-page {
    block-size: auto;
    overflow: visible;
  }
  /*
   * The stacked shell needs two rows, and this is the whole bug: the wide layout is one row of
   * `minmax(0, 1fr)` inside a 100%-height frame, so that the two panes scroll inside it. Switching to a
   * single column leaves the explorer stacked above the content but still inside a one-row template, and
   * the one explicit row gets the leftover height — measured at 1100px as `0px 872px`. The explorer then
   * renders as a 1px sliver (its own bottom border) and `overflow: hidden` clips its head, its tree and
   * its drop zone, so there is no cartridge open button on screen at all.
   *
   * So the rows become content-sized, the frame stops claiming a height it no longer scrolls inside, and
   * the content pane stops being an internal scroller: the page scrolls normally instead, which is what a
   * stacked layout wants.
   */
  main.tool-page .tool-shell {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto;
    block-size: auto;
  }
  main.tool-page .tool-content {
    overflow: visible;
    min-block-size: 0;
  }
  main.tool-page #explorer { border-inline-end: 0; border-block-end: 1px solid var(--border); }
  /* And the explorer keeps its own scroll, bounded, rather than growing to the height of a 28-row tree. */
  main.tool-page #tool-explorer { max-block-size: 60vh; }
  main.tool-page .tool-workspace .tool-col { border-inline-start: 0; padding-inline: 0; }
}
/* No page scroll while the frame is on screen: the panes scroll instead. */
@media (min-width: 1101px) {
  body:has(main.tool-page) {
    overflow: hidden;
    /* The tool is the whole window: the frame is exactly the height under the header, so a footer
       after it is 96 pixels of page that can only be reached by scrolling the app out of place.
       Measured at 1440x900: the frame ended at 900 and the document was 996. */
    block-size: 100dvh;
    grid-template-rows: auto minmax(0, 1fr);
  }
  body:has(main.tool-page) .foot { display: none; }
}
/* The prose about the tool scrolls with the tool, in the reading column's width. */
main.tool-page .tool-doc { max-inline-size: var(--measure); }
main:has(.tool) > .tool-doc {
  max-inline-size: var(--measure);
  margin-inline: auto;
}
.tool-mount { min-block-size: 5rem; }
.tool-doc {
  display: grid;
  gap: 1.1rem;
  line-height: 1.75;
  color: var(--dim);
}
.tool-doc h2 { font-size: 1.4rem; color: var(--text); margin-block-start: 1.2rem; }
.tool-doc h3 { font-size: 1.12rem; color: var(--text); margin-block-start: 1rem; }
.tool-doc a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.tool-doc code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: var(--elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.05rem 0.3rem;
}
/*
 * The shell: the explorer, and beside it the tool.
 *
 * Two columns, not three. The tool's own words come first in the content column
 * because that is the order they are read in, and the tables and the layout panels
 * sit under them in a row of their own — so a reader who has just arrived sees what
 * the tool is, and a reader who has opened a cartridge sees the work.
 */
.tool-shell {
  display: grid;
  grid-template-columns: minmax(200px, 276px) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
.tool-content {
  display: grid;
  gap: 1.1rem;
  min-inline-size: 0;
}
.tool-intro { display: grid; gap: 0.75rem; justify-items: center; text-align: center; }
.tool-hero { font-size: clamp(1.75rem, 3.4vw, 2.86rem); font-weight: 800; line-height: 1.1; letter-spacing: -0.02em; }
.tool-intro .lead { max-inline-size: 62ch; }
/*
 * The work cards: what each part of the tool is for, in a sentence each.
 *
 * They are read, not clicked — this build reads a cartridge and does not write one —
 * so nothing here carries a hover affordance that would promise otherwise.
 */
.tool-work { display: grid; gap: 0.75rem; }
.tool-work-head {
  font-size: 0.78rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--faint);
}
/* Three to a row on a wide screen, so eight cards are three full rows rather than a
   row and two stragglers. */
.tool-work-list { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.75rem; }
@media (max-width: 1400px) {
  .tool-work-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 1000px) {
  .tool-work-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .tool-work-list { grid-template-columns: minmax(0, 1fr); }
}
.tool-work-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
  min-inline-size: 0;
}
.tool-work-card h3 { font-size: 1rem; font-weight: 600; color: var(--text); }
.tool-work-card p { margin-block-start: 0.4rem; font-size: 0.92rem; line-height: 1.55; color: var(--dim); }
.tool-workspace {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(260px, 340px);
  gap: 0.9rem;
  align-items: start;
}
/* Before a cartridge is open there are no layouts to show, and a column that
   exists only to be empty is a column that reads as a failure. The track goes with
   it, so the stage takes the width instead of leaving a hole. */
.tool-workspace:not(:has(.tool-col-right > *)) {
  grid-template-columns: minmax(0, 1fr);
}
.tool-workspace:not(:has(.tool-col-right > *)) .tool-col-right { display: none; }
.tool-col {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem;
  min-inline-size: 0;
  display: grid;
  gap: 0.9rem;
  align-content: start;
  /* The backstop: a panel narrower than its own content scrolls inside itself
     rather than stretching the page it is on. */
  overflow-x: auto;
}
/* The explorer scrolls inside itself once it is taller than the window. A sticky
   column taller than the viewport pins at its top and hides its own end, so a view
   at the bottom of the list could never be reached at all; the scroll container is
   focusable for the same reason a table's wrapper is. */
.tool-col-left {
  position: sticky;
  inset-block-start: 4.2rem;
  max-block-size: calc(100vh - 5.6rem);
  overflow-y: auto;
}
/* ─── getting cartridges in ─────────────────────────────────── */
/*
 * The drop zone: the live site's own box, sized for this site's rail.
 *
 * A dashed edge, a wash of the surface behind it, the accent while a file is over
 * it, and the real file input inside rather than behind it — so the control the
 * platform draws is the control a reader gets, and the whole box is a target for a
 * pointer that drags.
 */
.tool-drop {
  display: grid;
  gap: 0.45rem;
  justify-items: center;
  text-align: center;
  border: 1px dashed var(--border-input);
  border-radius: var(--radius);
  padding: 0.7rem 0.6rem;
  background: color-mix(in oklab, var(--surface) 60%, transparent);
  transition: border-color 160ms, background-color 160ms;
}
.tool-drop[data-over] { border-color: var(--accent); background: var(--accent-dim); }
.tool-drop-lead {
  font-size: 0.82rem;
  line-height: 1.35;
  font-weight: 600;
  color: var(--dim);
}
.tool-drop-input {
  font: inherit;
  font-size: 0.72rem;
  color: var(--dim);
  inline-size: 100%;
  max-inline-size: 13rem;
}
.tool-drop-input::file-selector-button {
  font: inherit;
  font-size: 0.75rem;
  margin-inline-end: 0.4rem;
  padding: 0.25rem 0.5rem;
  color: var(--text);
  background: var(--elevated);
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.tool-hint { font-size: 0.78rem; color: var(--faint); line-height: 1.55; }
/* ─── the texture studio ────────────────────────────────────── */
/*
 * The editor: a rail of tools down the left, the image in the middle, the palette and the
 * layers beside it.
 *
 * The rail is the shape every paint program has, and it is here for the same reason: a
 * reader's hand goes back to the same few tools, and a column of them is a place the hand
 * learns. It is fixed width and the stage takes what is left, so the rail is always in the
 * same place whatever the image is and however wide the pane happens to be.
 *
 * The stage is capped at the image's own size plus its padding: a 32-pixel texture in a
 * thousand-pixel hole is a texture a reader has to hunt for. `min-inline-size: 0` on every
 * level is what lets the columns shrink at all — a grid item refuses to go below its
 * content otherwise, and one wide panel then pushes the whole editor off the pane.
 */
.studio {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(15rem, 19rem);
  gap: 0;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: color-mix(in oklab, var(--surface) 55%, transparent);
  min-inline-size: 0;
}
.tool-rail {
  display: grid;
  gap: 1px;
  align-content: start;
  padding: 0.35rem;
  border-inline-end: 1px solid var(--border);
  background: color-mix(in oklab, var(--surface-2) 45%, transparent);
}
.tool-rail-button {
  font: inherit;
  display: grid;
  justify-items: center;
  gap: 0.2rem;
  inline-size: 62px;
  padding: 0.45rem 0.2rem;
  color: var(--dim);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.tool-rail-button:hover { color: var(--text); background: var(--elevated); }
.tool-rail-button[aria-pressed="true"] { color: var(--accent); background: var(--accent-dim); border-color: color-mix(in oklab, var(--accent) 35%, transparent); }
.tool-rail-button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tool-rail-button svg { inline-size: 17px; block-size: 17px; }
.tool-rail-name { font-size: 0.66rem; letter-spacing: 0.02em; }
.studio-stage-column { display: grid; gap: 0.6rem; padding: 0.9rem; min-inline-size: 0; align-content: start; }
.studio-panel {
  display: grid;
  gap: 0.9rem;
  align-content: start;
  padding: 0.9rem;
  min-inline-size: 0;
  border-inline-start: 1px solid var(--border);
  background: color-mix(in oklab, var(--surface-2) 35%, transparent);
  max-block-size: 34rem;
  overflow-y: auto;
}
.studio-stage { display: grid; place-items: center; padding: 0.75rem; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); min-inline-size: 0; overflow: auto; }
/* The image, drawn at a whole-number scale with no smoothing: a four-colour texture blurred
   is a decoder that looks wrong. */
.studio-canvas {
  display: block;
  image-rendering: pixelated;
  cursor: crosshair;
  touch-action: none;
  background:
    linear-gradient(45deg, color-mix(in oklab, var(--faint) 18%, transparent) 25%, transparent 25% 75%, color-mix(in oklab, var(--faint) 18%, transparent) 75%),
    linear-gradient(45deg, color-mix(in oklab, var(--faint) 18%, transparent) 25%, transparent 25% 75%, color-mix(in oklab, var(--faint) 18%, transparent) 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}
.studio-status { font-size: 0.8rem; color: var(--dim); line-height: 1.5; min-block-size: 1.2em; }
.studio-label { font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); }
.studio-note { font-size: 0.78rem; color: var(--faint); line-height: 1.55; }
.studio-error { font-family: var(--mono); font-size: 0.75rem; color: var(--fail); }
.studio-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.studio-action {
  font: inherit;
  font-size: 0.78rem;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  color: var(--text);
  background: var(--elevated);
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.studio-action:hover { border-color: var(--accent); }
.studio-action svg { inline-size: 13px; block-size: 13px; color: var(--accent); }
.studio-action-primary { border-color: color-mix(in oklab, var(--accent) 45%, var(--border)); }
.studio-palette { display: grid; gap: 0.4rem; }
.studio-swatches { display: flex; flex-wrap: wrap; gap: 0.2rem; }
/* A checkerboard behind each swatch, because a transparent palette entry is a colour and
   has to look like one rather than like a hole in the panel. */
.studio-swatch {
  font: inherit;
  position: relative;
  inline-size: 30px;
  block-size: 30px;
  padding: 0;
  border: 1px solid var(--border-input);
  border-radius: 4px;
  cursor: pointer;
  background-color: var(--bg);
  background-image: linear-gradient(45deg, color-mix(in oklab, var(--faint) 25%, transparent) 25%, transparent 25% 75%, color-mix(in oklab, var(--faint) 25%, transparent) 75%);
  background-size: 8px 8px;
}
.studio-swatch[aria-pressed="true"] { outline: 2px solid var(--accent); outline-offset: 1px; }
.studio-swatch-fill { position: absolute; inset: 0; border-radius: 3px; background: var(--swatch); opacity: var(--swatch-alpha); }
.studio-swatch-index { position: absolute; inset-block-end: 0; inset-inline-end: 2px; font-family: var(--mono); font-size: 8px; color: var(--text); text-shadow: 0 0 2px var(--bg); }
.studio-colour { display: grid; gap: 0.3rem; }
.studio-channel { display: grid; grid-template-columns: 60px minmax(0, 1fr); align-items: center; gap: 0.5rem; font-size: 0.75rem; color: var(--faint); }
.studio-channel-alpha { grid-template-columns: 60px auto; }
.studio-range { inline-size: 100%; accent-color: var(--accent); }
/* A record's fields: one row per field, the label, the value, and what it was beside it. */
.studio-fields { display: grid; gap: 0.4rem; }
.studio-field-row { display: grid; grid-template-columns: 110px 90px minmax(0, 1fr); align-items: center; gap: 0.5rem; font-size: 0.75rem; }
.studio-field-label { color: var(--faint); }
.studio-field-hint { color: var(--dim); font-size: 0.7rem; }
.studio-field {
  inline-size: 100%;
  padding: 0.25rem 0.4rem;
  font: inherit;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.studio-field:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* A value the reader works out rather than reads: shown, and visibly not a box to type in. */
.studio-field-read {
  padding: 0.25rem 0.4rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--dim);
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}
.studio-layers { display: grid; gap: 0.4rem; }
.studio-layer-list { display: grid; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
.studio-layer-list li { display: grid; gap: 0.25rem; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-sm); }
/* A layer's head: what it is, where it sits in the stack, and the four buttons that move
   it. The order is worth a line of its own — a layer's position *is* its z-order, so "2 of
   3" is the number a reader is changing. */
.studio-layer-head { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.studio-layer-order { font-size: 0.68rem; color: var(--faint); }
.studio-layer-controls { display: flex; gap: 1px; margin-inline-start: auto; }
.studio-layer-button {
  font: inherit;
  font-size: 0.68rem;
  min-inline-size: 20px;
  padding: 0.1rem 0.3rem;
  color: var(--dim);
  background: none;
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.studio-layer-button:hover:not(:disabled) { color: var(--text); border-color: var(--accent); }
.studio-layer-button:disabled { color: color-mix(in oklab, var(--faint) 50%, transparent); cursor: default; }
.studio-layer-foot { display: flex; align-items: center; gap: 0.4rem; }
.studio-layer-name { font-family: var(--mono); font-size: 0.72rem; color: var(--dim); }
.studio-layer-field { display: grid; grid-template-columns: 64px minmax(0, 1fr); align-items: center; gap: 0.5rem; font-size: 0.72rem; color: var(--faint); }
.studio-layer-toggle { font: inherit; justify-self: start; font-size: 0.72rem; padding: 0.15rem 0.45rem; color: var(--dim); background: none; border: 1px solid var(--border-input); border-radius: var(--radius-sm); cursor: pointer; }
.studio-scale { grid-template-columns: auto auto; align-items: center; gap: 0.4rem; }
.studio-scale select { font-size: 0.72rem; padding: 0.15rem 0.3rem; }
.studio-search {
  font: inherit;
  font-size: 0.78rem;
  inline-size: 100%;
  padding: 0.3rem 0.5rem;
  color: var(--text);
  background: var(--elevated);
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
}
.studio-results { max-block-size: 12rem; overflow-y: auto; }
.studio-result-list { display: grid; gap: 1px; margin: 0; padding: 0; list-style: none; }
.studio-result { font: inherit; display: flex; gap: 0.5rem; align-items: baseline; inline-size: 100%; padding: 0.25rem 0.4rem; text-align: start; color: var(--dim); background: none; border: 0; border-radius: var(--radius-sm); cursor: pointer; }
.studio-result:hover { color: var(--text); background: var(--elevated); }
.studio-result-name { font-family: var(--mono); font-size: 0.72rem; }
.studio-result-note { margin-inline-start: auto; font-size: 0.7rem; color: var(--faint); }
@media (max-width: 900px) {
  .studio { grid-template-columns: minmax(0, 1fr); }
  .tool-rail { grid-auto-flow: column; grid-auto-columns: max-content; overflow-x: auto; border-inline-end: 0; border-block-end: 1px solid var(--border); }
  .tool-rail-button { inline-size: 58px; }
  .studio-panel { border-inline-start: 0; border-block-start: 1px solid var(--border); max-block-size: none; }
}
/* ─── views ─────────────────────────────────────────────────── */
/*
 * A grid item's min-width defaults to auto, which means it refuses to shrink
 * below its content: one table with thirty columns then stretches the whole page
 * sideways instead of scrolling inside its own wrapper. Every level between the
 * shell and the table has to say it may shrink, or the widest one wins.
 */
.tool-shell,
.tool-col,
.tool-stage,
.tool-view-head,
.tool-view-body,
.tool-table-host,
.tool-table-wrap,
.tool-summary,
.tool-picker,
.tool-struct { min-inline-size: 0; }
/*
 * The stage fills the room it is given and lets the view inside it do the scrolling.
 *
 * A region's view is a heading, a note and the work; the work is what a reader came for, so it
 * takes the leftover height and scrolls inside it rather than growing the pane. Measured at
 * 1440x900 with the file table open before this: the stage was 982 pixels in a 819-pixel pane,
 * the table began 507 pixels down and its last row was 201 pixels below the window.
 */
.tool-stage { display: flex; flex-direction: column; gap: 0.9rem; min-block-size: 0; }
.tool-stage > .tool-view {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-block-size: 0;
}
.tool-stage .tool-view-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-block-size: 0;
}
.tool-stage .tool-table-host { flex: 1 1 auto; }
.tool-stage .tool-table-wrap { flex: 1 1 auto; max-block-size: 100%; min-block-size: 0; }
.tool-view-head { flex: 0 0 auto; display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 1rem; }
.tool-view-head h2 { font-size: 1.05rem; color: var(--text); }
.tool-view-body { display: grid; gap: 0.7rem; min-inline-size: 0; }
.tool-note { font-size: 0.82rem; color: var(--faint); line-height: 1.6; }
.tool-error { font-family: var(--mono); font-size: 0.78rem; color: var(--fail); overflow-wrap: anywhere; }
.tool-more { font-size: 0.78rem; color: var(--faint); }
/* ─── a pane that can be minimised ───────────────────────────── */
/*
 * Each pane can give up its room, because a reader works in one at a time.
 *
 * `open` is the pane at its size, `head` folds it to its own header, and on the explorer `rail`
 * folds it to a narrow column that keeps every region's mark: the labels go, the navigation stays.
 * The width is the frame's one custom property, so a folded pane is the same mechanism as a dragged
 * one — the column track changes and nothing else has to know.
 */
.dock { display: flex; flex-direction: column; min-block-size: 0; min-inline-size: 0; }
.dock-body { display: flex; flex-direction: column; min-block-size: 0; min-inline-size: 0; }
.dock-toggle {
  font: inherit;
  font-size: 0.9rem;
  line-height: 1;
  min-inline-size: 22px;
  min-block-size: 22px;
  color: var(--dim);
  background: none;
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.dock-toggle:hover { color: var(--text); border-color: var(--accent); }
.dock[data-state="head"] .dock-body { display: none; }
main.tool-page .tool-col-left .dock { block-size: 100%; }
main.tool-page .tool-col-left[data-pane-state="head"] { align-self: start; }
main.tool-page .tool-col-left[data-pane-state="rail"] { flex: 0 0 auto; }
/* ─── one cartridge ─────────────────────────────────────────── */
.tool-summary-panel { display: grid; gap: 0.6rem; }
.tool-summary-panel h2 { font-size: 1.05rem; color: var(--text); }
.tool-summary { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 0.2rem 1rem; font-size: 0.82rem; }
.tool-summary dt { color: var(--faint); }
.tool-summary dd { margin: 0; font-family: var(--mono); overflow-wrap: anywhere; }
.summary-actions { display: flex; flex-wrap: wrap; gap: 0.35rem; }
/* ─── the gallery ───────────────────────────────────────────── */
/*
 * A picture is drawn on a checkerboard, because these images are index-coloured and index 0 is
 * transparent in most of them: the intro's first frame is 94 per cent transparent, so on a white
 * page it renders as almost nothing and reads as a picture that failed to decode. The board says
 * "this pixel is absent" instead of letting the page's own colour say it.
 */
.tool-image-frame {
  background-color: #6b7280;
  background-image:
    linear-gradient(45deg, #9ca3af 25%, transparent 25%, transparent 75%, #9ca3af 75%),
    linear-gradient(45deg, #9ca3af 25%, transparent 25%, transparent 75%, #9ca3af 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
  display: inline-block;
  line-height: 0;
  border: 1px solid var(--border);
}
.tool-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); gap: 0.6rem; }
.tool-gallery-cell {
  display: grid;
  justify-items: center;
  gap: 0.3rem;
  padding: 0.5rem;
  font: inherit;
  color: var(--dim);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}
.tool-gallery-cell:hover { border-color: var(--accent); color: var(--text); }
.tool-gallery-cell canvas { image-rendering: pixelated; max-inline-size: 100%; block-size: auto; }
.tool-gallery-label { font-family: var(--mono); font-size: 0.7rem; color: var(--text); }
.tool-gallery-kind { font-size: 0.66rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--faint); }
/* ─── the reading switch ────────────────────────────────────── */
.tool-switch { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); overflow: hidden; }
.tool-switch-choice {
  font: inherit;
  font-size: 0.8rem;
  padding: 0.4rem 0.75rem;
  color: var(--faint);
  background: transparent;
  border: 0;
  cursor: pointer;
}
.tool-switch-choice + .tool-switch-choice { border-inline-start: 1px solid var(--border-strong); }
.tool-switch-choice[aria-pressed="true"] { color: var(--text); background: var(--elevated); }
.tool-switch-choice:hover { color: var(--text); }
/* ─── picking a row's worth of data ─────────────────────────── */
.tool-picker { display: flex; align-items: center; }
.tool-picker-label { display: flex; align-items: center; gap: 0.5rem; font-size: 0.78rem; color: var(--faint); }
.tool-select {
  font: inherit;
  font-size: 0.8rem;
  max-inline-size: 18rem;
  padding: 0.25rem 0.4rem;
  color: var(--text);
  background: var(--elevated);
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
}
/* ─── taking a table away ───────────────────────────────────── */
.tool-exports { display: flex; align-items: center; gap: 0.3rem; margin-inline-start: auto; }
.tool-export {
  font: inherit;
  font-size: 0.8rem;
  padding: 0.35rem 0.65rem;
  color: var(--faint);
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.tool-export:hover { color: var(--text); border-color: var(--accent); }
/* ─── a record's bits ───────────────────────────────────────── */
.tool-bits { display: grid; gap: 0.3rem; overflow-x: auto; }
.tool-bit-track { display: flex; align-items: stretch; block-size: 2.1rem; min-inline-size: max-content; }
.tool-bit {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--elevated);
  border-inline-end: 1px solid var(--border);
}
.tool-bit-alt { background: color-mix(in oklab, var(--elevated) 70%, var(--bg)); }
/* The bits no field names: drawn, hatched, and never mistaken for a field. */
.tool-bit-gap {
  background: repeating-linear-gradient(135deg, transparent 0 3px, var(--border) 3px 4px);
}
.tool-bit-derived { border-block-end: 2px solid var(--accent); }
.tool-bit-constant { color: var(--faint); opacity: 0.65; }
.tool-bit-name {
  font-family: var(--mono);
  font-size: 0.62rem;
  padding-inline: 0.25rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tool-bit-legend { font-size: 0.75rem; color: var(--faint); }
/* ─── the values ────────────────────────────────────────────── */
/*
 * The data table: a toolbar, a scrolling grid, and a foot that admits what it left out.
 *
 * Every region of the tool is a table, so this is the component a reader looks at most and it
 * gets the room a dense desktop reader wants: a sticky heading, a monospace body, hairline rules
 * and a hover that moves with the pointer rather than an outline around every row.
 */
.tool-table-host { display: flex; flex-direction: column; gap: 0.5rem; min-inline-size: 0; min-block-size: 0; }
.dt-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-block-size: 30px;
}
.dt-toolbar-label { color: var(--dim); font-size: 0.8rem; }
.dt-search { position: relative; display: inline-flex; align-items: center; flex: 0 1 20rem; min-inline-size: 8rem; }
.dt-filter {
  inline-size: 100%;
  font: 0.8rem/1 var(--sans);
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.55rem;
}
.dt-filter::placeholder { color: var(--faint); }
.dt-filter:hover { border-color: var(--border-strong); }
.dt-filter:focus { border-color: var(--accent); outline: none; }
.dt-filter::-webkit-search-cancel-button { -webkit-appearance: none; }
.dt-count {
  color: var(--faint);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.dt-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.dt-note { color: var(--faint); font-size: 0.75rem; }
.dt-head { padding: 0; }
.dt-head-plain { padding: 0.42rem 0.55rem; }
.dt-sort-button {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  inline-size: 100%;
  font: 500 0.78rem/1 var(--sans);
  color: var(--faint);
  background: none;
  border: 0;
  padding: 0.5rem 0.55rem;
  cursor: pointer;
  white-space: nowrap;
}
.dt-sort-button:hover { color: var(--text); }
.dt-sort-button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.dt-head[aria-sort="ascending"] .dt-sort-button,
.dt-head[aria-sort="descending"] .dt-sort-button { color: var(--text); }
.dt-head-label { overflow: hidden; text-overflow: ellipsis; }
.dt-sort { color: var(--faint); font-size: 0.72rem; }
.dt-head[aria-sort="ascending"] .dt-sort,
.dt-head[aria-sort="descending"] .dt-sort { color: var(--accent); }
.tool-table-wrap {
  overflow: auto;
  max-block-size: 66vh;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  min-inline-size: 0;
}
.tool-table { border-collapse: separate; border-spacing: 0; font-size: 0.82rem; inline-size: 100%; }
.tool-table th,
.tool-table td {
  padding: 0.42rem 0.55rem;
  text-align: start;
  white-space: nowrap;
  border-block-end: 1px solid var(--border);
}
.tool-table thead th {
  position: sticky;
  inset-block-start: 0;
  z-index: 1;
  background: var(--elevated);
  color: var(--faint);
  font-weight: 500;
  border-block-end: 1px solid var(--border-strong);
}
.tool-table tbody th { color: var(--dim); font-weight: 400; }
.tool-table tbody td { font-family: var(--mono); color: var(--text); font-variant-numeric: tabular-nums; }
.tool-table tbody tr:last-child th,
.tool-table tbody tr:last-child td { border-block-end: 0; }
.tool-table tbody tr:hover { background: var(--surface-2); }
.tool-shell:has(.tool-table-wrap:focus-visible) .tool-row { outline: none; }
.tool-row { cursor: pointer; }
.tool-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tool-row[data-selected] { background: var(--accent-dim); }
.tool-values { border-collapse: collapse; font-size: 0.82rem; inline-size: 100%; }
.tool-values-wrap { overflow-x: auto; min-inline-size: 0; }
.tool-values th,
.tool-values td { padding: 0.38rem 0.55rem; text-align: start; border-block-end: 1px solid var(--border); vertical-align: top; }
.tool-values th { color: var(--dim); font-weight: 400; white-space: nowrap; }
.tool-values td { font-family: var(--mono); color: var(--text); }
.tool-values td.tool-doc { font-family: var(--sans); color: var(--faint); white-space: normal; }
/*
 * The grade beside a field, and the count of them above the table.
 *
 * Three words the repository uses everywhere else — confirmed, likely, hypothesis — and they
 * are computed from the layout and the record together rather than written on the field, which
 * is the only reason a reader can believe them. The colours are the site's own: the accent for
 * what is proved, the dim tone for what is inferred, and the faint one for what is a claim
 * nothing has yet read.
 */
.tool-grade { font-family: var(--sans); font-size: 0.72rem; white-space: nowrap; }
.tool-grade-confirmed { color: var(--accent); }
.tool-grade-likely { color: var(--dim); }
.tool-grade-hypothesis { color: var(--faint); }
.tool-field-missing th { color: var(--faint); }
.tool-value-none { color: var(--faint); font-style: italic; }
.tool-status-counts { margin: 0; color: var(--dim); font-size: 0.75rem; }
/* ─── the declaration beside the values ─────────────────────── */
.tool-struct { display: grid; gap: 0.5rem; }
.tool-struct h3 { font-size: 0.74rem; letter-spacing: 1.5px; text-transform: uppercase; color: var(--faint); }
.tool-schema {
  font-family: var(--mono);
  font-size: 0.68rem;
  line-height: 1.7;
  color: var(--dim);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.6rem;
  overflow: auto;
  max-block-size: 22rem;
  min-inline-size: 0;
  white-space: pre;
}
/* A block of emitted C: the same as a schema block, but it can be long. */
.tool-code {
  font-family: var(--mono);
  font-size: 0.68rem;
  line-height: 1.7;
  color: var(--dim);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.7rem;
  overflow: auto;
  max-block-size: 66vh;
  min-inline-size: 0;
  white-space: pre;
}
/* ─── narrow ────────────────────────────────────────────────── */
@media (max-width: 1100px) {
  .tool-shell { grid-template-columns: minmax(0, 1fr); }
  .tool-workspace { grid-template-columns: minmax(0, 1fr); }
  .tool-col-left { position: static; max-block-size: none; }
  .tool-view-list { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
}
@media (max-width: 640px) {
  main:has(.tool) { padding-inline: 0.75rem; }
  .tool-table-wrap { max-block-size: none; }
  .tool-schema { max-block-size: 16rem; }
  /*
   * The status row is a bar pinned to the foot of the screen, and the page reserves its height so nothing
   * is ever behind it. Sticky does not do this: the row is the last child of its container, so once its
   * natural position is on screen it scrolls away with the content — measured at 375, top 772 at rest and
   * 225 after scrolling 600, with a table cell painted behind it at rest.
   */
  .tool-status {
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    z-index: 40;
    background: var(--surface);
    min-block-size: 44px;
    font-size: 0.82rem;
    padding-inline: 0.9rem;
  }
  main:has(.tool) { padding-block-end: 3.5rem; }
  /* The frame stops being a frame once the row it reserved for the status bar is a fixed bar instead. */
  main.tool-page .tool { block-size: auto; grid-template-rows: auto; }
  /*
   * The drag handle belongs to the side-by-side layout. Stacked, it stays an eight-pixel column down the
   * right edge of the screen with `touch-action: none`, so it swallows touches on whatever is under it.
   */
  .tool-resizer { display: none; }
  /* Touch targets: a finger is not a mouse pointer. 44 is the floor for anything a thumb lands on. */
  main.tool-page .tool-tree-row { min-block-size: 44px; }
  main.tool-page .tool-tree-close { inline-size: 44px; block-size: 44px; }
  main.tool-page .tool-tree-close svg { inline-size: 16px; block-size: 16px; }
  main.tool-page .tool-icon-button { inline-size: 44px; block-size: 44px; }
  main.tool-page .tool-icon-button svg { inline-size: 18px; block-size: 18px; }
  .tool-switch-choice { padding: 0.62rem 1rem; font-size: 0.84rem; }
  .tool-export { padding: 0.6rem 0.85rem; font-size: 0.84rem; }
  .studio-action { min-block-size: 44px; }
  .tool-drop-input { min-block-size: 44px; }
  /* The native file button inside the label is its own box and does not inherit the input's height. */
  .tool-drop-input::file-selector-button { min-block-size: 44px; padding-inline: 0.9rem; }
  .tool-table th,
  .tool-table td { font-size: 0.82rem; }
  .tool-table tbody tr { block-size: auto; }
  /* A dense table stops being readable if every row is 44px tall; 40 is the compromise a reader can scan. */
  .tool-values th,
  .tool-values td { padding-block: 0.62rem; }
  /*
   * The game chip is a second copy of what the row already says, and on a phone it took 99px of a 235px
   * row, which is why the cartridge's name rendered as "wwf…". The name is the thing being read.
   */
  main.tool-page .tool-tree-badge { display: none; }
  /* An action row of five buttons does not fit beside a heading on a phone; it gets its own line. */
  .tool-view-head { gap: 0.5rem; }
  .tool-exports { flex-wrap: wrap; margin-inline-start: 0; inline-size: 100%; }
  .summary-actions { inline-size: 100%; }
}
/* ─── an animation's frames ─────────────────────────────────── */
/*
 * The pose figure: a spoke per bone from the centre out, at the angle the frame holds.
 *
 * A translated bone's spoke reaches further, which is not its translation — the translation is
 * three or four bytes and this is not a drawing of them — but which bones move is a fact of the
 * frame and it is worth seeing at a glance. The second figure in a comparison is drawn faintly, so
 * two poses are one picture rather than two.
 */
.anim-figure { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; }
.anim-pose { inline-size: 8rem; block-size: 8rem; overflow: visible; }
.anim-spoke { stroke: var(--accent); stroke-width: 1.6; stroke-linecap: round; opacity: 0.85; }
.anim-spoke-moved { stroke-width: 2.6; opacity: 1; }
.anim-hub { fill: var(--dim); }
.anim-pose-muted .anim-spoke { stroke: var(--faint); stroke-width: 1.2; opacity: 0.7; }
.anim-pose-muted .anim-hub { fill: var(--faint); }
.anim-panel { display: grid; gap: 0.75rem; }
.anim-scrub { grid-template-columns: auto 1fr; align-items: center; }
.anim-slider { inline-size: 100%; accent-color: var(--accent); }
.anim-bones { max-inline-size: 30rem; }
/* ─── the move studio ───────────────────────────────────────── */
/*
 * The moveset's own rows, clickable, and the moves one slot's category allows.
 *
 * A row that can be chosen has to look like it: the pointer and the hover are the whole of what
 * says so, and the chosen row keeps an edge so a reader who scrolled can see where they are. The
 * grid of moves is a list of buttons rather than a select, because a reader who is choosing a move
 * wants to see the ones on either side of it.
 */
.move-editor { display: grid; gap: 0.75rem; }
.move-table { max-inline-size: 52rem; }
.move-row { cursor: pointer; }
.move-row:hover th, .move-row:hover td { background: color-mix(in oklab, var(--accent) 8%, transparent); }
.move-row-selected th, .move-row-selected td { background: color-mix(in oklab, var(--accent) 14%, transparent); }
.move-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr)); gap: 0.25rem; }
.move-choice { display: flex; gap: 0.5rem; align-items: baseline; text-align: start; padding: 0.3rem 0.5rem; border: 1px solid var(--border); border-radius: 5px; background: none; color: var(--text); font: inherit; font-size: 0.8rem; cursor: pointer; }
.move-choice:hover { border-color: var(--accent); }
.move-choice-current { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 14%, transparent); }
.move-choice-index { color: var(--faint); font-family: var(--mono); min-inline-size: 2.5ch; }
.move-choices { display: grid; gap: 0.5rem; }
/* ─── the wrestler editor ───────────────────────────────────── */
/*
 * The figure and the parts beside it.
 *
 * The canvas is drawn at the figure's own scale and shown at it, because a body eighty pixels
 * across is a body nobody can judge. The parts are a grid of small buttons so a reader can pick
 * one and see it described without hunting for it in the list.
 */
.figure-editor { display: grid; gap: 0.75rem; }
.figure-row { display: flex; gap: 1.5rem; align-items: flex-start; flex-wrap: wrap; }
.figure-stage { display: grid; place-items: center; background: color-mix(in oklab, var(--surface-2) 40%, transparent); border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem; }
.figure-canvas { image-rendering: auto; }
.figure-controls { display: grid; gap: 0.6rem; min-inline-size: 20rem; flex: 1 1 20rem; }
.figure-parts { display: grid; grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr)); gap: 0.25rem; }
.figure-part { display: flex; gap: 0.4rem; align-items: baseline; padding: 0.25rem 0.45rem; border: 1px solid var(--border); border-radius: 5px; background: none; color: var(--dim); font: inherit; font-size: 0.78rem; cursor: pointer; }
.figure-part:hover { border-color: var(--accent); color: var(--text); }
.figure-part-chosen { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 14%, transparent); color: var(--text); }
.figure-part-slot { font-family: var(--mono); color: var(--faint); min-inline-size: 2ch; }
.studio-program {
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.55;
  inline-size: 100%;
  padding: 0.5rem 0.6rem;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
  resize: vertical;
  tab-size: 4;
}
/* A tool with nothing to open: the page is one thing and its notes, and no frame. */
.tool-free { padding: 1.2rem 0 3rem; }
.tool-free-content { display: grid; gap: 1.2rem; max-inline-size: 1200px; margin-inline: auto; padding-inline: 1rem; }






/* The workshop page is the editor's own grid with one row above it. */
.workshop { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.9rem; border: 0; background: none; overflow: visible; }
.workshop > .workshop-top { display: grid; gap: 0.9rem; }
.workshop > .studio { display: grid; grid-template-columns: auto minmax(0, 1fr) minmax(15rem, 19rem); gap: 0; align-items: stretch; }
.workshop-preview { display: grid; place-items: center; padding: 0.6rem; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.workshop-preview canvas { max-inline-size: 176px; block-size: auto; }
/* The workshop: where an image comes from on the left, what it becomes on the right. */
.workshop-source { display: grid; grid-template-columns: minmax(0, 1fr) 200px; gap: 0.9rem; align-items: start; }
.workshop-from { display: grid; gap: 0.7rem; align-content: start; }

/* The workshop: where an image comes from on the left, what it becomes on the right. */
.workshop-source { display: grid; grid-template-columns: minmax(0, 1fr) 200px; gap: 0.9rem; align-items: start; }
.workshop-from { display: grid; gap: 0.7rem; align-content: start; }

/* The workshop's own frame: what to work on, then the work. */
.workshop-tool { display: grid; gap: 1rem; }
.workshop-pick { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.workshop-pick .studio-action[aria-pressed="true"] { border-color: var(--accent); color: var(--text); background: var(--accent-dim); }
.workshop-name { font-family: var(--mono); font-size: 0.78rem; color: var(--dim); }
