/* SPEC §25 — the tactics trainer's visual language, inherited exactly.
   Members should experience one product, not two.

   Two themes, not five. §25 fixes the brand ramp, and a palette picker is a
   preferences surface with no payoff. But this tool is a READING tool in a way
   the trainer is not — Mike works through 448 comments in one file — and long
   prose on a dark field is the one place the trainer's language does not
   transfer cleanly. So: dark by default, so the two apps match on first run,
   and one switch for people doing a long session. The switch persists.

   Every colour below is a semantic role, not a hue. The themes swap the roles;
   nothing downstream knows which theme is on. Contrast is measured, not
   eyeballed — see the theme test. */
:root{
  /* Brand constants — identical in both themes. §25, verbatim from the trainer. */
  --orange:#e8650a; --orange2:#c84a00;
  --lt:#f0d9b5; --dk:#a9713f;          /* board: warm wood, deliberate (§25) */

  --serif:Georgia,serif;
  --mono:"Courier New",monospace;
  --ui:system-ui,Arial,sans-serif;
  --glyph:"Segoe UI Symbol","Apple Symbols","Noto Sans Symbols 2","DejaVu Sans","FreeSerif",serif;
}

/* ---- dark: the trainer's palette, with the secondary text fixed ---------
   The greys here used to be #7a9ab8 and #4a6a8a, which measured 5.26:1 and
   2.74:1 against the cards. 2.74 fails WCAG AA outright and 5.26 is punishing
   for 13.5px italic prose read for an hour. Same family, lifted until the
   numbers work. */
:root,
:root[data-theme="dark"]{
  --bg:#09141e; --panel:#0d1b2a; --card:#13253c; --border:#28486a;
  --text:#dde8f0;          /* body */
  --heading:#f0ede6;       /* main line, headings */
  --variation:#c2d9f2;     /* sideline moves — lighter and less saturated than
                              #9dc4ee, which read as "too blue" against navy */
  --prose:#83cca2;         /* comments, green like HIARCS — 8.2:1 */
  --muted:#9db6cd;         /* labels, move numbers — 7.4:1, was 5.26:1 */
  --faint:#8aa5be;         /* meta, fold controls — 6.0:1, was 2.74:1 */
  --accent:var(--orange);
  --on-accent:#150a02;
  --hover:#0d1b2a;
  --board-edge:0 0 0 3px rgba(255,255,255,.92),
               0 0 12px rgba(255,255,255,.42),
               0 0 40px rgba(255,255,255,.24),
               0 10px 34px rgba(0,0,0,.45);
  --shadow:0 6px 20px rgba(0,0,0,.45);
  color-scheme:dark;
}

/* ---- light: paper, not white -------------------------------------------
   A pure #fff page beside a warm wooden board reads as a bug. This is the
   cream already in the brand ramp, dropped to a page tint. The accent has to
   darken for text — #e8650a on near-white is 3.3:1 and fails — but the accent
   FILL keeps the true brand orange, so the current move looks the same in both
   themes. */
:root[data-theme="light"]{
  /* Cards are pure white. A cream card lowers the contrast of every glyph on it
     for no gain — the warmth belongs on the page behind, not under the text. */
  --bg:#ece7de; --panel:#f7f4ee; --card:#ffffff; --border:#d6cdbe;
  --text:#16222e;
  --heading:#0d1b2a;
  --variation:#1a4f9c;     /* sideline moves — see the note below */
  --prose:#17603a;         /* comments, green like HIARCS */
  --muted:#465767;         /* move numbers and labels: #5a6b7b read as grey mist */
  --faint:#566876;
  --accent:#a83e00;
  --on-accent:#fffaf5;
  --hover:#f0ebe2;
  --board-edge:0 0 0 2px rgba(27,40,54,.28),
               0 8px 26px rgba(27,40,54,.20);
  --shadow:0 6px 20px rgba(27,40,54,.22);
  color-scheme:light;
}

*{box-sizing:border-box}

/* `hidden` must mean hidden. The browser implements it as `[hidden]{display:none}`
   in the UA stylesheet, which ANY author rule setting `display` silently beats —
   and it beat two of them here. `.promo{display:flex}` left the promotion picker
   permanently on: a 72%-opaque sheet at z-index 6 over the board, which dimmed
   the pieces and swallowed every click, so moves and right-drag arrows both
   appeared broken with no error anywhere. `.nav{display:flex}` did the same to
   the undo/redo group, which showed before a file was even open.
   One rule, at the top, so it cannot happen a third time. */
[hidden]{display:none !important}

html,body{margin:0;min-height:100%}
body{background:var(--bg);color:var(--text);font-family:var(--serif);font-size:15px;
  line-height:1.45}

/* Font smoothing is per-theme, and this is why light mode looked "thinner,
   fancier, and not in a good way".
   `-webkit-font-smoothing:antialiased` removes subpixel rendering and makes
   glyph stems ONE greyscale pixel. On a dark field that is a fix — light-on-dark
   text blooms and needs thinning. On a light field it is the opposite of a fix:
   dark-on-light text is already visually thin, and stripping subpixel rendering
   takes away the only thing giving the stems weight. It was applied to both.
   Now dark gets the thinning it needs and light keeps its full weight, plus a
   little extra on the tree where the type is smallest. */
/* NO `-webkit-font-smoothing` anywhere, in either theme. Adding one back undoes
   the fix below; this comment is the guardrail.

   This is why the tree read thinner and "fancier" than HIARCS, and it took
   three rounds to find because it is invisible in a screenshot diff and does
   not move contrast at all. `-webkit-font-smoothing: antialiased` turns OFF
   subpixel rendering and draws every glyph stem as a single greyscale pixel.
   HIARCS is a native Windows app using ClearType, which paints stems at three
   times the horizontal resolution — on a 1x display that IS the difference in
   perceived weight, and no amount of colour or contrast work touches it.

   Measured, not guessed: the typeface was never the problem. `system-ui`
   resolves to Segoe UI here, the same font HIARCS uses — both render the string
   "Nxd5 Qxd5" at exactly 495px. Same font, different rasteriser.

   The browser default on Windows is subpixel. Leave it alone. */

/* The tree is a dense reference document read for an hour at a time, so it gets
   real weight rather than the browser's 400 — in BOTH themes, not just light. */
.tree{font-weight:500}
.run[data-depth="0"] .mv{font-weight:700}
.run:not([data-depth="0"]) .mv{font-weight:600}
.cmt{font-weight:500}

/* Light mode carries MORE weight than dark, not the same.
   Irradiation: light bleeds into dark at the edges, so light-on-dark text looks
   fatter and dark-on-light text looks thinner at identical weight. Matching the
   two numerically is what left light mode looking spindly next to a dark mode
   that reads fine. Every step here is one notch heavier. */
:root[data-theme="light"] .tree{font-weight:600}
:root[data-theme="light"] .run:not([data-depth="0"]) .mv{font-weight:700}
:root[data-theme="light"] .cmt{font-weight:600}
button{font:inherit;color:inherit;cursor:pointer}

.lab{font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);margin-bottom:8px}

/* ---- chrome ---------------------------------------------------------- */
.top{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;
  padding:14px 22px;border-bottom:1px solid var(--border);background:var(--panel)}
.top h1{margin:0;font-family:var(--serif);font-size:19px;color:var(--heading);font-weight:400}
.top h1 em{color:var(--accent);font-style:normal}
.top h1 .tm{font-size:.6em;vertical-align:super}
/* `flex-wrap` is load-bearing, not cosmetic. Without it this row measured 570px
   and forced the whole LAYOUT VIEWPORT to 593 on a 375px phone — so the page
   zoomed out and every piece of text on it shrank, including the move tree.
   Nothing looked broken; it just looked small, which is why it survived so
   long. v19 §11: traffic is overwhelmingly phones. */
.top-right{display:flex;align-items:center;gap:12px;flex-wrap:wrap;justify-content:flex-end;
  min-width:0}
.top-right .lab{margin:0}

.btn{border:1px solid var(--border);background:var(--card);color:var(--text);
  border-radius:10px;padding:9px 16px;font-family:var(--ui);font-size:13px;
  transition:border-color .15s,background .15s}
.btn:hover{border-color:var(--accent);background:var(--hover)}
.btn.ghost{background:transparent}
.btn.sm{padding:6px 11px;font-size:12px;border-radius:8px}
#theme{min-width:82px}

.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:14px 16px}

/* ---- layout: §30 board left, tree right, annotation below the tree ---- */
/* --board-size is set from the size control and persisted, the same way the
   trainer does it. */
.layout{display:grid;grid-template-columns:minmax(0,var(--board-size,520px)) minmax(340px,1fr);
  gap:20px;padding:20px;align-items:start;max-width:1500px;margin:0 auto}
.col-board,.col-tree{display:flex;flex-direction:column;gap:14px;min-width:0}
.col-tree{max-height:calc(100vh - 110px)}

/* ---- board ------------------------------------------------------------ */
.board-wrap{position:relative;aspect-ratio:1;width:100%}
.board{position:absolute;inset:0;display:grid;
  grid-template-columns:repeat(8,1fr);grid-template-rows:repeat(8,1fr);
  box-shadow:var(--board-edge);border-radius:2px;overflow:hidden;
  touch-action:none;user-select:none}
.sq{position:relative}
.sq.l{background:var(--lt)} .sq.d{background:var(--dk)}
.sq .co{position:absolute;font-family:var(--mono);font-size:9px;opacity:.6;pointer-events:none}
.sq .co.f{right:3px;bottom:1px} .sq .co.r{left:3px;top:1px}
.sq.l .co{color:rgba(0,0,0,.55)} .sq.d .co{color:rgba(255,255,255,.7)}
/* Each board preset carries its own last-move colour: gold reads well on the
   browns and muddy on the cool sets. Same variable name as the trainer. */
.sq.last::after{content:"";position:absolute;inset:0;background:var(--hl,#ffce54);opacity:.38}
.sq.sel::after{content:"";position:absolute;inset:0;background:rgba(232,163,61,.45)}
.sq.chk::before{content:"";position:absolute;inset:0;z-index:1;
  background:radial-gradient(circle,rgba(207,106,99,.8),transparent 70%)}
.sq.hint{box-shadow:inset 0 0 0 3px var(--orange)}
.dot{position:absolute;left:50%;top:50%;width:26%;height:26%;transform:translate(-50%,-50%);
  border-radius:50%;background:rgba(20,22,27,.30);pointer-events:none}
.dot.cap{width:100%;height:100%;background:none;box-shadow:inset 0 0 0 6px rgba(20,22,27,.28)}
.pieces{position:absolute;inset:0;pointer-events:none;font-size:60px}
.pc{position:absolute;width:12.5%;height:12.5%;display:flex;align-items:center;justify-content:center;
  line-height:1;will-change:transform;pointer-events:none;
  transition:transform var(--anim,150ms) cubic-bezier(.34,.03,.24,1)}
.pc.nofx{transition:none}
.pc svg{width:100%;height:100%;display:block;filter:drop-shadow(0 1px 1px rgba(0,0,0,.3))}

/* The Unicode piece set. Outlined rather than filled for White, or the white
   pieces vanish on a light square. Same treatment the trainer uses. */
.pc .gl{font-family:var(--glyph);font-size:1em;line-height:1}
.pc.b .gl{color:#191d24;text-shadow:0 1px 0 rgba(255,255,255,.16)}
.pc.w .gl{color:#fbfcfd;-webkit-text-stroke:1.6px #191d24;paint-order:stroke fill;
  text-shadow:0 1px 1px rgba(0,0,0,.3)}

/* §30 arrows and circles — %cal / %csl, drawn over the board */
.shapes{position:absolute;inset:0;pointer-events:none;overflow:visible}

.board-bar{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.nav{display:flex;gap:6px}
.board-zoom{display:flex;gap:5px}
#settings-btn[aria-expanded="true"]{border-color:var(--accent);color:var(--accent)}
.settings-pop{border-color:var(--accent)}

/* ---- the engine panel: §11, under the board where the eye already is ---- */
.engine-card .slot-note{margin:0;font-family:var(--ui);font-size:12.5px;color:var(--faint);line-height:1.55}
.engine-head{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:9px;
  flex-wrap:wrap}
/* Four controls and a readout. Unwrapped this measured 460px and was the last
   thing holding the layout viewport open on a phone — see the .top-right note. */
.engine-ops{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end;
  min-width:0}
.engine-ops select{max-width:100%}
.engine-meta{font-family:var(--mono);font-size:10.5px;color:var(--faint);white-space:nowrap}
#engine-preset,#engine-source{font-family:var(--ui);font-size:12px;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:7px;padding:5px 7px}
#engine-toggle.on{border-color:var(--accent);color:var(--accent)}

.engine-lines{display:flex;flex-direction:column;gap:3px;min-height:22px}
/* Numbers for a position you have already left: visible, but visibly not current. */
/* Dimmed, not gone. At .32 the lines read as having disappeared and come back
   on every move, which is the flicker — the numbers were there the whole time.
   Enough to say "this is the last position's answer" and no more. The
   transition is removed with it: fading is what made a 30ms gap look like an
   event. */
.engine-lines.stale{opacity:.72}
/* A FIXED row height, so a row that is waiting for the engine occupies exactly
   the same space as one that has arrived. Without this the panel still breathes
   as rows fill, and the move tree underneath moves with it. */
.eline{display:flex;align-items:baseline;gap:9px;font-family:var(--ui);font-size:13px;
  padding:3px 6px;border-radius:6px;height:24px;box-sizing:border-box}
.eline:hover{background:var(--hover)}
.eline.empty{pointer-events:none}
.eline .sc{font-family:var(--mono);font-weight:700;min-width:52px;text-align:right;color:var(--heading)}
.eline .sc.good{color:var(--green,#10B981)} .eline .sc.bad{color:var(--red,#ef4444)}
.eline .line{color:var(--variation);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
.eline .dep{font-family:var(--mono);font-size:10.5px;color:var(--faint)}

/* A piece under the cursor. It is a clone, not the rendered piece: board.js
   re-uses its elements between renders and animates their transforms, so
   dragging the real one fights the renderer for the same transform property. */
.drag-piece{position:fixed;z-index:30;pointer-events:none;
  filter:drop-shadow(0 8px 12px rgba(0,0,0,.5))}
.drag-piece svg{width:100%;height:100%;display:block}
.board.dragging{cursor:grabbing}
.fen-card{padding:11px 14px}
.fen{font-family:var(--mono);font-size:12px;color:var(--muted);word-break:break-all;display:block}

/* ---- board settings: size and colours, matching the trainer's (§25) --- */
.set-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.set-row + .set-row{margin-top:11px}
.set-row .lab{margin:0;min-width:52px}
.size{flex:1;min-width:120px;accent-color:var(--orange)}
.size-v{font-family:var(--mono);font-size:11.5px;color:var(--faint);min-width:44px;text-align:right}
.swatches{display:flex;gap:7px;flex-wrap:wrap}
.sw{width:30px;height:22px;border-radius:6px;border:2px solid transparent;
  padding:0;overflow:hidden;display:flex;cursor:pointer}
.sw i{flex:1;display:block}
.sw:hover{border-color:var(--muted)}
.sw[aria-current="true"]{border-color:var(--orange)}

/* ---- chapters --------------------------------------------------------- */
.rep-name{font-family:var(--serif);font-size:17px;color:var(--heading);
  margin:0 0 11px;display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.rep-name .rep-sub{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint)}
.rep-name .rename{font-size:16px;min-width:220px}
.chapters{display:flex;flex-wrap:wrap;gap:7px}
.chapters button{border:1px solid var(--border);background:transparent;color:var(--text);
  border-radius:8px;padding:6px 12px;font-family:var(--ui);font-size:12.5px}
.chapters button:hover{border-color:var(--accent)}
.chapters button[aria-current="true"]{border-color:var(--accent);color:var(--heading);
  box-shadow:inset 0 -2px 0 var(--accent)}
.chapters .empty-chapter{opacity:.55}
.chapters button[draggable="true"]{cursor:grab}
.chapters button.drag-over{border-color:var(--accent);border-style:dashed}

/* 46 chapters as pills filled the panel before the move tree got a pixel. Past
   a handful they become a select: one line, types-to-jump, grouped by the
   hierarchy already in the names. */
.chapter-pick{flex:1;min-width:0;max-width:100%;font-family:var(--ui);font-size:13px;
  color:var(--text);background:var(--bg);border:1px solid var(--border);
  border-radius:8px;padding:7px 10px}
.chapter-pick:focus{outline:none;border-color:var(--accent)}
/* The reorder that drag-and-drop used to do. */
.chapter-move{flex:0 0 auto;border:1px solid var(--border);background:var(--card);
  color:var(--muted);border-radius:8px;padding:6px 9px;font-size:10px;line-height:1}
.chapter-move:hover:not(:disabled){color:var(--heading);border-color:var(--accent)}
.chapter-move:disabled{opacity:.32;cursor:default}

/* Reorder mode: the whole list at once, and it stays put between moves. */
.reorder-list{display:flex;flex-direction:column;gap:3px;width:100%;
  max-height:52vh;overflow-y:auto}
.reorder-row{display:grid;grid-template-columns:auto 1fr auto auto;gap:8px;align-items:center;
  padding:6px 9px;border:1px solid var(--border);border-radius:8px;background:var(--bg);
  font-family:var(--ui);font-size:12.5px;cursor:grab}
.reorder-row.on{border-color:var(--accent);background:var(--hover)}
.reorder-row.drag-over{border-style:dashed;border-color:var(--accent)}
.reorder-row .grip{color:var(--faint);letter-spacing:-3px;cursor:grab}
.reorder-row .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.reorder-row .nm em{color:var(--faint);font-style:normal;font-size:11px}
.btn.on{border-color:var(--accent);color:var(--heading)}
.chapters .rename{font-family:var(--ui);font-size:12.5px;padding:6px 10px;
  border:1px solid var(--accent);border-radius:8px;background:var(--bg);color:var(--text);
  min-width:150px}
.chapters .rename:focus{outline:none}

/* ---- §12 reports: what a game did against the prep -------------------- */
/* The reports panel takes over the tree column the way the library does. It is
   `hidden` rather than display-swapped so the [hidden] rule at the top governs
   it — see the note there about .promo. */
.reports{display:flex;flex-direction:column;gap:12px;min-height:0;overflow-y:auto}
.col-tree.showing-games > .card,
.col-tree.showing-games > .library{display:none}

.game-input{width:100%;font-family:var(--mono);font-size:12px;line-height:1.5;
  color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:9px;
  padding:9px 11px;resize:vertical;margin-bottom:9px}
.game-input:focus{outline:none;border-color:var(--accent)}
.game-ops{gap:9px}

.paste-note{font-family:var(--ui);font-size:12.5px;line-height:1.55;margin:9px 0 0;
  color:var(--muted)}
.paste-note.warn{color:var(--orange)}
.color-choice{display:inline-flex;gap:7px;margin-left:9px}

/* One row per game: who played, what happened, where it happened, when. */
.game-list{display:flex;flex-direction:column;gap:1px}
.game-row{display:grid;grid-template-columns:auto 1fr auto auto auto auto;gap:10px;align-items:center;
  width:100%;text-align:left;padding:8px 10px;border:0;border-radius:8px;
  background:transparent;color:var(--text);font-family:var(--ui);font-size:12.5px}
.game-caret{color:var(--faint);font-size:10px}
.game-opening{color:var(--muted);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;max-width:26ch}
/* The detail opens UNDER its row so the run of games stays on screen — the
   sequence is the point of the list, and swapping it for a detail view makes
   it a detail view with a history attached instead. */
.game-detail{padding:4px 10px 12px 30px;border-left:2px solid var(--orange);margin:2px 0 8px 12px}
.game-row:hover{background:var(--hover)}
.game-row.on{background:var(--hover);box-shadow:inset 2px 0 0 var(--orange)}
.game-players{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.game-when{font-family:var(--mono);font-size:11px;color:var(--muted)}
.game-at{font-family:var(--mono);font-size:11.5px;color:var(--orange)}

/* The outcomes are colour-coded, and the coding carries §9's meaning: leaving
   your own prep is the member's to act on, an uncovered line is OUR gap, and
   neither is an error. Nothing here is red-for-bad. */
.game-outcome{font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;
  text-transform:uppercase;padding:2px 7px;border-radius:99px;white-space:nowrap}
.game-outcome.user_deviated{color:var(--orange);background:color-mix(in srgb,var(--orange) 14%,transparent)}
.game-outcome.opponent_out_of_book{color:var(--variation);background:color-mix(in srgb,var(--variation) 14%,transparent)}
.game-outcome.stayed_in_book{color:var(--prose);background:color-mix(in srgb,var(--prose) 14%,transparent)}
.game-outcome.no_repertoire,.game-outcome.not_analysed{color:var(--muted);background:var(--hover)}

.report-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px}

/* Opening / Deviation / Database — three labelled rows and a door. The label
   column is fixed so the three bodies line up and the card can be read down
   the left edge in one pass. */
.report-row{display:grid;grid-template-columns:78px 1fr;gap:12px;align-items:baseline;
  padding:9px 0;border-top:1px solid var(--border)}
.report-row:first-of-type{border-top:0}
.report-row > .lab{margin:0;padding-top:2px}
.report-body{font-family:var(--ui);font-size:13.5px;line-height:1.5;color:var(--text);min-width:0}
.report-body b{color:var(--heading);font-weight:600}
.eco{font-family:var(--mono);font-size:11.5px;color:var(--muted);margin-left:5px}
.report-moves{font-family:var(--mono);font-size:12px;color:var(--muted);margin-top:4px;
  overflow-wrap:anywhere}
.report-sub{font-size:12px;color:var(--muted);margin-top:5px}
/* Two doors, side by side. Replaying the game and reading the prep are
   different jobs; which one you want is yours to pick, not the layout's. */
.report-doors{display:flex;gap:9px;flex-wrap:wrap}
.btn.accent{background:var(--orange);color:var(--on-accent);border-color:var(--orange);font-weight:600}
.btn.accent:hover{filter:brightness(1.08)}

.report-outcome{font-family:var(--ui);font-size:15px;font-weight:600;line-height:1.4;
  margin:4px 0 10px;color:var(--heading)}
.report-outcome.opponent_out_of_book{color:var(--variation)}
.report-outcome.stayed_in_book{color:var(--prose)}
.report-line{font-family:var(--ui);font-size:13px;line-height:1.6;margin:0 0 7px;color:var(--text)}
.report-line.muted{color:var(--muted);font-size:12.5px}
.report-line .played{color:var(--orange)}
.report-line .prepared{color:var(--prose)}
.report-ops{display:flex;gap:8px;flex-wrap:wrap;margin-top:11px}
.lab.sub{margin-top:13px}
.lab.sub:first-child{margin-top:0}

/* A game laid over the tree. The trail says "your game went this way"; the
   fork says "and here is where it stopped following the file". Both are
   OUTLINES, never fills — the orange fill means "the move you are standing on"
   everywhere else in this app, and two meanings for one treatment would make
   the current move impossible to find. */
.mv.from-game{box-shadow:inset 0 -2px 0 var(--variation)}
.mv.from-game.exit{box-shadow:inset 0 0 0 1.5px var(--orange);border-radius:5px}
.mv.from-game[aria-current="true"]{box-shadow:none}

.overlay-bar{display:flex;align-items:center;justify-content:space-between;gap:10px;
  flex-wrap:wrap;padding:8px 12px;border:1px solid var(--border);border-left:3px solid var(--orange);
  border-radius:9px;background:var(--panel)}
.overlay-what{font-family:var(--ui);font-size:12.5px;color:var(--muted)}
.overlay-what b{color:var(--heading);font-weight:600}
.overlay-ops{display:flex;gap:6px;flex-wrap:wrap}

.lib-head{display:flex;justify-content:space-between;align-items:center;gap:10px}
.lib-ops{display:flex;align-items:center;gap:10px}

/* Search results: one row per file that holds the position. */
.find-card .lib-head{display:flex;justify-content:space-between;align-items:center}
.find-row{display:grid;grid-template-columns:1fr auto auto;gap:10px;align-items:baseline;
  width:100%;text-align:left;padding:8px 10px;border:0;border-radius:8px;
  background:transparent;color:var(--text);font-family:var(--ui);font-size:13px}
.find-row:hover{background:var(--hover)}
.find-row .nm{color:var(--heading);font-weight:600;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.find-row .where{color:var(--muted);font-size:12px}

/* ---- the library: two ways in, never one giant drop zone -------------- */
/* ---- the three tabs ---------------------------------------------------
   Analysis / Database / Games, the way the analysis boards Mike already works
   in are laid out: the engine is the top of the right column and the move list
   sits under it.

   These rules only ever HIDE. Showing is left to each element's own display
   rule and its own `hidden` attribute — a `display:block` here would override
   .library's flex column, and un-hiding the notes card would put it on screen
   at times its own logic keeps it away. */
.panel-tabs{display:flex;gap:0;border:1px solid var(--border);border-radius:9px;
  overflow:hidden;flex:0 0 auto}
.panel-tabs button{flex:1;border:0;background:transparent;color:var(--muted);
  font-family:var(--ui);font-size:12.5px;font-weight:600;padding:9px 10px;white-space:nowrap}
.panel-tabs button + button{border-left:1px solid var(--border)}
.panel-tabs button:hover{color:var(--heading);background:var(--hover)}
.panel-tabs button[aria-pressed="true"]{background:var(--orange);color:var(--on-accent)}

.col-tree[data-panel="analysis"] > .pane-database,
.col-tree[data-panel="analysis"] > .pane-games,
.col-tree[data-panel="analysis"] > .pane-files,
.col-tree[data-panel="database"] > .pane-analysis,
.col-tree[data-panel="database"] > .pane-games,
.col-tree[data-panel="database"] > .pane-files,
.col-tree[data-panel="games"] > .pane-analysis,
.col-tree[data-panel="games"] > .pane-database,
.col-tree[data-panel="games"] > .pane-files,
.col-tree[data-panel="files"] > .pane-analysis,
.col-tree[data-panel="files"] > .pane-database,
.col-tree[data-panel="files"] > .pane-games{display:none}

/* The engine is the first thing in the column now, so it gets room to breathe
   rather than the scrap it had under the board. */
.col-tree .engine-card{flex:0 0 auto}
/* No fixed min-height: the row count is fixed instead (see .eline), so the
   panel is exactly as tall as the number of lines you asked for. A floor here
   would only add dead space below ten lines and fight the padding above it. */

.library{display:flex;flex-direction:column;gap:14px}
.lib-choices{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
@media (max-width:900px){.lib-choices{grid-template-columns:1fr}}

/* Folders are a label, not a place — see normaliseFolder in src/library.js. */
.rep-folder{font-family:var(--ui);font-size:12px;color:var(--faint);cursor:pointer;
  border:1px solid var(--border);border-radius:7px;padding:3px 9px}
.rep-folder:hover{border-color:var(--accent);color:var(--accent)}
.rep-folder:empty::before{content:"＋ file this";color:var(--faint)}

.lib-group{margin-bottom:11px}
.lib-group:last-child{margin-bottom:0}
.lib-folder{display:flex;align-items:center;gap:8px;width:100%;padding:6px 4px;border:0;
  background:transparent;color:var(--muted);font-family:var(--mono);font-size:10.5px;
  letter-spacing:.12em;text-transform:uppercase;text-align:left}
.lib-folder:hover{color:var(--accent)}
.lib-folder .twist{font-size:9px;color:var(--faint);width:9px}
.lib-folder .count{margin-left:auto;color:var(--faint);letter-spacing:0}
.lib-group[data-open="false"] .lib-list-inner{display:none}
.lib-list-inner{display:flex;flex-direction:column;gap:6px;padding-left:6px}

.lib-note{font-family:var(--ui);font-size:13.5px;line-height:1.55;padding:13px 16px;
  border-radius:12px;border:1px solid var(--border);background:var(--card);color:var(--text)}
.lib-note.warn{border-color:var(--orange);color:var(--heading)}

.lib-head{display:flex;justify-content:space-between;align-items:baseline}
.lib-list{display:flex;flex-direction:column;gap:6px}
.lib-row{display:flex;align-items:center;gap:11px;padding:9px 11px;border-radius:9px;
  border:1px solid var(--border);background:transparent;text-align:left;width:100%}
.lib-row:hover{border-color:var(--accent);background:var(--hover)}
.lib-row .nm{font-family:var(--serif);font-size:15.5px;color:var(--heading);flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lib-row .meta{font-family:var(--mono);font-size:11px;color:var(--faint);white-space:nowrap}
.lib-row .kill{border:1px solid var(--border);border-radius:6px;background:transparent;
  color:var(--faint);font-family:var(--ui);font-size:11px;padding:3px 8px}
.lib-row .kill:hover{border-color:var(--red,#ef4444);color:var(--red,#ef4444)}
.lib-empty{font-family:var(--ui);font-size:13px;color:var(--faint);padding:6px 2px}

/* ---- what ships with the membership — v19 §3 --------------------------- */
/* A distinct shelf, never interleaved with the member's own files (§4). The
   orange edge is the same one the brand uses for "this is ours". */
.lib-group.shipped{border-left:2px solid var(--orange);padding-left:10px;margin-bottom:16px}
.shipped-note{font-family:var(--ui);font-size:12.5px;line-height:1.55;color:var(--muted);
  margin:2px 0 10px}
.lib-sub{margin-bottom:10px}
.lib-subhead{display:flex;align-items:center;gap:10px;padding:4px 2px 6px;
  font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint)}
.lib-subhead>span:first-child{flex:1}

.lib-row.official{cursor:pointer}
.lib-row.official .nm{font-style:normal}
.lockmark{font-size:11px;line-height:1;opacity:.75;flex:none}

.dup{border:1px solid var(--border);border-radius:7px;background:transparent;color:var(--accent);
  font-family:var(--ui);font-size:12px;padding:4px 10px;letter-spacing:0;text-transform:none}
.dup:hover{border-color:var(--accent);background:var(--hover)}
.dup:disabled{opacity:.55}
.dup.sm{font-size:11px;padding:3px 8px}
.dup-done{font-family:var(--ui);font-size:11.5px;color:var(--faint);letter-spacing:0;
  text-transform:none}

.scope{margin-bottom:10px}
.scope button{flex:1}

/* §3's update notice. Passive on purpose — no badge, no count, nothing to
   dismiss. It is information about a file they chose to fork, not a task. */
.update-bar{display:flex;align-items:center;flex-wrap:wrap;gap:9px;padding:10px 13px;
  margin-bottom:12px;border:1px solid var(--border);border-radius:10px;
  font-family:var(--ui);font-size:13px;line-height:1.5;color:var(--muted)}
.update-bar>span{flex:1;min-width:200px;color:var(--text)}

/* Read-only, said once and then out of the way. */
.readonly-bar{display:flex;align-items:center;gap:11px;padding:10px 13px;margin-bottom:12px;
  border:1px solid var(--orange);border-radius:10px;background:var(--hover);
  font-family:var(--ui);font-size:13px;line-height:1.5;color:var(--text)}
.readonly-bar>span:nth-child(2){flex:1}
.readonly-bar b{color:var(--heading)}

/* ---- §28 Rule 4: your own games from this position ---------------------- */
/* The bar carries a grey uncertainty band rather than a minimum-sample rule —
   §28: "Nothing is hidden, thin samples display with visibly enormous bands." */
.explorer-card{margin-top:14px}
.xp-row{display:flex;align-items:center;gap:10px;width:100%;padding:6px 8px;border-radius:7px;
  border:1px solid transparent;background:transparent;text-align:left}
.xp-row:hover{border-color:var(--accent);background:var(--hover)}
.xp-san{font-family:var(--mono);font-size:13px;color:var(--heading);min-width:56px}
.xp-san.book{color:var(--orange)}
.xp-san.book::after{content:"●";font-size:8px;vertical-align:middle;margin-left:5px}
.xp-count{font-family:var(--mono);font-size:11px;color:var(--faint);min-width:34px;text-align:right}
.xp-bar{position:relative;flex:1;height:9px;border-radius:5px;background:var(--border);overflow:hidden}
.xp-band{position:absolute;top:0;bottom:0;background:var(--faint);opacity:.35}
.xp-mark{position:absolute;top:-2px;width:2px;height:13px;background:var(--accent);margin-left:-1px}
.xp-pct{font-family:var(--mono);font-size:11px;color:var(--muted);min-width:34px;text-align:right}
.xp-none{flex:1;font-family:var(--ui);font-size:11px;color:var(--faint)}

/* ---- merge mode -------------------------------------------------------- */
/* The list stops being a set of doors and becomes a set of picks. The box on
   the left is what says so — without it, clicking a file and having it not
   open reads as broken. */
.lib-row .pick{width:15px;height:15px;flex:none;border:1px solid var(--border);border-radius:4px;
  display:flex;align-items:center;justify-content:center;font-size:10px;line-height:1;
  color:var(--bg)}
.lib-row.picked{border-color:var(--accent);background:var(--hover)}
.lib-row.picked .pick{background:var(--accent);border-color:var(--accent)}
.lib-folder .pick-all{margin-left:auto;border:1px solid var(--border);border-radius:6px;
  background:transparent;color:var(--faint);font-family:var(--ui);font-size:10.5px;
  letter-spacing:0;text-transform:none;padding:2px 8px}
.lib-folder .pick-all:hover{border-color:var(--accent);color:var(--accent)}
.lib-folder .pick-all + .count{margin-left:8px}

/* Chapter merge reuses the reorder list — same rows, a box instead of a grip. */
.reorder-row.pickable{cursor:pointer}
.reorder-row.pickable:hover{border-color:var(--accent)}
.reorder-row.picked{border-color:var(--accent);background:var(--hover)}
.reorder-row .pick{width:15px;height:15px;flex:none;border:1px solid var(--border);border-radius:4px;
  display:flex;align-items:center;justify-content:center;font-size:10px;line-height:1;color:var(--bg)}
.reorder-row.picked .pick{background:var(--accent);border-color:var(--accent)}
.reorder-row .meta{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--faint)}

.merge-bar{border:1px solid var(--accent);border-radius:10px;padding:11px 12px;margin-bottom:11px;
  background:var(--hover);display:flex;flex-direction:column;gap:9px}
.merge-say{font-family:var(--ui);font-size:12.5px;line-height:1.5;color:var(--text)}
.merge-ops{display:flex;gap:7px;align-items:center}
.merge-ops input{flex:1;min-width:0;font-family:var(--ui);font-size:13px;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:7px 10px}
.merge-ops input:focus{outline:none;border-color:var(--accent)}

.rep-ops{margin-left:auto;display:flex;gap:6px;align-items:center}
.chapter-ops{display:flex;gap:6px;margin-top:10px}
.saved-note{font-family:var(--ui);font-size:12px;color:var(--green,#10B981)}
.lib-back{align-self:flex-start}

/* Renaming was double-click only, which is invisible. A pencil that is always
   there is the difference between a feature and a secret. */
.pencil{border:1px solid transparent;border-radius:6px;background:transparent;
  color:var(--faint);font-size:13px;line-height:1;padding:3px 6px}
.pencil:hover{border-color:var(--accent);color:var(--accent)}
.chapters button .pencil{margin-left:5px}

.tree-controls{display:flex;align-items:center;gap:6px}
.tree-controls .btn{font-size:11px;padding:4px 9px}
.tree-controls .btn.on{border-color:var(--accent);color:var(--accent)}

/* ---- right-click menu on a move ---------------------------------------
   The command hierarchy is HIARCS's, which is fine to reproduce — a menu tree
   is a method of operation, not an expressive work (Lotus v. Borland, 1st Cir.
   1995, aff'd 516 U.S. 233). Their icons and artwork are NOT copied, and one
   command is deliberately missing: see the MENU spec in app.js. */
.menu{position:fixed;z-index:40;min-width:216px;padding:5px;border-radius:11px;
  background:var(--card);border:1px solid var(--border);box-shadow:var(--shadow)}
.menu-head{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint);padding:7px 10px 6px}
.menu button,.mi{display:flex;align-items:center;justify-content:space-between;gap:14px;
  width:100%;text-align:left;padding:7px 10px;border:0;border-radius:7px;
  background:transparent;color:var(--text);font-family:var(--ui);font-size:13px;cursor:pointer}
.menu button:hover,.mi:hover{background:var(--hover);color:var(--heading)}
.menu button.danger:hover{color:var(--red,#ef4444)}
.menu button:disabled{opacity:.38;cursor:default}
.menu button:disabled:hover{background:transparent;color:var(--text)}
.menu hr{border:0;border-top:1px solid var(--border);margin:5px 8px}
.mi .caret{color:var(--faint);font-size:11px}

.has-sub{position:relative}
.sub{display:none;position:absolute;left:100%;top:-5px;min-width:224px;padding:5px;
  border-radius:11px;background:var(--card);border:1px solid var(--border);box-shadow:var(--shadow)}

/* Opened by JS, not by :hover — see the hover-intent handler in app.js. With
   :hover the submenu vanished the instant the pointer crossed the sliver
   between the two panels, which is exactly where the pointer has to go. */
.has-sub.open > .sub{display:block}

/* And the sliver itself is bridged: the submenu's hit area reaches back over
   the parent's edge, so travelling between them never leaves both. The border
   is transparent rather than a margin so it costs no layout. */
.sub{border-left:10px solid transparent;background-clip:padding-box;margin-left:-10px}
.menu.flip .sub{left:auto;right:100%;border-left:0;margin-left:0;
  border-right:10px solid transparent;margin-right:-10px}

/* One symbols submenu with labelled sections, rather than three separate
   top-level entries. Glyphs in a grid; nobody reads "±" as a sentence. */
.sub.symbols{min-width:184px}
.sym-title{font-family:var(--mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint);padding:7px 8px 4px}
.sym-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:0 4px 4px}
.sym-grid button{justify-content:center;font-family:var(--glyph);font-size:15px;padding:8px 6px}
.sym-grid button[aria-pressed="true"]{background:var(--orange);color:#150a02}

/* ---- library search ---------------------------------------------------- */
.lib-search{width:100%;font-family:var(--ui);font-size:13px;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 11px;
  margin-bottom:9px}
.lib-search:focus{outline:none;border-color:var(--accent)}

.opener{display:flex;flex-direction:column;align-items:center;gap:7px;width:100%;
  padding:46px 24px;border:2px dashed var(--border);border-radius:16px;
  background:transparent;color:var(--text);text-align:center;
  transition:border-color .15s,background .15s}
.opener:hover{border-color:var(--accent);background:var(--hover)}
.opener-plus{font-family:var(--ui);font-size:32px;line-height:1;color:var(--accent)}
.opener-main{font-family:var(--serif);font-size:21px;color:var(--heading)}
.opener-sub{font-family:var(--ui);font-size:13px;color:var(--muted)}
body.dragging .opener{border-color:var(--accent);background:var(--hover)}

/* ---- the move tree: §30 "the tree IS the document" -------------------- */
/* `overflow:hidden` is load-bearing. The tree's own min-height can exceed what
   the column has left once the notes panel is pinned below it, and a flex child
   that cannot shrink far enough spills its content straight out of the card —
   moves appeared floating in the gap between the two panels, outside any
   border. Clipping here plus a small min-height on .tree keeps it contained. */
.tree-card{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
.tree-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.tree-meta{color:var(--faint)}

/* Sans, not Georgia. §25 already says "serif for display headings, sans for
   body" and the tree is body — setting it in the brand serif was the deviation.
   It also gave Georgia's old-style figures, so "8." and "9." sat with
   descenders below the baseline and the whole tree read as ornamental. HIARCS
   is plainer here and is right to be: this is a dense reference document, not
   prose. Lining tabular figures keep the move numbers in a column. */
/* Room past the end, the way a code editor scrolls past the last line. Without
   it the final moves of a chapter can only ever sit on the bottom edge — there
   is nothing below them to scroll up into — so the "keep the cursor a fifth of
   the way down" rule silently stops working exactly where lines get deepest. */
/* `min-height:0` so the flex child can actually shrink to its card, and the
   scroll-past-end padding in px — a percentage there resolves against WIDTH,
   which on a wide panel is hundreds of pixels of phantom height and pushed the
   tree straight out through the bottom of its own card. */
.tree{overflow-y:auto;flex:1;min-height:0;outline:none;padding-right:6px;padding-bottom:320px;
  font-family:var(--ui);font-variant-numeric:lining-nums tabular-nums;line-height:1.8}
.tree:focus-visible{box-shadow:inset 0 0 0 1px var(--border);border-radius:8px}

/* Indentation is driven by --d and nothing else. It used to be --d padding PLUS
   a per-depth margin rule, and the per-depth rule overrode the padding — so
   depths 3, 4 and 5 all landed on the same 26px and nesting past two levels was
   invisible. One system, one rule, no override. */
/* A reading measure, not the width of the monitor.
   On a wide screen a run of moves and prose stretched the full panel, and the
   eye loses the line on the way back to the left edge — the same reason a
   newspaper sets in columns. ~90 characters is the comfortable ceiling — 76 was
   too tight and left the panel visibly half empty, so the lines read truncated
   rather than measured. The panel can be as wide as it likes; the text stops. */
/* Four channels, not two. This is the thing HIARCS gets right and a wall of
   near-black text does not: main line, sideline, prose and symbol each get
   their own COLOUR, so the eye sorts them before it reads a word. Sidelines in
   dark ink differentiated only by font weight makes the whole tree one texture,
   and skimming a 467-variation chapter becomes work. Blue for sidelines is the
   convention every chess GUI already uses and Mike already reads. */
.run{margin:2px 0;max-width:90ch}
.run[data-depth="0"]{font-size:16.5px;color:var(--heading);margin:7px 0}
.run:not([data-depth="0"]){font-size:15.5px;color:var(--variation);
  margin-left:calc(var(--d) * 14px);padding-left:9px;
  border-left:2px solid var(--border)}
.run:not([data-depth="0"]) + .run:not([data-depth="0"]){margin-top:0}
.run.hidden{display:none}

/* The move number is part of the move, not a footnote to it. HIARCS sets "9.a3"
   as ONE thing — same size, same weight, same shade — and that is why it reads
   as a unit rather than as a label plus a move.
   No opacity, no lighter weight, no separate colour. Any of the three puts two
   different inks on one word and the eye has to reassemble it every time. The
   weight rules below mirror .mv exactly; if one changes, so must the other. */
.num{font-size:1em;color:inherit;font-weight:inherit;margin-right:2px}
.mv{display:inline-block;padding:1px 5px;border-radius:5px;cursor:pointer;
  border:1px solid transparent}
.mv:hover{background:var(--hover);border-color:var(--border)}
.mv[aria-current="true"]{background:var(--orange);color:#150a02;border-color:var(--orange)}
.mv .g{font-family:var(--glyph);color:var(--accent);margin-left:1px}
.mv[aria-current="true"] .g{color:#150a02}
/* Upright, not italic. Italic at 14px across hundreds of comments is the other
   half of what made this read as "fancy"; HIARCS sets prose upright and tells
   it apart from the moves by colour alone, which is enough. */
.cmt{color:var(--prose);margin:0 5px}
.fold{font-family:var(--mono);font-size:10.5px;color:var(--faint);cursor:pointer;
  border:1px solid var(--border);border-radius:4px;padding:0 5px;margin-left:5px;
  background:transparent;line-height:1.4}
.fold:hover{border-color:var(--accent);color:var(--accent)}

/* ---- the editor panel: §30's table, in one place ---------------------- */
.hint{font-family:var(--ui);font-size:12px;color:var(--faint);margin:0;text-align:center}

.editor-card{display:flex;flex-direction:column;gap:11px}

/* Docked to the bottom of the tree column, the same place it sits when pinned.
   Left and width are set in JS from the column's box. */
.editor-card.floating{position:fixed;z-index:35;border-color:var(--accent);box-shadow:var(--shadow)}
.editor-card:not(.floating) #close-notes{display:none}
.editor-head{display:flex;justify-content:space-between;align-items:center;gap:12px;margin:0}
#editor-at{color:var(--muted)}
.move-ops{display:flex;gap:6px}
.btn.danger:hover{border-color:var(--red,#ef4444);color:var(--red,#ef4444)}
.btn:disabled{opacity:.4;cursor:default}
.btn:disabled:hover{border-color:var(--border);background:transparent}

.nags{display:flex;flex-wrap:wrap;gap:5px}
.nag{font-family:var(--glyph);font-size:15px;line-height:1;min-width:32px;padding:6px 7px;
  border:1px solid var(--border);border-radius:7px;background:transparent;color:var(--text)}
.nag:hover:not(:disabled){border-color:var(--accent)}
.nag[aria-pressed="true"]{background:var(--orange);color:#150a02;border-color:var(--orange)}
.nag:disabled{opacity:.35;cursor:default}

.note-edit{width:100%;resize:vertical;min-height:64px;
  font-family:var(--ui);font-size:14px;line-height:1.55;color:var(--prose);
  background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:9px 11px}
.note-edit:focus{outline:none;border-color:var(--accent)}
.note-edit:disabled{opacity:.5}

/* Notes visibility lives in two places on purpose: a cycling button in the tree
   header for the quick flick while reading, and this spelled-out control in
   settings so it is findable by someone who has not met the button yet. Both
   drive the same preference. */
.segmented{display:flex;gap:0;border:1px solid var(--border);border-radius:8px;overflow:hidden}
.segmented button{border:0;background:transparent;color:var(--muted);
  font-family:var(--ui);font-size:12px;padding:6px 11px;white-space:nowrap}
.segmented button + button{border-left:1px solid var(--border)}
.segmented button:hover{color:var(--heading);background:var(--hover)}
.segmented button[aria-pressed="true"]{background:var(--orange);color:#150a02}

/* Five sets across is wider than the settings column at a small window, and a
   segmented control that overflows puts its last button off the edge where it
   cannot be clicked at all. */
#piece-set{flex-wrap:wrap}
#piece-set button{flex:1 0 auto}

/* Who drew the pieces and under what licence — §14. Small, but never absent:
   two of these sets are GPL and showing this is the condition of shipping them. */
.credit{font-family:var(--ui);font-size:11.5px;color:var(--muted);margin:0;min-height:1em}

.check{display:flex;align-items:center;gap:7px;font-family:var(--ui);font-size:12.5px;color:var(--muted)}
.check input{accent-color:var(--orange)}

.lesson-row .lab{min-width:52px}
.lesson{flex:1;min-width:140px;font-family:var(--ui);font-size:13px;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:7px 10px}
.lesson:focus{outline:none;border-color:var(--accent)}
.lesson:disabled{opacity:.5}

/* Promotion picker — only shown when a pawn actually reaches the last rank. */
.promo{position:absolute;inset:0;z-index:6;display:flex;align-items:center;justify-content:center;
  background:rgba(6,14,22,.72)}
.promo-inner{display:flex;gap:8px;padding:14px;border-radius:14px;
  background:var(--card);border:1px solid var(--border);box-shadow:var(--shadow)}
.promo button{width:60px;height:60px;padding:6px;border:1px solid var(--border);border-radius:10px;
  background:transparent;display:flex;align-items:center;justify-content:center}
.promo button:hover{border-color:var(--accent)}
.promo button svg{width:100%;height:100%}

/* ---- drop hint --------------------------------------------------------
   Was a full-screen modal. It got stuck open once and, because it covered the
   viewport, the whole page appeared frozen — every click landed on it. The
   lesson is not "fix the counter", it is that a transient element should never
   have been able to cover anything. So now it is a small pill at the top that
   cannot intercept a click and cannot hide the page even if it never closes.
   The real way in is the .opener button, which is always clickable. */
.drop-hint{position:fixed;top:14px;left:50%;transform:translateX(-50%);z-index:20;
  pointer-events:none;font-family:var(--ui);font-size:13px;color:#150a02;
  background:var(--orange);border-radius:999px;padding:7px 18px;box-shadow:var(--shadow)}

@media (max-width:900px){
  .layout{grid-template-columns:1fr}
  .col-tree{max-height:none}
}

/* ---- phones ------------------------------------------------------------
   v19 §11: "Traffic is overwhelmingly phones arriving from YouTube. Test the
   tree, the engine and the paste flow on a real device before any deploy."

   Everything below is about one rule: **nothing may be wider than the screen.**
   A single unwrappable row blows out the layout viewport and silently shrinks
   the whole app, which is exactly what the header was doing. */
@media (max-width:640px){
  .layout{padding:12px;gap:12px}

  /* The board is the app on a phone. It gets the width, and the size control
     stops mattering — 520px of board on a 375px screen is what forced the
     zoom-out in the first place. */
  .board-wrap{width:100%!important;max-width:100%!important}

  .top{padding:10px 12px;gap:10px}
  .top h1{font-size:16px;width:100%}
  .top-right{width:100%;justify-content:flex-start}
  /* The status line is a sentence; on a phone it belongs on its own row rather
     than squeezing every button next to it into an ellipsis. */
  .top-right .lab{width:100%;order:-1}

  /* Tap targets. 24px of glyph in a 20px box is a coin-flip with a thumb. */
  .btn.sm,.panel-tabs button{padding:9px 11px;min-height:38px;min-width:38px}

  /* The variation fold controls measure 18×16, which is a miss as often as a
     hit. The BOX cannot simply grow — it sits inline in the move text and a
     taller one would space the tree out — so the touch area is extended past
     the box with a transparent border instead. The glyph stays where it is. */
  .fold{border:11px solid transparent!important;background-clip:padding-box;
    margin:-11px -9px!important}
  .panel-tabs{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .panel-tabs button{flex:0 0 auto}

  .lib-choices{gap:10px}
  .opener{padding:22px 16px}
  .opener-main{font-size:17px}

  /* **16px minimum on every field.** iOS Safari zooms the page in on focus for
     anything smaller, which re-centres the layout on the input and cannot be
     undone by the user — they are left scrolled sideways in an app that was
     fitting a moment ago. The paste box was 12px, and pasting a game is the
     one thing v19 §11 names as needing a real-device test. */
  input,textarea,select{font-size:16px!important}

  /* Long file and chapter names must ellipsis, never widen the page. */
  .lib-row .nm,.chapter-pick{max-width:100%}
  .report-row{grid-template-columns:1fr;gap:4px}
  .reorder-row{grid-template-columns:auto 1fr auto auto}
}
