/* static/styles/game.css */

/* ---------- Status Bar ---------- */
/* Both counters are grouped above the board. They used to be pushed to the
   left and right page edges, which put the right-hand one underneath the help
   sidebar when it opens. */
.status-row {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.75em;
  margin: 0.5em 0;
}

.status-counter {
  border: 1px solid var(--fg);
  border-radius: 6px;
  padding: 0.4em 0.8em;
  vertical-align: middle;

  text-align: center;
  white-space: nowrap;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  transition: background-color 0.2s ease, border-color 0.2s ease;
  font-variant-numeric: tabular-nums; /* the number must not jitter as it changes */
}

/* Icon and number read as one unit: the icon carries the label, so it sits
   tight against the value rather than at the edge of the pill. */
.status-icon {
  display: inline-block;
  margin-right: 0.35em;
  line-height: 1;
}

.status-glyph {
  vertical-align: -0.2em;
}

.status-value {
  font-weight: 600;
}

/* Available to assistive technology, invisible on screen. The status counters
   are an icon plus a bare number, which needs spelling out when read aloud. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.status-row [help-id]:hover {
  border: 1px groove var(--accent);
  background-color: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* ---------- Shared transitions ---------- */
.board button,
.btn {
  transition: background 0.2s, transform 0.05s, color 0.2s;
}

/* ---------- Board / Tile buttons ----------
   The board sizes itself to the space it is given. `container-type` makes this
   element the reference box, so `cqw` below is a percentage of the board area's
   own width rather than of the viewport -- the distinction that matters, since
   the page is capped at 1000px and gutters come off that.

   `safe center` rather than plain `center`: a centred flex item that overflows
   its container overflows equally in both directions, and the part that lands
   before the scroll origin cannot be reached by scrolling at all. `safe` drops
   back to start alignment exactly when that would happen, which is what makes
   the overflow: auto below actually able to reach every column. */
.board-container {
  display: flex;
  justify-content: center;
  justify-content: safe center;
  margin: 1em 0;
  container-type: inline-size;
  overflow-x: auto;
}

.board-stage {
  position: relative;
  /* Keep the table and Reveal overlay in one predictable stacking context.
     Table descendants otherwise create browser-dependent paint ordering. */
  isolation: isolate;
  flex: none;
  margin: 16px 0;
}

/* --cols is set by render.js from the game's own column count. The vertical
   margin doubles as the room the probe badges and anchor rings need to hang
   outside the top row without being clipped by the scroll container. */
.board {
  border-collapse: collapse;
  margin: 0;
  position: relative;
  z-index: 1;
}

.board td {
  padding: var(--tile-gap);
}

.board button {
  /* Each column costs one tile plus the td padding on either side. Divide the
     available width by the column count to get the largest tile that still
     fits, then take the comfortable maximum if that is bigger (small boards
     must not blow up into slabs) and the readable minimum if it is smaller
     (very wide boards scroll sideways instead of shrinking to nothing). */
  --tile-fit: calc((100cqw - var(--tile-gap) * 2 * var(--cols, 10)) / var(--cols, 10));
  --tile-size: max(var(--tile-min), min(var(--tile-max), var(--tile-fit)));

  position: relative;
  width: var(--tile-size);
  height: var(--tile-size);
  border: none;
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: var(--fg);
  /* Clue text scales with the tile, not with the viewport: a 25-column board on
     a laptop has small tiles on a wide screen, and a viewport-based size would
     overflow them. */
  font-size: max(9px, min(15px, calc(var(--tile-size) * 0.38)));
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}

.tile-face {
  position: relative;
  z-index: 2;
}

.board button:hover:not(:disabled) {
  background: var(--btn-bg-hover);
}

.board button:active:not(:disabled) {
  transform: scale(0.95);
}

.board button:disabled {
  color: var(--muted);
  opacity: 0.9;
  cursor: not-allowed;
}

.board button.pick {
  outline: 2px solid var(--accent);
}

.tile.unexplored {
  color: var(--tile-muted);
}

.tile.pinned {
  color: var(--pin);
}

.tile.mine {
  font-weight: 700;
  color: var(--boom);
}

/* ---------- Sandbox reveal ----------
   Reveal is a read-only lens over the ordinary board. Covered tile faces fade
   back while a bottom-up wash shows Z-basis mine probability without inventing a
   fixed mine placement. */
.reveal-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.65rem 1rem;
  margin: 0.55rem 0 -0.25rem;
}

.reveal-toggle {
  gap: 0.45em;
  border-radius: 999px;
  padding-inline: 1rem;
}

.reveal-toggle.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

.reveal-eye {
  vertical-align: -0.18em;
}

.reveal-toggle.loading .reveal-eye {
  animation: reveal-breathe 1s ease-in-out infinite alternate;
}

.reveal-legend {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.4rem 0.8rem;
  color: var(--muted);
  font-size: 0.78rem;
}

.reveal-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  white-space: nowrap;
}

.reveal-legend-note {
  font-style: italic;
}

.reveal-legend-swatch {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  background: var(--boom);
}

.reveal-legend-swatch.superposition {
  background: conic-gradient(var(--boom) 0 50%, var(--btn-bg) 50% 100%);
  box-shadow: inset 0 0 0 1px var(--border);
}

.reveal-legend-swatch.entangled {
  background: transparent;
  border: 2px solid var(--accent);
  box-shadow: 0 0 5px var(--accent);
}

.reveal-legend-swatch.link {
  width: 1.1rem;
  height: 2px;
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 0 0 5px var(--accent);
}

.reveal-message {
  color: var(--muted);
  font-size: 0.85rem;
}

.reveal-error {
  color: var(--boom);
}

.board-revealed button.reveal-visible {
  overflow: visible;
  background:
    linear-gradient(
      to top,
      color-mix(in srgb, var(--boom) 34%, transparent) 0 calc(var(--mine-p) * 100%),
      color-mix(in srgb, var(--btn-bg) 48%, transparent) calc(var(--mine-p) * 100%) 100%
    );
  border: 1px solid color-mix(in srgb, var(--fg) 18%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--bg) 25%, transparent);
  backdrop-filter: blur(2px);
}

.board-revealed button.reveal-visible .tile-face {
  opacity: 0.18;
}

.reveal-content {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1;
  pointer-events: none;
}

.reveal-bomb {
  color: var(--boom);
  font-size: max(8px, min(15px, calc(var(--tile-size) * 0.36)));
  line-height: 1;
  text-shadow: 0 0 6px color-mix(in srgb, var(--boom) 55%, transparent);
}

/* A halo marks every cell that is entangled with some part of the board. */
.reveal-entangled .reveal-content::before {
  content: "";
  position: absolute;
  inset: 2px;
  border: 2px solid var(--accent);
  border-radius: calc(var(--radius) - 1px);
  box-shadow:
    inset 0 0 7px color-mix(in srgb, var(--accent) 45%, transparent),
    0 0 7px color-mix(in srgb, var(--accent) 55%, transparent);
}

.entanglement-links {
  position: absolute;
  inset: 0;
  /* The network is the inspection overlay, so it must remain legible across
     tiles, probability fills, and region badges. pointer-events keeps this
     visual layer from intercepting any board interaction. */
  z-index: 10;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.entanglement-link,
.entanglement-link-glow {
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
}

.entanglement-link-glow {
  stroke: color-mix(in srgb, var(--accent) 22%, transparent);
  stroke-width: 4px;
}

.entanglement-link {
  stroke: var(--accent);
  stroke-width: 1.25px;
  opacity: 0.72;
}

@keyframes reveal-breathe {
  to { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
  .reveal-toggle.loading .reveal-eye {
    animation: none;
  }
}

/* Explored cells are recessed; unexplored ones stay proud of the page. This is
   the board's primary readout and it belongs to the surface, not to the digit:
   before, only a zero-clue cell changed background, so a revealed "2.0" was
   drawn on exactly the same tile as an unexplored cell beside it and the two
   were told apart only by the coloured number.

   Written with three classes so it outweighs the theme overrides further down,
   which are more specific than a bare `.board button`. */
.board button.tile.clue,
.board button.tile.empty,
.board button.tile.mine {
  background: var(--zero-bg);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.16);
}

/* The clue ramp, green (low) through to red (high). render.js sets --clue-t in
   [0, 1] per cell; the sweep from a green hue to a red one is this one calc,
   and the lightness and chroma it is drawn at come from the theme (base.css),
   which is what the old JS-side rgb() could not do -- a single bright green
   cannot read on both a near-black and a near-white tile.

   A browser without oklch() drops the declaration and falls back to the plain
   --fg above, which is legible on both themes; only the ramp is lost. */
.tile.clue {
  /* Semibold: the digits have to carry at tile sizes down to 16px, and the
     extra weight buys back some of the contrast the light theme's ramp spends
     on staying colourful. */
  font-weight: 600;
  color: var(--fg);
  color: oklch(
    calc(var(--clue-l) - var(--clue-l-drop) * var(--clue-t, 0))
    var(--clue-c)
    calc(150deg - 125deg * var(--clue-t, 0))
  );
}

/* Probe outlines sit outside the tile, preserving its clue/pin colour. */
.board button.probe-a {
  outline: 3px solid var(--probe-a);
  outline-offset: -2px;
}

.board button.probe-b {
  box-shadow: inset 0 0 0 3px var(--probe-b), 0 1px 2px rgba(0,0,0,0.08) !important;
}

/* The anchor a shift-click extends its rectangle from. Drawn as an overlay
   ring rather than an outline, so an anchor that is itself in a region keeps
   that region's outline instead of replacing it. */
.board button.probe-anchor::after {
  content: "";
  position: absolute;
  inset: -4px;
  border: 2px dashed var(--probe-active, var(--accent));
  border-radius: 8px;
  pointer-events: none;
}

/* While a region is being edited the board owns the drag gesture: a finger
   dragged across it draws a rectangle rather than scrolling the page, and the
   pointer does not leave a trail of selected tile glyphs behind it. */
.board.probe-selecting button {
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* The rectangle under the pointer, before release. The extra class in the
   selector keeps this ahead of the themed .board button fills below. */
.board.probe-selecting button.probe-preview {
  outline: 3px dashed var(--probe-active, var(--accent));
  outline-offset: -3px;
  background: color-mix(in srgb, var(--probe-active, var(--accent)) 30%, var(--btn-bg)) !important;
}

/* A drag that started on an already-selected cell erases instead of draws. */
.board.probe-selecting button.probe-preview-remove {
  outline-style: dotted;
  background: color-mix(in srgb, var(--fg) 12%, var(--btn-bg)) !important;
  opacity: 0.7;
}

.probe-badge {
  position: absolute;
  margin: -0.7em 0 0 -0.25em;
  padding: 0 0.2em;
  border-radius: 3px;
  background: var(--header-bg);
  color: var(--fg);
  font-size: 0.65em;
  font-weight: 700;
  line-height: 1.2;
}

/* ---------- Entanglement probe ----------
   One row: controls at the start, readout at the end. It replaces a five-block
   panel (heading, button row, counts line, result line, breakdown line) that
   stood 164px tall between the board and the move tools and pushed them off a
   laptop screen. Three of those blocks only restated the others: the heading
   repeated the contextual help the row already opens on hover, and the counts
   line repeated the buttons above it. */
.probe-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.45rem;
  margin: 0.6rem 0 0.8rem;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--box-overlay);
}

/* Sized here rather than with the shared .btn.tool class, which tools.js owns:
   see probeButton() in render.js. */
.probe-bar .btn {
  gap: 0.4em;
  padding: 6px 10px;
  font-size: 0.85rem;
  min-width: 34px;
}

.probe-count {
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
}

/* Pushes the readout to the far end while the row fits on one line, and simply
   collapses when it wraps. */
.probe-spacer {
  flex: 1 1 0;
}

.probe-result {
  font-variant-numeric: tabular-nums; /* the number must not jitter as it changes */
}

.probe-breakdown {
  color: var(--muted);
  font-size: 0.85rem;
}

/* The breakdown is supplementary: the three entropies the mutual information is
   built from. On a narrow screen it would take a wrapped line of its own, which
   is exactly the height this redesign set out to recover. */
@media (max-width: 720px) {
  .probe-breakdown {
    display: none;
  }
}

/* Light: board buttons. Only the shadow differs here -- backgrounds come from
   the shared rules above, so the raised/recessed distinction is stated once.

   Nothing sets a background on :disabled any more. Every cell is disabled once
   the game ends, and giving them all the explored background made the whole
   board look revealed at the moment the player most wants to read it. */
html.light .board button {
  border-radius: 6px;
  box-shadow: 0 1px 2px rgba(30, 45, 80, 0.16);
}

html.light .board button.tile.clue,
html.light .board button.tile.empty,
html.light .board button.tile.mine {
  box-shadow: inset 0 1px 2px rgba(30, 45, 80, 0.18);
}

html.light .board button:disabled {
  opacity: 0.95;
}

/* ---------- Tool row layout ---------- */
.tool-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 6px 0;
}

.tools,
.actions {
  margin: 12px 0;
}

.tool-hint {
  min-height: 1.2em;
  margin: 6px 0 12px;
  color: var(--muted);
  font-size: 0.9rem;
  text-align: center;
}

.tool-row,
.actions-form {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* ---------- Game Over overlay ---------- */
.gameover-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(15, 17, 21, 0.25);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  text-align: center;
  transition: background 0.3s ease-in-out;
}

.gameover-box {
  background: var(--header-bg);
  border-radius: var(--radius);
  padding: clamp(18px, 4vw, 24px) clamp(20px, 5vw, 32px);
  box-shadow: 0 4px 16px rgba(0,0,0,0.6);
  max-width: 360px;
  width: min(92vw, 360px);
  animation: pop 0.25s ease;
}

.result-icon {
  font-size: clamp(36px, 10vw, 48px);
  margin: 8px 0;
  text-align: center;
}

.result-msg {
  margin: 0 0 12px;
  font-size: 1.4rem;
  font-weight: 700;
  text-align: center;
}

/* The two outcome colours are the palette's, so each theme announces a loss in
   the same red its board draws mines in. The glow is mixed from the same token
   rather than restating the colour twice more in rgba(). */
.result-msg.win {
  color: var(--win);
  text-shadow:
    0 0 6px color-mix(in srgb, var(--win) 60%, transparent),
    0 0 12px color-mix(in srgb, var(--win) 40%, transparent);
}

.result-msg.lost {
  color: var(--boom);
  text-shadow:
    0 0 6px color-mix(in srgb, var(--boom) 60%, transparent),
    0 0 12px color-mix(in srgb, var(--boom) 40%, transparent);
}

.gameover-box h2 {
  color: var(--accent);
  font-size: clamp(1.4rem, 4.8vw, 2rem);
  margin-bottom: 12px;
}

/* Wrap, not nowrap: with three children the third had nowhere to go, so
   "Take the Survey" wrapped its own label onto three lines instead. */
.gameover-box .actions form {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 12px;
}

/* The survey is not a way to keep playing, so it does not share a row with the
   two buttons that start a new game. flex-basis:100% is what breaks the line
   (the container wraps, above), and the accent fill marks it as the odd one
   out without needing a second colour in the palette. */
.gameover-box .actions .btn--survey {
  flex-basis: 100%;
  justify-content: center;
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--on-accent);
  font-weight: 600;
  text-decoration: none;
  /* Three beats to catch the eye on arrival, then it settles down rather than
     pulsing at the player for as long as the box is open. */
  animation: survey-pulse 1.6s ease-out 3;
}

.gameover-box .actions .btn--survey:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

@keyframes survey-pulse {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); }
  to   { box-shadow: 0 0 0 10px color-mix(in srgb, var(--accent) 0%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .gameover-box .actions .btn--survey { animation: none; }
}

.gameover-box button {
  padding: 10px 18px;
  font-size: 15px;
  border-radius: var(--radius);
}

.gameover-box p {
  margin-top: -4px;
  margin-bottom: 12px;
  font-size: clamp(1rem, 2.8vw, 1.1rem);
  font-weight: 500;
  color: var(--fg);
}
