/* Page chrome for build.html — the Build screen (item 6: what used to be the
   "App" tab). Modeled directly on demo.css, since this page is the board
   playground's layout with an Auto-Builder side panel bolted on instead of a
   move-list panel. Colors come from the --ui-* block in theme.css. */

* { box-sizing: border-box; }

/* The page is exactly one screen tall and never scrolls.
   The board fills it top to bottom, everything else fits in the width that
   leaves, and the only thing allowed its own scrollbar is `.rightcol` — and
   only on a window too short to hold it. */
html, body { height: 100%; }

body {
  margin: 0;
  background: var(--ui-bg);
  color: var(--ui-text);
  font: 0.875rem/1.5 var(--ui-font); /* 14px */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.625rem 1.125rem;
  background: var(--ui-panel);
  border-bottom: 1px solid var(--ui-line);
  z-index: 10;
}

.brand { font-weight: 700; letter-spacing: 0.14em; font-size: 0.8125rem; /* 13px */ }
.grow { flex: 1; }

.inline {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--ui-dim);
  cursor: pointer;
  user-select: none;
}

/* ── the shape of the screen ────────────────────────────────────────────────

     board          activity log
     (full          what your database says | what the databases say | trail
      height)       playing | engine | options
                    what to build | prep suggestions

   Read as four rows, which is how it was asked for; laid out as two columns,
   because a board that fills the window top to bottom leaves no "below" for
   the other three rows to be in. They go in the width it leaves instead.

   It was one 320px column beside a 640px board before this: nine panels in a
   gutter, and the ones that mattered during a run were the ones pushed off the
   bottom of the screen.

   `--build-reserve` is everything in the board column that is not the board —
   the header, the page padding, the status line, the move controls and the
   Start row. build.js reads it back through the resizer so the grip's ceiling
   and this layout cannot drift apart. */

:root {
  --build-reserve: 13.625rem;
  /* This page only, and deliberately not in theme.css, which is hand-edited.
     A board meant to fill a tall window needs a ceiling above theme.css's
     900px or the grip stops halfway up the screen. */
  --board-max: 87.5rem;
}

.layout {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  /* The board's own size, not `auto`. An `auto` track is
     minmax(min-content, max-content), and the max-content of this column is
     whatever the widest unwrapped thing in it happens to be. That was the
     movetext in "Lines built": one finished twelve-move line took a 682px board
     column to 1047px and left the four groups beside it in 505px. That panel is
     gone, and the rule stays — a column sized by its contents is a column that
     moves when a caption gets longer, which is not a thing to rediscover. */
  grid-template-columns: min(var(--board-size), 62vw) minmax(22.5rem, 1fr);
  gap: 1rem;
  padding: 1rem;
  overflow: hidden;
}

.boardcol {
  min-width: 0;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* The board column is three fixed-height things and nothing else since the
   "Lines built" panel went: head, board, controls. It is `overflow: hidden`, so
   anything that did not fit used to be cut in half — which is what happened to
   the Start button before it moved into the run card. Nothing below a
   full-height board can be relied on to be visible, so nothing that must be
   clickable lives there any more. */
.boardcol > .boardhead,
.boardcol > .boardwrap,
.boardcol > .controls { flex: 0 0 auto; }

/* ── the tab bar ────────────────────────────────────────────────────────────
   One group on screen at a time. Four stacked rows never fitted a window, and
   every attempt to make them fit was another thing cut down until it stopped
   being useful — an 88px trail, a receipt folded away, prose moved to tooltips.
   A tab bar buys all of that back at the price of one click. */
.tabbar {
  flex: 0 0 auto;
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--ui-line);
  margin-bottom: 2px;
  overflow-x: auto;
  scrollbar-width: none;
}
.tabbar::-webkit-scrollbar { display: none; }

.tab {
  flex: 0 0 auto;
  background: none;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: var(--ui-radius) var(--ui-radius) 0 0;
  color: var(--ui-dim);
  font: 600 0.75rem/1 var(--ui-font);
  letter-spacing: 0.04em;
  padding: 0.5625rem 0.8125rem;
  cursor: pointer;
  white-space: nowrap;
  /* Sits ON the bar's own border so the open tab joins the panel below it. */
  margin-bottom: -1px;
}
@media (hover: hover) { .tab:hover { color: var(--ui-text); } }
.tab.is-on {
  color: var(--ui-accent);
  background: var(--ui-panel);
  border-color: var(--ui-line);
  border-bottom: 1px solid var(--ui-panel);
}
.tab:focus-visible { outline: 2px solid var(--ui-accent); outline-offset: -2px; }

.tabcount {
  display: inline-block;
  margin-left: 0.3125rem;
  font-variant-numeric: tabular-nums;
  opacity: 0.75;
}
.tabcount:empty { display: none; }

/* A hidden panel must take no space at all — `hidden` alone loses to the
   `display: grid` / `display: flex` these carry. */
.tabpanel[hidden] { display: none !important; }

/* The rows whose panels hold a LIST get a definite row height, so the list
   inside is bounded by the screen and scrolls within itself.
   Without this the trail had nothing to bound it — alone in an auto-height grid
   row with `max-height: none`, twenty-seven entries simply grew to 1,215px and
   pushed the whole column into a scrollbar. The cap is the window, which is the
   only number that is never wrong.
   Settings is deliberately NOT in here: its panels are short by nature and
   stretching them to half a screen each is a lot of empty card. */
.tabpanel[data-panel="trail"],
.activitycol > .posrow { grid-auto-rows: minmax(0, 1fr); }
.tabpanel[data-panel="trail"] > .panel,
.activitycol > .posrow > .panel { min-height: 0; }

/* The Trail tab holds two panels, and only the first is a list. `1fr` for both
   would give a two-field settings card half the window and take that half off
   the list the tab exists for — so the rows are named: the trail takes what is
   left, the floor takes what it needs.

   The floor panel is second in the markup and NOT hidden when the trail is
   empty, which is when it most needs reading: an empty list under a floor of
   500 is explained by the field directly below it. */
.tabpanel[data-panel="trail"] { grid-template-rows: minmax(0, 1fr) auto; }

/* And never stretched, in either arrangement. With the trail card hidden — an
   empty trail — this becomes the FIRST grid item and would otherwise inherit
   the `1fr` row meant for the list, which is a two-field card half a window
   tall. */
.trailfloor { align-self: start; }

.trailfloor .row { align-items: center; gap: 0.625rem; }
/* The field is a number up to seven digits, not a full-width bar with one digit
   in it and 200px of empty box after it. */
.trailfloor input[type="number"] { width: 6.875rem; flex: 0 0 auto; }
.trailfloor .hint { flex: 1 1 10rem; }

/* And this one starts at the top of its column rather than stretching. */
.tabpanel[data-panel="settings"] { align-content: start; }

/* ── Games, which is a whole page of its own ──────────────────────────
   An iframe, so it takes the tab's whole height and does its own scrolling.
   `display: flex` on the panel is what lets the frame fill it — an iframe with
   `height: 100%` inside an `auto` box resolves to 150px, which is the default
   iframe height and looks exactly like a broken page. */
.tabpanel.gamespanel { display: flex; min-height: 0; }
.subframe {
  flex: 1 1 auto;
  width: 100%;
  min-height: 26.25rem;
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  background: var(--ui-bg);
}

/* ── White / Black, on "What your database says" ──────────────────────────── */
.sidepick {
  display: flex;
  gap: 2px;
  margin-bottom: 0.5rem;
}
.sidebtn {
  flex: 1 1 auto;
  background: var(--ui-btn);
  border: 1px solid var(--ui-line);
  color: var(--ui-dim);
  font: 600 max(0.6875rem, var(--text-min))/1 var(--ui-font); /* 11px */
  padding: 0.375rem 0.25rem;
  cursor: pointer;
}
.sidebtn:first-child { border-radius: var(--ui-radius) 0 0 var(--ui-radius); }
.sidebtn:last-child { border-radius: 0 var(--ui-radius) var(--ui-radius) 0; }
@media (hover: hover) { .sidebtn:hover { color: var(--ui-text); } }
.sidebtn.is-on {
  background: var(--ui-accent);
  color: var(--ui-accent-text);
  border-color: transparent;
}

/* The four groups, and the one element on the page that may scroll — a window
   too short to hold the settings must still be able to reach them. */
.rightcol {
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: 2px;
}

/* Flex children shrink by default, and a shrunk panel does not report that it
   has been shrunk — the column measured as a perfect fit while the panels
   inside it were being squashed and clipping their own content. Natural
   height, and let the column scroll if a window really is too short. */
.rightcol > * { flex: 0 0 auto; }

/* The open tab panel takes the whole column, because it is the only thing in
   it. That is what gives the trail its height back — it is a tab now, not one
   third of a row that was sharing a screen with three other groups. */
.rightcol > .tabpanel { flex: 1 1 auto; min-height: 0; }

/* Activity used to be `flex: 0 0 auto` — natural height, because a run card was
   all it held. It holds the two position panels as well now, and those are the
   ones with a list in them, so the column has to be given the height and the
   ROW inside it has to be the thing that takes what the card leaves. */
.activitycol > .panel { flex: 0 0 auto; }
.activitycol > .posrow { flex: 1 1 auto; min-height: 0; }

/* Superseded by the tab panels above, which each take the whole column when
   open. Kept because `grow` is still on the position row's markup and a rule
   that silently stops applying is worse than one that says why.

   ...except ONE row, which takes whatever height is left over.
   Otherwise the leftover collects at the bottom of the column as dead space
   while a trail of 86 lines is shown through an 88px window. Row 2 is the
   right one to give it to: all three of its panels hold a list that scrolls,
   so more height is straightforwardly more of the thing you are reading, and
   it grows again when rows 3 and 4 go away during a run. */
.rightcol > .grow {
  flex: 1 1 auto;
  /* A floor, not zero. Shrinking is how this row gives its height back on a
     short window, but past about here the three panels are headings with their
     content clipped underneath — and content you cannot reach is worse than the
     scrollbar that appears instead. */
  min-height: 9.375rem;
}
.gridrow.grow > .panel { min-height: 0; }

.gridrow {
  display: grid;
  gap: 0.75rem;
  /* `stretch`, not `start`. These three panels answer three questions about the
     same position and one of them is always much taller than the others — a
     seven-row database table beside a two-line "nothing here yet" left the row
     two thirds empty and reading as holes rather than as a row. Equal cards,
     and the lists inside them grow to use what they are given. */
  align-items: stretch;
}

/* Only the panels with something growable in them. A flex column here is what
   lets `.tablewrap` and `.traillist` take the leftover height instead of
   leaving it blank under a short table. */
#panelMine, #panelDatabases, #trailCard {
  display: flex;
  flex-direction: column;
}
/* ...and an id selector outranks the browser's own `[hidden] { display: none }`,
   which is an attribute selector. So `#trailCard.hidden = true` — what
   refreshTrail does when the trail is empty — set the attribute and changed
   nothing at all: the empty card stayed on screen, `tabHasContent` kept
   answering "yes" because it asks for the computed display, and the Trail tab
   was never hidden a single time.
   It went unnoticed while the card was the tab's only content, since an empty
   Trail tab and a Trail tab holding an empty card look much the same. It stops
   being invisible the moment there is a second panel under it. Same trap, same
   fix, as `.tabpanel[hidden]` above. */
#trailCard[hidden] { display: none !important; }
#panelMine .tablewrap,
#panelDatabases .tablewrap,
#trailCard .traillist {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* Bounded by the row, never by a number of its own. A hard cap here is what
   put a 4,634px row on the screen the moment the trail had 86 lines in it. */
#panelDatabases .tablewrap, #trailCard .traillist { max-height: none; }

/* ...but the trail keeps a floor, and it has to be set HERE to have it.
   `.traillist { min-height: … }` loses to the `#trailCard .traillist`
   rule above on specificity, so the floor was silently zero and the row
   squeezed the list to 82px — one entry out of eighty-three, which is the
   whole complaint. Three entries is the least that reads as a list you choose
   from rather than a peephole. */
#trailCard .traillist { min-height: 9.25rem; }
/* A folded panel is a heading and nothing else — it must not stretch to the
   row's height and sit there as a tall empty card. */
.gridrow > .panel.is-folded { align-self: start; }

/* `.cols-3` was here too. The screen was three panels across when Position,
   Trail and the setup panels shared one row; the tab bar made every row at most
   two, so only this one is left. */
.gridrow.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* A grid gap already spaces these, and a panel's own bottom margin on top of
   it reads as a row that has come loose from the one under it. */
.gridrow > .panel,
.activitycol > .panel { margin-bottom: 0; }

/* The position row keeps its own floor. Below about here the two tables are
   headings with their content clipped underneath, and a run card that has just
   grown a question would otherwise squeeze them to nothing. */
.activitycol > .posrow { min-height: 10.625rem; }

.activitycol {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* A row or column whose every child is hidden must not leave its gap behind. */
.gridrow:not(:has(> *:not([hidden]))),
.activitycol:not(:has(> *:not([hidden]))) { display: none; }

/* Nothing that decides what the next run does is shown while one is going.
   This used to be one wrapper div around a single column; a grid cannot wrap
   a subset of its cells, so the rule sits on the cells themselves. */
#layout.is-running [data-idle-only] { display: none; }

/* And the row itself, when idle-only is all it holds — otherwise rows 3 and 4
   go to zero height during a run but still spend their gaps. Row 2 stays: two
   of its three panels are about the position, which matters in both states. */
#layout.is-running .gridrow:not(:has(> *:not([data-idle-only]))) { display: none; }

@media (max-width: 900px) {
  html, body { height: auto; }
  body { overflow: auto; }
  .layout {
    grid-template-columns: minmax(0, 1fr);
    overflow: visible;
  }
  .rightcol { overflow: visible; }
  .gridrow.cols-2 { grid-template-columns: minmax(0, 1fr); }
  .boardcol { width: 100%; overflow: visible; }
}

/* ── resize grip ────────────────────────────────────────────────────────── */

.boardwrap { position: relative; }

/* The grip has to out-rank the board it overhangs. .cb-squares carries
   z-index: 1 and .boardwrap is positioned but sets no z-index of its own, so
   the two share a stacking context and the squares painted straight over the
   grip: elementsFromPoint at the grip's own centre returned .cb-square first,
   every pointerdown landed on the board, and no board in the app could
   actually be dragged to a new size. Above .cb-piece.cb-dragging (20) so a
   piece cannot cover it mid-drag either. */
.grip {
  position: absolute;
  z-index: 30;
  right: -0.3125rem;
  bottom: -0.3125rem;
  width: 1.25rem;
  height: 1.25rem;
  cursor: nwse-resize;
  border-radius: 0 0 var(--board-radius) 0;
  background:
    linear-gradient(135deg, transparent 45%, var(--ui-dim) 45%, var(--ui-dim) 55%, transparent 55%),
    linear-gradient(135deg, transparent 70%, var(--ui-dim) 70%, var(--ui-dim) 80%, transparent 80%);
  opacity: 0.55;
  touch-action: none;
}

.grip:focus-visible { opacity: 1; outline: none; }
@media (hover: hover) { .grip:hover { opacity: 1; outline: none; } }
.grip:focus-visible { box-shadow: 0 0 0 2px var(--ui-accent); }

body.cb-resizing { cursor: nwse-resize; user-select: none; }
body.cb-resizing * { cursor: nwse-resize !important; }

.controls {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.75rem;
  flex-wrap: wrap;
}

.status {
  margin-top: 0.625rem;
  color: var(--ui-dim);
  font-variant-numeric: tabular-nums;
}

.ply {
  color: var(--ui-dim);
  font-family: var(--ui-mono);
  font-size: 0.75rem;
}

.opening {
  font-size: 0.8125rem; /* 13px */
  color: var(--ui-accent);
  margin-top: 2px;
  min-height: 0;
  line-height: 1.4;
}
.opening:empty { display: none; }

/* ── panels ─────────────────────────────────────────────────────────────── */

.panel {
  background: var(--ui-panel);
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  padding: 0.75rem;
  margin-bottom: 0.875rem;
}

.panel h2 {
  margin: 0 0 0.5rem;
  font-size: max(0.6875rem, var(--text-min)); /* 11px */
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ui-dim);
  font-weight: 700;
}

.row {
  display: flex;
  gap: 0.625rem;
  align-items: center;
  margin-top: 0.375rem;
}

.row.wrap { flex-wrap: wrap; }

.hint {
  margin-top: 0.375rem;
  font-size: 0.75rem;
  color: var(--ui-dim);
  min-height: 1em;
}

/* An empty hint is a message that has not been written yet, and it used to
   hold a full line of height open for itself anyway. Twelve of them on this
   screen is a panel's worth of nothing. */
.hint:empty { min-height: 0; margin-top: 0; }

/* Same for a row whose every button is hidden — `:empty` does not match it,
   because the buttons are still there, just not shown. */
.row:not(:has(> *:not([hidden]))) { display: none; }

.hint.err { color: var(--learn-wrong); }

/* ── controls ───────────────────────────────────────────────────────────── */

.btn {
  background: var(--ui-btn);
  color: var(--ui-text);
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  padding: 0.375rem 0.6875rem;
  font: inherit;
  font-size: 0.8125rem; /* 13px */
  cursor: pointer;
}

@media (hover: hover) { .btn:hover { background: var(--ui-btn-hover); } }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: default; }

.btn.primary {
  background: var(--ui-accent);
  color: var(--ui-accent-text);
  border-color: transparent;
  font-weight: 600;
}
@media (hover: hover) { .btn.primary:hover { filter: brightness(1.08); } }

.btn.small { padding: 0.25rem 0.5rem; font-size: 0.75rem; }

textarea, input.sq, input[type="number"], select {
  width: 100%;
  background: var(--ui-input-bg);
  color: var(--ui-text);
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  padding: 0.5rem;
  font: 0.75rem/1.5 var(--ui-mono);
}

textarea { resize: vertical; }

select {
  width: auto;
  font-family: var(--ui-font);
  font-size: 0.8125rem; /* 13px */
  padding: 0.375rem 0.5rem;
}

textarea:focus, input:focus, select:focus {
  outline: 2px solid var(--ui-accent);
  outline-offset: -1px;
}

.minilabel {
  display: block;
  font-size: max(0.6875rem, var(--text-min)); /* 11px */
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ui-dim);
  margin: 0.5rem 0 0.1875rem;
}
.minilabel:first-child { margin-top: 0; }

.panel textarea { font-family: var(--ui-mono); font-size: 0.75rem; }

.checkline {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  font-size: 0.8125rem; /* 13px */
  cursor: pointer;
  padding: 2px 0;
}

.sub-panel {
  margin-top: 0.375rem;
  padding-left: 2px;
}

/* ── semi-automatic decision, INSIDE the run card ────────────────────────
   Item 7: "can't see the board while deciding". This is in the column beside
   the board, and every candidate is also drawn as an arrow on the board (see
   build.js) so the position and the choices are in the same glance.

   It used to be a panel of its own, first in the column, with a heading, a
   second copy of the line, an open receipt and four long-labelled buttons.
   Same question, same information, roughly half the height: it is now part of
   the run card, marked off by a rule rather than by a whole panel. */

.decision {
  margin: 0 0 0.75rem;
  padding-top: 0.625rem;
  border-top: 1px solid var(--ui-accent);
}

/* No line of its own while a run is live — the card's `runnow` above is
   already showing it. See refreshDecision. */
.decision .hint:empty { display: none; }

.candidates {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-top: 0.5rem;
}

.candidate {
  display: flex;
  justify-content: space-between;
  gap: 0.625rem;
  text-align: left;
  width: 100%;
}

.candidate .rank {
  font-family: var(--ui-mono);
  opacity: 0.7;
  margin-right: 0.375rem;
}

.candidate .status {
  color: var(--ui-dim);
  font-size: max(0.6875rem, var(--text-min)); /* 11px */
}

/* The four things to do other than pick a move. Short labels, one row; the
   sentence each of them used to spell out is on the tooltip. */
.decision .actions { margin-top: 0.5rem; gap: 0.375rem; }

/* What the analysis behind the candidates cost: engine, depth, confidence,
   nodes, seconds. Never absent — a decision with no receipt is a decision you
   cannot judge — but folded, because it is read once in twenty questions and
   was four lines tall in all the rest. The half that stays visible is the
   half you judge the answer by. */
.analysisline {
  margin-top: 0.5rem;
  padding: 0.3125rem 0.5rem;
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  color: var(--ui-dim);
  font: max(0.625rem, var(--text-min))/1.5 var(--ui-mono); /* 10px */
  word-break: break-word;
}
.analysisline b { color: var(--ui-text); font-weight: 700; }
.analysisline > summary {
  cursor: pointer;
  list-style: none;
  color: var(--ui-dim);
}
.analysisline > summary::-webkit-details-marker { display: none; }
/* Drawn, not typed, so it cannot be mistaken for part of the receipt — the
   same chevron the foldable panel headings use. */
.analysisline > summary::after {
  content: "";
  display: inline-block;
  width: 0.3125rem;
  height: 0.3125rem;
  margin-left: 0.375rem;
  vertical-align: 1px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform 120ms ease;
}
.analysisline[open] > summary::after { transform: rotate(-135deg) translate(-1px, -1px); }
@media (hover: hover) { .analysisline > summary:hover { color: var(--ui-text); } }
.analysisline .rest { display: block; margin-top: 0.25rem; }

/* ── the trail ──────────────────────────────────────────────────────────────
   The lines a build walked past, waiting to be built. The number of games
   leads each row because that is what the list is sorted by, and what Auto
   picks up next. */

/* No hard cap any more — inside a stretched row it is bounded by the row, and
   a fixed 88px window onto 86 parked lines was its own kind of silly. It still
   scrolls; it just uses the height the row already had spare. */
.traillist {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  /* Three entries, near enough. Below that this is not a list you can choose
     from, it is a peephole — and the row it sits in is free to make it taller
     whenever the panels beside it are taller, which is most of the time. */
  min-height: 9.25rem;
  overflow-y: auto;
  margin: 0.375rem 0;
}

.trailrow {
  display: flex;
  gap: 0.25rem;
  align-items: stretch;
}

/* One row: the move parked, the opening it happens in, the line, the numbers.
   The line itself stays — it is what you are choosing. What went is the SECOND
   copy of it: every row used to print the line it was left behind by as well,
   which is the same movetext minus one move, in the same font, directly
   underneath. Two PGNs in a 550px column is not a list you can read. */
.trailpick {
  flex: 1 1 auto;
  display: block;
  text-align: left;
  padding: 0.3125rem 0.5rem;
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  background: var(--ui-btn);
  color: var(--ui-text);
  cursor: pointer;
  min-width: 0;
}
@media (hover: hover) { .trailpick:hover { border-color: var(--ui-accent); } }

.trailrow.picked .trailpick {
  border-color: var(--ui-accent);
  box-shadow: inset 0 0 0 1px var(--ui-accent);
}

/* The move parked and the numbers, on one line — both are short. */
.thead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.625rem;
}
/* The move that was not taken: the identity of the row. */
.tmove {
  font: 700 0.8125rem/1.35 var(--ui-mono); /* 13px */
  color: var(--ui-accent);
  flex: 0 0 auto;
}
/* How much it is worth building. Every number labelled — a bare "2" beside a
   chess line reads as a move number, an evaluation, or anything else. */
.tmeta {
  color: var(--ui-dim);
  font: max(0.625rem, var(--text-min))/1.4 var(--ui-font); /* 10px */
  font-variant-numeric: tabular-nums;
  text-align: right;
  flex: 0 1 auto;
}
/* Where it happens: the opening, by name — ONE line of it.
   "C56 · Italian Game: Scotch Gambit, Max Lange Attack" wrapped to three lines
   in this column, which made a single trail entry taller than the window the
   list was shown through: eighty-three parked lines, one of them visible. The
   full name is on the row's `title`. */
.tname {
  display: block;
  margin-top: 1px;
  font: max(0.6875rem, var(--text-min))/1.35 var(--ui-font); /* 11px */
  color: var(--ui-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* `.tline` was here — the two-line clamp on the movetext inside a trail row.
   The movetext came out of the row when eighty-three entries at 81px each meant
   one of them was on screen; clicking the row puts the whole line on the board
   instead, which is a better place to read a chess line than a 10px column. */

.traildrop {
  flex: 0 0 auto;
  width: 1.625rem;
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  background: var(--ui-btn);
  color: var(--ui-dim);
  cursor: pointer;
  font-size: max(0.6875rem, var(--text-min)); /* 11px */
}
@media (hover: hover) { .traildrop:hover { color: var(--ui-text); border-color: var(--ui-accent); } }

/* ── the status line ────────────────────────────────────────────────────────
   One sentence in the header, replacing the run log that used to be the only
   way to find out what was happening. Right-aligned against the board so the
   eye finds it in the same place every time. */

.jobstatus {
  font: 600 0.75rem/1.4 var(--ui-font);
  color: var(--ui-accent);
  text-align: right;
  max-width: 60ch;
}

/* ── the run log ────────────────────────────────────────────────────────────
   Raw process output, admin-only, off by default. Monospace and dim, because
   it is evidence rather than interface. */

.loglines {
  max-height: 20rem;
  overflow-y: auto;
  font: max(0.6875rem, var(--text-min))/1.5 var(--ui-mono); /* 11px */
  color: var(--ui-dim);
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0.5rem 0;
  padding: 0.5rem;
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  background: var(--ui-bg);
}
.loglines .err { color: var(--learn-wrong); }
.loglines .sys { color: var(--ui-accent); }

/* "Lines built" was here — a list of finished lines under the board, each one a
   button, with a course picker and two filing buttons under them. It went on
   2026-09-05: the run files what it built into Calculated lines by itself, and
   a course is chosen on the Repertoire tab. What a finished run says now is
   .runcomplete, in the run card. */

details > summary {
  cursor: pointer;
  font-size: max(0.6875rem, var(--text-min)); /* 11px */
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ui-dim);
  font-weight: 700;
}

/* ── the live run card ─────────────────────────────────────────────────────
   Idle, the right-hand column holds the settings. Running, it holds this —
   and this is deliberately the loudest thing on the screen.

   The problem it solves: a build runs for hours, and the one fact that makes
   it feel like it is moving ("Calculating 9% — 1/11 lines") used to sit in
   small grey type in the header while the entire column beside the board was
   empty. Progress you have to hunt for is progress you stop trusting.

   Colours come from theme.css and nowhere else, so editing the theme restyles
   this along with everything else. */

.runcard {
  border-color: var(--ui-accent);
  /* A tint of the accent rather than a flat fill: enough to read as "this is
     live" without fighting the board for attention. */
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--ui-accent) 14%, var(--ui-panel)),
      var(--ui-panel) 60%);
}

.runphase {
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--ui-text);
  /* A phase name can be long ("Building the line after Qd7") and must never
     push the card wider than its column. */
  overflow-wrap: anywhere;
  margin-bottom: 0.75rem;
}

.runpct {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  margin-bottom: 0.5rem;
}

.runpctnum {
  font-size: 2.75rem;
  font-weight: 800;
  line-height: 1;
  color: var(--ui-accent);
  font-variant-numeric: tabular-nums;
}

.runpctlines {
  font-size: 0.8125rem; /* 13px */
  color: var(--ui-dim);
  font-variant-numeric: tabular-nums;
}

/* ── the bar ── */

.pbar {
  height: 0.625rem;
  border-radius: 62.4375rem;
  background: var(--ui-input-bg);
  border: 1px solid var(--ui-line);
  overflow: hidden;
  margin-bottom: 0.75rem;
}

.pfill {
  height: 100%;
  width: 0%;
  border-radius: 62.4375rem;
  background: linear-gradient(90deg,
    var(--ui-accent),
    color-mix(in srgb, var(--ui-accent) 55%, var(--status-up)));
  transition: width 400ms ease;
}

/* ── no run to describe ────────────────────────────────────────────────────
   `is-idle` is set by paintRunCard whenever there is no job the screen is
   describing — before the first run of a session, and after one finishes
   cleanly. It has been set since the card was written; there was never a rule
   for it, so it took nothing away.

   What that looked like: a fresh page opened on a 44px "0%" over an empty bar,
   and a finished build left the last variation's "5 moves deep" and a
   half-filled bar sitting there for as long as you looked at it. That second
   one is half of "the Activity Log should not say anything" — the numbers are
   about work that is over, and they are the loudest thing in the column.

   `display: none` rather than `visibility: hidden`, so the card collapses to
   the controls instead of keeping 70px of blank where the numbers were, and so
   a stale number is out of the accessibility tree as well as out of sight.

   The border and the accent tint go with them: a card with nothing running in
   it must not be painted as the live thing on the screen. */
.runcard.is-idle {
  border-color: var(--ui-line);
  background: var(--ui-panel);
}
.runcard.is-idle .runphase,
.runcard.is-idle .runpct,
.runcard.is-idle .pbar { display: none; }

/* A run that is paused should not look like a run that is working. */
.runcard.is-paused { border-color: var(--ui-line); }
.runcard.is-paused .runpctnum { color: var(--ui-dim); }
.runcard.is-paused .pfill { background: var(--ui-dim); }

/* Waiting for a decision is the one state where the app needs the user, so it
   gets the strongest signal on the card. */
.runcard.is-waiting { border-color: var(--status-up); }
.runcard.is-waiting .runphase { color: var(--status-up); }

/* The line being built. Smaller and shorter than it was: at fourteen moves
   this is the longest single thing in the card, and it is context for the
   question under it rather than the thing being read. */
.runnow {
  font-family: var(--ui-mono);
  font-size: max(0.6875rem, var(--text-min)); /* 11px */
  line-height: 1.45;
  color: var(--ui-text);
  background: var(--ui-input-bg);
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  padding: 0.375rem 0.5rem;
  margin-bottom: 0.5rem;
  max-height: 3.625rem;
  overflow: auto;
  overflow-wrap: anywhere;
}

.runstats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.75rem;
  font-size: 0.75rem;
  color: var(--ui-dim);
  font-variant-numeric: tabular-nums;
  margin-bottom: 0;
}

.runstats b {
  color: var(--ui-text);
  font-weight: 600;
}

/* `.runbuttons` was here — a flex row for Pause and Stop, from when the run
   controls were a panel of their own under the card. They are inside it now
   (`.runcontrols`), and .btn.big below is what actually sizes them. */

.btn.big {
  flex: 1 1 0;
  padding: 0.6875rem 0.75rem;
  font-size: 0.875rem; /* 14px */
  font-weight: 600;
}

.btn.big.stop {
  background: var(--status-down);
  border-color: var(--status-down);
  color: #fff;
}
@media (hover: hover) { .btn.big.stop:hover { filter: brightness(1.08); } }

.btn.big.resume {
  background: var(--status-up);
  border-color: var(--status-up);
  color: #10241b;
}
@media (hover: hover) { .btn.big.resume:hover { filter: brightness(1.08); } }

/* ── connection line ── */

.connline {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.625rem;
}

.conndot {
  width: 0.5625rem;
  height: 0.5625rem;
  border-radius: 50%;
  background: var(--ui-dim);
  flex: 0 0 auto;
}
.conndot.up { background: var(--status-up); }
.conndot.down { background: var(--status-down); }

.connstate { font-size: 0.8125rem; /* 13px */ color: var(--ui-text); }
/* In the heading it is a status line, not a field label. */
.headstatus .connstate { font-size: max(0.625rem, var(--text-min)); /* 10px */ color: var(--ui-dim); }

/* ── the position tables ────────────────────────────────────────────────────
   "What your database says" and "What the databases say", moved here from the
   Board tab. Both were a list of sentences with the numbers inside them —
   "e4 3 lines 2 due Italian: Max Lange…" — which is fine for two moves and
   unreadable at eight. The facts are columns now, and the numbers line up. */

.tablewrap { overflow-x: auto; margin: 0.5rem 0 0.25rem; }

.postable {
  width: 100%;
  border-collapse: collapse;
  font-size: max(0.6875rem, var(--text-min)); /* 11px */
}
.postable th {
  text-align: left;
  font: 600 max(0.625rem, var(--text-min))/1.6 var(--ui-font); /* 10px */
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ui-dim);
  border-bottom: 1px solid var(--ui-line);
  padding: 2px 0.375rem 0.25rem 0;
  white-space: nowrap;
}
.postable td {
  padding: 0.25rem 0.375rem 0.25rem 0;
  border-bottom: 1px solid var(--ui-line);
  vertical-align: baseline;
}
.postable tr:last-child td { border-bottom: 0; }

.postable .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.postable td.san {
  font: 700 0.75rem/1.4 var(--ui-mono);
  color: var(--ui-accent);
  white-space: nowrap;
}
.postable td.due:not(:empty) { color: var(--learn-xp, var(--ui-accent)); font-weight: 700; }
/* The move column takes the slack, so the numbers stay next to each other
   instead of one of them being flung at the right-hand edge. Without this the
   LAST column absorbs the table's spare width, which is what "Due" started
   doing the moment the "Where" column was deleted out from under it. */
.postable td.san, .postable th:first-child { width: 100%; }
.postable .num { width: 1%; padding-left: 0.875rem; }

.postable a.learnlink { color: var(--ui-accent); text-decoration: none; }
@media (hover: hover) { .postable a.learnlink:hover { text-decoration: underline; } }

/* The prep-suggestion rows were here — .preplist and .preprow with its three
   children and two kind modifiers. The Prep panel left this screen on
   2026-08-31 and the Games tab answers what it answered. games.css carries its
   own .preplist / .preprow for the list games.js still builds; these were the
   copy nothing rendered any more. */

/* ── above the board ────────────────────────────────────────────────────────
   Whose move it is and what the opening is called, read WITH the position
   rather than after it. */

.boardhead {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
  min-height: 1.25rem;
  flex-wrap: wrap;
}
.boardhead .status { margin-top: 0; }
.boardhead .opening { margin-top: 0; }

/* ── the run controls ───────────────────────────────────────────────────────
   Inside the run card now, marked off by a rule. They had a panel of their own
   so they would come after the log — "see what happened, then act on it" — but
   the log is admin-only and closed, so all that bought was a second border and
   36px of padding on a screen that has to fit a window. */

.runcontrols {
  margin-top: 0.625rem;
  padding-top: 0.625rem;
  border-top: 1px solid var(--ui-line);
}
.runcontrols .row { margin-top: 0; }
/* Nothing to show: no buttons, no message. Not even the rule. */
.runcontrols:not(:has(.btn:not([hidden]))):not(:has(.hint:not(:empty))) { display: none; }

/* ── how a variation ended ──────────────────────────────────────────────────
   "Databases exhausted — line completed." A finished run used to say nothing
   at all: the card stopped moving and what it had just built was announced
   nowhere. */

.runoutcome {
  margin-top: 0.625rem;
  padding: 0.5rem 0.625rem;
  border: 1px solid var(--ui-accent);
  border-radius: var(--ui-radius);
  background: var(--ui-btn);
}
.runoutcome b {
  display: block;
  font: 700 0.8125rem/1.4 var(--ui-font); /* 13px */
  color: var(--ui-accent);
}
.runoutcome .line {
  display: block;
  margin-top: 0.25rem;
  font: max(0.625rem, var(--text-min))/1.45 var(--ui-mono); /* 10px */
  color: var(--ui-dim);
  word-break: break-word;
}

/* Where the line it built actually went. Not dimmed like the movetext above
   it: this is the next thing to DO, and it was the missing half of "the run
   finished" — the work was saved and nothing on screen said so. */
.runoutcome .where {
  display: block;
  margin-top: 0.375rem;
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--ui-text);
}
.runoutcome .where:empty { display: none; }

/* ── the line is built ──────────────────────────────────────────────────────
   "Databases Exhausted - Line Fully Built", for ten seconds, in the middle of a
   card that has just emptied itself — and then gone. A clean finish takes
   everything else away, which is the behaviour José asked for and is staying,
   so this is the entire report of what can be an hour of work and it is sized
   like it.

   Green, not the accent. --learn-right is what this app already says "that was
   right" in, which is the same feeling as a line that ran to its end, and it is
   deliberately a different signal from the purple that means "look here, decide
   something". No new colour: both values are already in theme.css and both are
   set for the light theme as well.

   It shares runoutcome's shape rather than its rules because the two are not
   the same thing — that one describes a run that stopped, this one a run that
   finished — and a shared class would be one edit away from making a finish
   look like an interruption. */
.runcomplete {
  margin-top: 0.625rem;
  padding: 0.75rem 0.875rem;
  border: 2px solid var(--learn-right);
  border-radius: var(--ui-radius);
  background: var(--ui-btn);
  /* var(--anim-fade) is 0s under prefers-reduced-motion, which turns this off
     without a second rule. See theme.css. */
  animation: linebuilt var(--anim-fade) ease-out;
}
.runcomplete b {
  display: block;
  font: 700 1.0625rem/1.3 var(--ui-font); /* 17px */
  color: var(--learn-right);
}
/* The movetext of the line that was just built. It is the only copy of it left
   on this screen — the board has already gone back to the start position — so
   unlike runoutcome's, this one is not dropped on a short window. */
.runcomplete .line {
  display: block;
  margin-top: 0.375rem;
  font: max(0.625rem, var(--text-min))/1.45 var(--ui-mono); /* 10px */
  color: var(--ui-dim);
  word-break: break-word;
}
.runcomplete .line:empty { display: none; }
.runcomplete .where {
  display: block;
  margin-top: 0.375rem;
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--ui-text);
}
.runcomplete .where:empty { display: none; }

@keyframes linebuilt {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

/* The outside world is not answering. Not an error — it is retrying by
   itself — but it must not look like ordinary progress either. */
.runcard.is-outage .runphase { color: var(--learn-wrong); }

/* Why the analysis stopped, on its own line under the receipt. */
.analysisline .why {
  display: block;
  margin-top: 0.25rem;
  color: var(--ui-text);
}

/* Chessify's session, inside the Engine panel rather than in one of its own. */
.chessifyaccount {
  margin-top: 0.75rem;
  padding-top: 0.625rem;
  border-top: 1px solid var(--ui-line);
}

/* ── folding a panel away ───────────────────────────────────────────────────
   Nine panels in one column is a lot of scrolling for someone who right now
   cares about two of them. Click a heading to fold; the state is remembered.
   The decision panel and the run controls are deliberately not foldable — a
   question you have accidentally hidden is the bug this screen keeps fighting. */

.panel h2.foldhead {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  user-select: none;
  margin-bottom: 0.625rem;
}
@media (hover: hover) { .panel h2.foldhead:hover { color: var(--ui-text); } }
.panel h2.foldhead:focus-visible {
  outline: 2px solid var(--ui-accent);
  outline-offset: 2px;
}

/* The chevron points down when open, right when folded. Drawn rather than
   typed so it cannot be mistaken for part of the heading. */
.panel h2.foldhead::before {
  content: "";
  flex: 0 0 auto;
  width: 0.4375rem;
  height: 0.4375rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform 120ms ease;
}
.panel.is-folded h2.foldhead::before {
  transform: rotate(-45deg) translate(-1px, 1px);
}

/* Everything except the heading goes. `> *` rather than a wrapper element so
   no panel needed its markup changed to become foldable. */
.panel.is-folded > *:not(h2) { display: none; }
.panel.is-folded { padding-bottom: 0.625rem; }

/* A shorter window buys the same fit by spending less on everything: smaller
   gaps, smaller padding, a shorter trail, and none of the lines that explain
   rather than report — those are all on `title` as well. */
@media (max-height: 1000px) {
  :root { --build-reserve: 12.25rem; }
  .layout { gap: 0.75rem; padding: 0.75rem; }
  .rightcol, .gridrow, .activitycol { gap: 0.625rem; }
  .panel { padding: 0.625rem; }
  .runnow { max-height: 2.625rem; }
  #trailCard > .hint,
  #panelPlaying > .hint,
  #panelEngine > .hint,
  #panelOptions > .hint:not([id]),
  #panelPrep > .hint { display: none; }

  /* A run that STOPPED keeps its position on the board, so the line is already
     there to be read. Printing it twice is a luxury a short window cannot
     afford; the sentence above it and where it went both stay.
     .runcomplete's copy is deliberately not dropped here — that one describes a
     run that finished, and a finished run has already put the board back. */
  .runoutcome .line { display: none; }

  /* Still the biggest thing on the card, just not 45px of it. */
  .runpctnum { font-size: 1.875rem; /* 30px */ }
  .runphase { font-size: 0.9375rem; /* 15px */ }

  /* Two entries rather than three, on a window that has to find the room. */
  #trailCard .traillist { min-height: 6.5rem; }
}

/* Below this there is no arrangement that fits, so the page gives up on one
   screen and scrolls like an ordinary page. */

/* ── a status that survives a fold ───────────────────────────────────────────
   The Chessify session, in the Engine panel's heading. The panel starts folded
   because everything else in it is set once; whether the session is alive is
   not, and a run cannot start without it. `.panel.is-folded > *:not(h2)` hides
   the body and leaves the heading, so this is the one place it can live. */
.headstatus {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  margin-left: auto;
  font-size: max(0.625rem, var(--text-min)); /* 10px */
  letter-spacing: 0;
  text-transform: none;
  font-weight: 400;
  color: var(--ui-dim);
  min-width: 0;
}
.headstatus .connstate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The heading is a flex row already when it is foldable (h2.foldhead), but not
   before collapsible() runs, and not on a panel that never folds. */
.panel h2:has(.headstatus) { display: flex; align-items: center; gap: 0.5rem; }

/* ═══════════════════════════════════════════════════════════════════════════
   The Start flow — since 2026-09-15 (evening)
   ═══════════════════════════════════════════════════════════════════════════

   José decided the steps; build.js's header lists them. `data-flow` on #layout
   is the step, and these rules are the whole of "which parts are on screen":

     start … extra   the flow card, the databases, and every sub-tab
     running         the run card (the Activity log, question included), the
                     databases and the board — the other sub-tabs go
     finish          the flow card's finish step, the databases and the board

   Hidden with `!important` for the same reason as `.tabpanel[hidden]`: the run
   card is painted `hidden = false` on every poll by paintRunCard, and the flow
   decides over that, not the other way round. */

#layout:not([data-flow="running"]) #runCard,
#layout:not([data-flow="running"]) #logPanel { display: none !important; }
#flowCard[hidden],
.flowstep[hidden] { display: none !important; }

#layout[data-flow="running"] #buildTabs .tab:not([data-tab="activity"]),
#layout[data-flow="finish"] #buildTabs .tab:not([data-tab="activity"]) { display: none; }

/* The first sub-tab is "Start" before a run and "Activity" during one. Two
   fixed words rather than one rewritten by script, so both are translated. */
.tablabel-run { display: none; }
#layout[data-flow="running"] .tablabel-run,
#layout[data-flow="finish"] .tablabel-run { display: inline; }
#layout[data-flow="running"] .tablabel-idle,
#layout[data-flow="finish"] .tablabel-idle { display: none; }

.flowcard { display: flex; flex-direction: column; }
.flowstep { display: flex; flex-direction: column; gap: 0.625rem; }

.flowtitle {
  margin: 0;
  font: 800 1.0625rem/1.25 var(--ui-font); /* 17px */
  color: var(--ui-text);
}

.flowback { margin-top: 0; }

/* ── Playing: two big halves rather than two radio dots ── */
.colorpick {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.375rem;
}
.colorbtn {
  position: relative;
  display: block;
  cursor: pointer;
}
.colorbtn input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}
.colorbtn span {
  display: block;
  text-align: center;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  background: var(--ui-btn);
  color: var(--ui-dim);
  font-weight: 700;
}
.colorbtn input:checked + span {
  background: var(--ui-accent);
  border-color: transparent;
  color: var(--ui-accent-text);
}
.colorbtn input:focus-visible + span { outline: 2px solid var(--ui-accent); outline-offset: 2px; }

/* ── Paste PGN: folded, because playing moves on the board is the usual way ── */
.pastepgn > summary {
  cursor: pointer;
  color: var(--ui-dim);
  font-size: 0.8125rem; /* 13px */
  font-weight: 600;
}
.pastepgn[open] > summary { margin-bottom: 0.375rem; }
.pastepgn textarea { width: 100%; }

/* ── the big Start, the Repertoire screen's own (repertoire.css .startbtn) ──
   Copied rather than shared: repertoire.css is that page's stylesheet and this
   page does not load it. Keep the two in step by hand. */
.startbtn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  width: 100%;
  margin-top: 0.25rem;
  padding: 0.9375rem 1rem;
  border: 0;
  border-radius: calc(var(--ui-radius) * 2);
  background: var(--ui-text);
  color: var(--ui-bg);
  font: 800 1.25rem/1 var(--ui-font); /* 20px */
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: 0 0.25rem 0.875rem color-mix(in srgb, var(--ui-text) 22%, transparent);
  transition: transform var(--anim-fade) ease;
}
@media (hover: hover) { .startbtn:hover { transform: translateY(-1px); background: color-mix(in srgb, var(--ui-text) 88%, var(--ui-accent)); } }
.startbtn:focus-visible { outline: 3px solid var(--ui-accent); outline-offset: 3px; }
.startbtn:disabled { opacity: 0.5; cursor: default; transform: none; }
.starticon { font-size: 0.9375rem; color: var(--ui-accent); }

/* ── the three ways to calculate (repertoire.css .startopt, same look) ── */
.startoptions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0.625rem;
}
.startopt {
  --opt: var(--ui-accent);
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  align-items: flex-start;
  text-align: left;
  min-height: 6rem;
  padding: 0.875rem 1rem;
  border: 2px solid var(--opt);
  border-radius: calc(var(--ui-radius) * 1.5);
  background: color-mix(in srgb, var(--opt) 12%, var(--ui-panel));
  color: var(--ui-text);
  font: inherit;
  cursor: pointer;
}
@media (hover: hover) { .startopt:hover:not(:disabled) { background: color-mix(in srgb, var(--opt) 22%, var(--ui-panel)); } }
.startopt:disabled { cursor: default; border-color: var(--ui-line); background: var(--ui-panel); }
.startopt:disabled > :not(.sobadges) { opacity: 0.5; }
.startopt .sotitle { font: 800 1rem/1.2 var(--ui-font); color: var(--opt); }
.startopt:disabled .sotitle { color: var(--ui-dim); }
.startopt .sohint { font-size: 0.8125rem; color: var(--ui-dim); line-height: 1.4; }

/* Pro AND Coming soon, on one disabled option. The gold pill is role.css's
   locked-Pro badge drawn as an element — role.js only badges a control for an
   account that is not paying, and this one is not available to anybody yet.
   The outlined pill is the shell's "Coming soon" on Lessons (shell.css .soon). */
.sobadges { display: flex; flex-wrap: wrap; gap: 0.375rem; margin-top: auto; }
.probadge-static {
  font: 700 max(0.625rem, var(--text-min))/1 var(--ui-font); /* 10px */
  letter-spacing: 0.1em;
  padding: 0.25rem 0.4375rem;
  border-radius: 62.4375rem;
  background: var(--pro-gold, #e3b341);
  color: var(--pro-gold-text, #1b1405);
  white-space: nowrap;
}
.startopt .soon {
  font-size: max(0.625rem, var(--text-min)); /* 10px */
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.1875rem 0.4375rem;
  border: 1px solid var(--ui-dim);
  border-radius: 62.4375rem;
  color: var(--ui-dim);
  white-space: nowrap;
}

/* ── the opening picker ── */
#openingSearch {
  width: 100%;
  background: var(--ui-input-bg);
  color: var(--ui-text);
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  padding: 0.5rem 0.625rem;
  font: 0.875rem/1.4 var(--ui-font);
}
.openinglist {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  max-height: 15rem;
  overflow-y: auto;
}
.openinglist:empty { display: none; }
.openingitem {
  display: inline-flex;
  align-items: baseline;
  gap: 0.375rem;
  padding: 0.3125rem 0.625rem;
  border: 1px solid var(--ui-line);
  border-radius: 62.4375rem;
  background: var(--ui-btn);
  color: var(--ui-text);
  font: 600 0.8125rem/1.3 var(--ui-font);
  text-align: left;
  cursor: pointer;
}
@media (hover: hover) { .openingitem:hover { border-color: var(--ui-accent); } }
.openingitem .oeco { font: 0.75rem/1 var(--ui-mono); color: var(--ui-dim); }
/* The short list is the highlighted one. */
.openinglist.popular .openingitem {
  border-color: var(--ui-accent);
  background: color-mix(in srgb, var(--ui-accent) 14%, var(--ui-panel));
}
.openinglist.popular { max-height: none; }

/* ── extra settings ── */
.extrafields {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: 0.625rem;
  grid-auto-flow: column;
  grid-template-rows: auto auto;
}
.extrafields .minilabel { margin-top: 0; }
@media (max-width: 520px) {
  .extrafields { grid-auto-flow: row; grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  .extrafields .minilabel { margin-top: 0.5rem; }
}
.flowremembered {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.flowremembered .hint { margin-top: 0; }
.flowremembered[hidden] { display: none; }

/* ── the finish question ── */
.finishcard { margin-top: 0; }
.finishcard .line { white-space: pre-line; }
.finishbtns .btn.big { min-width: 6rem; }
