* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: linear-gradient(180deg, #2e2419 0%, #45372a 100%);
  color: #f6eee2; user-select: none; -webkit-user-select: none;
  touch-action: manipulation; overflow: hidden;
}
/* phones are all narrower than 560 so this is a no-op there; tablets stop
   being letterboxed into two-thirds of their screen */
#app { height: 100%; display: flex; flex-direction: column; max-width: min(100%, 820px); margin: 0 auto; }

#hdr { display: flex; align-items: center; gap: 8px; padding: 8px 12px; padding-top: max(8px, env(safe-area-inset-top)); }
#hdr .pill { background: #55432f; border: 2px solid #71593d; border-radius: 999px; padding: 6px 11px; font-weight: 800; font-size: 15px; min-width: 44px; min-height: 44px; display: flex; align-items: center; cursor: pointer; justify-content: center; color: inherit; font-family: inherit; }
#hdrHome { border: none; background: #c48a4a; color: #2e2419; font-size: 18px; }
/* single line — status must never reflow the board */
#status { flex: 1; min-width: 0; text-align: center; font-weight: 800; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.scr { flex: 1; display: none; flex-direction: column; overflow-y: auto; padding: 6px 12px 14px; }
.scr.on { display: flex; }

#scr-menu { align-items: center; text-align: center; gap: 12px; justify-content: center; }
#scr-menu h1 { font-size: clamp(30px, 8.5vw, 42px); }
#scr-menu .sub { color: #cdb79a; font-weight: 600; font-size: 15px; }
#tierRow { display: flex; gap: 8px; width: 100%; }
.tierCard { flex: 1; background: #55432f; border: 3px solid #71593d; border-radius: 16px; padding: 10px 3px; font-weight: 800; font-size: 13px; line-height: 1.3; cursor: pointer; color: #f6eee2; }
.tierCard small { display: block; color: #cdb79a; font-weight: 700; font-size: 11px; }
.tierCard .te { font-size: 24px; display: block; }
.tierCard.sel { border-color: #c48a4a; background: #71593d; }
.seg { display: flex; width: 100%; background: #55432f; border: 3px solid #71593d; border-radius: 999px; overflow: hidden; }
.seg button { flex: 1; border: 0; background: transparent; color: #f6eee2; font-weight: 800; font-size: 15px; padding: 12px 4px; cursor: pointer; font-family: inherit; }
.seg button.sel { background: #c48a4a; color: #2e2419; }
button.big { width: 100%; border: none; border-radius: 16px; min-height: 54px; font-size: 19px; font-weight: 800; background: #55432f; color: #f6eee2; border: 3px solid #71593d; cursor: pointer; font-family: inherit; }
button.big.primary { background: #c48a4a; border-color: #a06f36; color: #2e2419; }
button.big:active { transform: translateY(2px); }

/* play */
#topRow { display: flex; justify-content: center; font-weight: 800; font-size: 15px; min-height: 22px; }
#boardWrap { flex: 1; display: flex; align-items: center; justify-content: center; position: relative; min-height: 0; }

/* The board: an N×N grid of tap targets. Lines are drawn per-point with
   pseudo-elements so they land exactly on the intersections at any size. */
#board {
  --n: 9;
  display: grid;
  grid-template-columns: repeat(var(--n), 1fr);
  /* JS (sizeBoard) sets width/height/tracks/padding in px — aspect-ratio
     is Safari 15+ and collapses the board on older iPads. Placeholder only: */
  width: 90vw; height: 90vw;
  background: #dcb175; padding: calc(50% / var(--n) - 0.5px);
  border-radius: 6px; box-shadow: 0 8px 22px rgba(0,0,0,0.45);
  /* own stacking context: without this the stones' z-index:2 escapes into the
     parent and paints OVER the win/lose overlay */
  position: relative; z-index: 0;
}
.pt { position: relative; cursor: pointer; }   /* size comes from the px grid tracks */
/* horizontal + vertical line segments through each intersection */
.pt::before, .pt::after { content: ''; position: absolute; background: #6b4f2c; }
.pt::before { left: -50%; right: -50%; top: 50%; height: 1px; transform: translateY(-0.5px); }
.pt::after { top: -50%; bottom: -50%; left: 50%; width: 1px; transform: translateX(-0.5px); }
/* Edge clipping: stop the lines at the outermost intersections instead of
   letting them run out into the wood. These are `e`-prefixed because `b` and
   `w` on the same element mean "black stone" / "white stone" -- see the note in
   buildBoard(). */
.pt.el::before { left: 50%; } .pt.er::before { right: 50%; }
.pt.et::after { top: 50%; }   .pt.eb::after { bottom: 50%; }
.pt.star { z-index: 1; }
.pt.star .stone::before {
  content: ''; position: absolute; inset: 34%; border-radius: 50%; background: #6b4f2c;
}
.pt .stone {
  position: absolute; inset: 6%; border-radius: 50%; z-index: 2;
  display: block; transition: transform 0.08s;
}
.pt.b .stone { background: radial-gradient(circle at 34% 30%, #5c5c5c, #0b0b0b 72%); box-shadow: 0 1px 2px rgba(0,0,0,0.5); }
.pt.b .stone::before { display: none; }
.pt.w .stone { background: radial-gradient(circle at 34% 30%, #ffffff, #c9c4ba 76%); box-shadow: 0 1px 2px rgba(0,0,0,0.4); }
.pt.w .stone::before { display: none; }
.pt.ghost .stone { background: rgba(20,20,20,0.42); box-shadow: 0 0 0 2px #c48a4a; }
.pt.last .stone::after { content: ''; position: absolute; inset: 32%; border-radius: 50%; background: #e11d48; }
.pt.dead .stone { opacity: 0.32; }
.pt.dead .stone::after { content: '✕'; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #e11d48; font-weight: 900; font-size: 70%; }
.pt.atari .stone { box-shadow: 0 0 0 2px #f59e0b, 0 1px 2px rgba(0,0,0,0.5); }

/* R21 post-mortem: the group the winning move captured, redrawn where it stood
   with a red ring. A capture deletes its own evidence, so without this the
   losing position shows an empty crossing and teaches nothing. The colour comes
   from #board because a captured group is always all one colour. */
#board.gb .pt.gone .stone { background: radial-gradient(circle at 34% 30%, #5c5c5c, #0b0b0b 72%); }
#board.gw .pt.gone .stone { background: radial-gradient(circle at 34% 30%, #ffffff, #c9c4ba 76%); }
.pt.gone .stone { opacity: 0.45; box-shadow: 0 0 0 2px #e11d48; }
.pt.gone .stone::before { display: none; }   /* never the star dot underneath */

#actions, #scoreActions { display: flex; gap: 8px; margin-top: 8px; }
.act { flex: 1; border: none; border-radius: 14px; min-height: 52px; font-size: 16px; font-weight: 800; background: #55432f; color: #f6eee2; border: 3px solid #71593d; cursor: pointer; font-family: inherit; display: flex; align-items: center; justify-content: center; }
.act.primary { background: #c48a4a; border-color: #a06f36; color: #2e2419; }
.act:active { transform: translateY(2px); }
/* R-C20: a disabled take-back stays where it is and greys out. It must also
   stop looking pressable — no active transform, no pointer. */
.act:disabled { opacity: .42; cursor: default; }
.act:disabled:active { transform: none; }

.ovl { position: absolute; inset: 0; z-index: 30; display: none; align-items: center; justify-content: center; background: rgba(25,18,10,0.9); padding: 16px; border-radius: 8px; }
.ovl.on { display: flex; }
.ovlCard { text-align: center; width: 100%; max-width: 300px; display: flex; flex-direction: column; gap: 10px; }
/* Brings the result card back after "Look at the board". Small and out of the
   way, but always present -- studying the position must never be a one-way
   door out of the verdict. */
#showResult { position: absolute; top: 6px; right: 6px; z-index: 31; display: none;
  background: #c48a4a; color: #2e2419; border: 3px solid #a06f36; border-radius: 999px;
  min-height: 40px; padding: 0 14px; font-size: 15px; font-weight: 800;
  font-family: inherit; cursor: pointer; box-shadow: 0 3px 10px rgba(0,0,0,0.4); }
#showResult.on { display: block; }
#overTitle { font-size: 25px; font-weight: 800; }
#overSub { color: #cdb79a; font-weight: 700; min-height: 22px; }

#scr-how .card { background: #55432f; border: 3px solid #71593d; border-radius: 16px; padding: 14px; margin-bottom: 10px; font-size: 15px; line-height: 1.55; font-weight: 600; }
#scr-how h2 { margin-bottom: 8px; }

#toast { position: fixed; left: 50%; bottom: 90px; transform: translateX(-50%); background: #f6eee2; color: #2e2419; font-weight: 800; padding: 9px 16px; border-radius: 999px; opacity: 0; transition: opacity 0.25s; pointer-events: none; z-index: 40; max-width: 90vw; text-align: center; }
#toast.on { opacity: 1; }

/* docs/offline-pwa.md R-C15 — the PAGE must never scroll.
   The product is a fixed frame: header, a named scroll region, footer. If the
   document scrolls too, the header scrolls away, the version chip drifts, and
   a drag that reaches the end of the inner region chains out to the page and
   drags the whole board under the player's thumb. That is the "double scroll".
   `overflow: hidden` stops the second scrollbar; `overscroll-behavior: none`
   stops the chaining, which is the half that is invisible on a desktop mouse
   and obvious on a phone. */
html, body {
    overflow: hidden;
    overscroll-behavior: none;
}

/* ---------------------------------------------------------------------------
   Landscape: the board takes the full HEIGHT and everything else moves beside
   it (docs/offline-pwa.md, "A container sized by the thing it is supposed to
   be sizing" — the sibling of that bug).

   A square board is bounded by the smaller of the two dimensions. On a wide
   short window that is the height, and a COLUMN spends the scarce axis on
   furniture: the captures line and the two action rows. Measured with tests/browser/board-fill.js at 844x390, this
   game's board reached only 57% of the space it could have had, while the two
   games that already had a row layout scored 98-99%.

   `min-aspect-ratio: 1/1` rather than `orientation: landscape`, because the
   question is "is this box wider than it is tall", which is also true of a
   short desktop window that no phone would call landscape.

   Grid rather than flex: the board has to span every row of the side column,
   and `#scr-play.on` (1,1,0) is specific enough to beat `.scr { display: none }`
   without also un-hiding the screen when `.on` is absent.
   --------------------------------------------------------------------------- */
/* `and (max-height: 620px)` — the row layout is for a window whose HEIGHT is
   scarce, not for any window that happens to be wide.
   `min-aspect-ratio: 1/1` alone caught two different situations and only one
   of them wanted this. On a 1440x900 desktop the board came out bounded by the
   side COLUMN at 568px with 200px of height unused; the ordinary column layout
   gives it ~690px there. Measured at 1024x600 (row wins) and 1280x800 /
   1440x900 (column wins), the crossover sits just above a short laptop, so
   620px keeps every phone landscape on the row layout and hands every real
   desktop window back to the column. */
@media (min-aspect-ratio: 1/1) and (max-height: 620px) {
  /* Two grid gotchas, both of which this hit:
     `align-items: center` becomes `align-self: center` on every item, so the
     board cell stops STRETCHING and shrink-wraps its content — the container
     sized by the thing it is meant to size, one more time. And a bare `1fr`
     track has an automatic minimum of `min-content`, so a board that measures
     its wrapper and grows can push the very track that was supposed to bound
     it: go's board reached 750px inside a 286px screen. `minmax(0, 1fr)` plus
     an explicit `align-self: stretch` and `min-height: 0` closes both. */
  #scr-play.on {
    display: grid;
    /* minmax(0, 1fr), NOT 1fr — the SAME trap as the rows below, and I fixed
       the rows and left the columns. A `1fr` track has an automatic minimum of
       min-content, so it cannot shrink under its contents; the board measures
       its wrapper, grows, and the track grows with it. On a phone in landscape
       the height budget is small so the board never reaches the limit and the
       bug is invisible. On a DESKTOP window the height is plentiful, so the
       board is sized off the height, exceeds the column, and the play screen
       overflows sideways inside a centred 820px app — measured on production
       at 1680x1050: scrollWidth 1076 in a clientWidth of 820. The board did
       not overflow the WINDOW, which is why every "is it on screen" check
       passed; you just had to drag to reach the far files. */
    grid-template-columns: minmax(0, 1fr) minmax(160px, 32%);
    grid-template-rows: auto minmax(0, 1fr) auto;
    column-gap: 12px; align-items: center; min-height: 0;
  }
  #boardWrap { grid-column: 1; grid-row: 1 / -1; align-self: stretch; min-height: 0; min-width: 0; }
  #topRow       { grid-column: 2; grid-row: 1; align-self: end; }
  #actions      { grid-column: 2; grid-row: 2; flex-direction: column; }
  #scoreActions { grid-column: 2; grid-row: 3; flex-direction: column; }
}

/* ---------------------------------------------------------------------------
   PAD THE AXIS YOU HAVE TO SPARE, NOT THE ONE THAT BINDS.

   A square board is limited by exactly ONE of the two axes, and every pixel of
   padding on that axis comes straight off the board. Padding on the other axis
   costs nothing — there is spare there by definition. Symmetrical padding
   therefore charges the board full price for a gutter it only needs on one
   side of the problem.

   Measured on this game before the fix: in landscape the play screen's 20px of
   vertical padding was the ENTIRE difference between a board at 90% of the
   available height and one at ~98%, while ~560px of width sat unused beside
   it. In portrait it is the mirror image — the board is width-bound and the
   horizontal padding is what costs.

   4px rather than 0 on the binding axis: a board flush to the edge reads as
   clipped, and on a rounded-corner phone it can actually be.
   --------------------------------------------------------------------------- */
/* `and (max-height: 620px)` — the row layout is for a window whose HEIGHT is
   scarce, not for any window that happens to be wide.
   `min-aspect-ratio: 1/1` alone caught two different situations and only one
   of them wanted this. On a 1440x900 desktop the board ended up bounded by the
   side COLUMN at 568px while 220px of height sat unused; the ordinary column
   layout gives it ~708px there. Measured across 1024x600 (row wins, 496 vs
   416), 1280x800 (column wins, 616 vs 587) and 1440x900 (column wins, 708 vs
   568), the crossover sits just above a short laptop, so 620px keeps every
   phone landscape on the row layout and hands every real desktop window back
   to the column. */
@media (min-aspect-ratio: 1/1) {
  #scr-play.on { padding-top: 4px; padding-bottom: 4px; }
}
@media (max-aspect-ratio: 1/1) {
  #scr-play.on { padding-left: 4px; padding-right: 4px; }
}
