@charset "UTF-8";
/* ==========================================================================
   Bible Games — the board layer  (shared/board/board.css)
   --------------------------------------------------------------------------
   The parts of a BOARD GAME page that are the same whatever the board is: the
   frame the board sits in, the turn indicator, the captured tray, the control
   row, the move list, the rules sheet.

   It sits on shell.css exactly as quiz.css and grid.css do, and it adds a
   layer rather than replacing anything: the topbar, the Back link, the buttons
   and the end-of-round card all still come from the shell.

   HOW A PAGE LOADS IT

     <link rel="stylesheet" href="/biblegames/shared/theme.css?v=...">
     <link rel="stylesheet" href="/biblegames/shared/shell.css?v=...">
     <link rel="stylesheet" href="/biblegames/shared/board/board.css?v=...">
     <link rel="stylesheet" href="styles.css?v=...">

   In that order. Tokens, then the shell, then the board's shape, then the
   game's own identity — each free to override the one before it.

   --------------------------------------------------------------------------
   WHAT IS HERE AND WHAT IS NOT           (the Shared.md §5 test, applied)

   "Could a game reasonably want it different?"

     * The frame, the turn indicator, the control row, the move list, the
       rules sheet — NO. Every board game wants the same ones, and seventy-five
       copies is how they drift apart.  ->  here
     * The board's own colours, and its piece artwork — YES, obviously. Go
       stones and Mancala seeds are not the same object.  ->  each game's
       styles.css, by overriding the --board-* tokens in section 1.

   --------------------------------------------------------------------------
   THE LAYOUT CONTRACT (Standards.md §5)

   A game page must NEVER scroll, and nothing may ever scroll horizontally.
   The shell is a fixed-height flex column; this file adds two rows either side
   of .stage and puts the board INSIDE .stage, which is the one elastic row.

   A board game adds two things a quiz never had — a move list and a rules
   sheet — and neither is a row. Both are PANELS over the stage, opened by a
   button, because a row for either would take height from the board on every
   screen in order to be useful on none. A panel may scroll inside itself; the
   page still may not.

   The @media (max-height: 299px) exception is inherited from shell.css and
   extended here, and stays in step with MIN_NO_SCROLL_HEIGHT in
   BibleGames_Preview.py.

   --------------------------------------------------------------------------
   Contents
     1.  Tokens this layer adds
     2.  The board row, and how a board is sized
     3.  The board itself — cells, lines, pieces
     4.  Legal moves, the last move, focus
     5.  The turn indicator
     6.  The captured tray
     7.  The control row
     8.  Panels — the move list and the rules sheet
     9.  Responsive
     10. Reduced motion, forced colours, more contrast, print
   ========================================================================== */

/* -------------------------------------------------------------------------- */
/* 1. Tokens this layer adds                                                  */
/* -------------------------------------------------------------------------- */

/*
   A game overrides these in its own styles.css and changes nothing else. That
   is the whole customisation surface for a board's appearance, and it is
   deliberately small: seventy-five games each inventing their own class names
   is seventy-five games that cannot be restyled at once.
*/
:root {
  /* The board surface */
  --board-bg: rgba(18, 12, 42, 0.72);
  --board-edge: var(--panel-edge);
  --board-cell: rgba(255, 240, 205, 0.05);
  --board-cell-alt: rgba(255, 240, 205, 0.12);
  --board-line: rgba(255, 224, 160, 0.42);
  --board-line-width: 2;

  /* The two players. A game renames the colours; it does not rename the
     SHAPES, which come from sprites.js and are the signal that survives a
     colour-blind eye, a photocopy and forced colours. */
  --piece-0: #ffd45c;
  --piece-0-edge: #7a5313;
  --piece-1: #7fc8ff;
  --piece-1-edge: #143d5c;
  --piece-2: #9be89b;
  --piece-2-edge: #1d5220;
  --piece-3: #ff9c6e;
  --piece-3-edge: #6d2a12;

  /* Board furniture */
  --board-hint: rgba(255, 243, 176, 0.5);
  --board-last: var(--gold-2);
  --board-focus: var(--gold-1);
  --board-win: var(--ok);

  /* A track's jumps. The SHAPE is what tells them apart (see §3); these are
     the third signal, after the shape and the spoken label. */
  --board-ladder: #9be89b;
  --board-snake: #ff9c6e;
  --board-snake-eye: #6d2a12;

  /*
     A panel over the board must be OPAQUE, and this token exists because the
     first version was not.

     --panel-1 and --panel-2 are translucent, which is right for a panel over
     the page background and wrong for one over a board: the move list was
     readable, the board showed through it, and the gold pieces behind it
     turned brown. Every automated assertion passed on that page — no scroll,
     no overflow, no console error, seven moves in the list. Only the
     screenshot showed it.
  */
  --sheet-bg: linear-gradient(160deg, #241a4d, #140e33);

  --board-gap: 8px;
}

/* -------------------------------------------------------------------------- */
/* 2. The board row, and how a board is sized                                 */
/* -------------------------------------------------------------------------- */

/*
   THE BOARD IS NOT FORCED SQUARE, AND THAT IS THE FIX, NOT A COMPROMISE.

   The obvious approach is `aspect-ratio: 1/1` on a square box sized to the
   smaller viewport dimension. It is wrong for this set: Connect Four is 7x6,
   Fanorona is 9x5, a Snakes and Ladders track is 10x10 but the Royal Game of
   Ur is 8x3. Forcing a square would letterbox half the games inside a box we
   drew ourselves, and crop the wide ones.

   So the SVG keeps its OWN aspect ratio, from the viewBox the topology
   supplies, and `preserveAspectRatio="xMidYMid meet"` fits it inside whatever
   space the flex row gives it. The browser does the arithmetic, at every
   viewport, for all eight topologies, with no JavaScript and no media query.
*/
.board__frame {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2px;
}

.board {
  display: block;
  width: 100%;
  height: 100%;
  /* Without this an SVG in a flex child can refuse to shrink and push the
     control row off the bottom — the same fault min-height: 0 fixes on the
     stage, one level down. */
  min-width: 0;
  min-height: 0;
  overflow: visible;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.board__surface {
  fill: var(--board-bg);
  stroke: var(--board-edge);
  stroke-width: 2;
  rx: 10;
}

/* -------------------------------------------------------------------------- */
/* 3. The board itself — cells, lines, pieces                                 */
/* -------------------------------------------------------------------------- */

.board__cell {
  fill: var(--board-cell);
  stroke: var(--board-line);
  stroke-width: 1;
  transition: fill var(--dur) ease;
}

.board__cell.is-alt {
  fill: var(--board-cell-alt);
}

/* A dead cell — a hole in a peg board, a masked square. Drawn, but not part of
   the game, and never focusable. */
.board__cell.is-void {
  fill: none;
  stroke: none;
  pointer-events: none;
}

/* The lines of a graph board. On Alquerque the lines ARE the rules: a diagonal
   move looks legal only because a line is drawn there. */
.board__link {
  stroke: var(--board-line);
  stroke-width: var(--board-line-width);
  stroke-linecap: round;
  fill: none;
}

/* The route of a track board, drawn under the squares. */
.board__route {
  stroke: var(--board-line);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  opacity: 0.5;
}

/*
   A LADDER AND A SNAKE ARE TOLD APART BY SHAPE, AND ONLY THEN BY COLOUR.

   Standards.md §2, and this is the case it was written for: on a track board
   the snakes and the ladders are the only things that change what happens to
   you, and the traditional drawing tells them apart with red against green —
   which is the single commonest confusion there is.

   So a ladder is a straight double rail with rungs across it, and a snake is a
   wavy line with a round head on the square that swallows you. Different
   shapes, told apart in greyscale, in a photocopy and under forced colours.
   The colours below are the third signal, not the first, and topo-track's
   label() says the same thing in words for anyone who can see neither.

   Neither takes a click: the square underneath is the hit target, and a ladder
   drawn across four squares must not stop any of them being tapped.
*/
.board__ladder {
  stroke: var(--board-ladder);
  stroke-width: 5;
  stroke-linecap: round;
  fill: none;
  pointer-events: none;
}

.board__ladder--rungs {
  stroke-width: 3.5;
  opacity: 0.85;
}

.board__snake {
  stroke: var(--board-snake);
  stroke-width: 7;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  pointer-events: none;
}

.board__snake-head {
  fill: var(--board-snake);
  stroke: var(--board-snake-eye);
  stroke-width: 3;
  pointer-events: none;
}

/*
   Pieces never take a click. The cell underneath is the hit target and the
   focusable element, so a piece sitting on top of it must be transparent to
   the pointer — otherwise tapping a piece to pick it up does nothing, which is
   the first thing anybody tries.
*/
.board__piece {
  stroke-width: 3;
  paint-order: stroke;
  pointer-events: none;
  transition: transform var(--dur) ease;
}

.board__piece.is-side-0 { fill: var(--piece-0); stroke: var(--piece-0-edge); }
.board__piece.is-side-1 { fill: var(--piece-1); stroke: var(--piece-1-edge); }
.board__piece.is-side-2 { fill: var(--piece-2); stroke: var(--piece-2-edge); }
.board__piece.is-side-3 { fill: var(--piece-3); stroke: var(--piece-3-edge); }

/* The piece a player has picked up and not yet put down. */
.board__piece.is-lifted {
  opacity: 0.55;
}

/* Text drawn on the board — a square's number on a track, a stack's height. */
.board__ink {
  fill: var(--text-dim);
  font-family: var(--font-ui);
  font-size: 26px;
  font-weight: 600;
  text-anchor: middle;
  dominant-baseline: central;
  pointer-events: none;
  user-select: none;
}

/*
   A NUMBER IN THE CORNER, WHICH IS WHERE A REAL BOARD PRINTS IT.

   The middle of a square is where the ladders, the snakes and the counters all
   are. Centred, a track number under a ladder rail measured 1.23:1 — cream on
   light green — and a number under a pawn could not be seen at all. In the
   corner it sits on the plain square, it survives a counter standing on it, and
   it is where a child looks for it.

   The geometry decides this, not the game: topo-track answers inkAt() and
   render.js adds this class when it does.
*/
.board__ink--corner {
  text-anchor: start;
  dominant-baseline: hanging;
  font-size: 22px;
}

/*
   The chip a corner number is printed on.

   A diagonal ladder still clips the corner of a square on its way past, and a
   cream number over a pale green rail measures about 1.2:1 — on those few
   squares the number becomes the hardest thing on the board to read, which is
   the wrong way round for the thing a child navigates by.

   A DARKENING rather than a colour, so it vanishes on the ninety-odd plain
   squares whatever shade they are, and shows only where something has been
   drawn across the corner. That is what a printed board does: the number is on
   the square, and the artwork is painted around it.
*/
.board__ink-chip {
  fill: rgba(6, 4, 20, 0.55);
  pointer-events: none;
}

/* -------------------------------------------------------------------------- */
/* 4. Legal moves, the last move, focus                                       */
/* -------------------------------------------------------------------------- */

/*
   Three different things are marked on a board at once, and they must not be
   mistakable for one another by shape OR by colour alone:

     a legal move    a soft filled dot in the middle of the cell
     the last move   a ring around the cell's edge
     focus           a bright square bracket outside the cell

   Different shapes, different positions, different colours. A child using one
   signal still has the other two.
*/
.board__hint {
  fill: var(--board-hint);
  pointer-events: none;
  transition: opacity var(--dur) ease;
}

.board__last {
  fill: none;
  stroke: var(--board-last);
  stroke-width: 3;
  stroke-dasharray: 6 5;
  pointer-events: none;
}

/*
   The focus ring is DRAWN rather than left to `outline`.

   `outline` on an SVG child is inconsistent across browsers — some draw it
   around the element's bounding box, some around the whole SVG, some not at
   all — and Standards.md §2 requires a visible focus indicator that a keyboard
   or switch user can actually see. A drawn rectangle is the same everywhere.
   The CSS outline below stays as well, as a second belt for anything that
   renders the ring late.
*/
.board__focus {
  fill: none;
  stroke: var(--board-focus);
  stroke-width: 4;
  pointer-events: none;
}

.board__cell:focus {
  outline: 3px solid var(--board-focus);
  outline-offset: 2px;
}

.board__cell:focus:not(:focus-visible) {
  outline: none;
}

.board__cell:focus-visible {
  outline: 3px solid var(--board-focus);
  outline-offset: 2px;
}

/* The line that won the game. */
.board__win {
  fill: none;
  stroke: var(--board-win);
  stroke-width: 8;
  stroke-linecap: round;
  opacity: 0.85;
  pointer-events: none;
}

/*
   A PIECE YOU CAN PICK UP IS MARKED AT REST, AND NOT ONLY UNDER A POINTER.

   The dot above marks a square a piece can move TO. In a step game nothing is a
   destination until something has been lifted, so a board that marked only
   destinations showed a child nothing at all at the start of their turn. On a
   desktop the hover rule below hid that; on a phone there is no hovering, and
   twenty-five of the seventy-five games are step games.

   A rim rather than a second dot, and that is decision 9 rather than taste: a
   legal move is a DOT IN THE MIDDLE, a piece you may lift is a RIM AROUND THE
   EDGE, the last move is a DASHED RING and focus is a BRIGHT BRACKET OUTSIDE.
   Different shape, different position, different colour — so a child using any
   one of the three signals still has the other two.
*/
.board__cell.is-origin {
  stroke: var(--board-hint);
  stroke-width: 4;
}

/*
   Hover must be gated. On a touch screen the browser fakes a hover on tap and
   leaves the cell lit until you tap elsewhere — so every square a child has
   touched ends up glowing. Standards.md §2.
*/
@media (hover: hover) {
  .board__cell.is-playable:hover {
    fill: var(--board-cell-alt);
    cursor: pointer;
  }
}

/* -------------------------------------------------------------------------- */
/* 5. The turn indicator                                                      */
/* -------------------------------------------------------------------------- */

/*
   Whose turn it is, said three ways at once: the piece's SHAPE, the player's
   NAME, and the colour. Standards.md §2 — colour is never the only signal, and
   "it is the yellow player's turn" is unreadable to the one boy in twelve this
   rule exists for.
*/
.boardbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--board-gap);
  min-height: 34px;
}

.turn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 4px 12px;
  border: 1px solid var(--panel-edge);
  border-radius: 999px;
  background: linear-gradient(160deg, var(--panel-1), var(--panel-2));
  font-family: var(--font-ui);
  font-size: 0.92rem;
  color: var(--text);
}

.turn__shape {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
}

.turn__who {
  font-weight: 700;
}

.turn__what {
  color: var(--text-dim);
}

/* The game is over. Kept as a state on the same element so the indicator never
   disappears and shift the row's height. */
.turn.is-over {
  border-color: var(--panel-edge-lit);
}

/* -------------------------------------------------------------------------- */
/* 6. The captured tray                                                       */
/* -------------------------------------------------------------------------- */

.tray {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-family: var(--font-ui);
  font-size: 0.82rem;
  color: var(--text-dim);
}

.tray__group {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.tray__shape {
  width: 16px;
  height: 16px;
}

/* The count is the word half: "3 discs", never three shapes and a colour. */
.tray__count {
  font-variant-numeric: tabular-nums;
  color: var(--text);
  font-weight: 600;
}

/* -------------------------------------------------------------------------- */
/* 7. The control row                                                         */
/* -------------------------------------------------------------------------- */

.boardctl {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--board-gap);
}

/*
   THE ROW FOR MOVES THAT LAND ON NO SQUARE.

   A dice throw is a legal move with nothing to tap, and so is a pawn borne off
   the end of a track. Without a row for them the game reports legal moves and
   offers nothing to press, which is indistinguishable from a frozen board.

   It sits directly under the board rather than in the control row, because it
   is part of the TURN and Undo is not: a child looking for what to do next
   should find it beside the thing it acts on. The row collapses to nothing
   when there is nothing to offer, so a game that never has one pays no height.
*/
.boardactions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--board-gap);
}

.boardactions:empty {
  display: none;
}

/* Bigger and brighter than Undo, because it is the move. */
.board__action {
  min-height: 44px;
  padding: 0 22px;
  font-weight: 700;
  border-color: var(--panel-edge-lit);
  background: linear-gradient(180deg, #ffd45c, #e08c14);
  color: #241703;
}

@media (hover: hover) {
  .board__action:hover {
    background: linear-gradient(180deg, #ffe08a, #f09a1a);
  }
}

/* A disabled Undo must still be readable, not a grey smear — a child needs to
   see that the button exists and is simply not available yet. */
.boardctl .btn[disabled] {
  opacity: 0.45;
  cursor: default;
}

.boardctl .btn[disabled]:hover {
  border-color: var(--panel-edge);
}

/* The difficulty picker is a group of radio-like buttons, not a <select>,
   because a select on a phone opens a modal wheel for three options. */
.level {
  display: inline-flex;
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  overflow: hidden;
}

.level__btn {
  min-height: 44px;
  padding: 0 12px;
  border: 0;
  border-right: 1px solid var(--panel-edge);
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}

.level__btn:last-child {
  border-right: 0;
}

/* The chosen level is marked by weight and background as well as colour, and
   carries aria-pressed for anyone who cannot see either. */
.level__btn[aria-pressed="true"] {
  background: linear-gradient(180deg, #ffd45c, #e08c14);
  color: #241703;
  font-weight: 800;
}

/* -------------------------------------------------------------------------- */
/* 8. Panels — the move list and the rules sheet                              */
/* -------------------------------------------------------------------------- */

/*
   A panel covers the stage rather than taking a row of its own. It may scroll
   INSIDE itself; the page still may not. That is the same decision quiz.css
   made for the feedback panel, and for the same reason.
*/
.sheet {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--panel-edge-lit);
  border-radius: var(--radius);
  background: var(--sheet-bg);
  box-shadow: 0 18px 40px var(--panel-shadow);
}

.sheet__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--panel-edge);
}

.sheet__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1rem;
  color: var(--gold-2);
}

.sheet__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 8px 12px;
  font-family: var(--font-ui);
  font-size: 0.88rem;
  color: var(--text);
}

.sheet__body p {
  margin: 0 0 0.7em;
}

/* The stage has to be a positioning context for the panel to cover it. */
.stage {
  position: relative;
}

/*
   The move list is the transcript, and it is describe() made visible — the
   same sentences a screen reader hears. A game whose opponent moves in silence
   is unplayable without sight (01-Architecture.md §4), and this is the half of
   that a sighted player uses.
*/
.moves {
  margin: 0;
  padding: 0 0 0 2.6em;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  line-height: 1.55;
}

.moves li {
  margin: 0 0 2px;
  color: var(--text-dim);
}

.moves li::marker {
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.moves li:last-child {
  color: var(--text);
  font-weight: 600;
}

.moves__empty {
  margin: 0;
  color: var(--text-faint);
  font-style: italic;
}

/* -------------------------------------------------------------------------- */
/* 9. Responsive                                                              */
/* -------------------------------------------------------------------------- */

/*
   Phone landscape is the hard case for a board, not phone portrait, and it is
   the opposite of the quiz. A quiz is a column of text that wants width; a
   board is a shape that wants HEIGHT, and in landscape there is almost none.
   So the two rows either side of the board give up their padding first, and
   the board keeps every pixel it can.
*/
@media (max-height: 520px) {
  .boardbar {
    min-height: 0;
  }

  .turn {
    padding: 2px 10px;
    font-size: 0.85rem;
  }

  .tray {
    font-size: 0.76rem;
  }

  .boardctl .btn {
    min-height: 40px;
    padding: 0 12px;
    font-size: 0.88rem;
  }

  /* The action button keeps its 44px target where the ordinary controls give
     theirs up: it is the move, and a mis-tap on it costs a turn. */
  .board__action {
    padding: 0 18px;
  }
}

/*
   PHONE LANDSCAPE: THE CHROME MOVES BESIDE THE BOARD.

   This is the hard case for a board and it is the opposite of a quiz. A quiz is
   a column of text that wants width; a board is a shape that wants HEIGHT, and
   in landscape there is almost none — while there is more width than anything
   can use.

   Stacked, the chrome eats about 180 of a 360px window and Connect Four comes
   out 207x179: it passes every layout check, sits in the middle of a mostly
   empty screen, and its holes are thirty pixels across. Nothing is overflowing
   and nothing is scrolling. It is simply too small to play, which is the fault
   BibleGames_BoardPlay.py's "the board fills its space" check exists for, and
   the first version of it slipped past at 50% of the smaller side.

   Beside it, the same board is about 360x310 — three-quarters bigger on a side
   — with no change to the markup and nothing to maintain per game. The board
   spans every row of the left column; the turn, the actions, the controls and
   the hint stack down a fixed column on the right, and the last row is 1fr so
   any slack lands there rather than pushing the page taller.

   Held above 300px of height on purpose: below that the page is allowed to
   scroll (Standards.md §5) and the fixed-height board below takes over, and
   the two layouts would fight.
*/
@media (min-height: 300px) and (max-height: 520px) and (min-width: 520px) {
  .app {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(150px, 15rem);
    grid-template-rows: auto auto auto auto 1fr;
    column-gap: 12px;
    row-gap: 6px;
  }

  .topbar       { grid-column: 1 / -1; grid-row: 1; }
  .stage        { grid-column: 1; grid-row: 2 / -1; min-height: 0; }
  .boardbar     { grid-column: 2; grid-row: 2; }
  .boardactions { grid-column: 2; grid-row: 3; }
  .boardctl     { grid-column: 2; grid-row: 4; }
  .hint         { grid-column: 2; grid-row: 5; align-self: start; }

  /* The turn indicator is the one thing that must stay readable at a glance,
     so it keeps its size while the buttons give theirs up. */
  .boardbar {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }

  /* Two even columns rather than a wrapping row. Wrapped, four buttons of
     three different widths leave one stranded on a line of its own, which
     reads as a mistake; paired, they read as a keypad. */
  .boardctl {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
  }

  .boardctl .btn {
    width: 100%;
    padding: 0 8px;
  }

  .level {
    grid-column: 1 / -1;
  }

  .level__btn {
    flex: 1 1 0;
    padding: 0 6px;
  }
}

/* Narrow: the tray drops out rather than wrapping the row onto two lines and
   stealing the height back from the board. The counts are still in the move
   list and still announced, so nothing is lost that only exists here. */
@media (max-width: 420px) {
  .tray {
    display: none;
  }

  .boardctl {
    gap: 6px;
  }
}

/*
   Below 300px of viewport height the page may scroll — Standards.md §5. The
   board stops being the elastic row and takes a sensible fixed height, because
   a board squeezed into 120px is not a board.
*/
@media (max-height: 299px) {
  .board__frame {
    flex: 0 0 auto;
    height: 60vw;
    min-height: 220px;
  }

  .sheet {
    position: static;
    min-height: 220px;
  }
}

/* -------------------------------------------------------------------------- */
/* 10. Reduced motion, forced colours, more contrast, print                   */
/* -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .board__cell,
  .board__piece,
  .board__hint {
    transition: none !important;
  }

  .board__last {
    stroke-dasharray: none;
  }
}

/*
   Windows High Contrast discards the palette entirely. Every fill above
   becomes one of two system colours, so a disc and a ring would be two
   identical filled circles IF the shape were not carrying the signal.

   This is the case the shape vocabulary in sprites.js exists for, and the only
   thing needed here is to stop the browser flattening the pieces into the
   background and to keep the three markers apart by line style.
*/
@media (forced-colors: active) {
  .board__surface,
  .board__cell {
    forced-color-adjust: none;
    fill: Canvas;
    stroke: CanvasText;
  }

  .board__link,
  .board__route {
    forced-color-adjust: none;
    stroke: CanvasText;
  }

  /* Both become one colour, and that is fine: a straight rail with rungs and a
     wavy line with a head are still two different pictures. This is exactly
     what the shape was carrying the signal for. */
  .board__ladder,
  .board__snake {
    forced-color-adjust: none;
    stroke: CanvasText;
  }

  .board__snake-head {
    forced-color-adjust: none;
    fill: Canvas;
    stroke: CanvasText;
  }

  /* One side filled, the other hollow — so the two players stay apart even
     when both colours have become CanvasText. The shapes differ too; this is
     the belt as well as the braces. */
  .board__piece {
    forced-color-adjust: none;
    fill: Canvas;
    stroke: CanvasText;
    stroke-width: 4;
  }

  .board__piece.is-side-0 {
    fill: CanvasText;
    stroke: Canvas;
  }

  .board__focus {
    forced-color-adjust: none;
    stroke: Highlight;
    stroke-width: 5;
  }

  .board__last {
    forced-color-adjust: none;
    stroke: CanvasText;
  }

  .board__hint {
    forced-color-adjust: none;
    fill: Highlight;
  }

  .board__cell.is-origin {
    forced-color-adjust: none;
    stroke: Highlight;
    stroke-width: 4;
  }

  .sheet {
    border: 2px solid CanvasText;
    background: Canvas;
  }
}

@media (prefers-contrast: more) {
  :root {
    --board-line: rgba(255, 236, 190, 0.75);
    --board-cell-alt: rgba(255, 240, 205, 0.2);
  }

  .board__piece {
    stroke-width: 4;
  }
}

/* A board is worth printing — a child may want the position, or a teacher the
   rules sheet. Drop the chrome and let it fit the page. */
@media print {
  .boardctl,
  .tray,
  .beams {
    display: none !important;
  }

  .board__frame {
    height: auto;
  }

  .board__surface {
    fill: none;
    stroke: #000;
  }

  .board__cell {
    fill: none;
    stroke: #000;
  }

  .board__piece {
    stroke: #000;
  }
}
