/* Basecoat Nova integration. Layout and all board/piece states remain in styles.css.
   Existing Schness tokens win over the vendor's defaults in both themes. */
:root {
  --background: var(--paper);
  --foreground: var(--ink);
  --card: var(--surface);
  --card-foreground: var(--ink);
  --popover: var(--surface);
  --popover-foreground: var(--ink);
  --primary: var(--ink);
  --primary-foreground: var(--inverse);
  --secondary: var(--sunk);
  --secondary-foreground: var(--ink);
  --muted-foreground: var(--muted);
  --accent-foreground: var(--ink);
  --destructive: var(--danger);
  --border: var(--line);
  --input: var(--line);
  --ring: var(--focus);
  /* The vendor's own radius token maps onto ours; --font-sans lives in styles.css. */
  --radius: var(--radius-card);
}

/*
 * Four button roles, and nothing else in this product draws a rectangle of its
 * own. There were seven near-identical ones, each restating a border, a
 * background and a radius that `.btn` already supplied at higher specificity —
 * so most of them were already having no effect, which is the hard kind of
 * duplication to notice.
 *
 * Secondary is the default. Glyph buttons and board squares never get `.btn`.
 */
body .btn {
  min-height: 44px;
  height: auto;
  padding: .625rem .875rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font: 640 .875rem/1.4 var(--font-sans);
  white-space: normal;
  overflow-wrap: anywhere;
  text-decoration: none;
  box-shadow: none;
  transform: none;
  transition: background-color 120ms ease, border-color 120ms ease;
  cursor: pointer;
}
body .btn:not(:disabled):hover { background: var(--sunk); border-color: var(--ink); transform: none; }

body .btn[data-variant="primary"] { background: var(--ink); color: var(--inverse); border-color: var(--ink); }
body .btn[data-variant="primary"]:not(:disabled):hover { background: #2A332D; border-color: #2A332D; opacity: 1; }

body .btn[data-variant="ghost"] { border-color: transparent; background: transparent; }
body .btn[data-variant="ghost"]:not(:disabled):hover { border-color: transparent; background: var(--sunk); }

/* The fourth role: a square target with a glyph in it, otherwise secondary. */
body .btn[data-variant="icon"] { width: 44px; min-width: 44px; padding: 0; display: inline-grid; place-items: center; }

/* Drawn, not dimmed: opacity on a coloured button produces a different
   colour on every background it happens to sit on. A ghost button keeps its
   transparency — filling it in would make the disabled one the loudest thing
   in a quiet list. */
body .btn:disabled,
body .btn[data-variant="primary"]:disabled {
  border-color: var(--line);
  background: var(--sunk);
  color: var(--muted);
  opacity: 1;
  cursor: default;
}
body .btn[data-variant="ghost"]:disabled {
  border-color: transparent;
  background: transparent;
  color: var(--muted);
}

body .btn:focus-visible, body .input:focus-visible, body select.select:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  box-shadow: none;
}
body .btn[aria-pressed="true"], body .btn.current {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--inverse);
}
body select.select, body .input {
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  padding: .5rem .75rem;
  background: var(--surface);
  color: var(--ink);
  font: 400 .875rem/1.4 var(--font-sans);
  box-shadow: none;
}
body select.select {
  appearance: none;
  background-image: url('./assets/select-chevron.svg');
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: .875rem;
  padding-right: 2.75rem;
}
:root[data-theme="dark"] select.select { background-image: url('./assets/select-chevron-dark.svg'); }
body input[type="checkbox"], body input[type="radio"] { accent-color: var(--ink); }
body .header-actions .btn { padding: .5rem .625rem; font-size: .8rem; white-space: nowrap; }
body .dialog-close.btn { width: 44px; height: 44px; padding: 0; flex: 0 0 44px; }

/* Homepage: restrained hierarchy, not four competing oversized billboards. */
.lobby-page header { margin-bottom: clamp(2rem,7vh,4rem); }
.lobby-page .lobby-intro { margin-bottom: 1.5rem; }
.lobby-page .lobby-intro h2 { font-size: clamp(2rem,6vw,3rem); line-height: 1.15; letter-spacing: -.04em; }
.lobby-page .mode.btn {
  display: grid;
  justify-content: stretch;
  align-items: start;
  height: auto;
  gap: .25rem;
  padding: 1rem 1.125rem;
  text-align: left;
}
.lobby-page .mode.btn strong { font-size: 1rem; line-height: 1.4; }
.lobby-page .mode.btn small { color: var(--muted); font-size: .875rem; font-weight: 400; line-height: 1.5; }
.lobby-page .mode.mode-primary.btn { background: var(--ink); color: var(--inverse); border-color: var(--ink); }
.lobby-page .mode.mode-primary.btn small { color: var(--inverse); opacity: .8; }
.lobby-page .mode-group .mode.btn { border: 0; border-radius: 0; background: none; }
.lobby-page .mode-group .mode.btn + .mode.btn { border-top: 1px solid var(--hairline); }
.lobby-page .mode-group .mode.btn:not(:disabled):hover { background: var(--sunk); }
.lobby-page .rules-strip { margin-top: 2.5rem; gap: 1px; background: var(--line); }
.lobby-page .rules-strip article { border-color: var(--line); }
.lobby-page .rules-strip .btn { justify-self: start; width: auto; min-width: 44px; }
.lobby-page .rules-full.btn { width: 100%; margin-top: 1.25rem; }

/* Panels share surfaces and spacing without changing each mode's layout. */
.watch-panel, .puzzle-settings, .library-stats, .library-filters, .analysis-panel, .arena-turn,
.demo-panel, .network-card, .result-card {
  border-color: var(--line);
  border-radius: var(--radius-card);
  background-color: var(--surface);
  box-shadow: none;
}
.game :is(.watch-panel, .demo-panel, .puzzle-settings, .library-filters, .analysis-panel, .network-card, .result-card),
dialog :is(.replay-panel, .dialog-body, .dialog-head, .dialog-foot) { padding: 1.25rem; }
.library-stats { padding: 1rem; }
/* Mobile puzzle-panel is display:contents: its settings need their own inset. */
.puzzle-settings { box-sizing: border-box; }
.puzzle-page .puzzle-actions { padding: .75rem; }
.puzzle-toggles label { gap: .5rem; align-items: flex-start; }
.puzzle-toggles input { flex: 0 0 auto; margin-top: .2rem; }
.watch-status, .position-help, .online-setup-note { line-height: 1.5; }
.watch-controls .btn, .puzzle-actions .btn { width: 100%; }
.arena-quiet .btn, .arena-primary.btn { width: 100%; }
#watch-training summary, #training-panel summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; font-weight: 640; }
.watch-moves .btn { min-height: 44px; padding: .4rem .6rem; font-size: .8rem; }
.library-game.btn { display: grid; grid-template-columns: auto minmax(0,1fr); justify-content: stretch; height: auto; text-align: left; padding: 1rem; }
.library-game.btn:hover { transform: none; }
.library-game.btn small { font-weight: 400; }
.library-filters label { gap: .375rem; }
.analysis-panel label, .puzzle-toggles label, .sound-cues label { min-height: 44px; line-height: 1.5; }
.position-foot { flex-wrap: wrap; gap: .5rem; }
.chat-form .btn { flex-shrink: 0; }
.quick-chat .btn, .chat-event .btn { font-size: .8rem; padding: .4rem .65rem; }

/* Keep native dialog keyboard/focus behavior; migrate its visual surface only. */
dialog { border-color: var(--line); border-radius: var(--radius-dialog); background: var(--paper); color: var(--ink); }
dialog::backdrop { background: rgb(8 12 10 / .65); }
dialog .btn { max-width: 100%; }
dialog h2 { line-height: 1.25; }

@media (max-width: 480px) {
  body .header-actions .btn { font-size: .72rem; padding-inline: .5rem; }
  .lobby-page header { margin-bottom: 2rem; }
  .game :is(.watch-panel, .demo-panel, .puzzle-settings, .library-filters, .analysis-panel, .network-card, .result-card),
  dialog :is(.replay-panel, .dialog-body, .dialog-head, .dialog-foot) { padding: 1rem; }
  .library-stats { padding: 1rem; }
  .puzzle-page .puzzle-actions { padding: .75rem; }
  .watch-player select.select { max-width: 100%; padding-left: .625rem; padding-right: 2.75rem; font-size: .8rem; }
  .library-filters select.select { width: 100%; }
  .puzzle-actions .btn { min-height: 48px; }
}
@media (prefers-reduced-motion: reduce) {
  body .btn { transition: none; }
}

/* A pending/failed estimate is unknown, not an even position or a stale score. */
.analysis-bar:not([data-state="ready"]) { background: var(--line); }
.analysis-bar:not([data-state="ready"]) .analysis-track { visibility: hidden; }
.analysis-bar:not([data-state="ready"]) .analysis-side { color: var(--ink); }
:root { --link: var(--accent-text); }


/* The tutorial list owns the divider before its adjacent playable demo. */
.lobby-page .rules-strip + .rules-demo { border-top: 0; }

/* Shared board surfaces leave room around reserves without resizing pieces.
   Keyed off the seats themselves, not their position: the arena's turn card
   now sits above the top seat, and :first-child stopped matching. */
:is(.watch-stage, .replay-stage, .demo-stage, .puzzle-stage) > :is(.watch-player, .replay-seat) { margin-block: .625rem; }

/*
 * Centring a dialog is not a desktop concern. This rule used to live inside
 * `@media (min-width: 761px)`, so below 761px nothing centred any dialog and
 * the margin fell back to 0. Four of the five escaped only because each had
 * been given its own full-screen rule; #online-setup had none, so it opened
 * at x:0, y:0 on a phone — measured at 390x844 with 31px spare to its right
 * and 554px below it. The bug was invisible in a desktop browser, which is how
 * it shipped, and it is the reason the invite and the rules never looked like
 * one component: they never were one.
 *
 * Size is a class on the element now, never a per-dialog media query.
 */
body dialog {
  position: fixed;
  inset: 0;
  margin: auto;
  height: fit-content;
  max-height: calc(100svh - 3rem);
  max-width: calc(100vw - 3rem);
  overflow: auto;
}

@media (max-width: 760px) {
  /* There was an .is-sheet here — a bottom sheet flush to the bottom edge, for
     the invite dialog. It went because a dialog that small reads as displaced
     rather than deliberate; centred is what the one dialog using it wanted to
     be. If a sheet earns its place later it belongs back here as a class, not
     as a rule on one dialog's id. */
  body dialog.is-full {
    inset: 0;
    width: 100%;
    height: 100svh;
    max-height: 100svh;
    max-width: none;
    margin: 0;
    border-radius: 0;
  }
}

/*
 * Read and practise together, in the same native Rules dialog. The demo column
 * is sized from the height the dialog actually has, not from its own width:
 * the first thing a new visitor saw used to be rule 2 clipped at the top and
 * the board it was teaching cut off at the bottom.
 */
body .rules-dialog { width: min(64rem, calc(100vw - 2rem)); max-height: calc(100svh - 2rem); }
.rules-dialog .dialog-body { align-content: start; scroll-behavior: auto; }
.rules-lessons { display:flex; flex-wrap:wrap; gap:.5rem; margin-bottom:1rem; }
.rules-practice { margin:1rem 0; }
.rules-practice .rules-demo { margin:0; padding-top:.75rem; border:0; }
/* Sized so the board this dialog is teaching is whole when it opens. */
/* No percentage here: the stage sits in a `1fr` column, and a width that is a
   percentage of a track that is sized from its content resolves to zero. */
.rules-practice .demo-stage { width: min(22rem, calc(100svh - 25rem)); margin-inline: auto; }
/* The instruction is a child of the layout now, so a phone can put it above
   the board. On desktop it keeps its place at the top of the right column. */
.rules-practice .demo-layout {
  grid-template-columns:minmax(0,1fr) minmax(14rem,1fr);
  grid-template-areas:"stage instruction" "stage panel";
}
.rules-practice .demo-layout > #demo-instruction { grid-area:instruction; margin:0 0 .75rem; }
.rules-practice .demo-layout > .demo-stage { grid-area:stage; }
.rules-practice .demo-layout > .demo-panel { grid-area:panel; }

/* The stepper. Hidden markup on desktop; the dialog is the reading layout. */
.rules-stepper { display:grid; gap:.5rem; margin-bottom:1rem; }
.rules-progress { display:grid; grid-auto-flow:column; gap:.375rem; margin:0; padding:0; list-style:none; }
.rules-progress li { height:4px; border-radius:var(--radius-pill); background:var(--sunk); }
.rules-progress li[data-state="done"] { background:color-mix(in srgb, var(--accent) 45%, var(--sunk)); }
.rules-progress li[data-state="current"] { background:var(--accent); }
.rules-step-count { margin:0; color:var(--muted); font-size:.75rem; font-weight:640; }
.rules-stepper-actions { display:flex; gap:.5rem; width:100%; }
.rules-stepper-actions .btn { flex:1 1 0; }
.rules-list > li > strong:focus-visible { outline:2px solid var(--focus); outline-offset:2px; }
.rules-practice .demo-heading h2 { font-size:1.5rem; }
@media(max-width:760px) {
  /* Geometry comes from .is-full. */
  /* Read what to do, then see the board you are to do it on. */
  .rules-practice .demo-layout {
    grid-template-columns:1fr;
    grid-template-areas:"instruction" "stage" "panel";
  }
  /* Capped so one whole step fits at 390x844 without scrolling. Measured.
     The floor matters: an uncapped-below expression is how the puzzles board
     ended up at 128px on a small phone. Below the floor the step scrolls,
     which is the right trade — a board you cannot read is worth nothing. */
  .rules-practice .demo-stage { width: clamp(11rem, calc(100svh - 39rem), 20rem); max-width:24rem; }
  /* The cap belongs to the board, not the stage: capping the stage squeezed
     the reserve rows, which need the full width to hold three pieces and a
     label. The board is centred in what is left. */
  .is-stepping .demo-stage { width:100%; max-width:none; }
  .is-stepping #demo-board { width:clamp(11rem, calc(100svh - 39rem), 20rem); margin-inline:auto; }
  .is-stepping .demo-stage > .replay-seat { margin-block:.25rem; }
  /* One exit per screen: Back and Next are in the foot. */
  .is-stepping .demo-full-game { display:none; }
  /* In its resting state the strip says "Your turn — tap or drag a piece",
     which the instruction above the board has already said. It earns its 54px
     only when it has something of its own to report. */
  .is-stepping #demo-feedback[data-state="ready"] { display:none; }
  /* The counter numbers four rules in a list. A stepper shows one at a time
     and says "Rule 3 of 4" above it, so the marker only ever read "1". */
  .is-stepping .rules-list > li { grid-template-columns:minmax(0,1fr); }
  .is-stepping .rules-list > li::before { display:none; }
  .is-stepping .rules-list > li > strong { grid-column:1; display:block; font-size:1.15rem; margin-bottom:.35rem; }
  .is-stepping .rules-list > li > span { grid-column:1; }
  /* The "Try it" trigger is what the stepper presses for you. */
  .is-stepping .rule-demo-trigger { display:none; }
  .is-stepping .dialog-foot { position:sticky; bottom:0; background:var(--surface); border-top:1px solid var(--line); }
  .is-stepping .dialog-foot-actions, .is-stepping .dialog-foot-note { display:none; }
  .is-stepping .demo-heading { display:none; }
  /* One step is one screen, so the space between its parts is the budget.
     Measured at 390x844: 172px of the step was gaps and padding. */
  .is-stepping .dialog-body { gap:.5rem; padding:4px 16px 12px; }
  .is-stepping .rules-practice { margin:.25rem 0; }
  .is-stepping .rules-practice .rules-demo { padding-top:0; }
  .is-stepping .demo-layout { gap:.5rem; }
  /* One panel inside another reads as two boxes; the step is the box. */
  .is-stepping .demo-panel { gap:.5rem; padding:0; border:0; background:none; }
  .is-stepping #demo-instruction { margin:0; }
  .is-stepping .dialog-head { padding:12px 16px 8px; }
}

.square-coordinate { display:none; position:absolute; top:.25rem; right:.3rem; font:640 clamp(.55rem,1.4vw,.7rem)/1 var(--font-sans); color:var(--ink); background:color-mix(in srgb, var(--surface) 82%, transparent); border-radius:var(--radius-control); padding:.12rem .18rem; pointer-events:none; z-index:4; }
:root[data-coordinates="true"] .square-coordinate,
#rules-demo[data-coordinates="true"] .square-coordinate { display:block; }
.coordinate-toggle { justify-self:start; margin-block:.375rem; }

/* Keep the rule text and its playable example together on wide screens. */
body .rules-dialog .dialog-body { grid-template-columns:minmax(14rem,.8fr) minmax(0,1.2fr); }
.rules-dialog .rules-list { grid-column:1; grid-row:1; }
.rules-dialog .rules-practice { grid-column:2; grid-row:1; min-width:0; }
.rules-dialog .rules-gotchas { grid-column:1 / -1; }
.rules-practice .demo-layout { grid-template-columns:1fr; }
@media(max-width:760px) {
  body .rules-dialog .dialog-body { grid-template-columns:1fr; }
  .rules-dialog .rules-list, .rules-dialog .rules-practice { grid-column:1; grid-row:auto; }
}

/* Tutorial cards formerly stretched this pseudo-hit-area over their parent. */
.rules-practice .rule-demo-trigger { position:relative; margin-top:0; }
.rules-practice .rule-demo-trigger::before { content:none; display:none; }

/* The explanation and its action are one scannable numbered item. */
.rules-dialog .rules-list .rule-demo-trigger { grid-column:2; justify-self:start; position:relative; margin: .25rem 0 0; }
.rules-dialog .rules-list .rule-demo-trigger::before { content:none; display:none; }

.coordinate-option { display:flex; align-items:center; gap:.5rem; min-height:44px; font-size:.8rem; color:var(--muted); }
.coordinate-option .coordinate-toggle { margin:0; }
