/* Gambit Chess — reset, typography, page layout and motion. */
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0;
  font-variant-numeric: lining-nums;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
/* A faint chessboard at the top of every page, fading into the paper. */
body::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 520px;
  pointer-events: none;
  z-index: -1;
  background-image: conic-gradient(var(--pattern) 25%, transparent 0 50%, var(--pattern) 0 75%, transparent 0);
  background-size: 56px 56px;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 0%, transparent 100%);
}
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: var(--accent-2); text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 3px; transition: text-decoration-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease); }
a:hover { text-decoration-color: currentColor; }
h1, h2, h3, h4 { margin: 0; font-weight: 650; line-height: 1.15; }
h1, h2, .display { font-family: var(--font-display); }
h1 { font-size: clamp(28px, 3.4vw, 40px); font-weight: 760; letter-spacing: -0.025em; line-height: 1.08; }
h2 { font-size: clamp(20px, 2vw, 24px); font-weight: 700; letter-spacing: -0.015em; }
h3 { font-size: 16px; font-weight: 650; letter-spacing: -0.005em; }
.display { font-weight: 800; letter-spacing: -0.03em; }
p { margin: 0; }
.muted { color: var(--text-2); }
.faint { color: var(--text-3); }
.num, .tabular { font-variant-numeric: lining-nums tabular-nums; }
.italic { font-style: italic; }
.nowrap { white-space: nowrap; }
/* Facts separated by middle dots ("Plays like about 1650 · 14 reviewed games"):
   a dot that would start a wrapped line sits in the clipped margin, so no
   line starts or ends with a stray dot. */
.dots { display: block; overflow: hidden; }
.dots-in { display: flex; flex-wrap: wrap; margin-left: -0.85em; }
.dots-in > *::before { content: "·"; content: "·" / ""; display: inline-block; width: 0.85em; text-align: center; color: var(--text-3); font-weight: 400; }
.sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
::selection { background: var(--accent-glow); }

:focus { outline: none; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 6px; }

.skip-link { position: absolute; left: 12px; top: -60px; z-index: 1000; background: var(--accent); color: var(--on-accent); padding: 10px 14px; border-radius: 8px; font-weight: 600; }
.skip-link:focus { top: 10px; }

/* ----- shell ----- */
.topbar {
  position: sticky; top: 0; z-index: 50;
  height: var(--topbar-h);
  display: flex; align-items: center; gap: 18px;
  padding: 0 var(--gutter);
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: saturate(1.4) blur(12px);
  -webkit-backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 9px; color: var(--text); text-decoration: none !important; flex: none; }
.brand-crest { width: 30px; height: 30px; transition: transform var(--t-med) var(--spring); }
.brand:hover .brand-crest { transform: rotate(-6deg) scale(1.06); }
.brand-word { display: flex; align-items: baseline; gap: 5px; line-height: 1; }
.brand-word b { font-size: 19px; font-weight: 800; letter-spacing: -0.03em; }
.brand-word i { font-style: normal; font-size: 19px; font-weight: 500; letter-spacing: -0.02em; color: var(--text-3); }
.mainnav { display: flex; gap: 2px; margin-left: 12px; flex: 1; min-width: 0; }
.mainnav a {
  position: relative; display: inline-flex; align-items: center; height: 36px; padding: 0 12px;
  color: var(--text-2); font-weight: 600; font-size: 14.5px; border-radius: 8px; text-decoration: none; white-space: nowrap;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.mainnav a:hover { color: var(--text); background: color-mix(in srgb, var(--text) 6%, transparent); }
.mainnav a[aria-current="page"] { color: var(--text); }
.mainnav a::after {
  content: ""; position: absolute; left: 12px; right: 12px; bottom: -12px; height: 2px; border-radius: 2px;
  background: var(--accent); transform: scaleX(0); transition: transform var(--t-med) var(--ease);
}
.mainnav a[aria-current="page"]::after { transform: scaleX(1); }
/* While the underline slides from the old page's link (js/motion.js), the
   links do not grow or shrink their own. */
.mainnav.m-ink a::after { transition: none; }
.topbar-right { display: flex; align-items: center; gap: 10px; margin-left: auto; flex: none; }
/* The two signed-out buttons sit on one centre line (an icon first would
   otherwise lift "Log in" onto the text baseline). */
.me-slot { display: flex; align-items: center; gap: 6px; }
.conn-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--win); box-shadow: 0 0 0 3px color-mix(in srgb, var(--win) 18%, transparent); }
.conn-dot[data-state="connecting"] { background: var(--warn); animation: pulse 1.2s infinite; }
.conn-dot[data-state="offline"] { background: var(--danger); }

.tabbar { display: none; }
main#view { min-height: calc(100vh - var(--topbar-h)); outline: none; }
.page { max-width: var(--maxw); margin: 0 auto; padding: 32px var(--gutter) 72px; }
.page-narrow { max-width: 860px; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 24px; }
.page-head p { margin-top: 8px; max-width: 70ch; }
/* A quiet label above a heading: a small board square, then the words. */
.kicker {
  display: flex; align-items: center; gap: 8px;
  font-size: 12.5px; font-weight: 650; letter-spacing: 0.01em; color: var(--text-3);
  margin: 0 0 10px;
}
.kicker::before { content: ""; width: 7px; height: 7px; border-radius: 2px; background: var(--accent); flex: none; }
.section { margin-top: 40px; }
.grid { display: grid; gap: 16px; }
.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.row { display: flex; align-items: center; gap: 10px; }
.row-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.stack { display: flex; flex-direction: column; gap: 10px; }
.spacer { flex: 1; }
.center { text-align: center; }

/* ----- motion -----
   Four kinds: arriving (pages, sections, cards and rows rise in, staggered,
   and wait below the fold until they scroll into view), answering (hover,
   press, focus, the sliding tab and segment indicators), changing (numbers
   count and bump, a finished match or a seed's new status flashes) and
   living (live dots, the board). js/motion.js decides what arrives and
   when; every item arrives once, so live updates never replay anything.
   Only transform and opacity move, and nothing at all with reduced motion
   or with animation off in Settings (data-motion="off"). */
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes rise-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes pop-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: none; } }
@keyframes sheet-up { from { transform: translateY(100%); } to { transform: none; } }
@keyframes grow-x { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes ring { 0% { box-shadow: 0 0 0 0 var(--accent-glow); } 100% { box-shadow: 0 0 0 10px transparent; } }
@keyframes sweep { from { transform: translateX(-120%) skewX(-18deg); } to { transform: translateX(260%) skewX(-18deg); } }
@keyframes skel-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
/* A change: a wash of the accent that settles back to the element's own
   background (the "from" frame only, so it ends on the real colour). */
@keyframes m-flash { from { background-color: color-mix(in srgb, var(--accent) 30%, transparent); box-shadow: 0 0 0 3px var(--accent-glow); } }
@keyframes m-flash-win { from { background-color: color-mix(in srgb, var(--gold-2) 42%, transparent); } }
.m-flash { animation: m-flash 1.2s var(--ease) both; }
.m-flash-win { animation: m-flash-win 1.4s var(--ease) both; }

/* Waiting to scroll into view: hidden until js/motion.js lets it rise.
   Only on screens that may move, so print and reduced motion show all. */
@media screen and (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="off"]) .m-pre { opacity: 0 !important; }
}
/* Counting numbers and bumps scale: they need a box of their own. */
[data-countup], [data-bump] { display: inline-block; }

/* Settings' animation speed: the timing tokens follow it. */
:root[data-motion="fast"] { --t-fast: 100ms; --t-med: 170ms; --t-slow: 300ms; }
:root[data-motion="slow"] { --t-fast: 200ms; --t-med: 340ms; --t-slow: 600ms; }

/* A theme change: every colour eases into the new theme for a moment
   (js/motion.js themeSwap adds .m-theme only when motion is allowed). Plain
   transitions, so a click made meanwhile still lands. */
:root.m-theme, :root.m-theme *, :root.m-theme *::before, :root.m-theme *::after {
  transition-property: background-color, color, border-color, outline-color, box-shadow, fill, stroke !important;
  transition-duration: var(--t-med) !important; transition-timing-function: var(--ease) !important; transition-delay: 0s !important;
}

/* ----- responsive ----- */
@media (max-width: 1099px) {
  :root { --gutter: 20px; }
  .mainnav a { padding: 0 9px; font-size: 14px; }
  .mainnav a::after { left: 9px; right: 9px; }
  .cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* The bottom tab bar takes over as soon as the top menu is hidden, so no
   width is left without navigation. */
@media (max-width: 899px) {
  :root { --tabbar-h: 64px; }
  .mainnav { display: none; }
  .cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .page { padding-bottom: calc(var(--tabbar-h) + 32px); }
  .tabbar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    height: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: color-mix(in srgb, var(--bg) 90%, transparent);
    border-top: 1px solid var(--line);
    backdrop-filter: saturate(1.4) blur(12px); -webkit-backdrop-filter: saturate(1.4) blur(12px);
  }
  .tabbar a, .tabbar button {
    position: relative; flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    background: none; border: 0; color: var(--text-3); font-size: 11px; font-weight: 650; letter-spacing: 0.01em; text-decoration: none; min-height: 44px;
    transition: color var(--t-fast) var(--ease);
  }
  .tabbar svg { width: 22px; height: 22px; transition: transform var(--t-med) var(--spring); }
  .tabbar a:active svg, .tabbar button:active svg { transform: scale(0.88); }
  .tabbar [aria-current="page"] { color: var(--accent-2); }
  .tabbar [aria-current="page"]::before { content: ""; position: absolute; top: 0; left: 30%; right: 30%; height: 2px; border-radius: 0 0 2px 2px; background: var(--accent); animation: grow-x var(--t-med) var(--ease) both; }
}
@media (max-width: 767px) {
  :root { --gutter: 16px; --topbar-h: 54px; }
  body { font-size: 15px; }
  body::before { height: 360px; background-size: 44px 44px; }
  .page { padding: 20px var(--gutter) calc(var(--tabbar-h) + 32px); }
  .cols-2, .cols-3, .cols-4 { grid-template-columns: minmax(0, 1fr); }
  .brand-word b, .brand-word i { font-size: 18px; }
}
/* Small phones: "Log in" becomes its icon so the bar fits 360 to 390 px. */
@media (max-width: 520px) {
  .topbar { gap: 10px; }
  .me-login-word { display: none; }
  .me-login { min-width: 44px; padding: 0; }
}
/* Reduced motion, from the device or from Settings: everything is where it
   ends at once, nothing waits and nothing loops. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 1ms !important; animation-delay: 0s !important; animation-iteration-count: 1 !important; transition-duration: 1ms !important; transition-delay: 0s !important; scroll-behavior: auto !important; }
}
:root[data-motion="off"] *, :root[data-motion="off"] *::before, :root[data-motion="off"] *::after { animation-duration: 1ms !important; animation-delay: 0s !important; animation-iteration-count: 1 !important; transition-duration: 1ms !important; transition-delay: 0s !important; scroll-behavior: auto !important; }
