/* PaperGames – einfache, E‑Ink-freundliche Gestaltung */

:root {
  --bg: #f5f5f5;
  --bg-alt: #e3e3e3;
  --border: #111;
  --text: #111;
  --muted: #333;
  --accent: #111;
  --danger: #900;
  --square-light: #fdfdfd;
  --square-dark: #d5d5d5;
  --square-selected: #bbb;
  --square-last: #c5e1ff;
  --radius: 4px;
  --border-width: 2px;
  --shadow: 0 0 0 1px #000;
  --font-main: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-main);
  font-weight: 500;
}

/* Larger text sizes toggled by font-size-toggle.js. Tolino's own
   system-wide font size setting only affects the e-book reader, not
   this app's own web pages, so it needs its own independent control.
   Scaling the root font-size rather than adding a separate CSS variable
   works site-wide for free everywhere text is already sized in
   rem/em (nearly everywhere in this file), while leaving fixed-px board
   /card layouts (already sized to fit the viewport, board-a11y.js's
   float-grids, etc.) untouched, since those are unrelated to reading
   comfort and enlarging them would just waste screen space. */
html.text-size-large {
  font-size: 112.5%;
}

html.text-size-xlarge {
  font-size: 125%;
}

#app {
  max-width: 900px;
  margin: 0 auto;
  padding: 0.3rem;
}

.app-header {
  display: flex;
  justify-content: space-between;
align-items: center;
  margin-bottom: 0.3rem;
  border-bottom: var(--border-width) solid var(--border);
  padding-bottom: 0.3rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.app-header > * {
  margin: 0 1rem 1rem 0;
}

.app-header > *:last-child {
  margin-right: 0;
}


.title-wrap h1 {
  font-size: 1.5rem;
  font-weight: 800;
  margin: 0;
}

.app-title-link {
  color: inherit;
  text-decoration: none;
}

.app-title-link:visited {
  color: inherit;
}

.app-title-link:active,
.app-title-link:hover,
.app-title-link:focus {
  text-decoration: underline;
}

/* The PaperGames brand mark: a raised checkerboard tile. Pure flat fills
   and hard-edged shapes only (no gradients, no blur/filter) so it renders
   identically on 16-level-grayscale e-ink browsers, not just on phones -
   see the logo review in project history for why. */
.brand-icon {
  width: 1.15em;
  height: 1.15em;
  vertical-align: -0.2em;
  margin-right: 0.15em;
}

.app-title-author-link {
  color: inherit;
  text-decoration: none;
}

.app-title-author-link:visited {
  color: inherit;
}

.app-title-author-link:hover,
.app-title-author-link:active,
.app-title-author-link:focus {
  text-decoration: underline;
}


.subtitle {
  margin: 0.1rem 0 0;
  font-size: 0.8rem;
  color: var(--muted);
}

.user-panel {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
font-size: 0.8rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.user-panel > * {
  margin: 0 0.25rem 0.25rem 0;
}

.user-panel > *:last-child {
  margin-right: 0;
}


#user-status {
  white-space: nowrap;
  max-width: 100%;
  overflow-x: auto;
  font-weight: 600;
}

.user-panel-buttons {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.user-panel-buttons > * {
  margin: 0 0.4rem 0.4rem 0;
}

.user-panel-buttons > *:last-child {
  margin-right: 0;
}


.user-status-row {
  justify-content: space-between;
}

#settings-panel {
  margin-bottom: 0.4rem;
}

button,
a.primary,
a.secondary {
  font-family: inherit;
  border-radius: var(--radius);
  border: var(--border-width) solid var(--border);
  background: #fff;
  padding: 0.4rem 0.85rem;
  font-size: 0.9rem;
  font-weight: 700;
  line-height: 1.2;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
  color: var(--text);
}

button.primary,
a.primary {
  background: var(--accent);
  color: #fff;
}

button.secondary,
a.secondary {
  background: var(--bg-alt);
}

button.small,
a.small {
  font-size: 0.7rem;
  padding: 0.25rem 0.5rem;
}


button:disabled {
  opacity: 0.5;
  cursor: default;
}

.hidden {
  display: none !important;
}

.mode-section {
  margin: 0.5rem 0;
}

.mode-section h2 {
  font-size: 1rem;
  margin: 0 0 0.35rem;
}

.mode-section-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
flex-wrap: wrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.mode-section-row > * {
  margin: 0 0.5rem 0.5rem 0;
}

.mode-section-row > *:last-child {
  margin-right: 0;
}


.mode-buttons {
  display: flex;
  flex-wrap: wrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.mode-buttons > * {
  margin: 0 0.5rem 0.5rem 0;
}

.mode-buttons > *:last-child {
  margin-right: 0;
}

.mode-buttons .mode-btn {
  border-radius: var(--radius);
}

.mode-buttons .mode-btn.active-mode {
  background: #ffffff;
  color: #000000;
  border-width: 3px;
  font-weight: 800;
}


.card {
  background: #fff;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
  padding: 0.65rem 0.75rem;
  margin-bottom: 0.6rem;
}

/* A quick way to skip past 25 games' worth of cards to the category
   you actually want - three plain anchor jumps, not JS, so they work
   the same with scripting on or off and cost nothing on E-Ink. */
.game-section-nav {
  display: flex;
  flex-wrap: wrap;
margin: 0 0 0.6rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* A quick way to skip past 25 games' worth of cards to the category
   you actually want - three plain anchor jumps > *, not JS > *, so they work
   the same with scripting on or off and cost nothing on E-Ink. */
.game-section-nav > * {
  margin: 0 0.5rem 0.5rem 0;
}

/* A quick way to skip past 25 games' worth of cards to the category
   you actually want - three plain anchor jumps > *:last-child, not JS > *:last-child, so they work
   the same with scripting on or off and cost nothing on E-Ink. */
.game-section-nav > *:last-child {
  margin-right: 0;
}


.game-section-nav a {
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.85rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  color: var(--text);
  font-weight: 700;
  font-size: 0.9rem;
  line-height: 1.2;
  text-decoration: none;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.game-section-nav a > * {
  margin: 0 0.35rem 0.35rem 0;
}

.game-section-nav a > *:last-child {
  margin-right: 0;
}


.field-row {
  margin-bottom: 0.5rem;
  display: flex;
  align-items: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.field-row > * {
  margin: 0 0.5rem 0.5rem 0;
}

.field-row > *:last-child {
  margin-right: 0;
}


.field-row label {
  font-size: 0.9rem;
}

.field-row select {
  flex: 1;
  padding: 0.3rem 0.4rem;
  border-radius: var(--radius);
  border: var(--border-width) solid var(--border);
  background: #fff;
  font-weight: 600;
}

.checkbox-label {
  display: flex;
  align-items: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.checkbox-label > * {
  margin: 0 0.4rem 0.4rem 0;
}

.checkbox-label > *:last-child {
  margin-right: 0;
}


.status-text {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
  min-height: 1.2em;
}

.game-clocks {
  font-size: 0.8rem;
}

/* Brett */

#board-section {
  display: flex;
  flex-direction: column;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Brett */

#board-section > * {
  margin: 0 0.5rem 0.5rem 0;
}

/* Brett */

#board-section > *:last-child {
  margin-right: 0;
}


.board-header {
  display: flex;
  flex-direction: column;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.board-header > * {
  margin: 0 0.25rem 0.25rem 0;
}

.board-header > *:last-child {
  margin-right: 0;
}


.board-header-tools {
  display: flex;
  justify-content: flex-end;
margin-top: 0.15rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.board-header-tools > * {
  margin: 0 0.25rem 0.25rem 0;
}

.board-header-tools > *:last-child {
  margin-right: 0;
}



#game-label {
  font-weight: 600;
  font-size: 0.95rem;
}

#game-meta {
  font-size: 0.8rem;
  color: var(--muted);
}


#game-result {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
}

#game-result:empty {
  display: none;
}


#board-container {
  width: 100%;
  text-align: center;
}

#board {
  display: block;
  width: 100%;
  max-width: 760px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

/* Fallback, falls aspect-ratio nicht unterstützt wird, kümmert sich JS */

.square {
  position: relative;
  border-radius: 0;
  width: 12.5%;
  float: left;
  border: none;
  padding: 0;
  font-size: 1.8rem;
  line-height: 1;
  text-align: center;
  box-sizing: border-box;
}

.square.light {
  background: var(--square-light);
}

.square.dark {
  background: var(--square-dark);
}

/* Inline-SVG-Figuren zentriert im Feld, unabhängig von der tatsächlichen
   Feldgröße (die per JS dynamisch gesetzt wird). */
.square svg.piece-svg {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 82%;
  height: 82%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

/* Bolder outlines than the source artwork's default 1.5 - reads much
   more clearly on E‑Ink, where thin strokes tend to look faint/grey. */
.square svg.piece-svg [stroke-width] {
  stroke-width: 2.2;
}


/* Auswahl-Markierung für ein Feld */

/* Letzter Zug: dünner Rahmen plus dezente Tönung statt der früheren dicken
   Umrandung - liest sich ruhiger, bleibt aber dank des klaren Rahmens auch
   auf E-Ink-Displays mit wenigen Graustufen sicher erkennbar, ohne wie ein
   aktiver Auswahlrahmen (siehe .square.selected) auszusehen. */
.square.last-move {
  box-shadow: inset 0 0 0 2px var(--accent), inset 0 0 0 999px rgba(0, 0, 0, 0.22);
}

.square.selected {
  outline: 3px solid var(--border);
  outline-offset: -3px;
}

/* Züge unter dem Brett in einer Zeile, horizontal scrollend */
.moves-list {
  font-size: 0.7rem;
  line-height: 1.3;
  border-top: 1px solid var(--border);
  padding-top: 0.15rem;
  white-space: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
}

.moves-list.hidden {
  display: none;
}


/* Coordinate overlays inside squares.
   We only draw them on the "outer" ranks/files of the *visible* board:
   - left edge: rank number
   - bottom edge: file letter
   This works for both white and black orientation. */
.square[data-show-rank="1"]::before {
  content: attr(data-rank);
  position: absolute;
  top: 2px;
  left: 2px;
  font-size: 0.7rem;
  opacity: 0.9;
}

.square[data-show-file="1"]::after {
  content: attr(data-file);
  position: absolute;
  bottom: 2px;
  right: 2px;
  font-size: 0.7rem;
  opacity: 0.9;
}


.piece-black {
  color: #000;
}

.piece-white {
  color: #000;
}

/* Mobile tweaks */

@media (max-width: 600px) {
  .app-header {
    flex-direction: column;
    align-items: flex-start;
  }
  .user-panel {
    align-items: flex-start;
  }
}


/* Mode-Header mit About rechts */
.mode-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Mode-Header mit About rechts */
.mode-header > * {
  margin: 0 0.5rem 0.5rem 0;
}

/* Mode-Header mit About rechts */
.mode-header > *:last-child {
  margin-right: 0;
}


.mode-about-inline {
  font-size: 0.6rem;
  text-align: right;
  flex: 1;
}

.mode-about-inline a {
  text-decoration: underline;
}


.app-title-row {
  display: flex;
  align-items: baseline;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.app-title-row > * {
  margin: 0 0.5rem 0.5rem 0;
}

.app-title-row > *:last-child {
  margin-right: 0;
}


.app-title-author {
  font-size: 0.7rem;
  color: var(--muted);
}


.inline-options {
  display: inline-flex;
margin-left: 0.5rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.inline-options > * {
  margin: 0 0.25rem 0.25rem 0;
}

.inline-options > *:last-child {
  margin-right: 0;
}


/* Compact selects for inline controls (offline level + online time) */
#offline-ai-controls #ai-level-inline,
#online-controls .online-inline-row > select#time-profile {
  flex: 0 0 auto;
  width: auto;
}
.ai-color-btn.active {
  background: var(--accent);
  color: #fff;
}

#online-controls .online-inline-row {
  flex-wrap: wrap;
  width: 100%;
}

#online-controls .online-inline-row > select#time-profile {
  flex: 0 0 auto;
  width: auto;
}

#online-controls .online-inline-row .online-action-right {
  margin-left: auto;
  display: inline-flex;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
#online-controls .online-inline-row .online-action-right > * {
  margin: 0 0.5rem 0.5rem 0;
}

#online-controls .online-inline-row .online-action-right > *:last-child {
  margin-right: 0;
}


#online-controls .online-tabs-row {
  justify-content: space-between;
  width: 100%;
}

/* Flexbox `gap` fallback: margin-based spacing instead (see the .mode-buttons
   rule above) - this class is combined with .mode-buttons on the same
   element (see chess.html), it just overrides the gap value. */
.online-sub-tabs > * {
  margin: 0 0.35rem 0.35rem 0;
}

.online-sub-tabs > *:last-child {
  margin-right: 0;
}

.board-actions {
  display: flex;
justify-content: flex-start;
  flex-wrap: wrap;
  margin-top: 0.5rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.board-actions > * {
  margin: 0 0.5rem 0.5rem 0;
}

.board-actions > *:last-child {
  margin-right: 0;
}


.board-actions .small {
  padding: 0.2rem 0.6rem;
  font-size: 0.85rem;
}


/* color-choice labels for AI color selection (pawns instead of text) */
.inline-options .color-choice {
  display: inline-flex;
  align-items: center;
font-size: 1.2rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* color-choice labels for AI color selection (pawns instead of text) */
.inline-options .color-choice > * {
  margin: 0 4px 4px 0;
}

/* color-choice labels for AI color selection (pawns instead of text) */
.inline-options .color-choice > *:last-child {
  margin-right: 0;
}


/* Same cburnett pawn SVGs as on the board itself, sized as small inline
   icons here instead of the board's absolutely-positioned full-square use. */
.color-choice-icon {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
}

.color-choice-icon svg {
  width: 100%;
  height: 100%;
}


#undo-btn {
  padding-left: 0.2rem;
  padding-right: 0.2rem;
}


.board-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10rem;
  padding: 1.5rem 0;
  color: var(--muted);
}





.board-placeholder-icon {
  font-size: 1em;
  line-height: 1;
}

/* Raw inline SVG (e.g. chess's knight) has no intrinsic width/height of its
   own, only a viewBox, so size it explicitly to match the other games'
   1em icon classes (.go-stone-icon, .checkers-icon-disc). */
.board-placeholder-icon-svg svg {
  display: block;
  width: 1em;
  height: 1em;
}

.board-placeholder-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  margin-left: 0.4em;
}

.board-placeholder-title {
  font-size: 0.35em;
  line-height: 1;
}

.board-placeholder-subtitle {
  font-size: 0.18em;
  line-height: 1.1;
  opacity: 0.8;
}


.score-line {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  font-size: 0.7rem;
  line-height: 1.3;
  border-top: 1px solid var(--border);
  padding-top: 0.15rem;
  color: var(--muted);
  column-gap: 0.5rem;
}

.score-line.hidden {
  display: none;
}

.score-line-captures {
  justify-self: center;
  white-space: nowrap;
}

.score-line-diff {
  justify-self: end;
  white-space: nowrap;
}


/* About page */
.about-donate {
  margin-top: 1rem;
  font-size: 0.9rem;
}

.btn-donate {
  display: inline-block;
  padding: 0.4rem 0.75rem;
  border-radius: 4px;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  font-size: 0.9rem;
  border: 1px solid var(--border);
  word-break: normal;
}

.btn-donate:visited {
  color: #fff;
}

.btn-donate:hover,
.btn-donate:active,
.btn-donate:focus {
  text-decoration: underline;
}

/* Keep long links in other contexts from breaking layouts */
.about-donate a {
  word-break: break-all;
}

.about-qr {
  margin-top: 0.75rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.about-qr > * {
  margin: 0 0.25rem 0.25rem 0;
}

.about-qr > *:last-child {
  margin-right: 0;
}


.about-qr img {
  max-width: 180px;
  height: auto;
  border: 1px solid var(--border);
  padding: 0.25rem;
  border-radius: 4px;
}

.about-credits {
  margin-top: 1.25rem;
  font-size: 0.75rem;
  color: var(--muted);
}

.about-back {
  margin-top: 1.25rem;
  font-size: 0.85rem;
}

.stats-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1rem;
}

.stats-table th,
.stats-table td {
  border: var(--border-width) solid var(--border);
  padding: 0.4rem 0.6rem;
  text-align: left;
}

.stats-table th {
  background: var(--bg-alt);
}

.stats-table td:not(:first-child),
.stats-table th:not(:first-child) {
  text-align: center;
}

.stats-empty {
  margin-top: 1rem;
  color: var(--muted);
}

.stats-actions {
  margin-top: 1.25rem;
}

/* Site-wide nav bar and language switch (home/rules/guide/about pages) */

.lang-switch {
  display: flex;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Site-wide nav bar and language switch (home/rules/guide/about pages) */

.lang-switch > * {
  margin: 0 0.3rem 0.3rem 0;
}

/* Site-wide nav bar and language switch (home/rules/guide/about pages) */

.lang-switch > *:last-child {
  margin-right: 0;
}


/* A native <select>, built dynamically by i18n.js from however many
   languages exist - deliberately not a row of buttons, which stopped
   scaling once the app grew past a couple of languages. A native
   control also means the OS/browser draws the actual dropdown list,
   which behaves correctly and predictably on E-Ink browsers without
   any custom popover logic of our own. */
.lang-select,
.game-select {
  font-family: inherit;
  font-weight: 700;
  font-size: 0.7rem;
  padding: 0.25rem 1.5rem 0.25rem 0.5rem;
  border-radius: var(--radius);
  border: var(--border-width) solid var(--border);
  background: var(--bg-alt) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23111' d='M1 1l5 5 5-5'/%3E%3C/svg%3E") no-repeat right 0.5rem center;
  background-size: 0.6rem;
  color: var(--text);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
}

/* Game switcher: same treatment as the language picker, built at
   runtime by game-switcher.js on every game/rules/history page (not on
   the home page, which has no "current game" to switch away from). */
.game-switch {
  display: flex;
  align-items: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Game switcher: same treatment as the language picker > *, built at
   runtime by game-switcher.js on every game/rules/history page (not on
   the home page > *, which has no "current game" to switch away from). */
.game-switch > * {
  margin: 0 0.3rem 0.3rem 0;
}

/* Game switcher: same treatment as the language picker > *:last-child, built at
   runtime by game-switcher.js on every game/rules/history page (not on
   the home page > *:last-child, which has no "current game" to switch away from). */
.game-switch > *:last-child {
  margin-right: 0;
}


.game-switch-icon {
  font-size: 0.8rem;
  line-height: 1;
}

.game-select {
  max-width: 11rem;
}

.site-nav {
  display: flex;
  flex-wrap: wrap;
margin: 0.5rem 0;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.site-nav > * {
  margin: 0 0.4rem 0.4rem 0;
}

.site-nav > *:last-child {
  margin-right: 0;
}


.site-nav-link {
  display: inline-block;
  padding: 0.4rem 0.85rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  font-size: 0.9rem;
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  color: var(--text);
  background: #fff;
}

.site-nav-link.active-mode {
  background: var(--accent);
  color: #fff;
}

/* History library page: a plain, image-free reading list of every game's
   origin story, roughly in the order each game first appeared. */

.history-intro-note {
  font-size: 0.8rem;
  color: var(--muted);
  font-style: italic;
  margin: 0 0 1.25rem;
}

.history-timeline {
  margin: 0;
  padding: 0;
  list-style: none;
}

.history-timeline-item {
  padding: 1rem 0;
  border-top: var(--border-width) solid var(--border);
}

.history-timeline-item:first-child {
  border-top: none;
  padding-top: 0;
}

.history-timeline-era {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.2rem;
}

.history-timeline-title {
  font-size: 1.05rem;
  margin: 0 0 0.35rem;
}

.history-timeline-title a {
  color: var(--text);
  text-decoration: none;
}

.history-timeline-title a:hover {
  text-decoration: underline;
}

.history-timeline-desc {
  margin: 0 0 0.4rem;
  color: var(--text);
}

.history-timeline-link {
  font-size: 0.85rem;
  font-weight: 700;
}

.site-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
margin: 2rem 0 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.site-footer > * {
  margin: 0 1rem 0.3rem 0;
}

.site-footer > *:last-child {
  margin-right: 0;
}


.site-footer a {
  font-size: 0.8rem;
  color: var(--muted);
  text-decoration: none;
}

.site-footer a:hover,
.site-footer a:focus {
  text-decoration: underline;
}

/* Home page */

.home-hero {
  text-align: center;
  padding: 1.5rem 1rem;
}

.home-hero-icon {
  width: 5rem;
  height: 5rem;
  margin-bottom: 0.25rem;
}

.home-hero h1 {
  margin: 0.25rem 0;
  font-size: 2rem;
  font-weight: 800;
}

.home-tagline {
  color: var(--muted);
  font-weight: 700;
  margin: 0 0 0.75rem;
}

.home-languages {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 600;
  margin: 0 0 0.5rem;
}

.home-hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.home-hero-actions > * {
  margin: 0.75rem 0.5rem 0 0.5rem;
}

.home-play-button {
  display: inline-block;
  text-decoration: none;
  font-size: 1.1rem;
  padding: 0.6rem 1.4rem;
}

.nav-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.6rem;
  margin: 0.6rem 0;
}

.nav-card {
  display: block;
  text-decoration: none;
  color: var(--text);
}

.nav-card-title {
  font-weight: 800;
  font-size: 1.05rem;
  margin-bottom: 0.25rem;
}

.nav-card-desc {
  font-size: 0.85rem;
  color: var(--muted);
}


/* Rules / Guide content pages */

.content-page h2 {
  font-size: 1.1rem;
  margin: 1rem 0 0.4rem;
}

.content-page h2:first-child {
  margin-top: 0;
}

.rule-list {
  margin: 0 0 0.75rem;
}

.rule-list dt {
  font-weight: 800;
  margin-top: 0.5rem;
}

.rule-list dd {
  margin: 0.15rem 0 0;
  color: var(--text);
}

/* Go board: intersections rather than squares, built as absolutely
   positioned lines/points inside a padded square (see go-app.js) rather
   than a CSS gradient grid, since that's more predictable across the
   older browsers found on E-Ink devices. */

.go-board {
  position: relative;
  width: 100%;
  max-width: 640px;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
  padding: 6%;
}

.go-grid {
  position: relative;
  width: 100%;
  height: 100%;
}

.go-line {
  position: absolute;
  background: var(--border);
}

.go-line-h {
  left: 0;
  right: 0;
  height: 2px;
  transform: translateY(-1px);
}

.go-line-v {
  top: 0;
  bottom: 0;
  width: 2px;
  transform: translateX(-1px);
}

.go-hoshi {
  position: absolute;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--border);
  pointer-events: none;
}

.go-point {
  position: absolute;
  transform: translate(-50%, -50%);
  background: transparent;
  border: none;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
}

.go-stone {
  position: absolute;
  top: 4%;
  right: 4%;
  bottom: 4%;
  left: 4%;
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

/* A layered look (flat fill plus an inset rim) instead of a single flat
   disc - the same technique as the Ur pieces (.ur-piece::after): a ring
   set inside the stone reads as a lacquered highlight/shadow line without
   an actual gradient, so it stays safe on a 16-level grayscale E-Ink
   panel. Kept on ::before rather than ::after so ::after stays free for
   the last-move halo further down. */
.go-stone::before,
.go-stone-icon::before {
  content: "";
  position: absolute;
  top: 22%;
  left: 22%;
  width: 56%;
  height: 56%;
  border-radius: 50%;
  box-sizing: border-box;
}

.go-stone-black {
  background: #111;
  border: 1px solid #000;
}

.go-stone-black::before {
  border: 2px solid #555;
}

.go-stone-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

.go-stone-white::before {
  border: 2px solid #bbb;
}

/* Standalone stone icon for use outside the board (color-choice picker,
   home page card, placeholder) - same .go-stone-black/.go-stone-white
   coloring, including the inset rim above, so it reads as the same stone
   rather than a flatter stand-in, but sized/positioned inline rather than
   absolutely within a .go-point. Avoids relying on Unicode circle glyphs
   (●/○), whose look varies by device font. */
.go-stone-icon {
  position: relative;
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.go-point.last-move .go-stone::after {
  content: "";
  position: absolute;
  top: -22%;
  right: -22%;
  bottom: -22%;
  left: -22%;
  border: 2px solid var(--accent);
  border-radius: 50%;
}

/* Home page: game selection, Chess and Go shown with equal weight */

.game-select-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.6rem;
  margin: 0.6rem 0;
}

/* Empty-state message for a section whose nav link always points
   somewhere real (e.g. My Favorites) - plain page text, not a muted
   gray, since faint tones are hard to read on E-Ink. */
.section-empty-state {
  color: var(--text);
  padding: 0.75rem 0;
  margin: 0;
}

.game-select-item {
  position: relative;
  background: #fff;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
}

/* Favorite star toggle: filled vs. outline glyph (★/☆) rather
   than color, so it stays legible in grayscale on e-ink. Positioned over
   the card's own top-right corner rather than taking up layout space. */
.favorite-toggle {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  width: 1.8rem;
  height: 1.8rem;
  padding: 0;
  border: none;
  background: none;
  font-size: 1.2rem;
  line-height: 1.8rem;
  text-align: center;
  cursor: pointer;
  color: var(--text);
}

.favorite-toggle.is-favorite {
  color: var(--text);
}

/* Opt-in "suggest difficulty" toggle, on its own row below every AI
   opponent's level select rather than squeezed into that row (which is
   already tight with the New game button, color choice, and the level
   select's often-long option text, and .field-row doesn't wrap) - and
   the small note shown when it has just nudged the selection.
   See adaptive-difficulty.js. */
.adaptive-difficulty-row {
  flex-wrap: wrap;
}

.adaptive-difficulty-toggle {
  display: inline-flex;
  align-items: center;
  font-size: 0.8rem;
  white-space: nowrap;
}

.adaptive-difficulty-toggle input {
  margin-right: 0.3rem;
}

.adaptive-difficulty-note {
  font-size: 0.75rem;
  color: var(--muted);
}

/* Stats page: achievement badges, derived from GameStats rather than
   their own storage - locked ones stay visible (not hidden) so the
   player can see what's still ahead, just faded via opacity like every
   other "inactive" state in this app (:disabled buttons, etc.), since a
   color-only distinction wouldn't read on grayscale e-ink. */
.achievements-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 0.6rem;
  margin: 0.6rem 0;
}

.achievement-badge {
  display: flex;
  align-items: flex-start;
  background: #fff;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  padding: 0.6rem 0.75rem;
}

.achievement-badge.locked {
  opacity: 0.5;
}

.achievement-badge-icon {
  font-size: 1.5rem;
  line-height: 1;
  margin-right: 0.6rem;
  flex-shrink: 0;
}

.achievement-badge-text {
  display: flex;
  flex-direction: column;
}

.achievement-badge-name {
  font-weight: 600;
}

.achievement-badge-desc {
  font-size: 0.8rem;
  color: var(--muted);
}

.games-group-title {
  margin: 1rem 0 0.4rem;
}

.games-group-title:first-child {
  margin-top: 0;
}

.games-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin: 0.6rem 0;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.games-toolbar > * {
  margin: 0 0.5rem 0.5rem 0;
}

.games-toolbar > *:last-child {
  margin-right: 0;
}

.games-search-input {
  flex: 1 1 220px;
  padding: 0.4rem 0.6rem;
  font-size: 0.95rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  color: var(--text);
  box-sizing: border-box;
}

.games-sort-select {
  padding: 0.4rem 0.6rem;
  font-size: 0.95rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  color: var(--text);
}

.games-no-results {
  color: var(--muted);
  font-style: italic;
}

.game-select-title {
  font-weight: 800;
  font-size: 1.15rem;
  margin-bottom: 0.3rem;
}

.game-select-icon-svg {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: middle;
}

.game-select-icon-svg svg {
  display: block;
  width: 100%;
  height: 100%;
}

.game-select-desc {
  font-size: 0.85rem;
  color: var(--muted);
  margin: 0 0 0.6rem;
}

.game-select-actions {
  display: flex;
  flex-wrap: wrap;
  margin-top: auto;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.game-select-actions > * {
  margin: 0 0.5rem 0.5rem 0;
}

.game-select-actions > *:last-child {
  margin-right: 0;
}


/* End-of-game result popup, shared by every game via result-modal.js.
   Longhand top/right/bottom/left rather than the `inset` shorthand - a
   real device in this project's own test history silently ignores that
   shorthand entirely. */
.result-modal-overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  z-index: 1000;
}

.result-modal-overlay.hidden {
  display: none !important;
}

.result-modal {
  background: #fff;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  max-width: 320px;
  width: 100%;
  text-align: center;
}

/* Hidden by default; only .result-modal-win (a real win, not a loss or
   a draw - see classifyOutcome() in result-modal.js) shows it. A plain
   Unicode star rather than a drawn shape, matching how this project
   already uses a handful of Unicode symbols elsewhere (e.g. the resign
   button's flag) instead of an icon font. */
.result-modal-ornament {
  display: none;
  font-size: 2rem;
  line-height: 1;
  margin-bottom: 0.5rem;
}

.result-modal-win .result-modal-ornament {
  display: block;
}

/* A real win also gets a slightly heavier frame - still just a border
   weight/style change, no color, so it reads the same on a monochrome
   E-Ink panel as anywhere else. */
.result-modal-win {
  border-width: calc(var(--border-width) * 2);
}

.result-modal-title {
  font-size: 1.5rem;
  font-weight: 800;
  margin-bottom: 0.5rem;
}

.result-modal-message {
  font-size: 1rem;
  color: var(--muted);
  margin-bottom: 1.25rem;
}

.result-modal .result-modal-close {
  min-width: 6rem;
}

/* Checkers board: reuses .square/.light/.dark/.selected/.last-move from
   the chess board (same float-grid technique, already proven on a real
   E-Ink device) rather than inventing a new layout mechanism. */

#checkers-board {
  display: block;
  width: 100%;
  max-width: 760px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.checkers-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 78%;
  height: 78%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

/* Same layered-rim technique as the Go stones (.go-stone::before) and the
   Ur pieces (.ur-piece::after): a ring inset from the edge rather than a
   single flat disc, flat shapes only so it stays grayscale-safe. Kept on
   ::before, not ::after, so ::after/the nested .checkers-king-mark below
   stay free for the king badge. */
.checkers-piece::before,
.checkers-icon-disc::before {
  content: "";
  position: absolute;
  top: 22%;
  left: 22%;
  width: 56%;
  height: 56%;
  border-radius: 50%;
  box-sizing: border-box;
}

.checkers-piece-black {
  background: #111;
  border: 2px solid #000;
}

.checkers-piece-black::before,
.checkers-icon-disc-black::before {
  border: 2px solid #555;
}

.checkers-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

.checkers-piece-white::before,
.checkers-icon-disc-white::before {
  border: 2px solid #bbb;
}

/* King mark: a small layered rosette (a diamond behind a ring, the same
   idea as .ur-square-rosette) instead of a single plain ring, so a
   crowned piece reads as a deliberate badge rather than a dot dropped on
   top. This needs its own nested element (added/removed by checkers-
   app.js's updateCheckersBoard alongside the .checkers-piece-king class)
   rather than a second pseudo-element on .checkers-piece itself, since
   ::before is already the depth rim above and an element only has the
   two pseudo-elements to draw with. */
.checkers-king-mark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44%;
  height: 44%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.checkers-king-mark::before {
  content: "";
  position: absolute;
  top: 32%;
  left: 32%;
  width: 36%;
  height: 36%;
  transform: rotate(45deg);
  box-sizing: border-box;
}

.checkers-king-mark::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  box-sizing: border-box;
}

/* Contrast the badge against its own piece, same pairing the single-ring
   king mark used before: white on black, dark on white. */
.checkers-piece-black .checkers-king-mark::before {
  background: #fff;
}

.checkers-piece-black .checkers-king-mark::after {
  border: 2px solid #fff;
}

.checkers-piece-white .checkers-king-mark::before {
  background: var(--border);
}

.checkers-piece-white .checkers-king-mark::after {
  border: 2px solid var(--border);
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.checkers-icon-disc {
  position: relative;
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.checkers-icon-disc-black {
  background: #111;
  border: 2px solid #000;
}

.checkers-icon-disc-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Royal Game of Ur board: a 3x8 float-grid (same technique as the chess
   and checkers boards) with the four corner squares at columns 4-5 of
   rows 0/2 left as invisible gaps, forming the traditional H-shape - a
   private 4-square lane per player top/bottom, connected by a shared
   8-square lane through the middle row.
   The board is clipped to that same H-shape (a 4-column block, a
   2-column bridge through the middle row only, then a 2-column tail
   block) instead of sitting inside a plain rectangle, so the outline
   reads as the real board's silhouette rather than a box with two blank
   corners top and bottom. Getting a border to trace that outline -
   including its two inward-facing notch corners, where a plain CSS
   `border` would just vanish since border stays rectangular under a
   clip-path - takes two nested elements rather than one: #ur-board is
   the frame (filled with the border color, clipped to the full
   H-shape) and #ur-grid, the actual clickable grid inside it, is
   clipped to the same H-shape inset by the border width on every edge.
   Since #ur-grid's squares otherwise tile it edge-to-edge, that inset
   clip trims a uniform border-width sliver off their outer-facing
   edges everywhere - all along the perimeter and both notches alike -
   letting the frame color show through as a continuous ring. */

#ur-board {
  display: block;
  width: 100%;
  max-width: 760px;
  background: var(--border);
  clip-path: polygon(
    0% 0%, 50% 0%, 50% calc(100%/3), 75% calc(100%/3), 75% 0%, 100% 0%,
    100% 100%, 75% 100%, 75% calc(200%/3), 50% calc(200%/3), 50% 100%, 0% 100%
  );
  box-sizing: border-box;
  margin: 0 auto;
}

#ur-grid {
  display: block;
  overflow: hidden;
  clip-path: polygon(
    calc(0% + 3px) calc(0% + 3px),
    calc(50% - 3px) calc(0% + 3px),
    calc(50% - 3px) calc(100%/3 + 3px),
    calc(75% + 3px) calc(100%/3 + 3px),
    calc(75% + 3px) calc(0% + 3px),
    calc(100% - 3px) calc(0% + 3px),
    calc(100% - 3px) calc(100% - 3px),
    calc(75% + 3px) calc(100% - 3px),
    calc(75% + 3px) calc(200%/3 - 3px),
    calc(50% - 3px) calc(200%/3 - 3px),
    calc(50% - 3px) calc(100% - 3px),
    calc(0% + 3px) calc(100% - 3px)
  );
}

.ur-square {
  background: var(--square-light);
  border: 1px solid var(--border);
}

/* The shared lane (middle row) is where captures can happen - a warm
   tint sets it visually apart from each player's own safe private
   squares, so the "contested" zone reads at a glance. */
.ur-square-shared {
  background: #efe6d3;
}

.ur-square-gap {
  background: transparent;
  border: none;
  pointer-events: none;
}

/* A small layered rosette mark (a diamond behind a ring) rather than a
   plain circle, closer to the flower motif on the real board, built
   from flat shapes only - no gradients or shadows to stay e-ink safe. */
.ur-square-rosette::before {
  content: "";
  position: absolute;
  top: 38%;
  left: 38%;
  width: 24%;
  height: 24%;
  background: var(--muted);
  transform: rotate(45deg);
  box-sizing: border-box;
  pointer-events: none;
}

.ur-square-rosette::after {
  content: "";
  position: absolute;
  top: 25%;
  left: 25%;
  width: 50%;
  height: 50%;
  border: 2px solid var(--muted);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.ur-square-movable {
  background: var(--square-dark);
}

.ur-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 74%;
  height: 74%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.ur-piece::after {
  content: "";
  position: absolute;
  top: 22%;
  left: 22%;
  width: 56%;
  height: 56%;
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.ur-piece-black {
  background: #111;
  border: 2px solid #000;
}

.ur-piece-black::after {
  border: 2px solid #555;
}

.ur-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

.ur-piece-white::after {
  border: 2px solid #bbb;
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.ur-piece-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.ur-piece-icon-black {
  background: #111;
  border: 2px solid #000;
}

.ur-piece-icon-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Off-board "start" and finished "home" pieces have no board square of
   their own, so they're shown as simple labelled counters above/below
   the board instead. */
.ur-tray {
  display: flex;
max-width: 760px;
  margin: 0 auto 0.4rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Off-board "start" and finished "home" pieces have no board square of
   their own > *, so they're shown as simple labelled counters above/below
   the board instead. */
.ur-tray > * {
  margin: 0 0.6rem 0.6rem 0;
}

/* Off-board "start" and finished "home" pieces have no board square of
   their own > *:last-child, so they're shown as simple labelled counters above/below
   the board instead. */
.ur-tray > *:last-child {
  margin-right: 0;
}


.ur-tray-slot {
  flex: 1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.3rem 0.6rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  font-size: 0.85rem;
  box-sizing: border-box;
}

button.ur-tray-slot {
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.ur-tray-slot-static {
  cursor: default;
}

.ur-tray-slot-movable {
  background: var(--square-dark);
}

.ur-tray-slot-count {
  font-weight: 700;
}

.ur-dice-area {
  display: flex;
  align-items: center;
  justify-content: center;
margin-top: 0.5rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.ur-dice-area > * {
  margin: 0 0.6rem 0.6rem 0;
}

.ur-dice-area > *:last-child {
  margin-right: 0;
}


.ur-dice-display {
  display: flex;
  align-items: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.ur-dice-display > * {
  margin: 0 0.3rem 0.3rem 0;
}

.ur-dice-display > *:last-child {
  margin-right: 0;
}


.ur-die {
  display: inline-block;
  width: 0.9rem;
  height: 0.9rem;
  border: var(--border-width) solid var(--border);
  box-sizing: border-box;
}

.ur-die-marked {
  background: var(--accent);
}

.ur-die-blank {
  background: #fff;
}

.ur-die-total {
  font-weight: 700;
  margin-left: 0.3rem;
}

/* Nine Men's Morris board: a point-and-line grid (same technique as the
   Go board - a JS-enforced square container, since `aspect-ratio` alone
   is silently ignored on some E-Ink browsers) rather than a filled
   square grid, since the board is three nested squares of points
   connected by lines, not a checkerboard. */

.morris-board {
  position: relative;
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
  padding: 7%;
}

/* Points/lines are positioned inside this, not directly inside
   .morris-board - see the comment in morris-app.js's buildMorrisBoardDOM
   for why the padding above only acts as a margin for the outer ring of
   points' own radius when they're one level further in like this. */
.morris-grid {
  position: relative;
  width: 100%;
  height: 100%;
}

.morris-line {
  position: absolute;
  background: var(--border);
}

.morris-line-h {
  height: 2px;
  margin-top: -1px;
}

.morris-line-v {
  width: 2px;
  margin-left: -1px;
}

.morris-point {
  position: absolute;
  width: 11%;
  height: 11%;
  transform: translate(-50%, -50%);
  background: var(--square-light);
  border: 2px solid var(--border);
  border-radius: 50%;
  padding: 0;
  box-sizing: border-box;
  cursor: pointer;
}

.morris-point-movable {
  background: var(--square-dark);
}

.morris-point-selected {
  border-color: var(--accent);
  border-width: 3px;
}

.morris-point-removable {
  background: #f3d4d4;
  border-color: #a33;
}

.morris-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 82%;
  height: 82%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.morris-piece-black {
  background: #111;
  border: 2px solid #000;
}

.morris-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.morris-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.morris-icon-disc-black {
  background: #111;
  border: 2px solid #000;
}

.morris-icon-disc-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Fanorona board: 45 points in a 5x9 grid (wider than tall, unlike most
   boards here), so it uses the same percentage-based absolute-positioning
   technique as Quoridor/Hex/Go rather than a plain float-grid, with a
   JS-enforced non-square aspect ratio (see ensureFanoronaBoardAspect in
   fanorona-app.js). Connections are drawn as one non-interactive SVG
   layer UNDER the point buttons - simpler and more reliable across an
   old E-Ink WebView than rotated divs, since roughly half the board's
   lines are diagonal (see fanorona-core.js for the "strong"/"weak"
   point pattern that decides which). Pieces are told apart structurally,
   not by color alone: Black is a filled disc, White an outlined disc,
   the same convention used everywhere else in this app. */

.fanorona-board {
  position: relative;
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
}

.fanorona-lines {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}

.fanorona-lines line {
  stroke: var(--border);
  stroke-width: 2;
}

.fanorona-point {
  position: absolute;
  transform: translate(-50%, -50%);
  background: var(--square-light);
  border: 2px solid var(--border);
  border-radius: 50%;
  padding: 0;
  box-sizing: border-box;
  cursor: pointer;
  z-index: 2;
}

.fanorona-point-movable {
  background: var(--square-dark);
}

.fanorona-point-selected {
  border-color: var(--accent);
  border-width: 3px;
}

.fanorona-point.last-move {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.fanorona-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 76%;
  height: 76%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.fanorona-piece-black {
  background: #111;
  border: 2px solid #000;
}

.fanorona-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.fanorona-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.fanorona-icon-disc-black {
  background: #111;
  border: 2px solid #000;
}

.fanorona-icon-disc-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* The approach-vs-withdrawal choice and the "continue or stop" capture
   chain prompt: plain stacked buttons, margin-based spacing rather than
   flexbox `gap` (unsupported on the Tolino's Chrome 61 WebView). */
.fanorona-capture-choice,
.fanorona-chain-controls {
  margin-top: 0.6rem;
}

.fanorona-capture-choice button,
.fanorona-chain-controls button {
  margin: 0 0.5rem 0.5rem 0;
}

/* Worked-example diagrams on the rules page: two small SVG figures side
   by side on wide screens, stacked on narrow ones. Flexbox `gap`
   fallback (margin-based spacing) - same reason as above. */
.fanorona-example-row {
  display: flex;
  flex-wrap: wrap;
  margin: 0 0 1rem -1.5rem;
}

.fanorona-example-figure {
  margin: 0 0 1rem 1.5rem;
  max-width: 260px;
}

.fanorona-example-figure svg {
  display: block;
  max-width: 100%;
  height: auto;
}

.fanorona-example-figure figcaption {
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 0.4rem;
}

/* Backgammon board: a 13-column x 2-row CSS grid (12 point lanes per
   row plus a middle bar column spanning both rows), every cell placed
   explicitly by grid-column/grid-row rather than relying on auto-flow.
   Height is JS-enforced (see ensureBackgammonBoardSquare) rather than
   via `aspect-ratio`, which some E-Ink browsers silently ignore. */

.bg-board {
  position: relative;
  display: grid;
  grid-template-columns: repeat(6, 1fr) 0.7fr repeat(6, 1fr);
  grid-template-rows: 1fr 1fr;
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  gap: 1px;
  padding: 1px;
}

.bg-bar {
  background: #d9cba8;
  border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
}

.bg-point {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: #fff;
  border: none;
  padding: 2px 0;
  box-sizing: border-box;
  cursor: pointer;
  overflow: hidden;
}

.bg-point-top {
  justify-content: flex-start;
}

.bg-point-bottom {
  justify-content: flex-end;
}

.bg-point-alt {
  background: #efe6d3;
}

.bg-point-movable {
  background: var(--square-dark);
}

.bg-point-selected {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
}

.bg-point-label {
  font-size: 0.7rem;
  color: var(--muted);
  line-height: 1;
}

.bg-point-top .bg-point-label {
  order: -1;
}

.bg-point-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.bg-point-stack > * {
  margin: 0 2px 2px 0;
}

.bg-point-stack > *:last-child {
  margin-right: 0;
}


.bg-point-top .bg-point-stack {
  order: 1;
}

.bg-checker {
  width: 1.5em;
  height: 1.5em;
  border-radius: 50%;
  box-sizing: border-box;
  flex-shrink: 0;
}

.bg-checker-black {
  background: #111;
  border: 2px solid #000;
}

.bg-checker-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

.bg-checker-overflow {
  font-size: 0.6rem;
  font-weight: 700;
}

.bg-tray {
  display: flex;
max-width: 760px;
  margin: 0 auto 0.4rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.bg-tray > * {
  margin: 0 0.6rem 0.6rem 0;
}

.bg-tray > *:last-child {
  margin-right: 0;
}


.bg-tray-slot {
  flex: 1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.3rem 0.6rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  font-size: 0.85rem;
  box-sizing: border-box;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.bg-tray-slot-static {
  cursor: default;
}

.bg-tray-movable {
  background: var(--square-dark);
}

.bg-tray-slot-count {
  font-weight: 700;
}

.bg-dice-display {
  display: flex;
  align-items: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.bg-dice-display > * {
  margin: 0 0.4rem 0.4rem 0;
}

.bg-dice-display > *:last-child {
  margin-right: 0;
}


.bg-die {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  font-weight: 700;
  background: #fff;
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.bg-icon-checker {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.bg-icon-checker-black {
  background: #111;
  border: 2px solid #000;
}

.bg-icon-checker-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Xiangqi board: a point-and-line grid, same technique as the Go board
   (a JS-enforced square-per-cell container, since `aspect-ratio` alone
   is silently ignored on some E-Ink browsers). */

.xq-board {
  position: relative;
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  background: #f3ead9;
  border: 3px solid var(--border);
  box-sizing: border-box;
  padding: 6%;
}

.xq-grid {
  position: relative;
  width: 100%;
  height: 100%;
}

.xq-line {
  position: absolute;
  background: var(--border);
}

.xq-line-h {
  left: 0;
  right: 0;
  height: 2px;
  transform: translateY(-1px);
}

.xq-line-v {
  top: 0;
  bottom: 0;
  width: 2px;
  transform: translateX(-1px);
}

.xq-river {
  position: absolute;
  left: 0;
  right: 0;
  background: #dce8ec;
}

.xq-palace {
  position: absolute;
  background: #eadfc4;
}

.xq-point {
  position: absolute;
  transform: translate(-50%, -50%);
  background: transparent;
  border: none;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
  width: 11%;
  height: 11%;
}

.xq-point-movable::before {
  content: "";
  position: absolute;
  top: 30%;
  left: 30%;
  width: 40%;
  height: 40%;
  border-radius: 50%;
  background: var(--square-dark);
}

.xq-point-last-move::before {
  content: "";
  position: absolute;
  top: 10%;
  left: 10%;
  width: 80%;
  height: 80%;
  border: 2px dashed var(--muted);
  border-radius: 50%;
  box-sizing: border-box;
}

.xq-point-selected .xq-piece.xq-piece-filled {
  outline: 3px solid var(--accent);
  outline-offset: 1px;
}

.xq-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 88%;
  height: 88%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1;
}

.xq-piece-filled {
  background: #f3ead9;
  border: 2px solid var(--border);
}

/* Red vs. Black is a color distinction, which doesn't exist at all on
   a monochrome E-Ink display - both used to render as similarly dark
   ink on the same light disc. Black pieces get their own solid dark
   disc instead (the same filled-vs-outlined trick already used for
   every other two-color game here), so which piece is whose reads
   from the disc itself, not from a hue. Classic-character text color
   and the symbol-style SVGs' own stroke color (in xiangqi-app.js) are
   flipped to match: dark ink on Red's light disc, light ink on
   Black's now-dark one. */
.xq-piece-black.xq-piece-filled {
  background: #111;
  border: 2px solid #000;
}

.xq-piece-red {
  color: #111;
}

.xq-piece-black {
  color: #fdfdfd;
}

.xq-piece svg {
  width: 68%;
  height: 68%;
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.xq-icon-disc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.3em;
  height: 1.3em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
  background: #f3ead9;
  border: 2px solid var(--border);
  font-size: 0.7em;
  font-weight: 700;
  line-height: 1;
}

.xq-icon-disc-red {
  color: #111;
}

/* Same dark-filled-disc treatment as the board's own pieces - shown
   side by side with the red icon in the color-choice picker, so it
   needs the same monochrome-safe distinction, not just a text-color
   change. */
.xq-icon-disc-black {
  background: #111;
  border-color: #000;
  color: #fdfdfd;
}

/* Mancala board: a store well at each end of a 6x2 grid of pits,
   matching a physical Kalaha board's layout. The top row shows Player
   2's pits right-to-left and the bottom row shows Player 1's pits
   left-to-right, so sowing always reads as one continuous loop around
   the board rather than jumping between rows in raw array order. */

.mancala-board {
  display: flex;
  align-items: stretch;
max-width: 760px;
  margin: 0 auto;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Mancala board: a store well at each end of a 6x2 grid of pits > *, matching a physical Kalaha board's layout. The top row shows Player
   2's pits right-to-left and the bottom row shows Player 1's pits
   left-to-right > *, so sowing always reads as one continuous loop around
   the board rather than jumping between rows in raw array order. */

.mancala-board > * {
  margin: 0 0.4rem 0.4rem 0;
}

/* Mancala board: a store well at each end of a 6x2 grid of pits > *:last-child, matching a physical Kalaha board's layout. The top row shows Player
   2's pits right-to-left and the bottom row shows Player 1's pits
   left-to-right > *:last-child, so sowing always reads as one continuous loop around
   the board rather than jumping between rows in raw array order. */

.mancala-board > *:last-child {
  margin-right: 0;
}


.mancala-store {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
flex: 0 0 15%;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: var(--square-light);
  box-sizing: border-box;
  padding: 0.4rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.mancala-store > * {
  margin: 0 0.3rem 0.3rem 0;
}

.mancala-store > *:last-child {
  margin-right: 0;
}


.mancala-store-label {
  font-size: 0.75rem;
  color: var(--muted);
}

.mancala-store-count {
  font-size: 1.6rem;
  font-weight: bold;
}

.mancala-pits {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-auto-rows: 1fr;
  gap: 0.3rem;
}

/* Pit height is set in JS to match its own measured width (see
   ensureMancalaPitAspectRatio in mancala-app.js) - `aspect-ratio` alone
   is silently ignored on some E-Ink browsers, the same issue noted in
   the other games' board-rendering code. */
.mancala-pit {
  position: relative;
  border-radius: 50%;
  border: var(--border-width) solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font: inherit;
}

.mancala-pit:disabled {
  opacity: 0.55;
}

.mancala-pit-movable {
  background: var(--square-dark);
}

.mancala-pit-count {
  font-size: 1.3rem;
  font-weight: bold;
  pointer-events: none;
}

/* Small disc icon for the color-choice picker and board placeholder,
   representing a single seed/counter. */
.mancala-seed-icon {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  border-radius: 50%;
  background: var(--muted);
  vertical-align: middle;
}

/* Plain, non-interactive list of not-yet-built game ideas on the home
   page - deliberately undecorated (no icons, no action buttons) so it
   never looks like it could be tapped, unlike the playable games above. */
.coming-soon-list {
  margin: 0.5rem 0 0;
  padding-left: 1.2rem;
  color: var(--muted);
}

.coming-soon-list li {
  margin-bottom: 0.4rem;
}

.coming-soon-list strong {
  color: var(--text);
}

/* Othello/Reversi board: reuses .square from the chess board (same
   float-grid technique) but, unlike checkers, every square looks the
   same - a real Othello board is a uniform grid, not a checkered one. */

#othello-board {
  display: block;
  width: 100%;
  max-width: 760px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.othello-square {
  background: var(--square-light);
  border: 1px solid var(--border);
}

.othello-square-movable {
  background: var(--square-dark);
}

.othello-square:disabled {
  cursor: default;
}

.othello-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 78%;
  height: 78%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.othello-piece-black {
  background: #111;
  border: 2px solid #000;
}

.othello-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.othello-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.othello-icon-disc-black {
  background: #111;
  border: 2px solid #000;
}

.othello-icon-disc-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Connect Four board: same uniform-grid approach as Othello (a real
   Connect Four board isn't checkered either) - 6 rows x 7 columns,
   with the whole column clickable, not just the empty landing cell. */

#connectfour-board {
  display: block;
  width: 100%;
  max-width: 760px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.c4-square {
  /* Overrides .square's own 12.5% (built for an 8-column board): with
     7 columns, that width made floats wrap after 8 cells instead of 7,
     desyncing every row from its logical column past the first one. */
  width: calc(100% / 7);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.c4-square-movable {
  background: var(--square-dark);
}

.c4-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 78%;
  height: 78%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.c4-piece-black {
  background: #111;
  border: 2px solid #000;
}

.c4-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.c4-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.c4-icon-disc-black {
  background: #111;
  border: 2px solid #000;
}

.c4-icon-disc-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Gomoku board: a fixed 15x15 intersection grid, identical technique
   to Go's own board (percentage-positioned lines/points rather than a
   float grid, since stones sit ON intersections, not inside squares) -
   just without Go's variable board size or hoshi star points. */

.gomoku-board {
  position: relative;
  width: 100%;
  max-width: 640px;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
}

.gomoku-grid {
  position: relative;
  width: 100%;
  height: 100%;
}

.gomoku-line {
  position: absolute;
  background: var(--border);
}

.gomoku-line-h {
  left: 0;
  right: 0;
  height: 2px;
  transform: translateY(-1px);
}

.gomoku-line-v {
  top: 0;
  bottom: 0;
  width: 2px;
  transform: translateX(-1px);
}

.gomoku-point {
  position: absolute;
  transform: translate(-50%, -50%);
  background: transparent;
  border: none;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
}

.gomoku-stone {
  position: absolute;
  top: 4%;
  right: 4%;
  bottom: 4%;
  left: 4%;
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.gomoku-stone-black {
  background: #111;
  border: 1px solid #000;
}

.gomoku-stone-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

.gomoku-point.last-move .gomoku-stone::after {
  content: "";
  position: absolute;
  top: -22%;
  right: -22%;
  bottom: -22%;
  left: -22%;
  border: 2px solid var(--accent);
  border-radius: 50%;
}

/* Senet board: a 3x10 float-grid (same technique as the chess/checkers/
   Connect Four boards - a JS-enforced square size per cell, since CSS
   `aspect-ratio` is unreliable on E-Ink browsers), numbered in the
   traditional boustrophedon path across all three rows. The three named
   squares (House of Rebirth/Beauty/Water) get their own hatch patterns
   rather than a color, so they stay distinguishable on a monochrome
   E-Ink display. */

#senet-board {
  display: block;
  width: 100%;
  max-width: 820px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.senet-square {
  width: calc(100% / 10);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.senet-square-number {
  position: absolute;
  top: 2px;
  left: 4px;
  font-size: 0.6rem;
  line-height: 1;
  color: var(--muted);
  pointer-events: none;
}

.senet-square-rebirth {
  background: repeating-linear-gradient(45deg, var(--square-light), var(--square-light) 5px, #e6e6e6 5px, #e6e6e6 6px);
}

.senet-square-beauty {
  background: repeating-linear-gradient(-45deg, var(--square-light), var(--square-light) 5px, #e6e6e6 5px, #e6e6e6 6px);
}

.senet-square-water {
  background: repeating-linear-gradient(90deg, var(--square-light), var(--square-light) 4px, #d8d8d8 4px, #d8d8d8 6px);
}

.senet-square-movable {
  background: var(--square-dark);
}

.senet-piece {
  position: absolute;
  top: 55%;
  left: 50%;
  width: 62%;
  height: 62%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.senet-piece-a {
  background: #111;
  border: 2px solid #000;
}

.senet-piece-b {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

.senet-piece-count {
  position: absolute;
  bottom: 2px;
  right: 3px;
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.senet-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.senet-icon-disc-a {
  background: #111;
  border: 2px solid #000;
}

.senet-icon-disc-b {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

.senet-borne-off {
  text-align: center;
  font-size: 0.85rem;
  margin-bottom: 0.4rem;
  white-space: pre-wrap;
}

.senet-sticks-area {
  display: flex;
  align-items: center;
  justify-content: center;
margin-top: 0.5rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.senet-sticks-area > * {
  margin: 0 0.6rem 0.6rem 0;
}

.senet-sticks-area > *:last-child {
  margin-right: 0;
}


.senet-sticks-display {
  display: flex;
  align-items: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.senet-sticks-display > * {
  margin: 0 0.3rem 0.3rem 0;
}

.senet-sticks-display > *:last-child {
  margin-right: 0;
}


.senet-stick {
  display: inline-block;
  width: 0.5rem;
  height: 1.1rem;
  border: var(--border-width) solid var(--border);
  box-sizing: border-box;
}

.senet-stick-marked {
  background: var(--accent);
}

.senet-stick-blank {
  background: #fff;
}

.senet-stick-total {
  font-weight: 700;
  margin-left: 0.3rem;
}

/* Shogi board: a 9x9 float-grid (same technique as the chess/checkers/
   Connect Four boards - a JS-enforced square cell size, since CSS
   `aspect-ratio` is unreliable on E-Ink browsers). Pieces are plain
   pentagons carrying their kanji, rotated 180deg for White - so which
   side a piece belongs to is a shape fact, not a color fact, and
   promoted pieces get their own traditional single-kanji abbreviation
   instead of red ink. Both choices make this one of the more naturally
   E-Ink-friendly games here already, without needing a color fix. */

#shogi-board {
  display: block;
  width: 100%;
  max-width: 620px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.shogi-square {
  width: calc(100% / 9);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.shogi-square-movable {
  background: var(--square-dark);
}

.shogi-square-last-from,
.shogi-square-last-to {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.shogi-square-check {
  background: var(--square-dark);
}

.shogi-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 80%;
  height: 84%;
  transform: translate(-50%, -50%);
  clip-path: polygon(50% 0%, 92% 26%, 100% 100%, 0% 100%, 8% 26%);
  background: var(--square-light);
  border: 2px solid var(--border);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.shogi-piece svg {
  width: 62%;
  height: 62%;
}

.shogi-piece-w {
  transform: translate(-50%, -50%) rotate(180deg);
}

/* Small pentagon icons for the color-choice picker and board
   placeholder, matching the board's own piece styling. */
.shogi-icon-piece {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4em;
  height: 1.4em;
  clip-path: polygon(50% 0%, 92% 26%, 100% 100%, 0% 100%, 8% 26%);
  background: var(--square-light);
  border: 2px solid var(--border);
  box-sizing: border-box;
  vertical-align: middle;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--muted);
}

.shogi-icon-piece-w {
  transform: rotate(180deg);
}

/* Captured pieces switch sides and join the capturing player's hand -
   shown as a row of labelled buttons above/below the board, since they
   have no board square of their own until dropped. */
.shogi-hand {
  display: flex;
  flex-wrap: wrap;
justify-content: center;
  max-width: 620px;
  margin: 0 auto 0.4rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Captured pieces switch sides and join the capturing player's hand -
   shown as a row of labelled buttons above/below the board > *, since they
   have no board square of their own until dropped. */
.shogi-hand > * {
  margin: 0 0.3rem 0.3rem 0;
}

/* Captured pieces switch sides and join the capturing player's hand -
   shown as a row of labelled buttons above/below the board > *:last-child, since they
   have no board square of their own until dropped. */
.shogi-hand > *:last-child {
  margin-right: 0;
}


.shogi-hand-piece {
  display: flex;
  align-items: center;
padding: 0.25rem 0.5rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  font: inherit;
  cursor: pointer;
  box-sizing: border-box;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.shogi-hand-piece > * {
  margin: 0 0.25rem 0.25rem 0;
}

.shogi-hand-piece > *:last-child {
  margin-right: 0;
}


.shogi-hand-piece:disabled {
  opacity: 0.4;
  cursor: default;
}

.shogi-hand-piece-selected {
  background: var(--square-dark);
}

.shogi-hand-piece-label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.3em;
  height: 1.3em;
  clip-path: polygon(50% 0%, 92% 26%, 100% 100%, 0% 100%, 8% 26%);
  background: var(--square-light);
  border: 2px solid var(--border);
  box-sizing: border-box;
}

.shogi-hand-piece-label svg {
  width: 68%;
  height: 68%;
}

.shogi-hand-piece.shogi-piece-w .shogi-hand-piece-label {
  transform: rotate(180deg);
}

.shogi-hand-piece-count {
  font-weight: 700;
  font-size: 0.85rem;
}

.shogi-promotion-prompt {
  display: flex;
  align-items: center;
  justify-content: center;
margin-top: 0.5rem;
  flex-wrap: wrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.shogi-promotion-prompt > * {
  margin: 0 0.6rem 0.6rem 0;
}

.shogi-promotion-prompt > *:last-child {
  margin-right: 0;
}


/* Sudoku board: a 9x9 float-grid (same technique as the chess/checkers/
   Connect Four boards - a JS-enforced square cell size, since CSS
   `aspect-ratio` is unreliable on E-Ink browsers), with a thicker
   border marking each 3x3 box. Given cells are told apart from the
   player's own entries by weight (bold vs regular), and a conflicting
   entry gets a dashed outline - both structural, so nothing here
   depends on color to stay readable on a monochrome E-Ink display. */

#sudoku-board {
  display: block;
  width: 100%;
  max-width: 540px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.sudoku-cell {
  width: calc(100% / 9);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.sudoku-box-left {
  border-left-width: 3px;
}

.sudoku-box-top {
  border-top-width: 3px;
}

.sudoku-cell-value {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 1.1rem;
  font-size: clamp(0.9rem, 3.5vw, 1.4rem);
  line-height: 1;
  pointer-events: none;
}

.sudoku-cell-given .sudoku-cell-value {
  font-weight: 700;
}

.sudoku-cell-conflict {
  outline: 2px dashed var(--border);
  outline-offset: -2px;
}

.sudoku-numpad {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
max-width: 540px;
  margin: 0.6rem auto 0;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.sudoku-numpad > * {
  margin: 0 0.4rem 0.4rem 0;
}

.sudoku-numpad > *:last-child {
  margin-right: 0;
}


.sudoku-numpad-btn {
  width: 2.4rem;
  height: 2.4rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  font-size: 1.1rem;
  font-weight: 700;
  cursor: pointer;
  box-sizing: border-box;
}

.sudoku-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4em;
  height: 1.4em;
  border: 2px solid var(--border);
  box-sizing: border-box;
  font-weight: 700;
}

/* Kakuro board: a float-grid (same JS-enforced-square-cell technique as
   the Sudoku/chess/checkers boards, since CSS `aspect-ratio` is
   unreliable on E-Ink browsers), sized 7x7/9x9/11x11 by difficulty
   rather than Sudoku's fixed 9x9, so cell width is set inline per
   puzzle instead of a fixed CSS percentage. A black "clue" cell is
   solid black with white sum numbers tucked in its corners - the
   across sum upper-right, the down sum lower-left, split by a thin
   diagonal line only when a cell carries both (see .kakuro-cell-split)
   - the standard Kakuro notation, kept legible in pure black/white with
   no reliance on color. */

#kakuro-board {
  display: block;
  width: 100%;
  max-width: 540px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.kakuro-cell {
  background: var(--square-light);
  border: 1px solid var(--border);
}

.kakuro-cell-white {
  cursor: pointer;
}

.kakuro-cell-value {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 1.1rem;
  font-size: clamp(0.8rem, 3.2vw, 1.3rem);
  line-height: 1;
  font-weight: 700;
  pointer-events: none;
}

.kakuro-cell-conflict {
  outline: 2px dashed var(--border);
  outline-offset: -2px;
}

.kakuro-cell-black {
  background: var(--border);
  box-sizing: border-box;
}

/* A thin white seam along the main diagonal (top-left to bottom-right),
   just wide enough to read as a dividing line, only drawn when a black
   cell carries both an across and a down clue at once - a cell with
   only one clue needs no divider. */
.kakuro-cell-split {
  background:
    linear-gradient(to top right,
      var(--border) 0%, var(--border) calc(50% - 0.75px),
      #fff calc(50% - 0.75px), #fff calc(50% + 0.75px),
      var(--border) calc(50% + 0.75px), var(--border) 100%);
}

.kakuro-clue {
  position: absolute;
  color: #fff;
  font-weight: 700;
  line-height: 1;
  font-size: 0.55rem;
  font-size: clamp(0.42rem, 1.7vw, 0.68rem);
  pointer-events: none;
}

.kakuro-clue-right {
  top: 2px;
  right: 2px;
}

.kakuro-clue-down {
  bottom: 2px;
  left: 2px;
}

.kakuro-numpad {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
max-width: 540px;
  margin: 0.6rem auto 0;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.kakuro-numpad > * {
  margin: 0 0.4rem 0.4rem 0;
}

.kakuro-numpad > *:last-child {
  margin-right: 0;
}


.kakuro-numpad-btn {
  width: 2.4rem;
  height: 2.4rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  font-size: 1.1rem;
  font-weight: 700;
  cursor: pointer;
  box-sizing: border-box;
}

.kakuro-icon {
  display: inline-block;
  width: 1.4em;
  height: 1.4em;
  border: 2px solid var(--border);
  box-sizing: border-box;
  background: linear-gradient(to top right, var(--border) 0%, var(--border) 50%, transparent 50%, transparent 100%);
}

/* Peg Solitaire board: a 7x7 float-grid (same technique as the chess/
   checkers/Connect Four boards - a JS-enforced square cell size, since
   CSS `aspect-ratio` is unreliable on E-Ink browsers), with the four
   2x2 corners left as invisible gaps to form the classic cross shape -
   the same gap technique used for the Royal Game of Ur's board. */

#pegsolitaire-board {
  display: block;
  width: 100%;
  max-width: 460px;
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.peg-square {
  width: calc(100% / 7);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.peg-square-gap {
  background: transparent;
  border: none;
  pointer-events: none;
}

.peg-square-movable {
  background: var(--square-dark);
}

.peg-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 40%;
  height: 40%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 2px solid var(--border);
  box-sizing: border-box;
  pointer-events: none;
}

.peg-piece-filled {
  background: #111;
}

.peg-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4em;
  height: 1.4em;
}

.peg-icon::before {
  content: "";
  width: 0.7em;
  height: 0.7em;
  border-radius: 50%;
  background: #111;
  border: 2px solid var(--border);
  box-sizing: border-box;
}

/* Minesweeper board: a float-grid (same JS-enforced square cell size
   technique as the other games) at a fixed pixel width rather than a
   percentage one, wrapped in a horizontally scrolling container - so
   the 30-column Expert board keeps a legible ~32px cell size on a
   narrow e-reader screen instead of shrinking every cell down to
   illegibility. Numbers, flags and mines are told apart by the glyph
   itself, never by color, so this reads fine on a monochrome display
   without any special-casing. */

.minesweeper-toolbar {
  display: flex;
  align-items: center;
  justify-content: center;
margin-bottom: 0.5rem;
  flex-wrap: wrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Minesweeper board: a float-grid (same JS-enforced square cell size
   technique as the other games) at a fixed pixel width rather than a
   percentage one > *, wrapped in a horizontally scrolling container - so
   the 30-column Expert board keeps a legible ~32px cell size on a
   narrow e-reader screen instead of shrinking every cell down to
   illegibility. Numbers > *, flags and mines are told apart by the glyph
   itself > *, never by color > *, so this reads fine on a monochrome display
   without any special-casing. */

.minesweeper-toolbar > * {
  margin: 0 0.8rem 0.8rem 0;
}

/* Minesweeper board: a float-grid (same JS-enforced square cell size
   technique as the other games) at a fixed pixel width rather than a
   percentage one > *:last-child, wrapped in a horizontally scrolling container - so
   the 30-column Expert board keeps a legible ~32px cell size on a
   narrow e-reader screen instead of shrinking every cell down to
   illegibility. Numbers > *:last-child, flags and mines are told apart by the glyph
   itself > *:last-child, never by color > *:last-child, so this reads fine on a monochrome display
   without any special-casing. */

.minesweeper-toolbar > *:last-child {
  margin-right: 0;
}


.minesweeper-mine-counter,
.minesweeper-timer {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.minesweeper-board-wrap {
  overflow-x: auto;
  max-width: 100%;
}

#minesweeper-board {
  display: block;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.minesweeper-cell {
  background: var(--square-dark);
  border: 1px solid var(--border);
  box-sizing: border-box;
}

.minesweeper-cell-revealed {
  background: var(--square-light);
}

.minesweeper-cell-label {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 0.9rem;
  font-size: clamp(0.7rem, 3vw, 1.1rem);
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

.minesweeper-cell-mine {
  background: var(--square-dark);
}

.minesweeper-cell-exploded {
  outline: 2px solid var(--border);
  outline-offset: -2px;
}

.mine-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4em;
  height: 1.4em;
}

.mine-icon::before {
  content: "\25CF";
  font-size: 1em;
  line-height: 1;
}

/* Nonogram board: a CSS grid rather than the usual float-grid, since a
   Nonogram needs a row of column clues above the picture and a column
   of row clues to its left, in addition to the picture cells - a shape
   a simple square-per-cell float layout doesn't fit. A thicker border
   every 5th cell marks off easy 5-cell counting groups, the same idea
   as chess/checkers board coordinates but for line-counting instead. */

.nonogram-mode-toggle {
  display: flex;
  justify-content: center;
margin-bottom: 0.5rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Nonogram board: a CSS grid rather than the usual float-grid > *, since a
   Nonogram needs a row of column clues above the picture and a column
   of row clues to its left > *, in addition to the picture cells - a shape
   a simple square-per-cell float layout doesn't fit. A thicker border
   every 5th cell marks off easy 5-cell counting groups > *, the same idea
   as chess/checkers board coordinates but for line-counting instead. */

.nonogram-mode-toggle > * {
  margin: 0 0.5rem 0.5rem 0;
}

/* Nonogram board: a CSS grid rather than the usual float-grid > *:last-child, since a
   Nonogram needs a row of column clues above the picture and a column
   of row clues to its left > *:last-child, in addition to the picture cells - a shape
   a simple square-per-cell float layout doesn't fit. A thicker border
   every 5th cell marks off easy 5-cell counting groups > *:last-child, the same idea
   as chess/checkers board coordinates but for line-counting instead. */

.nonogram-mode-toggle > *:last-child {
  margin-right: 0;
}


.nonogram-wrap {
  overflow: auto;
  max-width: 100%;
}

.nonogram-grid {
  display: grid;
  width: fit-content;
  margin: 0 auto;
  border: 3px solid var(--border);
  box-sizing: border-box;
  background: var(--square-light);
}

.nonogram-corner {
  background: var(--square-light);
  border-right: 2px solid var(--border);
  border-bottom: 2px solid var(--border);
}

.nonogram-clue {
  display: flex;
  align-items: center;
  justify-content: center;
font-size: 0.75rem;
  font-weight: 700;
  background: var(--square-light);
  padding: 0.15em;
  box-sizing: border-box;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.nonogram-clue > * {
  margin: 0 0.15em 0.15em 0;
}

.nonogram-clue > *:last-child {
  margin-right: 0;
}


.nonogram-clue-col {
  flex-direction: column;
  border-bottom: 2px solid var(--border);
}

.nonogram-clue-row {
  flex-direction: row;
  justify-content: flex-end;
  border-right: 2px solid var(--border);
  white-space: nowrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead (see the
   .mode-buttons rule earlier) - this class is combined with the
   display:flex .nonogram-clue on the same element. */
.nonogram-clue-row > * {
  margin: 0 0.5em 0.5em 0;
}

.nonogram-clue-row > *:last-child {
  margin-right: 0;
}

.nonogram-cell {
  width: 1.8em;
  height: 1.8em;
  min-width: 1.4em;
  min-height: 1.4em;
  background: var(--square-light);
  border: 1px solid var(--border);
  box-sizing: border-box;
  font-weight: 700;
  padding: 0;
  cursor: pointer;
}

.nonogram-cell-block-left {
  border-left-width: 2px;
}

.nonogram-cell-block-top {
  border-top-width: 2px;
}

.nonogram-cell-filled {
  background: #111;
}

.nonogram-cell-marked {
  color: var(--muted);
}

.nonogram-icon {
  display: inline-grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  width: 1.4em;
  height: 1.4em;
  border: 2px solid var(--border);
  box-sizing: border-box;
  gap: 1px;
  padding: 1px;
}

.nonogram-icon::before,
.nonogram-icon::after {
  content: "";
  background: #111;
  grid-column: 2;
}

.nonogram-icon::before {
  grid-row: 1;
}

.nonogram-icon::after {
  grid-row: 3;
}

/* 2048 board: a 4x4 float-grid (same technique as the chess/checkers/
   Connect Four boards - a JS-enforced square cell size, since CSS
   `aspect-ratio` is unreliable on E-Ink browsers). Tiles are told apart
   purely by their printed number and a couple of border/weight steps
   as the value grows, never by color, unlike the classic game's usual
   color-per-value palette - so this reads fine on a monochrome E-Ink
   display without needing sixteen shades any reader could tell apart
   on some displays but not others. */

#twenty48-board {
  display: block;
  width: 100%;
  max-width: 420px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
  touch-action: none;
}

.twenty48-cell {
  width: 25%;
  background: var(--square-light);
  border: 1px solid var(--border);
  box-sizing: border-box;
}

.twenty48-cell-filled {
  border-width: 2px;
}

.twenty48-cell-value {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 1.2rem;
  font-size: clamp(0.9rem, 5vw, 1.6rem);
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

.twenty48-cell-big .twenty48-cell-value {
  font-size: 1rem;
  font-size: clamp(0.7rem, 4vw, 1.3rem);
}

.twenty48-cell-huge {
  background: var(--square-dark);
}

.twenty48-cell-huge .twenty48-cell-value {
  font-size: 0.85rem;
  font-size: clamp(0.6rem, 3.2vw, 1.1rem);
}

.twenty48-controls-pad {
  display: flex;
  flex-direction: column;
  align-items: center;
margin-top: 0.6rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.twenty48-controls-pad > * {
  margin: 0 0.4rem 0.4rem 0;
}

.twenty48-controls-pad > *:last-child {
  margin-right: 0;
}


.twenty48-controls-row {
  display: flex;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.twenty48-controls-row > * {
  margin: 0 0.4rem 0.4rem 0;
}

.twenty48-controls-row > *:last-child {
  margin-right: 0;
}


.twenty48-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2em;
  height: 1.4em;
  border: 2px solid var(--border);
  box-sizing: border-box;
  font-size: 0.6rem;
  font-weight: 700;
}

/* Lights Out board: a fixed 5x5 float-grid (same JS-enforced square cell
   size technique as 2048/Minesweeper). Cells are told apart purely by a
   background fill - on (lit) is dark, off is light - never by color, so
   this reads fine on a monochrome E-Ink display. */

#lightsout-board {
  display: block;
  width: 100%;
  max-width: 360px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.lightsout-cell {
  width: 20%;
  background: var(--square-light);
  border: 1px solid var(--border);
  box-sizing: border-box;
  padding: 0;
  cursor: pointer;
}

.lightsout-cell-on {
  background: #141413;
}

/* Mahjong Solitaire board: absolute positioning on a percentage grid
   (the same approach as Go/Xiangqi/Gomoku's point grids) rather than
   the usual float-grid, since tiles overlap across three layers
   instead of sitting in a simple rectangular arrangement. Each layer
   gets a small fixed pixel nudge (in mahjong-app.js) purely for the
   classic layered "mound" look. Tiles are matched by their printed
   glyph alone, so nothing here needs color to stay readable on a
   monochrome E-Ink display. */

.mahjong-wrap {
  overflow: auto;
  max-width: 100%;
}

#mahjong-board {
  position: relative;
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
}

.mahjong-tile {
  position: absolute;
  background: #fff;
  border: 2px solid var(--border);
  border-radius: 3px;
  box-sizing: border-box;
  box-shadow: 2px 2px 0 var(--border);
  padding: 0;
  cursor: pointer;
}

.mahjong-tile-label {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 0.9rem;
  font-size: clamp(0.6rem, 2.2vw, 1.2rem);
  line-height: 1;
  pointer-events: none;
}

.mahjong-tile-blocked {
  background: var(--square-dark);
}

.mahjong-tile-selected {
  outline: 3px solid var(--border);
  outline-offset: 1px;
}

.mahjong-actions-row {
  display: flex;
  justify-content: center;
  margin-top: 0.5rem;
}

.mahjong-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4em;
  height: 1.4em;
  font-size: 1.1rem;
}

/* FreeCell board: free cells and foundations in a row above eight
   cascading tableau columns. Cards overlap via a negative margin
   rather than the float-grid/percentage-grid techniques used
   elsewhere, since a solitaire cascade is a variable-height stack, not
   a fixed board shape - the page simply scrolls if a column grows
   tall. Suits are told apart by their glyph shape (already
   unambiguous - spade/club vs. heart/diamond), never by the
   traditional red/black coloring, so this reads fine on a monochrome
   E-Ink display without needing a color-blindness-style retrofit. */

.freecell-top-row {
  display: flex;
  justify-content: space-between;
max-width: 900px;
  margin: 0 auto 0.6rem;
  flex-wrap: wrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* FreeCell board: free cells and foundations in a row above eight
   cascading tableau columns. Cards overlap via a negative margin
   rather than the float-grid/percentage-grid techniques used
   elsewhere > *, since a solitaire cascade is a variable-height stack > *, not
   a fixed board shape - the page simply scrolls if a column grows
   tall. Suits are told apart by their glyph shape (already
   unambiguous - spade/club vs. heart/diamond) > *, never by the
   traditional red/black coloring > *, so this reads fine on a monochrome
   E-Ink display without needing a color-blindness-style retrofit. */

.freecell-top-row > * {
  margin: 0 1rem 1rem 0;
}

/* FreeCell board: free cells and foundations in a row above eight
   cascading tableau columns. Cards overlap via a negative margin
   rather than the float-grid/percentage-grid techniques used
   elsewhere > *:last-child, since a solitaire cascade is a variable-height stack > *:last-child, not
   a fixed board shape - the page simply scrolls if a column grows
   tall. Suits are told apart by their glyph shape (already
   unambiguous - spade/club vs. heart/diamond) > *:last-child, never by the
   traditional red/black coloring > *:last-child, so this reads fine on a monochrome
   E-Ink display without needing a color-blindness-style retrofit. */

.freecell-top-row > *:last-child {
  margin-right: 0;
}


.freecell-freecells,
.freecell-foundations {
  display: flex;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.freecell-freecells > *, .freecell-foundations > * {
  margin: 0 0.3rem 0.3rem 0;
}

.freecell-freecells > *:last-child, .freecell-foundations > *:last-child {
  margin-right: 0;
}


.freecell-cell {
  width: 2.6rem;
  height: 3.6rem;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  font-weight: 700;
  font-size: 1rem;
  box-sizing: border-box;
  cursor: pointer;
}

.freecell-foundation-empty {
  color: var(--muted);
  font-weight: 400;
}

.freecell-cell-selected {
  outline: 3px solid var(--border);
  outline-offset: 1px;
}

.freecell-columns {
  display: flex;
max-width: 900px;
  margin: 0 auto;
  align-items: flex-start;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.freecell-columns > * {
  margin: 0 0.4rem 0.4rem 0;
}

.freecell-columns > *:last-child {
  margin-right: 0;
}


.freecell-column {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* Height is set in JS from the measured column width (see
   ensureFreeCellCardAspectRatio in freecell-app.js) rather than CSS
   `aspect-ratio`, which some E-Ink browsers (Tolino confirmed) don't
   support reliably. */
.freecell-card {
  position: relative;
  width: 100%;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  box-sizing: border-box;
  margin-top: -1.6rem;
  padding: 0.2rem 0 0 0.3rem;
  text-align: left;
  font-weight: 700;
  font-size: 0.8rem;
  font-size: clamp(0.65rem, 2.4vw, 0.95rem);
  cursor: pointer;
}

.freecell-card:first-child {
  margin-top: 0;
}

.freecell-card-selected {
  outline: 3px solid var(--border);
  outline-offset: -2px;
}

.freecell-actions-row {
  display: flex;
  justify-content: center;
  margin-top: 0.6rem;
}

.freecell-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.8em;
  height: 1.4em;
  border: 2px solid var(--border);
  border-radius: 3px;
  box-sizing: border-box;
  font-size: 0.65rem;
  font-weight: 700;
}

/* Klondike board: stock/waste and foundations in a row above seven
   cascading tableau columns - the same overlap-via-negative-margin
   layout as FreeCell's cascade, since a Klondike column is likewise a
   variable-height stack rather than a fixed board shape. Suits are
   told apart by glyph shape (spade/club vs. heart/diamond), never by
   red/black coloring, matching FreeCell. Face-down cards get a
   hatched back instead of a plain color, the same structural, not
   color-based, convention used for player fills elsewhere in this
   app (e.g. Dots and Boxes). */

.klondike-top-row {
  display: flex;
  justify-content: space-between;
max-width: 900px;
  margin: 0 auto 0.6rem;
  flex-wrap: wrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.klondike-top-row > * {
  margin: 0 1rem 1rem 0;
}

.klondike-top-row > *:last-child {
  margin-right: 0;
}

.klondike-stockwaste,
.klondike-foundations {
  display: flex;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.klondike-stockwaste > *, .klondike-foundations > * {
  margin: 0 0.3rem 0.3rem 0;
}

.klondike-stockwaste > *:last-child, .klondike-foundations > *:last-child {
  margin-right: 0;
}

.klondike-cell {
  width: 2.6rem;
  height: 3.6rem;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  font-weight: 700;
  font-size: 1rem;
  box-sizing: border-box;
  cursor: pointer;
}

.klondike-foundation-empty {
  color: var(--muted);
  font-weight: 400;
}

.klondike-card-selected {
  outline: 3px solid var(--border);
  outline-offset: 1px;
}

.klondike-columns {
  display: flex;
max-width: 900px;
  margin: 0 auto;
  align-items: flex-start;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.klondike-columns > * {
  margin: 0 0.4rem 0.4rem 0;
}

.klondike-columns > *:last-child {
  margin-right: 0;
}

.klondike-column {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* Height is set in JS from the measured column width (see
   ensureKlondikeCardAspectRatio in klondike-app.js) rather than CSS
   `aspect-ratio`, which some E-Ink browsers (Tolino confirmed) don't
   support reliably. */
.klondike-card {
  position: relative;
  width: 100%;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  box-sizing: border-box;
  margin-top: -1.6rem;
  padding: 0.2rem 0 0 0.3rem;
  text-align: left;
  font-weight: 700;
  font-size: 0.8rem;
  font-size: clamp(0.65rem, 2.4vw, 0.95rem);
  cursor: pointer;
}

.klondike-card:first-child {
  margin-top: 0;
}

.klondike-card-back {
  background: repeating-linear-gradient(45deg, #fff, #fff 4px, var(--border) 4px, var(--border) 5px);
}

.klondike-empty-slot {
  display: block;
  width: 100%;
  border: var(--border-width) dashed var(--muted);
  border-radius: 4px;
  box-sizing: border-box;
}

.klondike-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.8em;
  height: 1.4em;
  border: 2px solid var(--border);
  border-radius: 3px;
  box-sizing: border-box;
  font-size: 0.65rem;
  font-weight: 700;
}

/* Onitama board: a 5x5 float-grid (same technique as chess/checkers -
   note the explicit width override below, since the shared .square
   base class hardcodes 12.5% for an 8-column board). Sides are told
   apart structurally, not by color alone: Blue is a filled disc, Red
   is an outlined disc, and either side's master gets its own ring
   marker on top - the same convention used for checkers kings. */

#onitama-board {
  display: block;
  width: 100%;
  max-width: 460px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.onitama-square {
  width: calc(100% / 5);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.onitama-square-movable {
  background: var(--square-dark);
}

.onitama-square-shrine::before {
  content: "";
  position: absolute;
  top: 8%;
  left: 8%;
  width: 84%;
  height: 84%;
  border: 2px dashed var(--muted);
  box-sizing: border-box;
  pointer-events: none;
}

.onitama-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 74%;
  height: 74%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.onitama-piece-blue {
  background: #111;
  border: 2px solid #000;
}

.onitama-piece-red {
  background: var(--square-light);
  border: 2px solid var(--border);
}

.onitama-piece-king::after {
  content: "";
  position: absolute;
  top: 25%;
  left: 25%;
  width: 50%;
  height: 50%;
  border-radius: 50%;
  box-sizing: border-box;
}

.onitama-piece-blue.onitama-piece-king::after {
  border: 2px solid #fff;
}

.onitama-piece-red.onitama-piece-king::after {
  border: 2px solid var(--border);
}

/* Small icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.onitama-icon-piece {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.onitama-icon-piece-blue {
  background: #111;
  border: 2px solid #000;
}

.onitama-icon-piece-red {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Card rows above/below the board (each side's two cards) and the
   neutral-card display between them. Cards are plain buttons holding
   a name and a tiny 5x5 dot-diagram of that card's actual move
   offsets, so a player can read a card's moves without memorizing
   names. */
.onitama-cards-row {
  display: flex;
  justify-content: center;
margin: 0 auto 0.4rem;
  max-width: 460px;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
/* Card rows above/below the board (each side's two cards) and the
   neutral-card display between them. Cards are plain buttons holding
   a name and a tiny 5x5 dot-diagram of that card's actual move
   offsets > *, so a player can read a card's moves without memorizing
   names. */
.onitama-cards-row > * {
  margin: 0 0.6rem 0.6rem 0;
}

/* Card rows above/below the board (each side's two cards) and the
   neutral-card display between them. Cards are plain buttons holding
   a name and a tiny 5x5 dot-diagram of that card's actual move
   offsets > *:last-child, so a player can read a card's moves without memorizing
   names. */
.onitama-cards-row > *:last-child {
  margin-right: 0;
}


.onitama-neutral-row {
  display: flex;
  align-items: center;
  justify-content: center;
margin: 0.4rem auto 0;
  max-width: 460px;
  font-size: 0.85rem;
  color: var(--muted);
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.onitama-neutral-row > * {
  margin: 0 0.5rem 0.5rem 0;
}

.onitama-neutral-row > *:last-child {
  margin-right: 0;
}


.onitama-card {
  display: flex;
  flex-direction: column;
  align-items: center;
padding: 0.3rem;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  box-sizing: border-box;
  cursor: pointer;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.onitama-card > * {
  margin: 0 0.2rem 0.2rem 0;
}

.onitama-card > *:last-child {
  margin-right: 0;
}


.onitama-card-selected {
  outline: 3px solid var(--border);
  outline-offset: -2px;
  background: var(--square-dark);
}

.onitama-card-name {
  font-weight: 700;
  font-size: 0.7rem;
  font-size: clamp(0.6rem, 2.2vw, 0.8rem);
}

.onitama-card-diagram {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-template-rows: repeat(5, 1fr);
  gap: 1px;
  width: 2.6rem;
  height: 2.6rem;
  background: var(--border);
}

.onitama-card-cell {
  background: var(--square-light);
}

.onitama-card-cell-center {
  background: var(--muted);
}

.onitama-card-cell-move {
  background: #111;
}

/* Hnefatafl board: a 7x7 float-grid (same technique as chess/checkers -
   note the explicit width override below, since the shared .square
   base class hardcodes 12.5% for an 8-column board). Sides are told
   apart structurally, not by color alone: attackers are a filled
   disc, defenders are an outlined disc, and the king gets his own
   crown marker on top of a defender disc - the same "own ring/shape
   marker" convention used for kings elsewhere in this app. */

#hnefatafl-board {
  display: block;
  width: 100%;
  max-width: 460px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.hnefatafl-square {
  width: calc(100% / 7);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.hnefatafl-square-movable {
  background: var(--square-dark);
}

.hnefatafl-square-last-from,
.hnefatafl-square.last-move {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.hnefatafl-square-throne,
.hnefatafl-square-corner {
  background: var(--square-dark);
}

.hnefatafl-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 74%;
  height: 74%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.hnefatafl-piece-attacker {
  background: #111;
  border: 2px solid #000;
}

.hnefatafl-piece-defender {
  background: var(--square-light);
  border: 2px solid var(--border);
}

.hnefatafl-piece-king::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 48%;
  height: 48%;
  transform: translate(-50%, -50%);
  clip-path: polygon(0% 100%, 0% 40%, 25% 60%, 50% 30%, 75% 60%, 100% 40%, 100% 100%);
  background: var(--border);
  box-sizing: border-box;
}

/* Small icons for the side-choice picker and board placeholder,
   matching the board's own piece styling. */
.hnefatafl-icon-piece {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.hnefatafl-icon-piece-attacker {
  background: #111;
  border: 2px solid #000;
}

.hnefatafl-icon-piece-defender {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Quoridor board: percentage-based absolute positioning (the same
   technique as Go's line-and-point board), since walls need clickable
   targets between cells, not just on them - a plain 9x9 float-grid
   has nowhere to put those. Pawns are told apart structurally, not by
   color alone: Player 1 is a filled disc, Player 2 an outlined disc,
   the same convention used everywhere else in this app. */

.quoridor-board {
  position: relative;
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
}

.quoridor-cell {
  position: absolute;
  box-sizing: border-box;
  border: 1px solid var(--border);
  background: var(--square-light);
  padding: 0;
  margin: 0;
}

.quoridor-cell-movable {
  background: var(--square-dark);
}

.quoridor-cell.last-move {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.quoridor-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 62%;
  height: 62%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.quoridor-piece-p1 {
  background: #111;
  border: 2px solid #000;
}

.quoridor-piece-p2 {
  background: var(--square-light);
  border: 2px solid var(--border);
}

.quoridor-wall-layer {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  pointer-events: none;
  z-index: 2;
}

/* The whole-board tap target for "place a wall" mode: inert (and
   invisible) the rest of the time, so ordinary pawn-move clicks on
   the cells beneath it pass straight through. Turning the entire
   board into one forgiving target - rather than 64+ small, easily
   overlapping candidate buttons squeezed between cells - is what
   makes wall placement reliable to tap on a touchscreen; see
   quoridor-app.js's onQuoridorBoardTap/selectNearestWallAtPoint. */
.quoridor-wall-tap-catcher {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 4;
  pointer-events: none;
}

.quoridor-wall-tap-catcher.active {
  pointer-events: auto;
  cursor: crosshair;
  background: rgba(0, 0, 0, 0.03);
}

.quoridor-wall-bar {
  position: absolute;
  box-sizing: border-box;
}

.quoridor-wall-bar-placed {
  background: var(--border);
}

.quoridor-wall-bar-preview {
  background: var(--accent);
  opacity: 0.55;
}

/* Small icons for the side-choice picker and board placeholder,
   matching the board's own piece styling. */
.quoridor-icon-piece {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.quoridor-icon-piece-p1 {
  background: #111;
  border: 2px solid #000;
}

.quoridor-icon-piece-p2 {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Hex board: a rhombus of hexagonal cells (percentage-based absolute
   positioning, the same technique as Go's board), with a JS-enforced
   non-square aspect ratio - the board is noticeably wider than tall,
   unlike every other board here. Stones are told apart structurally
   (filled vs. outlined), and each player's pair of target edges gets
   its own distinct thicker border so it's clear which side is trying
   to connect where without relying on color for that either. */

.hex-board-wrap {
  max-width: 620px;
  margin: 0 auto;
}

.hex-board {
  position: relative;
  width: 100%;
  box-sizing: border-box;
}

/* A clip-path polygon clips away a plain CSS border wherever the
   polygon's edge is diagonal (only the two vertical flat sides of
   this pointy-top hexagon would show a stroke), so each cell is built
   from two stacked, identically-clipped hexagons instead: a slightly
   larger one in the border color underneath, and a slightly inset one
   in the fill color on top - the same trick used to fake a uniform
   outline on any non-rectangular CSS shape. */
.hex-cell {
  position: absolute;
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  border: none;
  background: transparent;
}

.hex-cell-border,
.hex-cell-fill {
  position: absolute;
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
}

.hex-cell-border {
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: var(--border);
}

.hex-cell-fill {
  top: 6%;
  right: 6%;
  bottom: 6%;
  left: 6%;
  background: var(--square-light);
}

.hex-cell.last-move .hex-cell-fill {
  background: var(--square-dark);
}

.hex-legend {
  display: flex;
  justify-content: center;
margin: 0.5rem auto 0;
  max-width: 620px;
  font-size: 0.8rem;
  color: var(--muted);
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.hex-legend > * {
  margin: 0 1.2rem 1.2rem 0;
}

.hex-legend > *:last-child {
  margin-right: 0;
}


.hex-legend-item {
  display: inline-flex;
  align-items: center;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.hex-legend-item > * {
  margin: 0 0.35rem 0.35rem 0;
}

.hex-legend-item > *:last-child {
  margin-right: 0;
}


.hex-stone {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 56%;
  height: 56%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.hex-stone-r {
  background: #111;
  border: 2px solid #000;
}

.hex-stone-b {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Small icons for the color-choice picker and board placeholder,
   matching the board's own stone styling. */
.hex-icon-piece {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.hex-icon-piece-r {
  background: #111;
  border: 2px solid #000;
}

.hex-icon-piece-b {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Halma board: a 10x10 float-grid (same technique as chess/checkers -
   note the explicit width override below, since the shared .square
   base class hardcodes 12.5% for an 8-column board). Sides are told
   apart structurally, not by color alone (filled vs. outlined disc),
   and each side's home/goal camp is shaded so it's clear at a glance
   where pieces still need to travel to. */

#halma-board {
  display: block;
  width: 100%;
  max-width: 460px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.halma-square {
  width: calc(100% / 10);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.halma-square-movable {
  background: var(--square-dark);
}

.halma-square-camp-p1,
.halma-square-camp-p2 {
  background: repeating-linear-gradient(45deg, var(--square-light), var(--square-light) 4px, var(--square-dark) 4px, var(--square-dark) 5px);
}

.halma-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 70%;
  height: 70%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.halma-piece-p1 {
  background: #111;
  border: 2px solid #000;
}

.halma-piece-p2 {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Small icons for the side-choice picker and board placeholder,
   matching the board's own piece styling. */
.halma-icon-piece {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.halma-icon-piece-p1 {
  background: #111;
  border: 2px solid #000;
}

.halma-icon-piece-p2 {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Chinese Checkers ("sternhalma" internally - see sternhalma-core.js
   for why) board: a six-pointed star of round holes on a triangular
   lattice, laid out via percentage-based absolute positioning (the
   same technique as Hex's board) with a JS-enforced aspect ratio,
   since - like Hex - this board isn't square. Each hole is itself a
   round button (rather than a square cell with a round piece drawn
   on top), and an occupied hole additionally shows a marble disc,
   told apart structurally like every other 2-player board here
   (filled vs. outlined disc) rather than by color alone. Each
   side's home/goal point gets the same shaded-diagonal-stripe
   treatment as Halma's camps above. */

.sternhalma-board {
  position: relative;
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  box-sizing: border-box;
}

.sternhalma-cell {
  position: absolute;
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--square-light);
}

.sternhalma-cell-movable {
  background: var(--square-dark);
}

.sternhalma-cell-home-p1,
.sternhalma-cell-home-p2 {
  background: repeating-linear-gradient(45deg, var(--square-light), var(--square-light) 4px, var(--square-dark) 4px, var(--square-dark) 5px);
}

/* box-shadow (not outline) so the ring actually follows the circular
   border-radius instead of drawing a rectangle around it. */
.sternhalma-cell.selected {
  box-shadow: 0 0 0 3px var(--accent);
}

.sternhalma-cell.last-move {
  box-shadow: inset 0 0 0 2px var(--accent), inset 0 0 0 999px rgba(0, 0, 0, 0.22);
}

.sternhalma-marble {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 70%;
  height: 70%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.sternhalma-marble-p1 {
  background: #111;
  border: 2px solid #000;
}

.sternhalma-marble-p2 {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Small icons for the side-choice picker and board placeholder,
   matching the board's own marble styling. */
.sternhalma-icon-piece {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.sternhalma-icon-piece-p1 {
  background: #111;
  border: 2px solid #000;
}

.sternhalma-icon-piece-p2 {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Yatzy: five pip-dot dice drawn as inline SVG (see yatzy-app.js) laid
   out in a row, plus a scoresheet built on the same .stats-table used
   by the Stats page. A held die is shown inverted (dark face, light
   pips) rather than picking it out with color, so it stays legible on
   e-ink. */
.yatzy-dice-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin: 0.5rem 0;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.yatzy-dice-row > * {
  margin: 0 0.5rem 0.5rem 0;
}

.yatzy-dice-row > *:last-child {
  margin-right: 0;
}

.yatzy-die {
  display: block;
  width: 2.6rem;
  height: 2.6rem;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.yatzy-die:disabled {
  cursor: default;
}

.yatzy-die svg {
  display: block;
  width: 100%;
  height: 100%;
}

.yatzy-die-held {
  transform: scale(0.92);
}

.yatzy-scoresheet {
  margin-top: 0.75rem;
}

.yatzy-scoresheet .yatzy-cat-score,
.yatzy-scoresheet .yatzy-subtotal-value {
  width: 4.5rem;
}

.yatzy-score-btn {
  width: 100%;
  padding: 0.25rem 0.4rem;
  font-weight: 700;
  background: var(--bg);
  color: var(--text);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}

.yatzy-score-btn:disabled {
  cursor: default;
}

.yatzy-score-filled {
  background: var(--bg-alt);
}

.yatzy-score-preview {
  background: var(--square-last);
}

.yatzy-subtotal-row td {
  font-weight: 700;
  background: var(--bg-alt);
}

.yatzy-grand-total-row td {
  font-size: 1.05em;
}

/* Mastermind: four guess slots, each a button that cycles through the
   six flat shapes (see mastermind-app.js) on tap - no color, no
   drag-and-drop, no hover state needed. Feedback pegs below each row of
   the history are plain filled/hollow circles rather than colored dots. */
.mastermind-guess-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin: 0.5rem 0;
}

.mastermind-guess-row > * {
  margin: 0 0.5rem 0.5rem 0;
}

.mastermind-guess-row > *:last-child {
  margin-right: 0;
}

.mastermind-slot {
  display: block;
  width: 3.2rem;
  height: 3.2rem;
  padding: 0.3rem;
  background: var(--square-light);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}

.mastermind-slot:disabled {
  cursor: default;
  opacity: 0.6;
}

.mastermind-slot svg {
  display: block;
  width: 100%;
  height: 100%;
}

.mastermind-legend {
  font-size: 0.85em;
  color: var(--muted);
  margin: 0.3rem 0 0.6rem;
}

.mastermind-legend-row {
  display: flex;
  align-items: center;
  margin: 0.15rem 0;
}

.mastermind-legend-row .mastermind-peg {
  margin-right: 0.4em;
}

.mastermind-history {
  margin-top: 0.75rem;
  display: flex;
  flex-direction: column;
}

.mastermind-history-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  border-top: 1px solid var(--bg-alt);
  padding: 0.4rem 0;
}

.mastermind-history-row:first-child {
  border-top: none;
}

.mastermind-history-number {
  width: 4.2rem;
  font-weight: 700;
  flex-shrink: 0;
}

.mastermind-history-shapes {
  display: flex;
  margin-right: 0.6rem;
}

.mastermind-history-shape {
  display: block;
  width: 1.6rem;
  height: 1.6rem;
  margin-right: 0.25rem;
}

.mastermind-history-shape svg {
  display: block;
  width: 100%;
  height: 100%;
}

.mastermind-history-pegs {
  display: grid;
  grid-template-columns: repeat(2, auto);
  gap: 0.2rem;
  margin-left: auto;
}

.mastermind-peg {
  display: inline-block;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  border: 1.5px solid var(--border);
  box-sizing: border-box;
}

.mastermind-peg-black {
  background: var(--text);
}

.mastermind-peg-white {
  background: var(--bg);
}

.mastermind-peg-empty {
  background: transparent;
  border-style: dashed;
  opacity: 0.5;
}

/* Dots and Boxes board: percentage-based absolute positioning (the same
   technique as Quoridor's board), since line segments need clickable
   targets BETWEEN dots, not just on them - a plain float-grid has
   nowhere to put those. A fixed 5x5 dot grid (4x4 boxes) throughout,
   not configurable, like every other board here. Completed boxes are
   told apart structurally, not by color alone: Player 1 gets a solid
   fill, Player 2 a hatched fill, the same convention already used
   elsewhere (see .halma-square-camp-p1/p2) applied to filled areas
   instead of discs. */

.dab-board {
  position: relative;
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
}

.dab-box {
  position: absolute;
  box-sizing: border-box;
  pointer-events: none;
}

.dab-box-p1 {
  background: #111;
}

.dab-box-p2 {
  background: repeating-linear-gradient(45deg, var(--square-light), var(--square-light) 4px, var(--border) 4px, var(--border) 5px);
}

.dab-line {
  position: absolute;
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  z-index: 1;
}

.dab-line:disabled {
  cursor: default;
}

.dab-line-bar {
  position: absolute;
  background: var(--square-dark);
  pointer-events: none;
}

.dab-line-bar-h {
  left: 4%;
  right: 4%;
  top: 50%;
  height: 22%;
  transform: translateY(-50%);
}

.dab-line-bar-v {
  top: 4%;
  bottom: 4%;
  left: 50%;
  width: 22%;
  transform: translateX(-50%);
}

.dab-line-drawn .dab-line-bar {
  background: var(--border);
}

.dab-line:not(:disabled):hover .dab-line-bar {
  opacity: 0.7;
}

.dab-dot {
  position: absolute;
  width: 3.5%;
  height: 3.5%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--border);
  pointer-events: none;
  z-index: 2;
}

/* Small icons for the player-choice picker and board placeholder,
   matching the board's own box-fill styling. */
.dab-icon-swatch {
  display: inline-flex;
  width: 1.1em;
  height: 1.1em;
  box-sizing: border-box;
  vertical-align: middle;
  border: 2px solid var(--border);
}

.dab-icon-swatch-p1 {
  background: #111;
}

.dab-icon-swatch-p2 {
  background: repeating-linear-gradient(45deg, var(--square-light), var(--square-light) 3px, var(--border) 3px, var(--border) 4px);
}

/* Amazons board: percentage-based absolute positioning (the same
   technique as Wall Maze/Hex), a plain 10x10 grid of clickable cell
   buttons with a JS-enforced square aspect ratio. Amazons are told
   apart structurally, not by color alone: Player 1 is a filled disc,
   Player 2 an outlined disc, the same convention used everywhere else
   in this app. A burned square (wherever an arrow has landed) gets
   its own crosshatch fill - a criss-cross of both diagonals, visually
   distinct from every single-direction stripe pattern used elsewhere
   (Senet's rebirth/beauty squares, Halma's camps, Dots and Boxes'
   player-2 fill) - so it reads unmistakably as "different terrain",
   not as a movable-square highlight or another player's marker. */

.amazons-board {
  position: relative;
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
}

.amazons-cell {
  position: absolute;
  box-sizing: border-box;
  border: 1px solid var(--border);
  background: var(--square-light);
  padding: 0;
  margin: 0;
}

.amazons-cell-burned {
  background-color: var(--square-dark);
  background-image:
    repeating-linear-gradient(45deg, transparent, transparent 3px, var(--border) 3px, var(--border) 4px),
    repeating-linear-gradient(-45deg, transparent, transparent 3px, var(--border) 3px, var(--border) 4px);
}

/* Step 2: legal move destinations for the amazon already selected. */
.amazons-cell-movable {
  background-color: var(--square-dark);
}

/* Step 3: legal arrow landings from the amazon's new square - a
   dashed accent ring rather than a plain fill, so it reads as a
   distinct "now aiming" mode instead of looking like another
   move-destination square. */
.amazons-cell-arrow-target {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.amazons-cell.selected {
  outline: 3px solid var(--border);
  outline-offset: -3px;
}

/* The destination already chosen in step 2, while step 3 (aiming the
   arrow) is in progress - kept visually marked so the board still
   shows where the moving amazon ended up. */
.amazons-cell-pending-to {
  box-shadow: inset 0 0 0 2px var(--border);
}

.amazons-cell.last-move {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.amazons-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 62%;
  height: 62%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.amazons-piece-p1 {
  background: #111;
  border: 2px solid #000;
}

.amazons-piece-p2 {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Small icons for the side-choice picker, board placeholder, and
   rules page, matching the board's own piece/terrain styling. */
.amazons-icon-piece {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.amazons-icon-piece-p1 {
  background: #111;
  border: 2px solid #000;
}

.amazons-icon-piece-p2 {
  background: var(--square-light);
  border: 2px solid var(--border);
}

.amazons-icon-burned {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  box-sizing: border-box;
  vertical-align: middle;
  border: 1px solid var(--border);
  background-color: var(--square-dark);
  background-image:
    repeating-linear-gradient(45deg, transparent, transparent 3px, var(--border) 3px, var(--border) 4px),
    repeating-linear-gradient(-45deg, transparent, transparent 3px, var(--border) 3px, var(--border) 4px);
}

/* Bagh-Chal board: 25 points in a 5x5 grid (square, unlike Fanorona's
   wider-than-tall board), connected by horizontal, vertical, AND
   diagonal lines wherever the alquerque-style lattice allows (see
   baghchal-core.js's "strong"/"weak" point pattern). Uses the exact
   same percentage-based absolute-positioning + SVG-line-layer technique
   as .fanorona-board, just square: a JS-enforced pixel height (see
   ensureBaghChalBoardAspect in baghchal-app.js) rather than CSS
   `aspect-ratio`, which some E-Ink browsers silently ignore. Pieces are
   told apart structurally, not by color alone: tigers are a filled
   disc, goats an outlined disc, the same convention used everywhere
   else in this app. */

.baghchal-board {
  position: relative;
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
}

.baghchal-lines {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}

.baghchal-lines line {
  stroke: var(--border);
  stroke-width: 2;
}

.baghchal-point {
  position: absolute;
  transform: translate(-50%, -50%);
  background: var(--square-light);
  border: 2px solid var(--border);
  border-radius: 50%;
  padding: 0;
  box-sizing: border-box;
  cursor: pointer;
  z-index: 2;
}

.baghchal-point-movable {
  background: var(--square-dark);
}

.baghchal-point-selectable {
  box-shadow: inset 0 0 0 2px var(--muted);
}

.baghchal-point-capture {
  box-shadow: inset 0 0 0 3px var(--accent);
}

.baghchal-point-selected {
  border-color: var(--accent);
  border-width: 3px;
}

.baghchal-point.last-move {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.baghchal-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 78%;
  height: 78%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.baghchal-piece-tiger {
  background: #b5651d;
  border: 2px solid #000;
}

.baghchal-piece-goat {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Small disc icons for the side-choice picker and board placeholder,
   matching the board's own piece styling. */
.baghchal-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.baghchal-icon-disc-tiger {
  background: #b5651d;
  border: 2px solid #000;
}

.baghchal-icon-disc-goat {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Spider Solitaire board: a stock indicator and completed-sequences
   counter in a row above ten cascading tableau columns - the same
   overlap-via-negative-margin cascade layout as Klondike/FreeCell,
   just with more (narrower) columns since Spider deals 10 instead of
   7/8. Suits are told apart by glyph shape, never red/black coloring,
   matching Klondike/FreeCell. Unlike those two, `.spider-card` and
   `.spider-empty-slot` do NOT use CSS `aspect-ratio` - their height is
   set explicitly in JS from the measured column width instead
   (spidersolitaire-app.js: ensureSpiderCardAspectRatio), since
   `aspect-ratio` is unreliable on E-Ink browsers. No CSS min-height
   fallback here (Klondike/FreeCell have none either): `min-height`
   always wins over a smaller explicit `height`, so on a narrow board
   (a column under 80px wide, well within Tolino-class screen widths)
   it silently overrode the correctly-computed, narrower aspect-ratio
   height and stretched every card. */

.spider-top-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
max-width: 900px;
  margin: 0 auto 0.6rem;
  flex-wrap: wrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.spider-top-row > * {
  margin: 0 1rem 1rem 0;
}

.spider-top-row > *:last-child {
  margin-right: 0;
}

.spider-cell {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.6rem;
  height: 3.6rem;
  padding: 0 0.5rem;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  font-weight: 700;
  font-size: 1rem;
  box-sizing: border-box;
  cursor: pointer;
}

.spider-stock-blocked {
  color: var(--muted);
  cursor: not-allowed;
}

.spider-sequences {
  font-weight: 700;
  font-size: 1rem;
  cursor: default;
}

.spider-columns {
  display: flex;
max-width: 900px;
  margin: 0 auto;
  align-items: flex-start;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.spider-columns > * {
  margin: 0 0.25rem 0.4rem 0;
}

.spider-columns > *:last-child {
  margin-right: 0;
}

.spider-column {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.spider-card {
  position: relative;
  width: 100%;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  box-sizing: border-box;
  margin-top: -1.5rem;
  padding: 0.15rem 0 0 0.25rem;
  text-align: left;
  font-weight: 700;
  font-size: 0.7rem;
  font-size: clamp(0.55rem, 2vw, 0.85rem);
  cursor: pointer;
}

.spider-card:first-child {
  margin-top: 0;
}

.spider-card-back {
  background: repeating-linear-gradient(45deg, #fff, #fff 4px, var(--border) 4px, var(--border) 5px);
}

.spider-card-selected {
  outline: 3px solid var(--border);
  outline-offset: -2px;
}

.spider-empty-slot {
  display: block;
  width: 100%;
  border: var(--border-width) dashed var(--muted);
  border-radius: 4px;
  box-sizing: border-box;
}

.spider-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.8em;
  height: 1.4em;
  border: 2px solid var(--border);
  border-radius: 3px;
  box-sizing: border-box;
  font-size: 0.65rem;
  font-weight: 700;
}

/* Tablut board: a 9x9 float-grid (same technique as hnefatafl above -
   note the explicit width override below, since the shared .square
   base class hardcodes 12.5% for an 8-column board). Sides are told
   apart structurally, not by color alone: attackers are a filled
   disc, defenders an outlined disc, and the king gets his own crown
   marker on top of a defender disc - the same convention used for
   the smaller Brandub board. */

#tablut-board {
  display: block;
  width: 100%;
  max-width: 460px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.tablut-square {
  width: calc(100% / 9);
  background: var(--square-light);
  border: 1px solid var(--border);
}

.tablut-square-movable {
  background: var(--square-dark);
}

.tablut-square.last-move {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.tablut-square-throne,
.tablut-square-corner {
  background: var(--square-dark);
}

.tablut-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 74%;
  height: 74%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.tablut-piece-attacker {
  background: #111;
  border: 2px solid #000;
}

.tablut-piece-defender {
  background: var(--square-light);
  border: 2px solid var(--border);
}

.tablut-piece-king::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 48%;
  height: 48%;
  transform: translate(-50%, -50%);
  clip-path: polygon(0% 100%, 0% 40%, 25% 60%, 50% 30%, 75% 60%, 100% 40%, 100% 100%);
  background: var(--border);
  box-sizing: border-box;
}

/* Small icons for the side-choice picker and board placeholder,
   matching the board's own piece styling. */
.tablut-icon-piece {
  display: inline-flex;
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.tablut-icon-piece-attacker {
  background: #111;
  border: 2px solid #000;
}

.tablut-icon-piece-defender {
  background: var(--square-light);
  border: 2px solid var(--border);
}

/* Abalone board: a 61-cell hexagon rendered as plain circular buttons
   at hex-grid positions (see abalone-app.js's cube-coordinate pixel
   math) - round holes/marbles read just as clearly on e-ink as true
   hexagon cells and are far simpler to get pixel-perfect on an old
   WebView. Height is JS-enforced from the measured width (see
   ensureAbaloneBoardAspect) rather than via `aspect-ratio`, which some
   E-Ink browsers silently ignore. */
.abalone-board-wrap {
  max-width: 640px;
  margin: 0 auto;
}

.abalone-board {
  position: relative;
  width: 100%;
  box-sizing: border-box;
}

.abalone-cell {
  position: absolute;
  transform: translate(-50%, -50%);
  background: var(--square-light);
  border: 2px solid var(--border);
  border-radius: 50%;
  padding: 0;
  box-sizing: border-box;
  cursor: pointer;
  z-index: 2;
}

.abalone-cell-movable {
  background: var(--square-dark);
}

.abalone-cell-selected {
  border-color: var(--accent);
  border-width: 3px;
}

/* A push destination: dashed border on top of the movable shading, so
   "this can capture" is conveyed structurally rather than by color
   alone. */
.abalone-cell-sumito {
  border-style: dashed;
  border-width: 3px;
  border-color: var(--accent);
}

.abalone-cell.last-move {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.abalone-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 78%;
  height: 78%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.abalone-piece-black {
  background: #111;
  border: 2px solid #000;
}

.abalone-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own marble styling. */
.abalone-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.abalone-icon-disc-black {
  background: #111;
  border: 2px solid #000;
}

.abalone-icon-disc-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* The current-selection hint and its "Clear selection" button: plain
   stacked content, margin-based spacing rather than flexbox `gap`
   (unsupported on the Tolino's Chrome 61 WebView). */
.abalone-selection-controls {
  margin-top: 0.6rem;
}

.abalone-selection-controls button {
  margin: 0 0.5rem 0.5rem 0;
}

/* Worked-example diagram on the rules page. Flexbox `gap` fallback:
   margin-based spacing instead, since some E-Ink browsers (Tolino
   confirmed) don't support `gap` on a flex container and render items
   with zero space between them. */
.abalone-example-row {
  display: flex;
  flex-wrap: wrap;
  margin: 0 0 1rem -1.5rem;
}

.abalone-example-figure {
  margin: 0 0 1rem 1.5rem;
  max-width: 300px;
}

.abalone-example-figure svg {
  display: block;
  max-width: 100%;
  height: auto;
}

.abalone-example-figure figcaption {
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 0.4rem;
}


/* Pyramid Solitaire board: stock/waste/redeal row above a 7-row card
   triangle. The triangle itself uses absolute positioning sized in
   pixels by pyramidsolitaire-app.js's layoutPyramidBoard (measure the
   container's width, then set an explicit pixel height and per-card
   left/top/width/height) rather than CSS aspect-ratio, which isn't
   supported on the older E-Ink browsers this app targets. Lower rows
   get a higher z-index in that same JS so they visually sit in front,
   overlapping the row above - matching how a real card pyramid looks.
   Suits are told apart by glyph shape (spade/club vs. heart/diamond),
   never by red/black coloring, matching Klondike/FreeCell. All 28
   pyramid cards are dealt face-up (the standard rule for this game),
   so a covered card just gets a dimmed, non-interactive look instead
   of a hatched face-down back like the stock pile does. */

.pyramidsolitaire-top-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: 640px;
  margin: 0 auto 0.6rem;
  flex-wrap: wrap;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.pyramidsolitaire-top-row > * {
  margin: 0 1rem 1rem 0;
}

.pyramidsolitaire-top-row > *:last-child {
  margin-right: 0;
}

.pyramidsolitaire-stockwaste {
  display: flex;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.pyramidsolitaire-stockwaste > * {
  margin: 0 0.3rem 0.3rem 0;
}

.pyramidsolitaire-stockwaste > *:last-child {
  margin-right: 0;
}

.pyramidsolitaire-cell {
  width: 2.6rem;
  height: 3.6rem;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  font-weight: 700;
  font-size: 1rem;
  box-sizing: border-box;
  cursor: pointer;
}

.pyramidsolitaire-cell:disabled {
  cursor: default;
}

.pyramidsolitaire-card-back {
  background: repeating-linear-gradient(45deg, #fff, #fff 4px, var(--border) 4px, var(--border) 5px);
}

.pyramidsolitaire-board {
  position: relative;
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  box-sizing: border-box;
}

.pyramidsolitaire-card {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: #fff;
  box-sizing: border-box;
  font-weight: 700;
  font-size: 0.85rem;
  font-size: clamp(0.6rem, 2.3vw, 0.9rem);
  padding: 0;
  cursor: pointer;
}

.pyramidsolitaire-card-covered {
  background: var(--square-dark);
  color: var(--muted);
  cursor: default;
}

.pyramidsolitaire-card-selected {
  outline: 3px solid var(--border);
  outline-offset: 1px;
}

.pyramidsolitaire-card-gone {
  display: none;
}

.pyramidsolitaire-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.8em;
  height: 1.4em;
  border: 2px solid var(--border);
  border-radius: 3px;
  box-sizing: border-box;
  font-size: 0.65rem;
  font-weight: 700;
}

/* Surakarta board: a plain 6x6 grid of points, square (unlike Fanorona's
   wider-than-tall 5x9), so it reuses the same percentage-based
   absolute-positioning technique but with equal width/height totals.
   The board's namesake "loop tracks" - the 4 corner arcs that let a
   capturing slide curve off one edge and re-enter along a perpendicular
   line - are drawn as curved SVG paths in the same non-interactive
   layer as the ordinary grid lines, under the point buttons (see
   surakarta-app.js's buildSurakartaBoardDOM). Pieces use the same
   filled-disc-vs-outlined-disc convention as everywhere else in this
   app. */

.surakarta-board {
  position: relative;
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  background: var(--square-light);
  border: 3px solid var(--border);
  box-sizing: border-box;
}

.surakarta-lines {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}

.surakarta-lines line {
  stroke: var(--border);
  stroke-width: 2;
}

/* The 4 corner loop-track arcs, plus their short straight stub lines
   connecting the board's own edge points out to where each curve
   begins - drawn a little thicker/lighter than the plain grid lines so
   the loop tracks read as a visually distinct feature, per the task's
   emphasis that this is the game's defining (and least obvious)
   mechanic. */
.surakarta-loop-arc {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3;
  opacity: 0.75;
}

.surakarta-loop-stub {
  stroke: var(--accent);
  stroke-width: 2;
  opacity: 0.55;
}

.surakarta-point {
  position: absolute;
  transform: translate(-50%, -50%);
  background: var(--square-light);
  border: 2px solid var(--border);
  border-radius: 50%;
  padding: 0;
  box-sizing: border-box;
  cursor: pointer;
  z-index: 2;
}

.surakarta-point-movable {
  background: var(--square-dark);
}

.surakarta-point-capturable {
  box-shadow: inset 0 0 0 3px var(--accent);
}

.surakarta-point-selected {
  border-color: var(--accent);
  border-width: 3px;
}

.surakarta-point.last-move {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.surakarta-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 76%;
  height: 76%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.surakarta-piece-black {
  background: #111;
  border: 2px solid #000;
}

.surakarta-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.surakarta-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.surakarta-icon-disc-black {
  background: #111;
  border: 2px solid #000;
}

.surakarta-icon-disc-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Worked-example loop-capture diagram on the rules page: a single
   wider figure (the loop makes for a wide diagram) rather than the
   side-by-side pair Fanorona uses for its two capture types. */
.surakarta-example-figure {
  max-width: 100%;
  margin: 0 0 1rem 0;
}

.surakarta-example-figure svg {
  display: block;
  max-width: 100%;
  height: auto;
}

.surakarta-example-figure figcaption {
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 0.4rem;
}

/* Hashiwokakero (Bridges) board: islands scattered over a sparse grid
   rather than a filled one, so - like Fanorona/Hex/Quoridor/Go - it's
   rendered as absolutely-positioned circular island buttons over a
   single SVG line layer, with the container's height JS-enforced from
   its measured width each time it's built or the window resizes (CSS
   `aspect-ratio` is unreliable on E-Ink browsers; see hashi-app.js's
   ensureHashiBoardAspect). Every candidate bridge connection is drawn as
   an SVG line UNDER the islands, same as Fanorona's connection lines,
   but unlike Fanorona's purely decorative lines these ARE the primary
   clickable element: an invisible wide "hit" line sits under a thin
   visible one (or two, offset apart, for a double bridge) that's shown
   or hidden as bridges are placed/removed. */

#hashi-board {
  display: block;
  position: relative;
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  background: var(--square-light);
  border: var(--border-width) solid var(--border);
  box-sizing: border-box;
  overflow: hidden;
}

.hashi-lines {
  display: block;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.hashi-edge-hit {
  stroke: rgba(0, 0, 0, 0.01);
  stroke-width: 22;
  cursor: pointer;
}

.hashi-edge-line {
  stroke: var(--text);
  stroke-width: 6;
  pointer-events: none;
}

.hashi-island {
  position: absolute;
  border-radius: 50%;
  border: 3px solid var(--border);
  background: #fff;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
}

.hashi-island-value {
  font-size: 1rem;
  font-size: clamp(0.7rem, 3.4vw, 1.3rem);
  font-weight: 800;
  line-height: 1;
  pointer-events: none;
}

.hashi-island-selected {
  border-color: var(--accent);
  border-width: 4px;
  box-shadow: 0 0 0 3px var(--accent) inset;
}

/* An island whose current bridge count already matches its required
   number - the positive counterpart to Kakuro's conflict highlighting
   (see .kakuro-cell-conflict): a filled disc instead of an error
   outline, since here reaching the target is progress, not a mistake. */
.hashi-island-satisfied {
  background: var(--border);
}

.hashi-island-satisfied .hashi-island-value {
  color: #fff;
}

/* An island with MORE bridges than its number allows - always a mistake
   to fix, flagged the same dashed-outline way KakuroCore flags an
   overshot run sum. */
.hashi-island-over {
  outline: 2px dashed var(--border);
  outline-offset: 2px;
}

.hashi-icon {
  display: inline-block;
  width: 1.4em;
  height: 1.4em;
  border-radius: 50%;
  border: 2px solid var(--border);
  box-sizing: border-box;
  background: #fff;
}



/* Fleet Battle: two 10x10 grids (own fleet + enemy waters), each an 11-
   column CSS grid (a fixed-width row/column-label column plus the 10
   1fr board-cell columns). CSS grid `gap` is fine on this project's
   target E-Ink browsers (unlike flexbox `gap`, which several of this
   file's other rules work around with margin-based spacing instead), so
   no fallback is needed for the small gutters below. Cell WIDTH is left
   to the grid engine's own 1fr distribution; cell HEIGHT is set from JS
   (see ensureFbSquareCells in fleetbattle-app.js) once the grid has been
   measured, the same JS-enforced-square-cell approach checkers/kakuro
   use, since `aspect-ratio` isn't reliable on this project's target E-Ink
   browsers. */

.fb-boards {
  margin-top: 0.5rem;
}

.fb-board-block {
  margin-bottom: 1.25rem;
}

.fb-board-block:last-child {
  margin-bottom: 0;
}

.fb-board-block h2 {
  font-size: 1rem;
  margin: 0 0 0.4rem;
}

.fb-grid-wrap {
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
}

.fb-grid {
  display: grid;
  grid-template-columns: 1.7em repeat(10, 1fr);
  gap: 1px;
  width: 100%;
  border: 2px solid var(--border);
  background: var(--border);
  box-sizing: border-box;
}

.fb-label,
.fb-corner {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.65rem;
  color: var(--muted);
  background: var(--bg-alt);
}

.fb-cell {
  position: relative;
  display: block;
  width: 100%;
  background: var(--square-light);
  border: none;
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  cursor: default;
}

.fb-cell.fb-clickable:not(:disabled) {
  cursor: pointer;
}

/* Own ship, not yet hit - a plain filled square (hull). */
.fb-cell.fb-ship::before {
  content: "";
  position: absolute;
  top: 14%;
  left: 14%;
  width: 72%;
  height: 72%;
  background: var(--square-dark);
  border: 1px solid var(--border);
  box-sizing: border-box;
}

/* Miss - a small hollow ring, distinct from a hit without relying on color. */
.fb-cell.fb-miss::before {
  content: "";
  position: absolute;
  top: 37%;
  left: 37%;
  width: 26%;
  height: 26%;
  border-radius: 50%;
  border: 2px solid var(--border);
  box-sizing: border-box;
}

/* Hit (ship not yet fully sunk) - a filled circle. */
.fb-cell.fb-hit::before {
  content: "";
  position: absolute;
  top: 18%;
  left: 18%;
  width: 64%;
  height: 64%;
  border-radius: 50%;
  background: #000;
  border: 2px solid #000;
  box-sizing: border-box;
}

/* Sunk ship - every cell gets a diagonal hatch fill plus an inverted
   ring marker, the same "distinct pattern, not just a color" technique
   as .amazons-icon-burned above, so a fully destroyed ship reads as
   clearly different from a plain in-progress hit. */
.fb-cell.fb-sunk {
  background-image: repeating-linear-gradient(45deg, #000 0, #000 2px, transparent 2px, transparent 6px);
}

.fb-cell.fb-sunk::after {
  content: "";
  position: absolute;
  top: 27%;
  left: 27%;
  width: 46%;
  height: 46%;
  border-radius: 50%;
  background: var(--square-light);
  border: 2px solid #000;
  box-sizing: border-box;
}

.fb-cell.fb-last {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
}

.fb-legend {
  margin-top: 0.75rem;
}

.fb-legend-row {
  display: flex;
  align-items: center;
  margin: 0 0 0.35rem;
  font-size: 0.85rem;
}

.fb-legend-mark {
  position: relative;
  display: inline-block;
  flex-shrink: 0;
  width: 1.5em;
  height: 1.5em;
  margin-right: 0.5rem;
  border: 1px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
}

.fb-placement-status {
  margin-bottom: 0.5rem;
  font-weight: 600;
}

/* Skyscrapers board: a real CSS grid (n+2) x (n+2), unlike the other
   puzzle boards' float-grid technique - a grid is a natural fit here
   since a row/column of edge-clue cells needs to sit flush around all
   four sides of the board, not just the fillable n x n interior. Grid
   `gap` is fine to use on E-Ink per this project's compatibility notes
   (only flexbox `gap` needs the margin-based fallback); the 1px gap on
   top of a solid background acts as the grid line between cells. Each
   cell's height is set inline by JS to match its computed square width,
   since CSS `aspect-ratio` is unreliable on E-Ink browsers. */

#skyscrapers-board {
  display: grid;
  gap: 1px;
  width: 100%;
  max-width: 540px;
  margin: 0 auto;
  box-sizing: border-box;
  border: 3px solid var(--border);
  background: var(--border);
  overflow: hidden;
}

.skyscrapers-cell {
  background: var(--square-light);
  box-sizing: border-box;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.skyscrapers-corner {
  background: transparent;
}

.skyscrapers-clue-cell {
  background: transparent;
}

.skyscrapers-clue-value {
  font-weight: 700;
  line-height: 1;
  font-size: 0.9rem;
  font-size: clamp(0.7rem, 3vw, 1.1rem);
}

.skyscrapers-clue-ok .skyscrapers-clue-value {
  text-decoration: underline;
}

.skyscrapers-clue-violated .skyscrapers-clue-value {
  outline: 2px dashed var(--border);
  outline-offset: 2px;
  padding: 0 0.15em;
}

.skyscrapers-cell-board {
  cursor: pointer;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
}

.skyscrapers-cell-board.selected {
  outline: 3px solid var(--border);
  outline-offset: -3px;
}

.skyscrapers-cell-value {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 1.1rem;
  font-size: clamp(0.9rem, 3.5vw, 1.4rem);
  line-height: 1;
  pointer-events: none;
}

.skyscrapers-cell-conflict {
  outline: 2px dashed var(--border);
  outline-offset: -2px;
}

.skyscrapers-numpad {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  max-width: 540px;
  margin: 0.6rem auto 0;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.skyscrapers-numpad > * {
  margin: 0 0.4rem 0.4rem 0;
}

.skyscrapers-numpad > *:last-child {
  margin-right: 0;
}

.skyscrapers-numpad-btn {
  width: 2.4rem;
  height: 2.4rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  font-size: 1.1rem;
  font-weight: 700;
  cursor: pointer;
  box-sizing: border-box;
}

/* A tiny ascending skyline drawn with stacked background rectangles,
   anchored to the bottom of the icon box - no raw SVG needed, the same
   "one element, layered backgrounds" trick as .kakuro-icon's diagonal
   split, just with four bars instead of two triangles. */
.skyscrapers-icon {
  display: inline-block;
  width: 1.4em;
  height: 1.4em;
  border: 2px solid var(--border);
  box-sizing: border-box;
  background-repeat: no-repeat;
  background-image:
    linear-gradient(var(--border), var(--border)),
    linear-gradient(var(--border), var(--border)),
    linear-gradient(var(--border), var(--border)),
    linear-gradient(var(--border), var(--border));
  background-size: 16% 32%, 16% 52%, 16% 42%, 16% 78%;
  background-position: 8% 100%, 34% 100%, 60% 100%, 84% 100%;
}


/* Konane board: reuses .square's float-grid layout from the chess/
   checkers boards (see ensureKonaneSquareAspectRatio in konane-app.js for
   the JS-enforced row height), but unlike checkers every one of the 64
   squares is playable - Konane fills the whole board with alternating
   stones at the start, not just the dark squares - so both .light and
   .dark squares get a piece span and a click handler here. */

#konane-board {
  display: block;
  width: 100%;
  max-width: 760px;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  margin: 0 auto;
}

.konane-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 78%;
  height: 78%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  box-sizing: border-box;
  pointer-events: none;
}

.konane-piece-black {
  background: #111;
  border: 2px solid #000;
}

.konane-piece-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* Opening-ritual removal targets: a distinct reddish tint (the same
   "about to be removed" convention morris-app.js uses for its own mill-
   capture phase, see .morris-point-removable), so the two-stone opening
   ritual reads as visually different from an ordinary jump destination. */
.konane-square.konane-square-removable {
  background: #f3d4d4;
}

.konane-square.konane-square-movable {
  background: var(--square-dark);
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own piece styling. */
.konane-icon-disc {
  position: relative;
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
}

.konane-icon-disc-black {
  background: #111;
  border: 2px solid #000;
}

.konane-icon-disc-white {
  background: #fdfdfd;
  border: 2px solid var(--border);
}

/* The "continue or stop" jump-chain prompt: a plain stacked button,
   margin-based spacing rather than flexbox `gap` (unsupported on the
   Tolino's Chrome 61 WebView) - same pattern as Fanorona's own chain
   controls (.fanorona-chain-controls) just above. */
.konane-chain-controls {
  margin-top: 0.6rem;
}

.konane-chain-controls button {
  margin: 0 0.5rem 0.5rem 0;
}



/* Slitherlink board: a dot grid where every potential edge between two
   neighboring dots is a real, invisible-until-drawn <button> - one tap
   cycles it empty -> line -> marked-off -> empty. The dots, clue
   numbers and the loop lines themselves are a purely decorative SVG
   layer underneath those buttons (re-styled in place as state changes,
   never rebuilt), the mirror image of fanorona-app.js's board, which
   draws its fixed lines as decorative SVG *under* real button points -
   here it's the lines that are clickable and the dots that are just
   markers. Sized with a JS-enforced pixel height (see
   ensureSlitherlinkAspectRatio in slitherlink-app.js) rather than CSS
   `aspect-ratio`, which is unreliable on E-Ink browsers. */

#slitherlink-board {
  position: relative;
  display: block;
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
  border: 3px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  overflow: hidden;
  touch-action: manipulation;
}

.slitherlink-svg {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.slitherlink-dot {
  fill: var(--border);
}

.slitherlink-dot.slitherlink-conflict {
  fill: var(--square-light);
  stroke: var(--danger);
  stroke-width: 3;
}

.slitherlink-edge-line {
  stroke: var(--muted);
  stroke-opacity: 0.28;
  stroke-width: 3;
  stroke-linecap: round;
}

.slitherlink-edge-line.slitherlink-edge-on {
  stroke: var(--border);
  stroke-opacity: 1;
  stroke-width: 9;
}

.slitherlink-edge-line.slitherlink-edge-marked {
  stroke: var(--danger);
  stroke-opacity: 0.85;
  stroke-width: 4;
  stroke-dasharray: 6 8;
}

.slitherlink-clue-ring {
  fill: none;
  stroke: none;
}

.slitherlink-clue-ring.slitherlink-conflict {
  stroke: var(--danger);
  stroke-width: 2.5;
  stroke-dasharray: 5 4;
}

.slitherlink-clue-text {
  font-weight: 700;
  fill: var(--text);
  font-size: 34px;
  pointer-events: none;
  user-select: none;
}

.slitherlink-clue-text.slitherlink-conflict {
  fill: var(--danger);
}

.slitherlink-edge-btn {
  position: absolute;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
}

.slitherlink-edge-btn:focus {
  outline: 2px dashed var(--border);
  outline-offset: 1px;
}

.slitherlink-icon {
  display: inline-block;
  width: 1.4em;
  height: 1.4em;
  box-sizing: border-box;
  border: 2px solid var(--border);
  border-radius: 2px;
  position: relative;
}

.slitherlink-icon::before {
  content: "";
  position: absolute;
  top: 25%;
  right: 25%;
  bottom: 25%;
  left: 25%;
  border: 2px solid var(--border);
  border-right: none;
  border-bottom: none;
}

/* Ludo board: a single 15x15 CSS grid (CSS grid `gap` is fine on E-Ink
   browsers - only flexbox `gap` isn't - though this board doesn't use
   even that, every cell is simply placed at its own grid-area). Built
   from a JS-measured square container (no `aspect-ratio`), holding the
   52 shared track cells, each color's 5-cell home stretch, four 6x6
   home bases and a purely decorative center hub - see ludo-app.js for
   the coordinate tables. */

#ludo-board {
  display: grid;
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  border: var(--border-width) solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  position: relative;
}

.ludo-filler {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  box-sizing: border-box;
}

.ludo-cell {
  position: relative;
  background: var(--square-light);
  border: 1px solid var(--border);
  box-sizing: border-box;
  padding: 0;
  overflow: hidden;
}

.ludo-cell-safe::before {
  content: "";
  position: absolute;
  top: 30%;
  left: 30%;
  width: 40%;
  height: 40%;
  border: 2px solid var(--muted);
  transform: rotate(45deg);
  box-sizing: border-box;
  pointer-events: none;
}

/* Plain fixed tint colors rather than color-mix() (unsupported on the
   Chrome 61 WebView this project targets - see CLAUDE conventions). */
.ludo-cell-home-column-red { background: #f0d9d5; }
.ludo-cell-home-column-green { background: #d7ecdd; }
.ludo-cell-home-column-yellow { background: #f3e3c4; }
.ludo-cell-home-column-blue { background: #d8e2f0; }

.ludo-cell-movable {
  background: var(--square-dark);
}

/* Width/height 100% here is load-bearing, not decorative: .ludo-token
   below sizes itself as a PERCENTAGE of this element, and a percentage
   only resolves against a definite parent size. Without an explicit
   size here this box has none (absolute positioning alone doesn't give
   it one), so every on-board token was silently collapsing to its
   `min-width`/`min-height` floor - tiny and the same size regardless of
   the board's actual size - instead of scaling with the cell like every
   other board element. */
.ludo-cell-tokens {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  height: 100%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.ludo-cell-tokens > * {
  margin: 0 1px 0 0;
}

.ludo-cell-tokens > *:last-child {
  margin-right: 0;
}

.ludo-token {
  display: inline-block;
  width: 62%;
  height: 62%;
  min-width: 8px;
  min-height: 8px;
  border-radius: 50%;
  border: 2px solid var(--border);
  box-sizing: border-box;
}

.ludo-token-stacked {
  margin-left: -4px;
}

/* Four colors are indistinguishable on a grayscale E-Ink panel (Tolino
   confirmed - no color at all, not even a color->gray conversion), so
   each color is a distinct fill+border TEXTURE instead of a hue: red is
   a solid filled disc, the other three are hollow rings with a
   different border-style each. This same red/green/yellow/blue ->
   filled/solid-ring/dashed-ring/dotted-ring mapping is reused below for
   every other per-color indicator (home slots, turn chips, hub wedges,
   icon discs) so the pattern only needs to be learned once. */
.ludo-token-red { background: var(--border); }
.ludo-token-green { background: var(--square-light); }
.ludo-token-yellow { background: var(--square-light); border-style: dashed; }
.ludo-token-blue { background: var(--square-light); border-style: dotted; }

.ludo-home-base {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 10%;
  padding: 18%;
  box-sizing: border-box;
  border: 1px solid var(--border);
}

.ludo-home-base-red { background: #f5e5e2; }
.ludo-home-base-green { background: #e2f0e6; }
.ludo-home-base-yellow { background: #f7ecd4; }
.ludo-home-base-blue { background: #e3e9f3; }

.ludo-home-slot {
  border-radius: 50%;
  border: 2px solid var(--border);
  background: var(--square-light);
  box-sizing: border-box;
  padding: 0;
  cursor: pointer;
}

/* Same filled/solid-ring/dashed-ring/dotted-ring texture as .ludo-token-*
   (see comment there) - no color, since a grayscale panel can't show one. */
.ludo-home-slot-filled.ludo-home-slot-red { background: var(--border); }
.ludo-home-slot-filled.ludo-home-slot-green { background: var(--square-light); }
.ludo-home-slot-filled.ludo-home-slot-yellow { background: var(--square-light); border-style: dashed; }
.ludo-home-slot-filled.ludo-home-slot-blue { background: var(--square-light); border-style: dotted; }

.ludo-home-slot-movable {
  box-shadow: 0 0 0 3px var(--square-dark) inset;
}

/* The center hub's decorative cells (see ludo-app.js for why its 4
   corners are ordinary track-cell buttons instead, and not part of
   this overlay). */
.ludo-hub-wedge {
  border: 1px solid var(--border);
  box-sizing: border-box;
  pointer-events: none;
}

/* Same filled/solid-ring/dashed-ring/dotted-ring texture as .ludo-token-*
   (see comment there). Border is thickened to 2px so the ring styles
   still read as a marked wedge rather than blending into the plain
   1px-bordered cells around it. */
.ludo-hub-wedge-red { background: var(--border); }
.ludo-hub-wedge-green { background: var(--square-light); border-width: 2px; }
.ludo-hub-wedge-yellow { background: var(--square-light); border-width: 2px; border-style: dashed; }
.ludo-hub-wedge-blue { background: var(--square-light); border-width: 2px; border-style: dotted; }

.ludo-hub-center {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--square-light);
  border: 1px solid var(--border);
  box-sizing: border-box;
  pointer-events: none;
}

/* One <span> per active color, wrapped into a 2-column grid (2x1 for a
   2-player game, 2x2 for 3-4 players) rather than one long space-joined
   line - that's what lets this sit at a readable font-size instead of
   the illegibly tiny one a single "R:0 G:0 Y:0 B:0" line would need to
   fit this single board cell. */
.ludo-finished-tally {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  justify-items: center;
  align-items: center;
  font-size: 0.65rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1.15;
}

/* Turn indicator: one chip per active color, the current player's chip
   highlighted - see the board header in ludo.html. */
.ludo-turn-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.3rem 0;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.ludo-turn-indicator > * {
  margin: 0 0.3rem 0.3rem 0;
}

.ludo-turn-indicator > *:last-child {
  margin-right: 0;
}

.ludo-turn-chip {
  display: inline-block;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  border: 2px solid var(--border);
  box-sizing: border-box;
  opacity: 0.45;
}

/* Same filled/solid-ring/dashed-ring/dotted-ring texture as .ludo-token-*
   (see comment there). */
.ludo-turn-chip-red { background: var(--border); }
.ludo-turn-chip-green { background: var(--square-light); }
.ludo-turn-chip-yellow { background: var(--square-light); border-style: dashed; }
.ludo-turn-chip-blue { background: var(--square-light); border-style: dotted; }

.ludo-turn-chip-active {
  opacity: 1;
  box-shadow: 0 0 0 3px var(--square-dark);
}

/* Black text with a white halo reads on both the solid-black (red) chip
   and the light-filled (green/yellow/blue) chips, unlike a single fixed
   text color which would only work on one of the two fills. */
.ludo-turn-chip-ai::after {
  content: "AI";
  display: block;
  font-size: 0.45rem;
  font-weight: 700;
  line-height: 1.1rem;
  text-align: center;
  color: var(--border);
  text-shadow: 0 0 2px #fff, 0 0 2px #fff, 0 0 2px #fff, 0 0 2px #fff;
}

.ludo-dice-area {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 0.5rem;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.ludo-dice-area > * {
  margin: 0 0.6rem 0.6rem 0;
}

.ludo-dice-area > *:last-child {
  margin-right: 0;
}

.ludo-dice-display {
  display: flex;
  align-items: center;
  justify-content: center;
}

.ludo-die {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  font-weight: 700;
  background: #fff;
}

/* Small disc icons for the color-choice picker and board placeholder,
   matching the board's own token styling. */
.ludo-icon-disc {
  display: inline-block;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
  border: 2px solid var(--border);
}

/* Same filled/solid-ring/dashed-ring/dotted-ring texture as .ludo-token-*
   (see comment there). */
.ludo-icon-disc-red { background: var(--border); }
.ludo-icon-disc-green { background: var(--square-light); }
.ludo-icon-disc-yellow { background: var(--square-light); border-style: dashed; }
.ludo-icon-disc-blue { background: var(--square-light); border-style: dotted; }

/* Print view for the grid puzzles (Sudoku/Kakuro/Nonogram): print exactly
   the puzzle grid, nothing else - not the site chrome around it, not the
   on-screen number pad / mode toggle a sheet of paper has no use for.
   Scoped to body.printable-puzzle so it can't affect any other page. */
@media print {
  body.printable-puzzle .app-header,
  body.printable-puzzle .site-nav,
  body.printable-puzzle #settings-panel,
  body.printable-puzzle #board-actions,
  body.printable-puzzle #game-result,
  body.printable-puzzle #board-info,
  body.printable-puzzle .site-footer,
  body.printable-puzzle #print-puzzle-button,
  body.printable-puzzle #sudoku-numpad,
  body.printable-puzzle #kakuro-numpad,
  body.printable-puzzle #nonogram-mode-toggle {
    display: none !important;
  }
}

/* "New version available" banner - see update-banner.js. A normal,
   in-flow element at the very top of <body> rather than position:fixed,
   so it never overlaps the header underneath it and needs no dynamic
   height bookkeeping - it just pushes the rest of the page down while
   it's shown. */
.update-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  padding: 0.5rem 0.75rem;
  background: #fff;
  border-bottom: var(--border-width) solid var(--border);
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.update-banner > * {
  margin: 0 0.5rem 0 0;
}

.update-banner > *:last-child {
  margin-right: 0;
}

/* Also used directly (not inside .update-banner-lines) by
   error-banner.js's single-line message - keep its own flex sizing so
   that usage keeps working unchanged. */
.update-banner-text {
  flex: 1 1 auto;
  font-size: 0.85rem;
}

/* Wraps the main message and the optional "what's new" line as two
   stacked lines inside the same flex slot the plain text span used to
   occupy - see update-banner.js. */
.update-banner-lines {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}

.update-banner-whats-new {
  font-size: 0.75rem;
  color: var(--muted);
}

.update-banner-close {
  padding: 0 0.5rem;
  line-height: 1.4;
}

/* Opt-in high-contrast mode for low-light e-ink reading conditions -
   see high-contrast.js. Overrides the same shared CSS custom
   properties every card, button and board grid line across all 46
   games already draws from, so this needs no per-component styling. */
:root.high-contrast {
  --bg: #ffffff;
  --bg-alt: #ffffff;
  --muted: #000000;
  --border-width: 3px;
  --square-dark: #bdbdbd;
  --square-selected: #999999;
}

/* Settings menu - see settings-menu.js. Same fixed-overlay technique as
   .result-modal-overlay (already proven on a real Tolino: longhand
   top/right/bottom/left rather than the `inset` shorthand, which that
   device silently ignores), and the same card look as .result-modal,
   just taller and scrollable since this holds several sections instead
   of one message. */
.settings-modal-overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  z-index: 1000;
}

.settings-modal-overlay.hidden {
  display: none !important;
}

.settings-modal {
  background: #fff;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  max-width: 420px;
  width: 100%;
  max-height: 90vh;
  overflow-y: auto;
  text-align: left;
}

.settings-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 0 0 1rem 0;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
}

.settings-modal-header h2 {
  margin: 0;
  font-size: 1.25rem;
}

.settings-modal-close {
  min-width: 2rem;
  padding: 0.2rem 0.5rem;
}

.settings-section {
  margin-bottom: 1.25rem;
}

.settings-section:last-child {
  margin-bottom: 0;
}

.settings-section h3 {
  margin: 0 0 0.5rem 0;
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
}

.settings-section .lang-switch select {
  width: 100%;
}

/* Flexbox `gap` fallback: margin-based spacing instead, since some E-Ink browsers (Tolino confirmed) don't support `gap` on a flex container and render items with zero space between them. */
.settings-segmented {
  display: flex;
}

.settings-segmented > * {
  margin: 0 0.3rem 0 0;
}

.settings-segmented > *:last-child {
  margin-right: 0;
}

.settings-segmented button {
  flex: 1;
}

.settings-segmented button.active {
  border-width: calc(var(--border-width) * 2);
  font-weight: 800;
}

.settings-checkbox-row {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.settings-checkbox-row input {
  margin-right: 0.5rem;
}

.settings-hint {
  color: var(--muted);
  margin: 0 0 0.75rem 0;
}
