/*
 * Chess Studio Web.
 *
 * Geometry and colour are taken from the desktop app so the two read as one product:
 *   rail widths 120 / 42, icon 30px + 13px label, secondary 22px + 11px  (MainMenu)
 *   panel corner radius 15, board corner radius 5                        (SideBarPanel, BoardColors)
 *   coordinates at square/5, rank top-left, file bottom-right            (BoardView.paintComponent)
 *   promotion strip: 1 square wide, 4.5 squares tall, Q R B N then close (BoardView.getPromoteArea)
 *   board + overlay colours                                              (BoardColors)
 *   foreground #BABABA                                                   (gui/themes/FlatDarkLaf.properties)
 */

:root {
  /*
   * A soft blue-grey, rather than the desktop app's neutral grey.
   *
   * This is a deliberate divergence: the two front ends match in layout and behaviour,
   * but the web one is asked to look like a modern product, and a faint blue in the
   * surfaces is what carries the accent through the whole app instead of leaving it
   * stranded on one button. Board colours are untouched - those are the game.
   */
  --window: #2f343d;
  --rail: #2a2f38;
  --panel: #363d48;
  --panel-head: #262b34;
  --sunken: #2f3540;
  --hover: #414a58;
  --border: #414a58;
  --fg: #b6bdc8;
  --fg-strong: #f2f5f9;
  --fg-dim: #838d9c;
  --accent: #4b6eaf;
  --accent-hi: #6d93da;
  --on: #32d74b;
  --danger: #ff453a;

  /* BoardColors.BoardTheme.BROWN */
  --sq-dark: rgb(181, 136, 99);
  --sq-light: rgb(240, 217, 181);

  /* BoardColors overlay constants (alpha = value/255) */
  --mark: rgba(255, 255, 0, 0.27);
  --gray: rgba(0, 0, 0, 0.14);
  --check: rgba(180, 20, 40, 0.51);
  --frame: rgba(255, 255, 255, 0.59);
  --arrow: rgba(90, 178, 100, 0.63);

  /* Marking colours, in the order the modifiers reach them: none, shift, alt, shift+alt. */
  --mk-green: rgba(90, 178, 100, .70);
  --mk-red: rgba(210, 66, 58, .70);
  --mk-blue: rgba(78, 132, 214, .70);
  --mk-yellow: rgba(232, 184, 66, .72);
  --mk-hint: rgba(90, 178, 100, .63);

  /*
   * GameView's layout constants, name for name, so the two front ends lay out identically:
   *   MAIN_PADDING 15, BOARD_CONTAINER_PADDING 4, PLAYER_INFO_HEIGHT 40,
   *   EvaluationBar.DEFAULT_WIDTH 20, SIDE_BAR_MIN/MAX 300/680, BOARD_VIEW_MIN 230.
   */
  /* Color.darker() on the panel: RGB x 0.7. */
  --cell-cur: #2e3133;

  /* Glass surfaces, used by the sign-in dialog. */
  --glass: rgba(52, 56, 60, .72);
  --glass-edge: rgba(255, 255, 255, .11);
  --glass-input: rgba(255, 255, 255, .05);

  /*
   * Surface overlays. Every one of these used to be a literal rgba(255,255,255,x), which
   * is invisible on a light background - the divider lines, the striped rows and the
   * evaluation graph all disappeared when the theme was switched.
   */
  --hairline: rgba(255, 255, 255, .055);
  --row-alt: rgba(255, 255, 255, .022);
  --row-hover: rgba(255, 255, 255, .07);
  --lift: rgba(255, 255, 255, .09);

  /* The evaluation graph draws White's share of the position. */
  --graph-line: #e8ecf2;
  --graph-area: rgba(232, 236, 242, .14);

  --rail-w: 120px;
  --main-pad: 15px;
  --cell-pad: 4px;
  --seat-h: 40px;
  --eval-w: 20px;
  --eval-gap: 8px;          /* two cell paddings between the board and the bar */
  --panel-gap: 30px;        /* two main paddings between the board column and the panel */
  --side-min: 300px;
  --side-max: 680px;
  --board-min: 230px;
  /* Board plus the evaluation bar beside it: what the board row costs in width. */
  --board-extra: calc(var(--eval-w) + var(--eval-gap));
}

[data-board="green"] { --sq-dark: rgb(119, 148, 85); --sq-light: rgb(235, 235, 208); }

[data-theme="light"] {
  --window: #eaeef4; --rail: #e2e7ef; --panel: #ffffff; --panel-head: #dde4ee;
  --sunken: #f1f4f9; --hover: #dbe3ee; --border: #ccd5e2;
  --fg: #3d4652; --fg-strong: #131920; --fg-dim: #6d7787;
  --accent: #3f63a8; --accent-hi: #35589b;
  --cell-cur: #c3ccda;
  --glass: rgba(255, 255, 255, .82);
  --glass-edge: rgba(0, 0, 0, .10);
  --glass-input: rgba(0, 0, 0, .03);

  --hairline: rgba(0, 0, 0, .075);
  --row-alt: rgba(0, 0, 0, .025);
  --row-hover: rgba(0, 0, 0, .055);
  --lift: rgba(0, 0, 0, .06);

  /* Dark ink on a pale panel, so the same shape reads the same way round. */
  --graph-line: #46506b;
  --graph-area: rgba(70, 80, 107, .16);
}

* { box-sizing: border-box; }

/* The platform scrollbar renders a pale track that reads as a white frame around the
   engine lines and move list; give every scroller the panel's own colours. */
* { scrollbar-width: thin; scrollbar-color: #6a6f72 transparent; }
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #6a6f72; border-radius: 5px; border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: #868b8e; background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

html, body {
  margin: 0; height: 100%;
  background: var(--window); color: var(--fg);
  font: 13px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}
button, select, input, textarea { font: inherit; color: inherit; }
[hidden] { display: none !important; }

/* Monochrome icons ship black or grey; tint them to the theme foreground. */
.ico { width: 22px; height: 22px; object-fit: contain; flex: none; transition: filter .12s; }
.ico.mono { filter: invert(74%) sepia(6%) saturate(0%) brightness(105%); }
[data-theme="light"] .ico.mono { filter: invert(25%); }

/* Every clickable icon lifts on hover, the way IconButton brightens its SVG. */
button:hover > .ico.mono, a:hover > .ico.mono,
.rail-btn:hover .ico.mono, .tool:hover .ico.mono, .nav-btn:hover:not(:disabled) .ico.mono {
  filter: invert(96%) sepia(0%) saturate(0%) brightness(115%);
}
[data-theme="light"] button:hover > .ico.mono,
[data-theme="light"] .rail-btn:hover .ico.mono,
[data-theme="light"] .tool:hover .ico.mono { filter: invert(0%); }
button:hover > .ico:not(.mono), .rail-btn:hover .ico:not(.mono) { filter: brightness(1.25); }
button:disabled .ico, .nav-btn:disabled .ico { filter: grayscale(1) opacity(.5); }

.app { display: grid; grid-template-columns: var(--rail-w) 1fr; height: 100vh; }
.app.collapsed { --rail-w: 42px; }
.app.collapsed .rail-btn span { display: none; }
.app.collapsed .rail-btn { justify-content: center; padding-left: 0; padding-right: 0; }

/* ---------------- left rail (MainMenu) ---------------- */

.rail {
  background: var(--rail);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 8px 0 20px;
  overflow: hidden;
}
.rail-group { display: flex; flex-direction: column; }

.rail-btn {
  display: flex; align-items: center; gap: 8px;
  background: none; border: 0; width: 100%;
  padding: 7px 6px;                     /* MAIN_MENU_INSETS */
  font-size: 13px;                      /* MAIN_FONT_SIZE */
  cursor: pointer; text-align: left;
  white-space: nowrap;
  transition: background .12s, color .12s;
}
.rail-btn .ico { width: 30px; height: 30px; }   /* MAIN_ICON_SIZE */
.rail-btn:hover { background: var(--hover); color: var(--fg-strong); }
.rail-btn[aria-current="true"] { background: var(--hover); color: var(--fg-strong); }

.rail-btn.sm { padding: 5px 10px 5px 8px; font-size: 11px; }   /* SECONDARY_* */
.rail-btn.sm .ico { width: 22px; height: 22px; }

/* ---------------- stage ---------------- */

/*
 * Screens with nothing to show on a board hide it and take the whole stage.
 *
 * Home, the profile, and the two library screens are reading and choosing, not playing -
 * a board sitting beside them is decoration that costs the content half the window. The
 * panel widens to a comfortable measure rather than the full span, because a line of text
 * 1200px wide is unreadable.
 */
.stage.full .board-col { display: none; }
.stage.full .panel {
  width: min(100%, 860px); flex: 1 1 auto; max-width: 860px;
}
.stage.full { justify-content: center; }

/*
 * GameView.rearrange, transcribed.
 *
 *   available   = stage - fixed chrome
 *   board       = max(230, min(available width - 300, available height))
 *   side panel  = clamp(300, whatever width is left, 680)
 *
 * The board is bounded by the window height on any normal screen, and the panel then
 * takes every pixel that is left rather than being capped - which is why the desktop
 * never shows a corridor between the two. The web port used to size the board the same
 * way but cap the panel, so the leftover width became empty space.
 */
.stage {
  display: flex; align-items: center; justify-content: center;
  gap: var(--panel-gap); padding: var(--main-pad); min-width: 0; min-height: 0;

  --stage-w: calc(100vw - var(--rail-w));
  --stage-h: 100vh;
  --fixed-w: calc(2 * var(--main-pad) + 2 * var(--cell-pad)
                  + var(--board-extra) + var(--panel-gap));           /* 96 */
  --fixed-h: calc(2 * var(--main-pad) + 2 * var(--cell-pad)
                  + 2 * var(--seat-h) + 2 * var(--eval-gap));         /* 134 */

  --board-size: max(var(--board-min), min(
      calc(var(--stage-w) - var(--fixed-w) - var(--side-min)),
      calc(var(--stage-h) - var(--fixed-h))));
  --panel-w: clamp(var(--side-min),
      calc(var(--stage-w) - var(--fixed-w) - var(--board-size)), var(--side-max));
}

.board-col {
  flex: 0 0 auto; display: flex; flex-direction: column; justify-content: center;
  gap: var(--eval-gap); padding: var(--cell-pad); min-width: 0; min-height: 0;
}

.seat {
  display: flex; align-items: center; gap: 10px;
  height: var(--seat-h);
}
.seat .avatar {
  width: 44px; height: 44px; border-radius: 6px;
  background: var(--sunken); display: grid; place-items: center; flex: none;
}
.seat .avatar img { width: 32px; height: 32px; }
.seat-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.seat .who { color: var(--fg-strong); font-weight: 600; font-size: 15px; }
.seat .taken {
  font-size: 13px; color: var(--fg-dim);
  display: flex; align-items: center; min-height: 18px;
}
.seat .taken img { width: 17px; height: 17px; opacity: .8; margin-right: -5px; }
.seat .taken .plus { margin-left: 8px; font-weight: 700; font-size: 12px; }
.seat .clock {
  margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 700; font-size: 19px;
  background: var(--sunken); padding: 5px 14px; border-radius: 6px; color: var(--fg-dim);
  letter-spacing: .5px;
}
.seat.active .clock { color: var(--fg-strong); background: var(--hover); }

/*
 * Two stages of urgency.
 *
 * Under thirty seconds the clock warms to amber - noticeable without being alarming.
 * Under ten, and only for the side whose time is actually running, it beats: a double
 * pulse like a heartbeat rather than an even blink, because an irregular rhythm catches
 * the eye that a metronome stops catching after a few seconds.
 */
.seat .clock.warn { color: #f0c15c; }
.seat .clock.low {
  color: #fff; background: var(--danger);
  animation: heartbeat 1s ease-in-out infinite;
  will-change: transform;
}
@keyframes heartbeat {
  0%   { transform: scale(1);    box-shadow: 0 0 0 0 rgba(255, 69, 58, .55); }
  14%  { transform: scale(1.07); box-shadow: 0 0 0 5px rgba(255, 69, 58, .18); }
  28%  { transform: scale(1);    box-shadow: 0 0 0 9px rgba(255, 69, 58, 0); }
  42%  { transform: scale(1.05); box-shadow: 0 0 0 0 rgba(255, 69, 58, .35); }
  56%  { transform: scale(1);    box-shadow: 0 0 0 9px rgba(255, 69, 58, 0); }
  100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(255, 69, 58, 0); }
}
/* Somebody who has asked for less motion still gets the colour, without the beat. */
@media (prefers-reduced-motion: reduce) {
  .seat .clock.low { animation: none; }
}

.board-row { display: flex; gap: var(--eval-gap); align-items: stretch; }

/* ---------------- board ---------------- */

.board {
  position: relative; flex: none;
  width: var(--board-size); height: var(--board-size);
  border-radius: 5px;                   /* BoardColors.ARC */
  overflow: hidden; user-select: none; touch-action: none;
  container-type: size;
  box-shadow: 0 6px 26px rgba(0, 0, 0, .38);
}
.squares { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(8, 1fr); }
.overlay { position: absolute; inset: 0; pointer-events: none; }

/*
 * An arrow appearing instantly reads as a glitch; one that grows reads as a stroke. The
 * preview while the button is held is dimmer, so a finished arrow is distinguishable from
 * one still being aimed.
 */
.arrows path, .arrows circle { transition: opacity .12s; }
.arrow-in { animation: arrow-in .17s cubic-bezier(.2, .9, .3, 1); transform-origin: center; }
@keyframes arrow-in { from { opacity: 0; } to { opacity: 1; } }
.arrow-drawing { opacity: .62; }
@media (prefers-reduced-motion: reduce) { .arrow-in { animation: none; } }

.sq { position: relative; }
.sq.light { background: var(--sq-light); }
.sq.dark  { background: var(--sq-dark); }
.sq.last::after  { content: ""; position: absolute; inset: 0; background: var(--mark); }
.sq.sel::after   { content: ""; position: absolute; inset: 0; background: var(--mark); }
.sq.check::after { content: ""; position: absolute; inset: 0;
  background: radial-gradient(circle, var(--check) 30%, transparent 72%); }
/* Kept for the lesson highlights, which do want the whole square coloured. */
.sq.marked::before {
  content: ""; position: absolute; inset: 0;
  background: var(--mark-colour, var(--mk-green)); opacity: .55;
}

/* Coordinates: Arial Bold at square/5 == 2.5% of board height. */
.sq .coord {
  position: absolute; font-family: Arial, Helvetica, sans-serif; font-weight: 700;
  font-size: 2.5cqh; line-height: 1; pointer-events: none;
}
.sq.light .coord { color: var(--sq-dark); }
.sq.dark  .coord { color: var(--sq-light); }
.sq .coord.rank { left: 5%; top: 6%; }
.sq .coord.file { right: 5%; bottom: 4%; }
.app.no-coords .sq .coord { display: none; }

.piece {
  position: absolute; width: 12.5%; height: 12.5%;
  background-size: contain; background-repeat: no-repeat; background-position: center;
  pointer-events: auto;
  /* The same hand cursors the Swing board uses (AppIcons.OPEN_HAND / CLOSE_HAND). */
  cursor: url("/icons/openhand.svg") 12 12, grab;
}
.board:active .piece,
.piece.dragging { cursor: url("/icons/closedhand-cropped.svg") 12 12, grabbing; }

/*
 * Opening and ending a game.
 *
 * The pieces settle onto the board from the outside in, and a mated king topples the way
 * a player tips their own over. Both are transform/opacity only, so they cost a compositor
 * frame and nothing else, and both are sized from the animation setting - at "none" the
 * classes are never added at all.
 */
.piece.dealing {
  animation: deal-in var(--deal-ms, 200ms) cubic-bezier(.2, .9, .3, 1) var(--deal, 0ms) backwards;
}
@keyframes deal-in {
  from { opacity: 0; transform: scale(.62); }
  to { opacity: 1; transform: none; }
}

.piece.toppled {
  z-index: 3;
  animation: topple .5s cubic-bezier(.3, .9, .35, 1) forwards;
}
@keyframes topple {
  0% { transform: rotate(0) translateY(0); }
  62% { transform: rotate(78deg) translateY(9%); }
  78% { transform: rotate(66deg) translateY(9%); }
  100% { transform: rotate(72deg) translateY(10%); }
}

/* The board steps back while the result is read. */
.board.game-ended .squares { filter: saturate(.72) brightness(.9); transition: filter .45s ease; }
.board.game-ended .sq.check { animation: mate-flash .58s ease-out 2; }
@keyframes mate-flash {
  0%, 100% { box-shadow: inset 0 0 0 0 rgba(255, 69, 58, 0); }
  50% { box-shadow: inset 0 0 0 5px rgba(255, 69, 58, .5); }
}
@media (prefers-reduced-motion: reduce) {
  .piece.dealing, .piece.toppled, .board.game-ended .sq.check { animation: none; }
}
/*
 * A dragged piece is displaced with a transform and left where it started.
 *
 * Writing left/top per pointer event re-ran layout for the whole board every frame, which
 * is what made the piece trail the pointer. A transform is handed straight to the
 * compositor, and `will-change` is set only while dragging so the other 31 pieces are not
 * each given a layer for nothing.
 */
.piece.dragging {
  z-index: 30; pointer-events: none;
  will-change: transform;
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, .5));
}
/* A piece travelling to its square rides above the rest, as it does when dragged. */
.piece.sliding { z-index: 25; will-change: transform; }
.mini .ms img { will-change: transform; }

.hint { position: absolute; width: 12.5%; height: 12.5%; display: grid; place-items: center; }
.hint i { display: block; width: 30%; height: 30%; border-radius: 50%; background: var(--gray); }
.hint.capture i {
  width: 88%; height: 88%; background: none; border-radius: 50%;
  box-shadow: inset 0 0 0 6cqh var(--gray);
}
.ring {
  position: absolute; width: 12.5%; height: 12.5%;
  box-shadow: inset 0 0 0 0.5cqh var(--frame);
  border-radius: 2px;
}

/* Promotion strip: 1 square wide, 4.5 tall - four pieces then the close button. */
.promo {
  position: absolute; width: 12.5%; height: 56.25%;
  background: var(--panel-head); border-radius: 5px;
  box-shadow: 0 4px 18px rgba(0, 0, 0, .55);
  z-index: 40; display: flex; flex-direction: column; pointer-events: auto;
}
.promo.up { flex-direction: column-reverse; }
.promo button {
  flex: 0 0 22.222%;                    /* one square of 4.5 */
  border: 0; background: transparent center/86% no-repeat; cursor: pointer;
}
.promo button:hover { background-color: var(--hover); }
.promo .cancel {
  flex: 0 0 11.111%;                    /* the trailing half square */
  display: grid; place-items: center; background: transparent;
}
.promo .cancel img { width: 34%; height: auto; }

/* ---------------- evaluation bar ---------------- */

.evalbar {
  position: relative; width: var(--eval-w); flex: none;
  border-radius: 4px; overflow: hidden; background: #262626;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .10);
}
/* The white share grows from whichever end of the bar White is playing from, so the
   bar flips with the board. */
.evalbar .fill {
  position: absolute; left: 0; right: 0; bottom: 0; height: 50%;
  background: #efefef; transition: height .35s cubic-bezier(.4, 0, .2, 1);
}
.evalbar.flipped .fill { bottom: auto; top: 0; }
.evalbar .num {
  position: absolute; left: 0; right: 0; bottom: 2px;
  text-align: center; font-size: 8px; font-weight: 700; color: #1e1e1e;
  letter-spacing: -.3px;
}
/* The score sits on the leading side's end of the bar, coloured to stay legible. */
.evalbar.num-far .num { bottom: auto; top: 3px; }
.evalbar.num-on-dark .num { color: #efefef; }
.evalbar.off { opacity: .3; }

/* ---------------- right panel (SideBarPanel) ---------------- */

.panel {
  width: var(--panel-w); flex: 0 0 auto; align-self: stretch;
  background: var(--panel); border-radius: 15px;      /* arc: 15 */
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
.panel-head {
  background: var(--panel-head); position: relative;
  display: flex; align-items: center; padding: 12px 14px; min-height: 64px;
}
.panel-title {
  position: absolute; left: 0; right: 0; text-align: center;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 25px; font-weight: 700; color: var(--fg-strong); pointer-events: none;
}
.panel-title .ico { width: 30px; height: 30px; }
.head-btn {
  background: none; border: 0; cursor: pointer; padding: 4px; border-radius: 5px;
  display: grid; place-items: center; z-index: 1;
}
.head-btn:hover { background: var(--lift); }
.head-btn .ico { width: 18px; height: 18px; }

.panel-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.menu-body { padding: 14px; gap: 10px; overflow-y: auto; }

.wide-btn {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  background: var(--sunken); border: 1px solid var(--border); border-radius: 8px;
  padding: 11px 14px; cursor: pointer; width: 100%; transition: background .12s;
}
.wide-btn:hover { background: var(--hover); color: var(--fg-strong); }
.wide-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.wide-btn.primary:hover { background: var(--accent-hi); }
.wide-btn .ico { width: 20px; height: 20px; }

.field {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 7px 2px; min-height: 34px;
}
.field.col { flex-direction: column; align-items: stretch; gap: 6px; }
.field > span { color: var(--fg); }
.field select, .field input, .field textarea {
  background: var(--sunken); border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 8px; min-width: 120px;
}
.field.col input, .field.col textarea, .field.col select { width: 100%; }
.field textarea {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; resize: vertical;
}
.field select:focus, .field input:focus, .field textarea:focus {
  outline: none; border-color: var(--accent);
}

.side-pick { display: flex; gap: 10px; justify-content: center; padding: 4px 0 10px; }
.side-pick .pick {
  width: 62px; height: 62px; border-radius: 9px; cursor: pointer;
  background: var(--sunken); border: 2px solid var(--border); display: grid; place-items: center;
}
.side-pick .pick img { width: 44px; height: 44px; }
.side-pick .pick[aria-pressed="true"] { border-color: var(--accent); background: var(--hover); }

/* toggle switch (ToggleSwitch) */
.switch {
  width: 38px; height: 21px; border-radius: 11px; border: 0; flex: none;
  background: #6a6f72; position: relative; cursor: pointer; transition: background .16s;
}
.switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 17px; height: 17px;
  border-radius: 50%; background: #fff; transition: transform .16s;
}
.switch[aria-checked="true"] { background: var(--on); }
.switch[aria-checked="true"]::after {
  transform: translateX(17px);
  background: #fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.3 L4.8 8.6 L9.5 3.6' fill='none' stroke='%2332d74b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/11px no-repeat;
}

/* ---------------- figurines (move list, engine lines) ---------------- */

/*
 * PieceIconManager.createDynamicThemeIconSet, in CSS: an mpchess glyph painted in the
 * colour of the text it sits in. The SVG supplies only the silhouette - masking it over
 * `currentColor` is what makes it follow the foreground, and brighten with the text on
 * hover, the way IconManager.ForegroundAwareIcon does.
 */
.fig {
  display: inline-block; vertical-align: -0.16em;
  width: 1.15em; height: 1.15em; margin-right: 2px; flex: none;
  background-color: currentColor;
  -webkit-mask: var(--fig) center/contain no-repeat;
  mask: var(--fig) center/contain no-repeat;
}

/* ---------------- engine lines ---------------- */

.engine-head {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px; border-bottom: 1px solid var(--border);
}
.engine-head .lbl { color: var(--fg); }
.engine-head .depth { margin-left: auto; font-size: 12px; color: var(--fg-dim); }

.lines { border-bottom: 1px solid var(--border); max-height: 132px; overflow-y: auto; }
.line {
  display: flex; align-items: flex-start; gap: 7px;
  padding: 4px 6px 4px 5px; font-size: 12px; cursor: pointer;
}
.line:hover { background: var(--row-hover); }
.line + .line { border-top: 1px solid var(--hairline); }
/* JLabel styled "background: darken(@background,8%); arc: 5", bold 12, insets 1/5. */
.line .sc {
  flex: none; min-width: 42px; text-align: center; font-weight: 700; font-size: 12px;
  padding: 1px 5px; border-radius: 5px;
  background: var(--panel-head); color: var(--fg-strong);
  line-height: 1.4;
}
.line .pv {
  flex: 1; min-width: 0; color: var(--fg); line-height: 1.5;
  overflow: hidden; white-space: nowrap; text-overflow: clip;
}
.line.open .pv { white-space: normal; }
.line .pv .mv {
  display: inline-flex; align-items: center; cursor: pointer;
  padding: 0 2px; border-radius: 3px; vertical-align: baseline;
}
.line .pv .mv:hover { background: var(--hover); color: var(--fg-strong); }
.line .pv .no { margin-right: 3px; }
.line .exp { flex: none; background: none; border: 0; cursor: pointer; padding: 2px; opacity: .7; }
.line .exp:hover { opacity: 1; }
.line .exp img { width: 10px; height: 10px; }
.lines .empty { color: var(--fg-dim); font-style: italic; padding: 7px 10px; font-size: 12px; }

/* Engine settings popup, anchored under the gear in the engine header. */
.engine-head { position: relative; }
.engine-menu {
  position: absolute; top: calc(100% + 4px); left: 58px; z-index: 60;
  background: var(--panel-head); border: 1px solid var(--border); border-radius: 9px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
  padding: 4px 14px 8px; min-width: 250px;
}
.engine-menu .field { padding: 6px 0; min-height: 30px; gap: 18px; }

/* Hover preview of a position inside an engine line. */
.preview {
  position: fixed; z-index: 150; pointer-events: none;
  /* Anchored to the panel, not the cursor: it must not jump as the pointer travels
     along a line. */
  padding: 5px; background: var(--panel-head);
  border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
}
.mini {
  width: 168px; height: 168px; display: grid;
  grid-template-columns: repeat(8, 1fr); border-radius: 3px; overflow: hidden;
}
.mini .ms { position: relative; }
.mini .ms.light { background: var(--sq-light); }
.mini .ms.dark { background: var(--sq-dark); }
.mini .ms.mk::after { content: ""; position: absolute; inset: 0; background: var(--mark); }
.mini .ms img { position: absolute; inset: 0; width: 100%; height: 100%; }

/* ---------------- move list ---------------- */

.moves { flex: 1; overflow-y: auto; min-height: 0; padding: 4px 0; }
.mv-row { display: grid; grid-template-columns: 42px 1fr 1fr; align-items: center; min-height: 30px; }
.mv-row:nth-child(odd) { background: var(--row-alt); }
.mv-no { color: var(--fg-dim); font-size: 11px; text-align: right; padding-right: 9px; }
.moves .cell {
  padding: 5px 8px; margin: 2px 3px; border-radius: 5px; cursor: pointer;
  display: flex; align-items: center; gap: 2px;
}
.moves .cell:hover { background: var(--row-hover); }
/* MoveView.paintComponent: a rounded rect of getBackground().darker(), radius 7. */
.moves .cell.cur {
  background: var(--cell-cur); color: var(--fg-strong); border-radius: 7px;
}
.moves .cell.pending { color: var(--fg-dim); cursor: default; }
.moves .cell.pending:hover { background: none; }

.moves .empty { color: var(--fg-dim); font-style: italic; padding: 8px 12px; }

/*
 * What an imported PGN said about a move: the annotator's mark, the clock after it, and a
 * dot when there is prose to read below. The old importer threw all three away.
 */
.nag { font-weight: 800; font-size: 11px; margin-left: 2px; letter-spacing: -.04em; }
.n-brilliant { color: #1baaa6; }
.n-good { color: #5b8bb0; }
.n-interesting { color: #96bc4b; }
.n-dubious { color: #f0c15c; }
.n-mistake { color: #e08b3a; }
.n-blunder { color: #ca3431; }

.cell-clock {
  margin-left: auto; padding-left: 6px; font-size: 10.5px; color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
}
.moves .cell.has-note::after {
  content: ""; width: 4px; height: 4px; border-radius: 50%; flex: none;
  background: var(--accent-hi); margin-left: 4px;
}

.move-note {
  display: flex; gap: 7px; align-items: flex-start;
  border-top: 1px solid var(--border); padding: 10px 12px;
  font-size: 12.5px; line-height: 1.55; color: var(--fg);
  background: var(--sunken);
}
.mn-mark { color: var(--accent-hi); font-size: 19px; line-height: 1; font-weight: 700; }

.banner {
  padding: 9px 12px; text-align: center; font-weight: 600;
  background: var(--accent); color: #fff;
}
.banner.draw { background: var(--hover); }
.banner.loss { background: #8c2b2b; }

/* ---------------- panel footer ---------------- */

.panel-foot { border-top: 1px solid var(--border); padding: 8px; }

/*
 * Resign, rematch and the offers sit above the move controls, where they are read before
 * the hand drops to the jump buttons - and where a mis-tap cannot resign a game.
 */
.gamerow { display: flex; gap: 6px; margin-bottom: 7px; }
.gamerow button {
  flex: 1; background: var(--sunken); border: 1px solid var(--border); border-radius: 7px;
  padding: 9px 6px; cursor: pointer; font-size: 12.5px; color: var(--fg);
  display: flex; align-items: center; justify-content: center; gap: 6px;
  transition: background .12s, color .12s, border-color .12s;
}
.gamerow button:hover:not(:disabled) { background: var(--hover); color: var(--fg-strong); }
.gamerow button:disabled { opacity: .4; cursor: not-allowed; }
.gamerow button.danger:hover:not(:disabled) { border-color: var(--danger); color: var(--danger); }
.gamerow .ic { width: 15px; height: 15px; }

.navrow { display: flex; gap: 6px; }
.nav-btn {
  flex: 1; background: var(--sunken); border: 1px solid var(--border); border-radius: 7px;
  padding: 12px 0; cursor: pointer; display: grid; place-items: center; transition: background .12s;
}
.nav-btn:hover:not(:disabled) { background: var(--hover); }
.nav-btn:disabled { opacity: .35; cursor: not-allowed; }
.nav-btn .ico { width: 17px; height: 17px; }

.toolrow { display: flex; align-items: center; gap: 3px; margin-top: 7px; }
.toolrow .grow { flex: 1; }
.tool {
  background: none; border: 0; cursor: pointer; padding: 5px; border-radius: 5px;
  display: grid; place-items: center;
}
.tool:hover { background: var(--hover); }
.tool:disabled { opacity: .35; cursor: not-allowed; }
.tool .ico { width: 16px; height: 16px; }

/* ---------------- board editor ---------------- */

.palette {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px;
  touch-action: none;
  background: var(--sunken); border: 1px solid var(--border);
  border-radius: 8px; padding: 6px;
}
.palette button {
  aspect-ratio: 1; border: 2px solid transparent; border-radius: 6px;
  background: transparent center/82% no-repeat; cursor: pointer;
  display: grid; place-items: center;
  touch-action: none; -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;              /* no long-press menu over a piece */
}
.palette button:hover { background-color: var(--hover); }
.palette button[aria-pressed="true"] { border-color: var(--accent); background-color: var(--hover); }
.palette button .ico { width: 16px; height: 16px; }

.castle-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 10px; padding: 2px 0; }
.chk { display: flex; align-items: center; gap: 7px; cursor: pointer; user-select: none; font-size: 12px; }
.chk input { accent-color: var(--accent); width: 14px; height: 14px; cursor: pointer; }

.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ed-status { font-size: 12px; min-height: 18px; color: var(--fg-dim); }
.ed-status.bad { color: var(--danger); }
.ed-status.good { color: var(--on); }

/* ---------------- saved analysis ---------------- */

.saved-list { display: flex; flex-direction: column; gap: 6px; }
.saved-row {
  display: flex; align-items: center; gap: 8px;
  background: var(--sunken); border: 1px solid var(--border);
  border-radius: 7px; padding: 8px 10px; cursor: pointer;
}
.saved-row:hover { background: var(--hover); }
.saved-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.saved-title { color: var(--fg-strong); font-weight: 600; font-size: 12.5px; }
.saved-prev {
  color: var(--fg-dim); font-size: 11px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.saved-row .tool { flex: none; }
.saved-list .empty { color: var(--fg-dim); font-style: italic; padding: 10px 2px; }

/* Variations, indented under the move they branch from (MoveInfo / VariationLine). */
.variation {
  padding: 2px 8px 3px 12px; font-size: 12px; color: var(--fg-dim); line-height: 1.7;
  border-left: 2px solid var(--border); margin: 1px 0 1px 34px;
}
.variation .no { color: var(--fg-dim); margin: 0 2px 0 4px; font-size: 11px; }
.variation .cell { display: inline-flex; padding: 1px 4px; }
.moves .cell.blank { visibility: hidden; }

/* Right-click menu on a move. */
.move-menu {
  position: fixed; z-index: 180; min-width: 190px;
  background: var(--panel-head); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .55); padding: 4px;
  display: flex; flex-direction: column;
}
.move-menu button {
  background: none; border: 0; text-align: left; padding: 7px 10px;
  border-radius: 5px; cursor: pointer;
}
.move-menu button:hover { background: var(--hover); color: var(--fg-strong); }

/* Piece being dragged from the editor tray. */
.ghost {
  position: fixed; left: 0; top: 0; z-index: 200; pointer-events: none;
  will-change: transform;
  background-size: contain; background-repeat: no-repeat; background-position: center;
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, .5));
}

.ed-hint { font-size: 11px; color: var(--fg-dim); line-height: 1.5; }

/* ---------------- play a friend ---------------- */

.mt-intro p { margin: 0 0 4px; font-size: 12.5px; color: var(--fg-dim); line-height: 1.6; }
.mt-note { margin: -4px 0 4px; font-size: 11.5px; color: var(--fg-dim); }

/* The two ways into a live game, chosen before anything else on the form. */
.mt-kinds { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.mt-kind {
  display: flex; flex-direction: column; gap: 3px; text-align: left; cursor: pointer;
  background: var(--sunken); border: 1px solid var(--border); border-radius: 10px;
  padding: 12px; transition: border-color .14s, background .14s;
}
.mt-kind:hover { background: var(--hover); }
.mt-kind[aria-pressed="true"] { border-color: var(--accent); background: var(--hover); }
.mt-kind-ic { color: var(--accent-hi); display: flex; }
.mt-kind-name { font-weight: 700; color: var(--fg-strong); font-size: 13.5px; }
.mt-kind-desc { font-size: 10.5px; color: var(--fg-dim); line-height: 1.4; }

.mt-custom { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.mt-custom input { width: 100%; }

.mt-searching {
  display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 26px 12px;
}
.mt-spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--border); border-top-color: var(--accent-hi);
  animation: spin 900ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.mt-searching-title { font-size: 15px; font-weight: 700; color: var(--fg-strong); }
.mt-searching-sub { font-size: 12px; color: var(--fg-dim); margin-bottom: 6px; }
.mt-searching .wide-btn { width: 100%; }

/* An offer sits over the board, where both players are already looking. */
.offer-bar {
  display: flex; align-items: center; gap: 10px; align-self: stretch;
  background: var(--accent); color: #fff; border-radius: 9px;
  padding: 9px 12px; font-size: 13px; font-weight: 600;
  animation: bubble-in .22s cubic-bezier(.2, 1.2, .4, 1);
}
.offer-bar.waiting { background: var(--panel-head); color: var(--fg); font-weight: 500; }
.offer-text { flex: 1; }
.offer-actions { display: flex; gap: 6px; flex: none; }
.offer-actions button {
  border: 0; border-radius: 6px; padding: 5px 12px; cursor: pointer; font-size: 12px;
  font-weight: 600;
}
.offer-yes { background: var(--on); color: #10240f; }
.offer-no { background: rgba(0, 0, 0, .28); color: #fff; }
.offer-actions button:hover { filter: brightness(1.1); }
.mt-error {
  margin: 0; font-size: 12.5px; color: var(--danger);
  background: rgba(255, 69, 58, .10); border: 1px solid rgba(255, 69, 58, .3);
  border-radius: 7px; padding: 8px 10px;
}

.mt-invite { display: flex; flex-direction: column; gap: 12px; align-items: stretch; }
/* The code is drawn black-on-white whatever the theme: a scanner needs the contrast. */
.mt-qr {
  align-self: center; background: #fff; padding: 10px; border-radius: 12px;
  line-height: 0; box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
}
.mt-qr svg { display: block; }
.mt-hint { margin: 0; font-size: 12px; color: var(--fg-dim); text-align: center; }
.mt-invite .copyrow input {
  flex: 1; background: var(--sunken); border: 1px solid var(--border); border-radius: 6px;
  padding: 8px 10px; font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.mt-waiting {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 12.5px; color: var(--fg-dim); padding: 4px;
}
.mt-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--on);
  animation: waiting 1.4s ease-in-out infinite;
}
@keyframes waiting { 0%, 100% { opacity: .25; transform: scale(.8); } 50% { opacity: 1; transform: scale(1); } }

/* What replaces the link once the seat has been taken. */
.mt-joined {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 13px; font-weight: 600; color: var(--on); padding: 10px 4px;
}
.mt-joined .ic { width: 16px; height: 16px; stroke-width: 2.6; }

/* ---------------- the opponent's chat bubble ---------------- */

/*
 * Sits above the engine's seat, where a person across the table would be. It says its
 * piece and then goes away on its own - a panel that stays open would compete with the
 * move list for attention it does not deserve.
 */
.bot-bubble {
  position: relative; align-self: flex-start; max-width: 82%;
  background: var(--panel-head); border: 1px solid var(--border);
  border-radius: 12px 12px 12px 4px;
  padding: 9px 13px; margin: 0 0 2px 54px;
  font-size: 12.5px; line-height: 1.5; color: var(--fg);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .3);
}
.bot-bubble.in { animation: bubble-in .26s cubic-bezier(.2, 1.2, .4, 1); }
@keyframes bubble-in {
  from { opacity: 0; transform: translateY(6px) scale(.96); }
  to { opacity: 1; transform: none; }
}
.bot-tail {
  position: absolute; left: 14px; bottom: -6px; width: 10px; height: 10px;
  background: var(--panel-head); border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border); transform: rotate(45deg);
}

/* ---------------- home ---------------- */

.home-body { padding: 0; overflow-y: auto; }

/*
 * A lit header rather than a list of buttons. The board is already the loudest thing on
 * screen, so this leans on space and type instead of colour.
 */
.hm-hero {
  position: relative; overflow: hidden;
  padding: 30px 22px 26px; text-align: center;
  background:
    radial-gradient(110% 130% at 50% -10%, rgba(90, 130, 204, .30) 0%, transparent 62%),
    linear-gradient(180deg, var(--row-alt), transparent 70%);
  border-bottom: 1px solid var(--border);
}
.hm-glow {
  position: absolute; left: 50%; top: -90px; width: 320px; height: 220px;
  transform: translateX(-50%); pointer-events: none;
  background: radial-gradient(closest-side, rgba(120, 160, 235, .22), transparent 72%);
}
.hm-greeting {
  position: relative; font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--fg-dim); margin-bottom: 8px;
}
.hm-title {
  position: relative; margin: 0; font-size: 30px; font-weight: 800; letter-spacing: -.022em;
  color: var(--fg-strong);
}
.hm-sub { position: relative; margin: 8px 0 0; font-size: 13px; color: var(--fg-dim); line-height: 1.55; }

.hm-tiles { display: flex; flex-direction: column; gap: 8px; padding: 14px; }
.hm-tile {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: var(--sunken); border: 1px solid var(--border); border-radius: 12px;
  padding: 13px 14px; cursor: pointer;
  transition: transform .14s cubic-bezier(.2, .8, .3, 1), border-color .14s, background .14s;
}
.hm-tile:hover {
  background: var(--hover); border-color: var(--accent); transform: translateY(-1px);
}
.hm-tile:active { transform: translateY(0); }
.hm-tile.primary { border-color: rgba(90, 130, 204, .45); }
.hm-ic {
  width: 40px; height: 40px; border-radius: 11px; flex: none; display: grid; place-items: center;
  background: var(--panel-head); color: var(--accent-hi);
}
.hm-ic .ic { width: 20px; height: 20px; }
.hm-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.hm-name { font-size: 14.5px; font-weight: 700; color: var(--fg-strong); }
.hm-desc { font-size: 11.5px; color: var(--fg-dim); line-height: 1.45; }
.hm-go { color: var(--fg-dim); display: flex; flex: none; transition: transform .16s, color .16s; }
.hm-go .ic { width: 17px; height: 17px; }
.hm-tile:hover .hm-go { color: var(--accent-hi); transform: translateX(3px); }

.hm-strip {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  margin: 0 14px; background: var(--border); border: 1px solid var(--border);
  border-radius: 11px; overflow: hidden;
}
.hm-fact {
  background: var(--panel); padding: 13px 6px; text-align: center;
  display: flex; flex-direction: column; gap: 3px;
}
.hm-fact-v {
  font-size: 19px; font-weight: 800; color: var(--fg-strong); font-variant-numeric: tabular-nums;
}
.hm-fact-k { font-size: 10px; letter-spacing: .07em; text-transform: uppercase; color: var(--fg-dim); }

.hm-quiet-row { display: flex; justify-content: center; gap: 4px; margin: 6px 0 14px; }
.hm-quiet {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  background: none; border: 0; cursor: pointer; color: var(--fg-dim);
  padding: 14px 18px; font-size: 12.5px; border-radius: 8px;
}
.hm-quiet:hover { color: var(--fg-strong); }
.hm-quiet .ic { width: 15px; height: 15px; }

/* ---------------- game over ---------------- */

/*
 * GameOverOverlay, on the web: a scrim, a rounded card, three ways on. The desktop draws
 * it over the board only; here the modal covers the window, which is the same idea on a
 * layout that has no fixed board rectangle to sit inside.
 */
.over-dialog {
  position: relative; width: min(360px, 90vw); text-align: center;
  background: var(--glass); border: 1px solid var(--glass-edge);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  backdrop-filter: blur(20px) saturate(140%);
  box-shadow: 0 28px 64px rgba(0, 0, 0, .55);
  border-radius: 20px;                 /* CARD_ARC 26, scaled to the smaller web card */
}
.over-close { position: absolute; top: 10px; right: 10px; z-index: 2; }
.over-art { padding: 28px 24px 8px; display: flex; flex-direction: column; align-items: center; gap: 4px; }
.over-mark {
  width: 62px; height: 62px; border-radius: 50%; display: grid; place-items: center;
  margin-bottom: 8px; animation: over-pop .42s cubic-bezier(.2, 1.5, .4, 1);
}
.over-mark .ic { width: 30px; height: 30px; stroke-width: 1.9; }
.over-mark.win { background: rgba(50, 215, 75, .16); color: var(--on); }
.over-mark.loss { background: rgba(255, 69, 58, .14); color: var(--danger); }
.over-mark.draw { background: var(--lift); color: var(--fg); }
@keyframes over-pop {
  0% { transform: scale(.4) rotate(-12deg); opacity: 0; }
  60% { transform: scale(1.12) rotate(3deg); }
  100% { transform: scale(1) rotate(0); opacity: 1; }
}
.over-art h2 { margin: 0; font-size: 25px; font-weight: 800; color: var(--fg-strong); letter-spacing: -.015em; }
.over-art p { margin: 0; font-size: 13px; color: var(--fg-dim); }
.over-actions { display: flex; flex-direction: column; gap: 8px; padding: 16px 22px 22px; }
.over-actions .wide-btn { padding: 12px 14px; }

/* ---------------- little boards ---------------- */

/*
 * The same squares and the same piece set as the real board, small.
 *
 * Used wherever a menu is describing something that happens on a board: a position says
 * what a mode is far faster than a line of text, and it makes the menus look like a chess
 * app rather than a settings screen.
 */
.mini-board {
  display: grid; grid-template-columns: repeat(8, 1fr);
  width: 100%; aspect-ratio: 1; border-radius: 5px; overflow: hidden;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .32);
}
.mb-sq { position: relative; }
.mb-sq.light { background: var(--sq-light); }
.mb-sq.dark { background: var(--sq-dark); }
.mb-sq.mk::after { content: ""; position: absolute; inset: 0; background: var(--mark); }
.mb-p {
  position: absolute; inset: 6%;
  background-size: contain; background-repeat: no-repeat; background-position: center;
}

/* On a home tile the board sits at the left, where the icon used to. */
.hm-board { width: 62px; flex: none; }
.hm-tile .hm-ic { width: 34px; height: 34px; border-radius: 9px; }
.hm-tile .hm-ic .ic { width: 17px; height: 17px; }

/*
 * A preview is a picture on a card, not the card itself: capped, so a wide card gives
 * more room to the words rather than a board the size of a real one.
 */
.pm-board { display: block; width: 100%; max-width: 124px; margin-bottom: 9px; }
.puz-mode { padding: 11px 12px; }

.ac-card { padding-right: 58px; }
.ac-card-board { display: block; width: 100%; max-width: 104px; margin-top: 7px; }

/* ---------------- inline icons ---------------- */

/*
 * These are drawn in `currentColor`, so they take the colour of the text they sit beside
 * and brighten with it on hover - no filters, no second asset per theme.
 */
.ic { width: 18px; height: 18px; flex: none; display: block; }
.wide-btn .ic { width: 17px; height: 17px; }
.pm-icon .ic { width: 19px; height: 19px; }
.ac-course-icon .ic { width: 17px; height: 17px; }
.ac-tick .ic { width: 11px; height: 11px; stroke-width: 3; }
.ls-done-mark .ic { width: 24px; height: 24px; stroke-width: 2.6; }
.rv-empty-icon .ic { width: 46px; height: 46px; stroke-width: 1.4; margin: 6px auto 0; }
.wide-btn > span:not(.ic) { display: inline-flex; }

/* ---------------- academy ---------------- */

.academy-body { padding: 18px; gap: 18px; overflow-y: auto; }

.ac-top {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 16px;
  background:
    radial-gradient(120% 140% at 12% 0%, rgba(90, 130, 204, .22) 0%, transparent 62%),
    var(--sunken);
  border: 1px solid var(--border); border-radius: 14px; padding: 18px 20px;
}
.ac-crest {
  width: 48px; height: 48px; border-radius: 14px; display: grid; place-items: center;
  background: var(--panel-head); color: var(--accent-hi);
}
.ac-crest .ic { width: 24px; height: 24px; }
.ac-top-text h2 { margin: 0; font-size: 20px; color: var(--fg-strong); letter-spacing: -.01em; }
.ac-top-text p { margin: 4px 0 0; font-size: 12.5px; color: var(--fg-dim); line-height: 1.5; }

/* A ring rather than a bar: it sits in the corner and says one number. */
.ac-ring { position: relative; width: 54px; height: 54px; flex: none; }
.ac-ring svg { width: 54px; height: 54px; transform: rotate(-90deg); }
.ac-ring circle { fill: none; stroke-width: 4; }
.ac-ring-track { stroke: var(--border); }
.ac-ring-fill { stroke: var(--on); stroke-linecap: round; transition: stroke-dashoffset .5s; }
.ac-ring span {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 12px; font-weight: 800; color: var(--fg-strong);
}

.ac-section { display: flex; flex-direction: column; gap: 4px; }
.ac-section-title {
  display: flex; align-items: center; gap: 9px; margin: 0;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--fg-dim); font-weight: 600;
}
.ac-section-title .ic { width: 16px; height: 16px; color: var(--accent-hi); }
.ac-section-count {
  margin-left: auto; letter-spacing: 0; font-variant-numeric: tabular-nums;
  background: var(--panel-head); border-radius: 20px; padding: 2px 10px; font-size: 10.5px;
}
.ac-section-count.all { color: var(--on); }
.ac-section-blurb { margin: 0 0 8px; font-size: 12px; color: var(--fg-dim); }

/* Two columns, as the Learn page uses - one card is one lesson, and its state is visible
   without opening it. */
.ac-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ac-card {
  position: relative; overflow: hidden; text-align: left; cursor: pointer;
  display: flex; flex-direction: column; gap: 3px;
  background: var(--sunken); border: 1px solid var(--border); border-radius: 10px;
  padding: 12px 13px; padding-right: 58px;
  transition: transform .13s cubic-bezier(.2, .8, .3, 1), border-color .13s, background .13s;
}
.ac-card:hover { background: var(--hover); border-color: var(--accent); transform: translateY(-1px); }
.ac-card-name { font-size: 13.5px; font-weight: 700; color: var(--fg-strong); }
.ac-card-blurb { font-size: 11px; color: var(--fg-dim); line-height: 1.45; }
.ac-card-badge {
  position: absolute; top: 10px; right: 11px;
  font-size: 10px; color: var(--fg-dim); font-variant-numeric: tabular-nums;
}
/* A finished lesson keeps a corner ribbon - the point is to see what you have done. */
.ac-card.done { border-color: rgba(50, 215, 75, .34); }
.ac-card.done::before {
  content: ""; position: absolute; top: 0; right: 0;
  border-width: 0 34px 34px 0; border-style: solid;
  border-color: transparent var(--on) transparent transparent;
}
.ac-card.done .ac-card-badge { top: 3px; right: 3px; color: #10240f; }
.ac-card.done .ac-card-badge .ic { width: 11px; height: 11px; stroke-width: 3.2; }

.ac-reset {
  align-self: center; background: none; border: 0; cursor: pointer;
  color: var(--fg-dim); font-size: 12px; padding: 10px 16px; border-radius: 8px;
}
.ac-reset:hover { color: var(--danger); }

/* ---- a lesson in progress ---- */

.lesson-body { padding: 14px; gap: 12px; overflow-y: auto; }
.ls-head { display: flex; flex-direction: column; gap: 2px; }
.ls-course {
  font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--fg-dim);
}
.ls-head h3 { margin: 0; font-size: 18px; color: var(--fg-strong); }

.ls-pips { display: flex; gap: 5px; }
.ls-pip {
  flex: 1; height: 4px; border-radius: 3px; background: var(--border); transition: background .2s;
}
.ls-pip.on { background: var(--on); }
.ls-pip.now { background: var(--accent); }

.ls-text {
  font-size: 13.5px; line-height: 1.65; color: var(--fg);
  background: var(--sunken); border: 1px solid var(--border); border-radius: 10px;
  padding: 13px 14px;
}

.ls-prompt {
  font-size: 12.5px; font-weight: 600; color: var(--accent-hi);
  border-left: 3px solid var(--accent); padding: 6px 10px; border-radius: 0 6px 6px 0;
  background: rgba(75, 110, 175, .12);
}
.ls-prompt.wrong {
  color: var(--danger); border-left-color: var(--danger); background: rgba(255, 69, 58, .12);
  animation: nudge .3s;
}
@keyframes nudge {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

.ls-actions { display: grid; grid-template-columns: 1fr 2fr; gap: 8px; }
.ls-actions .wide-btn:disabled { opacity: .4; cursor: not-allowed; }

.ls-done {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  background: rgba(50, 215, 75, .09); border: 1px solid rgba(50, 215, 75, .3);
  border-radius: 12px; padding: 22px 16px;
}
.ls-done-mark {
  width: 46px; height: 46px; border-radius: 50%; background: var(--on); color: #10240f;
  display: grid; place-items: center; font-size: 24px; font-weight: 800;
  animation: verdict-pop .3s cubic-bezier(.2, 1.5, .4, 1);
}
.ls-done-title { font-size: 16px; font-weight: 700; color: var(--fg-strong); margin-bottom: 6px; }
.ls-done .wide-btn { width: 100%; }
.ls-done .wide-btn + .wide-btn { margin-top: 8px; }

/* ---------------- administration ---------------- */

.admin-body { padding: 16px; gap: 14px; overflow-y: auto; }
.ad-login { display: flex; flex-direction: column; align-items: center; text-align: center; }
.ad-badge {
  width: 54px; height: 54px; border-radius: 16px; display: grid; place-items: center;
  background: var(--panel-head); color: var(--accent-hi); margin-bottom: 10px;
}
.ad-badge .ic { width: 26px; height: 26px; }
.ad-login h2 { margin: 0; font-size: 20px; color: var(--fg-strong); }
.ad-login p { margin: 5px 0 0; font-size: 12.5px; color: var(--fg-dim); }
.ad-login .auth-form { width: min(340px, 100%); padding: 16px 0 0; }

.ad-warn {
  font-size: 12px; line-height: 1.55; color: #f0c15c;
  background: rgba(240, 193, 92, .10); border: 1px solid rgba(240, 193, 92, .32);
  border-radius: 8px; padding: 10px 12px;
}
.ad-warn code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
}
.ad-where { font-size: 11.5px; color: var(--fg-dim); }

.ad-table {
  border: 1px solid var(--border); border-radius: 10px; overflow: hidden;
  background: var(--sunken);
}
.ad-row {
  display: grid; grid-template-columns: 1.6fr 1.2fr .8fr .8fr .8fr;
  gap: 8px; padding: 9px 12px; font-size: 12.5px; align-items: center;
  border-bottom: 1px solid var(--hairline);
}
.ad-row:last-child { border-bottom: 0; }
.ad-row.head {
  background: var(--panel-head); color: var(--fg-dim); font-size: 10.5px;
  letter-spacing: .07em; text-transform: uppercase; font-weight: 600;
}
.ad-row span:not(.ad-name) { font-variant-numeric: tabular-nums; color: var(--fg); }
.ad-name { font-weight: 600; color: var(--fg-strong); overflow: hidden; text-overflow: ellipsis; }
.ad-empty { padding: 18px 12px; text-align: center; color: var(--fg-dim); font-style: italic; }

/* ---------------- profile ---------------- */

.profile-body { padding: 14px; gap: 12px; overflow-y: auto; }
.pf-head { display: flex; align-items: center; gap: 12px; }
.pf-avatar {
  width: 54px; height: 54px; border-radius: 50%; flex: none;
  background: linear-gradient(140deg, var(--accent), var(--accent-hi));
  color: #fff; display: grid; place-items: center; font-size: 24px; font-weight: 800;
}
.pf-name { font-size: 19px; font-weight: 700; color: var(--fg-strong); }
.pf-since { font-size: 11.5px; color: var(--fg-dim); }

.pf-signedout { text-align: center; display: flex; flex-direction: column; gap: 10px; }
.pf-signedout p { margin: 0; font-size: 12.5px; color: var(--fg-dim); line-height: 1.6; }

.pf-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.pf-card {
  background: var(--sunken); border: 1px solid var(--border); border-radius: 10px;
  padding: 11px 6px; text-align: center; display: flex; flex-direction: column; gap: 4px;
}
.pf-card .k { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-dim); }
.pf-card .v {
  font-size: 19px; font-weight: 800; color: var(--fg-strong); font-variant-numeric: tabular-nums;
}

.pf-section {
  margin: 6px 0 -2px; font-size: 11px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--fg-dim); font-weight: 600;
}

/* The way back into the app: three real positions, each one a button. */
/*
 * `minmax(0, 1fr)` rather than `1fr`: a bare `1fr` floors at the column's min-content, so
 * the longest label ("Play the computer") stole width from the other two and the three
 * boards came out different sizes.
 */
.pf-continue { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; }
.pf-cont {
  display: flex; flex-direction: column; gap: 2px; text-align: left; cursor: pointer;
  background: var(--sunken); border: 1px solid var(--border); border-radius: 11px;
  padding: 9px 9px 10px;
  transition: transform .14s cubic-bezier(.2, .8, .3, 1), border-color .14s, background .14s;
}
.pf-cont:hover { background: var(--hover); border-color: var(--accent); transform: translateY(-2px); }
.pf-cont-board { display: block; width: 100%; max-width: 116px; margin-bottom: 7px; }
.pf-cont-name {
  font-size: 12.5px; font-weight: 700; color: var(--fg-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pf-cont-note {
  font-size: 10.5px; color: var(--fg-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.pf-wld-bar {
  display: flex; height: 10px; border-radius: 6px; overflow: hidden; background: var(--sunken);
}
.pf-wld-bar i { display: block; transition: width .4s; }
.pf-wld-bar .w { background: var(--on); }
.pf-wld-bar .d { background: #7a7f82; }
.pf-wld-bar .l { background: var(--danger); }
.pf-wld-key {
  display: flex; justify-content: space-between; margin-top: 6px;
  font-size: 11.5px; color: var(--fg-dim);
}
.pf-wld-key b { color: var(--fg-strong); }

.pf-rows { display: flex; flex-direction: column; }
.pf-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 2px; border-bottom: 1px solid var(--hairline); font-size: 12.5px;
}
.pf-row:last-child { border-bottom: 0; }
.pf-row b { color: var(--fg-strong); font-variant-numeric: tabular-nums; }

.pf-academy { display: flex; flex-direction: column; gap: 8px; }
.pf-course { display: grid; grid-template-columns: 1fr 90px auto; align-items: center; gap: 9px; }
.pf-course-name { font-size: 12.5px; color: var(--fg); }
.pf-course-bar { height: 6px; border-radius: 4px; background: var(--sunken); overflow: hidden; }
.pf-course-bar i { display: block; height: 100%; background: var(--accent); transition: width .35s; }
.pf-course-count {
  font-size: 11px; color: var(--fg-dim); font-variant-numeric: tabular-nums; min-width: 30px;
  text-align: right;
}

/* ---------------- game review ---------------- */

/*
 * One badge, used in three places: the move list, the board, and the summary. Each verdict
 * gets a colour and a glyph rather than an image, so it stays sharp at any size and costs
 * nothing to load.
 */
.verdict {
  display: inline-grid; place-items: center; flex: none;
  min-width: 17px; height: 17px; padding: 0 3px; margin-left: 4px;
  border-radius: 5px; font-size: 10px; font-weight: 800; line-height: 1;
  color: #fff; letter-spacing: -.02em;
}
.v-brilliant  { background: #1baaa6; }
.v-great      { background: #5b8bb0; }
.v-best       { background: #7aab5c; }
.v-excellent  { background: #96bc4b; }
.v-good       { background: #96af8b; }
.v-inaccuracy { background: #f0c15c; color: #3a2d00; }
.v-mistake    { background: #e08b3a; }
.v-blunder    { background: #ca3431; }
.v-forced     { background: #7a7f82; }

/* On the board the badge sits on the corner of the destination square. */
.sq-verdict {
  position: absolute; width: 12.5%; height: 12.5%; pointer-events: none;
  display: flex; align-items: flex-start; justify-content: flex-end;
}
.sq-verdict .verdict {
  margin: 4%; min-width: 3.2cqh; height: 3.2cqh; font-size: 1.8cqh; border-radius: 1cqh;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .5);
  animation: verdict-pop .22s cubic-bezier(.2, 1.5, .4, 1);
}
@keyframes verdict-pop { from { transform: scale(.3); opacity: 0; } to { transform: scale(1); opacity: 1; } }

.review-body { padding: 14px; gap: 14px; overflow-y: auto; }

/* The reviewed-game strip: both accuracies, and a way back to the full summary. */
.rv-strip {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px;
  padding: 7px 10px; border-bottom: 1px solid var(--border); background: var(--sunken);
}
.rv-strip-acc {
  font-weight: 700; font-variant-numeric: tabular-nums; color: var(--fg-strong);
  font-size: 13px; text-align: center;
}
.rv-strip-acc:first-child { text-align: left; }
.rv-strip-acc:last-child { text-align: right; }
.rv-strip-open {
  background: none; border: 1px solid var(--border); border-radius: 20px;
  padding: 3px 12px; font-size: 11px; color: var(--fg-dim); cursor: pointer;
}
.rv-strip-open:hover { color: var(--fg-strong); background: var(--hover); }

.rv-empty { text-align: center; display: flex; flex-direction: column; gap: 10px; }
.rv-empty img { width: 54px; height: 54px; margin: 8px auto 0; opacity: .9; }
.rv-empty h3 { margin: 0; font-size: 19px; color: var(--fg-strong); }
.rv-empty p { margin: 0; font-size: 12.5px; color: var(--fg-dim); line-height: 1.6; }
.rv-error {
  margin: 0; font-size: 12.5px; color: var(--danger);
  background: rgba(255, 69, 58, .10); border: 1px solid rgba(255, 69, 58, .3);
  border-radius: 7px; padding: 8px 10px;
}

.rv-progress { display: flex; flex-direction: column; gap: 8px; padding: 20px 0; }
.rv-bar { height: 7px; border-radius: 4px; background: var(--sunken); overflow: hidden; }
.rv-bar i { display: block; height: 100%; width: 0; background: var(--accent); transition: width .3s; }
.rv-progress-label { font-size: 12px; color: var(--fg-dim); text-align: center; }

.rv-content { display: flex; flex-direction: column; gap: 14px; }
.rv-accuracy {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px;
  background: var(--sunken); border: 1px solid var(--border); border-radius: 12px;
  padding: 16px 12px;
}
.rv-side { text-align: center; display: flex; flex-direction: column; gap: 5px; }
.rv-acc {
  font-size: 27px; font-weight: 800; color: var(--fg-strong); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.rv-who { font-size: 11.5px; color: var(--fg-dim); display: flex; align-items: center;
  justify-content: center; gap: 5px; }
.rv-dot { width: 9px; height: 9px; border-radius: 50%; border: 1px solid var(--border); }
.rv-dot.white { background: #efefef; }
.rv-dot.black { background: #1e1e1e; }
.rv-vs { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg-dim); }

/* ---- the evaluation graph ---- */

.rv-graph-wrap {
  background: var(--sunken); border: 1px solid var(--border); border-radius: 10px;
  padding: 8px 8px 6px;
}
.rv-graph { display: block; width: 100%; height: 90px; overflow: visible; }
.rv-mid { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 3; }
/* White's share of the position, filled from the bottom - the eval bar, unrolled. */
.rv-area { fill: var(--graph-area); }
.rv-line { fill: none; stroke: var(--graph-line); stroke-width: 1.7; stroke-linejoin: round; }
.rv-dot { stroke: var(--panel); stroke-width: 1.3; }
.rv-dot.v-blunder { fill: #ca3431; }
.rv-dot.v-mistake { fill: #e08b3a; }
.rv-dot.v-inaccuracy { fill: #f0c15c; }
.rv-dot.v-brilliant { fill: #1baaa6; }
.rv-dot.v-great { fill: #5b8bb0; }
.rv-hit { fill: transparent; cursor: pointer; }
.rv-hit:hover { fill: var(--row-hover); }
.rv-cursor { stroke: var(--accent-hi); stroke-width: 1.4; pointer-events: none; }
.rv-graph-key {
  display: flex; justify-content: space-between; gap: 8px; margin-top: 5px;
  font-size: 10px; color: var(--fg-dim);
}
#rv-graph-hint { opacity: .75; }

/* ---- the move being explained ---- */

.rv-move {
  background: var(--panel-head); border: 1px solid var(--border); border-radius: 10px;
  padding: 11px 12px; display: flex; flex-direction: column; gap: 5px;
}
.rv-move-head { display: flex; align-items: center; gap: 7px; }
.rv-move-san {
  font-weight: 700; color: var(--fg-strong); font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.rv-move-head .verdict { margin-left: 0; }
.rv-move-text { margin: 0; font-size: 12.5px; line-height: 1.6; color: var(--fg); }

.rv-table { display: flex; flex-direction: column; }
.rv-row {
  display: grid; grid-template-columns: 40px 1fr 40px; align-items: center;
  padding: 7px 4px; border-bottom: 1px solid var(--hairline);
}
.rv-row:last-child { border-bottom: 0; }
.rv-count {
  text-align: center; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--fg-strong);
}
.rv-label {
  display: flex; align-items: center; gap: 7px; justify-content: center;
  font-size: 12.5px; color: var(--fg);
}
.rv-label .verdict { margin-left: 0; }
.rv-note { margin: 0; font-size: 11px; color: var(--fg-dim); line-height: 1.6; }

/* ---------------- puzzles ---------------- */

.puz-hero {
  background: var(--sunken); border: 1px solid var(--border); border-radius: 12px;
  padding: 18px 14px; text-align: center;
}
.puz-hero-rating {
  font-size: 44px; font-weight: 800; line-height: 1; color: var(--fg-strong);
  font-variant-numeric: tabular-nums;
}
.puz-hero-label {
  font-size: 11px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--fg-dim); margin-top: 6px;
}
.puz-hero-stats { font-size: 12px; color: var(--fg-dim); margin-top: 10px; }
.puz-note { font-size: 11.5px; color: var(--fg-dim); line-height: 1.6; margin: 2px 0 0; }

/* ---- mode picker ---- */

.puz-modes { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.puz-mode {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  background: var(--sunken); border: 1px solid var(--border); border-radius: 10px;
  padding: 11px 12px; cursor: pointer; text-align: left;
  transition: border-color .14s, background .14s;
}
.puz-mode:hover { background: var(--hover); }
.puz-mode[aria-pressed="true"] { border-color: var(--accent); background: var(--hover); }
.pm-icon { font-size: 17px; line-height: 1; }
.pm-name { font-weight: 700; color: var(--fg-strong); font-size: 13.5px; }
.pm-desc { font-size: 10.5px; color: var(--fg-dim); }
.pm-best { font-size: 10.5px; color: var(--on); font-weight: 600; min-height: 13px; }

/* ---- a run in progress ---- */

.puz-run {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 10px;
  background: var(--panel-head); border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 14px;
}
.puz-clock {
  font-size: 21px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--fg-strong); letter-spacing: .01em;
}
.puz-clock.low { color: var(--danger); animation: clock-pulse 1s ease-in-out infinite; }
@keyframes clock-pulse { 50% { opacity: .45; } }

.puz-score {
  font-size: 30px; font-weight: 800; color: var(--fg-strong); text-align: center;
  font-variant-numeric: tabular-nums; line-height: 1;
}
/* The counter jumps when it changes, so a solve is felt as well as counted. */
.puz-score.bump { animation: score-bump .34s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes score-bump {
  0% { transform: scale(1); }
  40% { transform: scale(1.32); color: var(--on); }
  100% { transform: scale(1); }
}

.puz-strikes { display: flex; gap: 6px; justify-content: flex-end; }
.strike {
  width: 20px; height: 20px; border-radius: 5px; position: relative;
  background: var(--sunken); border: 1px solid var(--border);
}
.strike.on { background: var(--danger); border-color: var(--danger); }
/* The X is drawn rather than typed, so it lands crisply at any size. */
.strike.on::before, .strike.on::after {
  content: ""; position: absolute; left: 25%; right: 25%; top: 50%; height: 2px;
  background: #fff; border-radius: 2px;
}
.strike.on::before { transform: rotate(45deg); }
.strike.on::after { transform: rotate(-45deg); }
.strike.just { animation: strike-in .38s cubic-bezier(.2, 1.5, .4, 1); }
@keyframes strike-in {
  0% { transform: scale(.4) rotate(-14deg); opacity: 0; }
  55% { transform: scale(1.22) rotate(5deg); }
  100% { transform: scale(1) rotate(0); opacity: 1; }
}

/* ---- the end of a run ---- */

.puz-over {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: var(--sunken); border: 1px solid var(--border); border-radius: 12px;
  padding: 22px 16px;
}
.po-score {
  font-size: 58px; font-weight: 800; color: var(--fg-strong); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.po-score.bump { animation: score-bump .5s cubic-bezier(.2, 1.6, .4, 1); }
.po-label { font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-dim); }
.po-best { font-size: 12.5px; color: var(--fg); margin: 6px 0 12px; text-align: center; }
.puz-over .wide-btn { width: 100%; }
.puz-over .wide-btn + .wide-btn { margin-top: 8px; }

.puzzle-body { padding: 14px; gap: 12px; overflow-y: auto; }

/*
 * The status block carries the whole result: a coloured edge, a headline and one line of
 * detail. It is the only thing that changes between solving, solved and missed, so the
 * panel never rearranges under the solver.
 */
.puz-status {
  position: relative; padding: 14px 14px 14px 18px; border-radius: 10px;
  background: var(--sunken); border: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 2px;
  transition: background .18s, border-color .18s;
}
.puz-status::before {
  content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 4px;
  border-radius: 0 3px 3px 0; background: var(--accent); transition: background .18s;
}
.puz-status.good { background: rgba(50, 215, 75, .10); border-color: rgba(50, 215, 75, .35); }
.puz-status.good::before { background: var(--on); }
.puz-status.bad { background: rgba(255, 69, 58, .10); border-color: rgba(255, 69, 58, .32); }
.puz-status.bad::before { background: var(--danger); }
.puz-status.busy::before { background: var(--fg-dim); }
.puz-headline { font-size: 17px; font-weight: 700; color: var(--fg-strong); }
.puz-status.good .puz-headline { color: var(--on); }
.puz-status.bad .puz-headline { color: var(--danger); }
.puz-sub { font-size: 12px; color: var(--fg-dim); }
.puz-dot { display: none; }

.puz-meta { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.puz-chip {
  background: var(--sunken); border: 1px solid var(--border); border-radius: 9px;
  padding: 9px 6px; text-align: center; display: flex; flex-direction: column; gap: 3px;
}
.puz-chip .k {
  font-size: 10px; letter-spacing: .07em; text-transform: uppercase; color: var(--fg-dim);
}
.puz-chip .v {
  font-size: 17px; font-weight: 700; color: var(--fg-strong); font-variant-numeric: tabular-nums;
}

.puz-themes { display: flex; flex-wrap: wrap; gap: 6px; min-height: 22px; }
.puz-themes .theme {
  font-size: 11px; padding: 3px 9px; border-radius: 20px;
  background: var(--panel-head); color: var(--fg); border: 1px solid var(--border);
}
/* The themes name the answer, so they are held back until the puzzle is over. */
.puz-themes .theme.masked { color: var(--fg-dim); letter-spacing: .18em; }

.puz-progress { display: flex; gap: 6px; min-height: 8px; }
.puz-progress .pip {
  flex: 1; max-width: 46px; height: 5px; border-radius: 3px; background: var(--border);
  transition: background .2s;
}
.puz-progress .pip.on { background: var(--on); }
.puz-progress .pip.bad { background: var(--danger); }

.puz-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.puz-actions .wide-btn { padding: 10px 8px; font-size: 12.5px; }
.puz-actions .wide-btn:disabled { opacity: .4; cursor: not-allowed; }
.puzzle-body .wide-btn:disabled { opacity: .4; cursor: not-allowed; }
.puz-source {
  font-size: 11.5px; color: var(--fg-dim); text-align: center; text-decoration: none;
  padding: 4px;
}
.puz-source:hover { color: var(--fg-strong); text-decoration: underline; }

/* ---------------- accounts ---------------- */

/*
 * The sign-in dialog is the one screen with no desktop counterpart to match, so it is
 * allowed to be the most finished thing in the app: a translucent panel over a blurred
 * board, a lit header, and type with room to breathe.
 */
.auth-dialog {
  position: relative; width: min(408px, 92vw);
  background: var(--glass); border: 1px solid var(--glass-edge);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  backdrop-filter: blur(22px) saturate(150%);
  box-shadow: 0 30px 70px rgba(0, 0, 0, .55), 0 2px 0 rgba(255, 255, 255, .06) inset;
  border-radius: 18px; overflow: hidden;
}
.auth-close { position: absolute; top: 12px; right: 12px; z-index: 3; }

.auth-art {
  position: relative; overflow: hidden;
  padding: 30px 24px 22px; text-align: center; color: #fff;
  background:
    radial-gradient(120% 120% at 20% 0%, rgba(90, 130, 204, .55) 0%, transparent 60%),
    linear-gradient(155deg, rgba(75, 110, 175, .85) 0%, rgba(30, 34, 40, .75) 100%);
}
/* A soft light behind the mark, so the header has depth rather than a flat gradient. */
.auth-glow {
  position: absolute; left: 50%; top: -70px; width: 260px; height: 200px;
  transform: translateX(-50%); pointer-events: none;
  background: radial-gradient(closest-side, rgba(255, 255, 255, .32), transparent 72%);
}
.auth-mark {
  position: relative; width: 58px; height: 58px; margin: 0 auto 12px;
  border-radius: 17px; display: grid; place-items: center;
  background: rgba(255, 255, 255, .13);
  border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: 0 10px 26px rgba(0, 0, 0, .3);
}
.auth-mark img { width: 32px; height: 32px; }
.auth-art h2 { position: relative; margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -.01em; }
.auth-art p { position: relative; margin: 7px 0 0; font-size: 12.5px; opacity: .82; line-height: 1.55; }

.auth-tabs { border-bottom: 1px solid var(--glass-edge); background: transparent; }
.auth-tabs button { padding: 12px 4px; }
.auth-form { display: flex; flex-direction: column; gap: 13px; padding: 20px 22px 22px; }
.auth-field { display: flex; flex-direction: column; gap: 6px; }
.auth-field span { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--fg-dim); }
.auth-field input {
  background: var(--glass-input); border: 1px solid var(--glass-edge); border-radius: 10px;
  padding: 12px 13px; font-size: 14.5px; width: 100%;
  transition: border-color .15s, box-shadow .15s, background .15s;
}
.auth-field input:focus {
  outline: none; border-color: var(--accent-hi); background: var(--sunken);
  box-shadow: 0 0 0 3px rgba(90, 130, 204, .18);
}
.auth-hint { margin: -4px 0 0; font-size: 11.5px; color: var(--fg-dim); }
.auth-error {
  margin: 0; font-size: 12.5px; color: var(--danger);
  background: rgba(255, 69, 58, .10); border: 1px solid rgba(255, 69, 58, .3);
  border-radius: 7px; padding: 8px 10px;
}
.auth-form .wide-btn { padding: 12px 14px; font-size: 14px; }

.auth-signed { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 22px 20px; }
.auth-avatar {
  width: 62px; height: 62px; border-radius: 50%; background: var(--accent); color: #fff;
  display: grid; place-items: center; font-size: 26px; font-weight: 700;
}
.auth-who { font-size: 18px; font-weight: 700; color: var(--fg-strong); }
.auth-stats { font-size: 12px; color: var(--fg-dim); text-align: center; margin-bottom: 6px; }

/* ---------------- modals ---------------- */

.modal {
  position: fixed; inset: 0; background: rgba(10, 12, 14, .55);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  display: grid; place-items: center; z-index: 100;
  animation: modal-in .18s ease-out;
}
@keyframes modal-in { from { opacity: 0; } to { opacity: 1; } }
.modal .dialog { animation: dialog-in .22s cubic-bezier(.2, .9, .3, 1); }
@keyframes dialog-in {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
  to { opacity: 1; transform: none; }
}
.dialog {
  width: min(430px, 92vw); max-height: 86vh; overflow: hidden;
  background: var(--panel); border-radius: 12px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, .55);
  display: flex; flex-direction: column;
}
.dialog-head {
  display: flex; align-items: center; justify-content: space-between;
  background: var(--panel-head); padding: 10px 12px;
}
.dialog-title { display: flex; align-items: center; gap: 8px; font-weight: 700; color: var(--fg-strong); }
.dialog-title .ico { width: 17px; height: 17px; }

.tabs { display: flex; border-bottom: 1px solid var(--border); }
.tabs button {
  flex: 1; background: none; border: 0; padding: 9px 4px; cursor: pointer;
  border-bottom: 2px solid transparent;
}
.tabs button:hover { color: var(--fg-strong); }
.tabs button[aria-pressed="true"] { border-bottom-color: var(--accent); color: var(--fg-strong); }

.tab-body { padding: 10px 16px 16px; overflow-y: auto; }
.copyrow { display: flex; gap: 7px; align-items: flex-start; }
.copyrow textarea { flex: 1; }
.btn {
  background: var(--sunken); border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 12px; cursor: pointer;
}
.btn:hover { background: var(--hover); }

.toast {
  position: fixed; bottom: 20px; left: 50%;
  transform: translateX(-50%) translateY(14px);
  background: var(--panel-head); border: 1px solid var(--border); color: var(--fg-strong);
  padding: 9px 16px; border-radius: 7px; box-shadow: 0 8px 28px rgba(0, 0, 0, .5);
  opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s; z-index: 200;
}
.toast.on { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------------- responsive ---------------- */

/*
 * Three layouts, not one squeezed three ways.
 *
 *   wide    - rail, board, panel side by side, as the desktop app
 *   narrow  - the rail keeps its icons but drops its labels; the panel narrows
 *   phone   - one column: board first, panel under it, and the rail becomes a bottom bar
 *
 * The phone layout is where the old rules gave up: a 42px rail still ate the width, the
 * board was sized against the viewport height it no longer had, and the page scrolled
 * sideways. Below 860px the grid is abandoned for a single column.
 */

@media (max-width: 1180px) {
  :root { --panel-w: clamp(268px, 30vw, 340px); }
  .app { grid-template-columns: 56px 1fr; }
  .app .rail-btn span { display: none; }
  .app .rail-btn { justify-content: center; padding-left: 0; padding-right: 0; }
  .stage { --rail-w: 56px; }
  .panel-title { font-size: 21px; }
  .panel-head { min-height: 56px; }
}

@media (max-width: 860px) {
  html, body {
    overflow: visible; height: auto;
    /* Keep a rubber-band scroll from dragging the whole page while a piece is moving. */
    overscroll-behavior-y: contain;
  }
  .app {
    display: block; height: auto; min-height: 100dvh;
    padding-bottom: calc(60px + env(safe-area-inset-bottom));
  }

  /* The rail becomes a bottom bar: a thumb reaches the bottom of a phone, not its edge. */
  .rail {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
    flex-direction: row; align-items: center;
    padding: 0 4px env(safe-area-inset-bottom);
    height: calc(60px + env(safe-area-inset-bottom));
    border-right: 0; border-top: 1px solid var(--border);
  }
  /* Both groups share the bar evenly, so the five icons sit on one rhythm rather than
     bunching at the ends. */
  .rail-group { flex: 1; flex-direction: row; align-items: center; justify-content: space-evenly; }
  .rail-btn, .rail-btn.sm {
    width: auto; padding: 9px 14px; border-radius: 10px;
  }
  .rail-btn span { display: none; }
  .rail-btn .ico, .rail-btn.sm .ico { width: 25px; height: 25px; }
  /*
   * Eight icons do not fit a phone's thumb reach. The bar keeps the five destinations and
   * the account; collapsing has nothing to collapse in one column, and Settings and the
   * theme both live on the Home screen.
   */
  #rail-collapse, #rail-setting, #rail-theme { display: none; }

  .stage {
    display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start;
    gap: 10px; padding: 10px;
    /* Width first: a phone is narrower than it is tall, so the board is bounded by the
       viewport width, and by the height only in landscape. */
    --board-size: min(
        calc(100vw - 20px - var(--board-extra)),
        calc(100dvh - 190px));
  }
  .board-col { align-self: center; }

  /*
   * One scroller, not three. On the desktop the panel is a fixed-height column with the
   * move list scrolling inside it; on a phone that nests a scroll region inside a page
   * that also scrolls, and neither reliably gets the gesture. Here the panel grows to fit
   * its content and the page is the only thing that moves.
   */
  .panel {
    width: 100%; max-height: none; border-radius: 12px; align-self: auto;
    overflow: visible;
  }
  .panel-head { min-height: 50px; padding: 9px 12px; border-radius: 12px 12px 0 0; }
  .panel-title { font-size: 19px; }
  .panel-title .ico { width: 23px; height: 23px; }
  .moves { max-height: none; overflow: visible; }
  .lines { max-height: none; }
  .menu-body, .puzzle-body { overflow: visible; }
  /* One column of lesson cards, and the modes stack, on a phone. */
  .ac-grid, .puz-modes, .mt-kinds, .ac-top { grid-template-columns: 1fr; }
  .ac-top { text-align: center; justify-items: center; gap: 10px; }
  .ad-row { grid-template-columns: 1.4fr .9fr .6fr .6fr; }
  .ad-row span:nth-child(5), .ad-row.head span:nth-child(5) { display: none; }
  .puz-hero-rating { font-size: 38px; }
  .puz-actions .wide-btn { padding: 13px 8px; }

  /* The move controls ride above the tab bar, so stepping through a game never means
     scrolling to the end of the move list first. */
  .panel-foot {
    position: sticky; z-index: 6;
    bottom: calc(60px + env(safe-area-inset-bottom));
    background: var(--panel); border-radius: 0 0 12px 12px;
    box-shadow: 0 -8px 20px rgba(0, 0, 0, .3);
  }

  /* Fingers, not a mouse: everything tappable gets a bigger target. */
  .nav-btn { padding: 15px 0; }
  .nav-btn .ico { width: 19px; height: 19px; }
  .toolrow { gap: 2px; }
  .tool { padding: 10px; }
  .tool .ico { width: 18px; height: 18px; }
  .head-btn { padding: 8px; }
  .field { min-height: 42px; }
  .field select, .field input { min-width: 108px; padding: 8px 9px; }
  .wide-btn { padding: 13px 14px; }
  .switch { width: 46px; height: 26px; border-radius: 13px; }
  .switch::after { width: 22px; height: 22px; }
  .switch[aria-checked="true"]::after { transform: translateX(20px); }

  .seat { min-height: 42px; }
  .seat .avatar { width: 36px; height: 36px; border-radius: 5px; }
  .seat .avatar img { width: 26px; height: 26px; }
  .seat .who { font-size: 14px; }
  .seat .taken img { width: 15px; height: 15px; }
  .seat .clock { font-size: 17px; padding: 4px 11px; }

  .dialog { width: calc(100vw - 20px); max-height: 84dvh; }
  .engine-menu { left: 8px; right: 8px; min-width: 0; }
  .preview { display: none; }                  /* nothing hovers on a touch screen */
  .toast { bottom: calc(74px + env(safe-area-inset-bottom)); }
  .move-menu { min-width: 210px; }
  .move-menu button { padding: 11px 12px; }
}

/* Very narrow phones: give the board the last few pixels the eval bar was taking. */
@media (max-width: 400px) {
  :root { --eval-w: 11px; --eval-gap: 6px; }
  .evalbar .num { display: none; }             /* unreadable at this width; the bar says it */
  .stage { padding: 7px; gap: 8px; --board-size: min(
      calc(100vw - 14px - var(--board-extra)), calc(100dvh - 180px)); }
  .menu-body { padding: 11px; gap: 9px; }
  .mv-row { grid-template-columns: 36px 1fr 1fr; }
}

/* Phone held sideways: the board is bounded by height, so put the panel back beside it. */
@media (max-width: 940px) and (orientation: landscape) and (max-height: 500px) {
  .stage {
    flex-direction: row; align-items: flex-start;
    --board-size: calc(100dvh - 118px);
  }
  .panel { width: auto; flex: 1; max-height: calc(100dvh - 20px); }
  .moves { max-height: none; }
}
