/*! © 2026 JuJu Games — https://juju.games */
/* Sitewide rules only. A game's rules go in css/games/, in a sheet named like its script. */
:root {
  --c-primary: #6c5ce7;
  --c-primary-dark: #5345c9;
  --c-accent: #fdcb6e;
  --c-bg: #f7f7fb;
  --c-surface: #ffffff;
  --c-text: #2d3436;
  --c-text-soft: #636e72;
  --c-border: #e6e6ef;
  --c-success: #00b894;
  --c-error: #d63031;
  --radius: 14px;
  --shadow: 0 2px 10px rgba(45, 52, 84, .08);
  --shadow-lg: 0 8px 28px rgba(45, 52, 84, .14);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans", "Noto Sans CJK SC", sans-serif;
  background: var(--c-bg);
  color: var(--c-text);
  line-height: 1.6;
}
img { max-width: 100%; }
a { color: var(--c-primary); }
h1, h2, h3 { line-height: 1.25; }

.container { max-width: 1080px; margin: 0 auto; padding: 0 16px; }

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background-color: rgba(255, 255, 255, .92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--c-border);
  transition: background-color .18s ease, border-color .18s ease;
}
/* Not sticky here: below 1000px in landscape nothing may assume the bar holds a band on screen.
   The anchors go with it — the scroll margins further down clear a bar that is not there. The
   `html` prefix and `[data-game]` earn the specificity to beat them and the per-game 73px,
   all of which come later in the cascade. */
@media (max-width: 999px) and (orientation: landscape) {
  .site-header { position: static; }
  html .topic-featured, html .games-section, html .more-games, html .more-topics,
  html .game-root[data-game] { scroll-margin-top: 0; }
}
.header-inner { display: flex; align-items: center; justify-content: space-between; height: 56px; }
.logo { font-size: 1.15rem; font-weight: 800; text-decoration: none; color: var(--c-text); white-space: nowrap; }
.logo em { color: var(--c-primary); font-style: normal; }
.site-nav { display: flex; align-items: center; gap: 14px; }
.site-nav > a { text-decoration: none; color: var(--c-text-soft); font-weight: 600; font-size: .95rem; }
.site-nav > a:hover { color: var(--c-primary); }

.lang-switch { position: relative; }
.lang-btn {
  border: 1px solid var(--c-border); background: var(--c-surface); border-radius: 999px;
  padding: 5px 12px; font-size: .85rem; cursor: pointer; color: var(--c-text);
}
.lang-menu {
  display: none; position: absolute; right: 0; top: calc(100% + 6px);
  margin: 0; padding: 6px; list-style: none;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 12px;
  box-shadow: var(--shadow-lg); min-width: 130px;
}
.lang-menu.open { display: block; }
.lang-menu a { display: block; padding: 7px 12px; border-radius: 8px; text-decoration: none; color: var(--c-text); font-size: .92rem; }
.lang-menu a:hover { background: var(--c-bg); }
.lang-menu a.active { color: var(--c-primary); font-weight: 700; }

/* ---------- hero ---------- */
.hero { text-align: center; padding: 44px 8px 26px; }
.hero h1 { font-size: clamp(1.7rem, 4.5vw, 2.7rem); margin: 0 0 12px; }
.hero .lead { max-width: 640px; margin: 0 auto 22px; color: var(--c-text-soft); font-size: 1.05rem; }

/* ---------- buttons ---------- */
.btn {
  display: inline-block; border: 0; cursor: pointer; text-decoration: none;
  border-radius: 999px; font-weight: 700; font-size: .95rem;
  padding: 10px 22px; transition: transform .12s ease, box-shadow .12s ease;
}
.btn:active { transform: scale(.97); }
.btn-primary { background: var(--c-primary); color: #fff; box-shadow: var(--shadow); }
.btn-primary:hover { background: var(--c-primary-dark); }
.btn-ghost { background: transparent; color: var(--c-text-soft); }
.btn-lg { padding: 13px 30px; font-size: 1.05rem; }

/* Hero CTA on topics flagged 'strong_cta'. A pill sized by its label is half the width of
   a phone screen and a seventh of a desktop one, so it has to be sized against the
   viewport, not against the words in it. */
.btn-play { display: inline-flex; align-items: center; justify-content: center; gap: 11px; }
.btn-play-mark { width: .6em; height: .72em; fill: currentColor; flex: none; }
@media (min-width: 641px) {
  .btn-play { min-width: 290px; padding: 17px 44px; font-size: 1.2rem; }
}
.btn-play:hover, .btn-mark:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
/* .btn:active is declared above, so the hover lift would otherwise swallow the press. */
.btn-play:active, .btn-mark:active { transform: translateY(0) scale(.97); }
/* The mark and the lift without the size or the nudge that come with .btn-play. */
.btn-mark { display: inline-flex; align-items: center; justify-content: center; gap: 11px; }

/* One nudge for a visitor who has not moved at all; app.js adds the class after an idle
   spell, any input cancels it and it never loops. */
@keyframes btn-play-nudge {
  0%   { transform: scale(1); box-shadow: var(--shadow); }
  45%  { transform: scale(1.05); box-shadow: 0 0 0 9px rgba(108, 92, 231, .15), var(--shadow-lg); }
  100% { transform: scale(1); box-shadow: 0 0 0 22px rgba(108, 92, 231, 0), var(--shadow); }
}
.btn-play.is-nudge { animation: btn-play-nudge .75s ease-out; }

/* The ring has to hold through the ~400ms of smooth scrolling that runs before the board
   is even on screen, or it has faded out by the time anyone can see it. */
@keyframes board-arrive {
  0%, 45% { box-shadow: 0 0 0 3px rgba(108, 92, 231, .55), var(--shadow); }
  100%    { box-shadow: 0 0 0 3px rgba(108, 92, 231, 0), var(--shadow); }
}
.game-root.is-arrive { animation: board-arrive 1.1s ease-out; }

/* Page chrome only, and it is the ONLY reduced-motion rule on the site: a game's own
   animations are how a board answers a tap, so they play whatever the OS preference says.
   Nothing under a `#game-root` belongs in here. */
@media (prefers-reduced-motion: reduce) {
  .btn-play.is-nudge { animation: none; }
  .btn-play:hover, .btn-mark:hover { transform: none; }
}

/* ---------- games grid ---------- */
.games-section { padding: 18px 0 8px; }
.games-section h2, .why-section h2, .faq-section h2, .more-games h2, .game-research h2 { font-size: 1.45rem; }
.games-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.game-card {
  display: flex; flex-direction: column; gap: 6px; position: relative;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius);
  padding: 18px; text-decoration: none; color: var(--c-text);
  box-shadow: var(--shadow); transition: transform .15s ease, box-shadow .15s ease;
}
.game-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.game-card h3 { margin: 2px 0; font-size: 1.1rem; }
.game-card p { margin: 0; color: var(--c-text-soft); font-size: .9rem; flex: 1; }
.game-icon { font-size: 2rem; line-height: 1; }
.game-play { color: var(--c-primary); font-weight: 700; font-size: .9rem; margin-top: 8px; }
.game-card-sm { padding: 14px; align-items: center; flex-direction: row; gap: 10px; }
.game-card-sm h3 { font-size: .98rem; margin: 0; }

.game-cat {
  position: absolute; top: 14px; right: 14px;
  font-size: .72rem; font-weight: 700; padding: 3px 9px; border-radius: 999px;
}
.cat-memory    { background: #e8e3ff; color: #5345c9; }
.cat-attention { background: #dff7ef; color: #00795f; }
.cat-math      { background: #fff1d6; color: #9a6a00; }
.cat-reaction  { background: #ffe3e3; color: #b33939; }
.cat-typing    { background: #e3edff; color: #2952cc; }
.cat-word      { background: #fce4f0; color: #a83a72; }

/* ---------- text sections ---------- */
.why-section, .faq-section { padding: 22px 0 4px; }
.why-section p { color: var(--c-text-soft); }
.faq-item {
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: 12px; padding: 12px 16px; margin-bottom: 10px;
}
.faq-item summary { font-weight: 700; cursor: pointer; }
.faq-item p { color: var(--c-text-soft); margin: 10px 0 2px; }

.feedback-banner {
  text-align: center; background: linear-gradient(135deg, #6c5ce7, #8e7bf0);
  color: #fff; border-radius: var(--radius); padding: 30px 20px; margin: 30px 0 40px;
}
.feedback-banner h2 { margin-top: 0; }
.feedback-banner .btn-primary { background: #fff; color: var(--c-primary); }

/* understated feedback prompt at the bottom of topic + game pages */
.page-feedback {
  margin: 34px 0 8px; padding: 22px 18px 6px;
  border-top: 1px solid var(--c-border); text-align: center;
}
.page-feedback p { color: var(--c-text-soft); margin: 0 0 14px; }
.page-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.btn-share { background: var(--c-surface); color: var(--c-primary); box-shadow: var(--shadow); }
.btn-share:hover { background: #f3f0ff; }
.btn-share:disabled { cursor: default; opacity: .75; }

/* ---------- game page ---------- */
.game-page { padding-top: 22px; }
.breadcrumb { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: .85rem; color: var(--c-text-soft); margin: 0 0 12px; }
.breadcrumb a { color: var(--c-text-soft); text-decoration: none; }
.breadcrumb a:hover { color: var(--c-primary); }
.breadcrumb .crumb-current { color: var(--c-text); font-weight: 600; }
.game-header { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; margin-bottom: 14px; }
/* The title block takes what is left rather than asking for its text's full width. A flex
   line wraps BEFORE it shrinks, so with an auto basis a long one-liner pushed the jump
   button onto a line of its own, where `margin-left: auto` parked it in the empty right-hand
   half of the page — 80px of button alone above the board. min-width lets it shrink past its
   longest word; without it the basis is ignored and the wrap comes back. */
.game-header > div { flex: 1 1 0; min-width: 0; }
/* Jumps to the board. A plain anchor so it works before any script does, and
   .game-root already carries the scroll-margin that clears the sticky header.
   Not .game-play -- that name is taken by the "Play →" text on the game cards,
   and its colour would repaint this button's label its own background. */
.game-jump { margin-left: auto; flex-shrink: 0; white-space: nowrap; }
/* Below this width the header can no longer keep the button on the title's line, and a
   wrapped jump-to-board button is pure height above a board that is already in view. */
@media (max-width: 640px) { .game-jump { display: none; } }
.game-icon-lg { font-size: 3rem; line-height: 1; }
.game-header h1 { margin: 0 0 4px; font-size: clamp(1.5rem, 4vw, 2.1rem); }
.game-header .lead { margin: 0 0 6px; color: var(--c-text-soft); }
.game-meta { display: flex; gap: 10px; align-items: center; margin: 0; }
.game-meta .game-cat { position: static; }
.game-best { font-size: .9rem; color: var(--c-text-soft); }
/* Which mode the best belongs to, for games whose modes keep separate records (mahjong). */
.game-best-mode { font-weight: 600; }

.game-root {
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 22px; margin: 14px 0 26px;
  min-height: 320px; display: flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: transparent;
}

.game-info { display: grid; gap: 14px; grid-template-columns: 1fr; margin-bottom: 10px; }
.info-block {
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius); padding: 6px 22px 14px;
}
.info-block h2 { font-size: 1.15rem; }
/* Opens the full-rules dialog. Sits under the short how-to steps, not beside them, so the
   two info blocks keep the same shape as every other game page. */
.rules-open {
  margin-top: 4px; border: 1px solid var(--c-border); background: var(--c-bg);
  color: var(--c-text); font: inherit; font-size: .88rem; font-weight: 600;
  padding: 8px 14px; border-radius: 10px; cursor: pointer;
}
.rules-open:hover { background: var(--c-surface); }

/* Long-form rules live in a dialog: the measure is capped in ch because prose set to the
   full width of a 1080px container runs to ~98 characters a line and the eye loses its
   place coming back to the left margin. */
/* display is set on [open] only — a bare `display: flex` here would defeat the dialog
   element's own hiding and leave the rules on the page. */
.rules-dialog {
  width: min(94vw, 740px); max-height: 86vh; padding: 0;
  border: 0; border-radius: var(--radius); background: var(--c-surface); color: var(--c-text);
  box-shadow: 0 18px 50px rgba(0, 20, 10, .3);
}
.rules-dialog[open] { display: flex; flex-direction: column; }
.rules-dialog::backdrop { background: rgba(10, 14, 30, .55); }
/* overscroll-behavior is what stops the scroll chaining to the page once the rules hit
   their top or bottom, and measured, it is sufficient on its own.
   Do NOT add an overflow lock on <html> or <body> as well: with the document made
   unscrollable the browser clamps scrollY to the new maximum, and the page behind the
   backdrop visibly jumps the moment the dialog opens (measured: 1996 -> 1199). Pinning
   the body with position:fixed is worse again — the document collapses, so restoring the
   offset on close lands somewhere else entirely. */
.rules-body {
  padding: 20px 26px 18px; overflow-y: auto; flex: 1 1 auto; min-height: 0;
  overscroll-behavior: contain;
}

.rules-body h2 { margin: 0 0 4px; font-size: 1.3rem; }
.rules-body h3 { font-size: 1.02rem; font-weight: 700; margin: 22px 0 6px; }
.rules-body p { margin: 0; line-height: 1.65; color: var(--c-text-soft); }
.rules-body ol { margin: 14px 0 0; padding-left: 22px; }
.rules-body ol li { margin-bottom: 10px; line-height: 1.6; color: var(--c-text-soft); }
/* Outside the scrolling body, so it is always reachable and never covers the last
   paragraph the way a sticky button inside the scroller does. */
.rules-close {
  flex: none; border: 0; background: var(--c-primary); color: #fff;
  font: inherit; font-weight: 700; padding: 13px; cursor: pointer;
  border-radius: 0 0 var(--radius) var(--radius);
}
.info-block li { margin-bottom: 6px; color: var(--c-text-soft); }
.game-desc-seo { color: var(--c-text-soft); font-size: .95rem; }
.more-games { margin: 26px 0 40px; }

/* ---------- related research ---------- */
.game-research { margin: 0 0 44px; }
.game-research h2 { margin-bottom: 4px; }
.research-note { color: var(--c-text-soft); font-size: .95rem; margin: 0 0 14px; }
.paper-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 12px; }
.paper {
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius); padding: 12px 16px;
}
.paper-title { display: block; font-weight: 600; color: var(--c-primary); text-decoration: none; }
.paper-title:hover { text-decoration: underline; }
.paper-cite { display: block; color: var(--c-text-soft); font-size: .9rem; margin-top: 3px; }

/* ---------- shared game UI ---------- */
.g-center { text-align: center; width: 100%; }
.g-stats { display: flex; justify-content: center; gap: 22px; margin-bottom: 14px; flex-wrap: wrap; }
.g-stat { font-size: .9rem; color: var(--c-text-soft); }
.g-stat strong { font-size: 1.25rem; color: var(--c-text); display: block; }
.g-msg { min-height: 1.4em; font-weight: 700; margin: 8px 0; }

/* Completion is the headline; the score stays small and grey. */
.result-card { text-align: center; }
.result-card .result-done { font-size: 2.2rem; font-weight: 800; color: var(--c-success); margin: 4px 0 10px; }
.result-card .result-value { font-size: 1.25rem; font-weight: 600; color: var(--c-text-soft); margin: 4px 0 10px; }
.result-card .result-best { color: var(--c-text-soft); margin-bottom: 4px; }
.result-card .result-egg { color: #9a6a00; background: #fff6e3; display: inline-block; padding: 4px 14px; border-radius: 999px; font-size: .9rem; margin: 6px 0 10px; }
.result-card .result-trdone { font-size: 1.15rem; font-weight: 800; color: var(--c-primary); margin: 14px 0 2px; }
.result-card .btn { margin-top: 10px; }

/* ---------- daily training ---------- */
.training-page { max-width: 640px; margin: 0 auto; padding-bottom: 30px; }
.hero-sm { padding: 30px 8px 18px; }
.training-alt { text-align: center; margin-top: 18px; }
.training-section { padding: 6px 0 10px; }
.training-card {
  background: linear-gradient(135deg, #ffffff, #f3f1ff);
  border: 1px solid #ddd8f7; border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 20px 22px 22px; text-align: center;
}
.training-card h2 { margin: 0 0 2px; }
.tr-sub { color: var(--c-text-soft); margin: 2px 0 14px; font-size: .95rem; }
.tr-stats { display: flex; justify-content: center; gap: 22px; flex-wrap: wrap; margin-bottom: 12px; }
.tr-stats .g-stat strong { display: inline; font-size: 1.15rem; margin-left: 4px; color: var(--c-primary); }
.tr-streak { color: var(--c-success); font-weight: 700; }
.tr-plan { list-style: none; margin: 0 auto 14px; padding: 0; max-width: 340px; text-align: left; }
.tr-plan li {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 10px;
  padding: 8px 14px; margin-bottom: 6px; font-weight: 600;
}
.tr-plan li.done { opacity: .65; }
.tr-plan li.done span:first-child { text-decoration: line-through; }
.tr-check { color: var(--c-success); font-weight: 800; }
.tr-done-msg { font-size: 1.1rem; font-weight: 800; color: var(--c-success); margin: 10px 0; }
.tr-week { margin-top: 16px; }
.tr-week-label { color: var(--c-text-soft); font-size: .85rem; margin: 0 0 6px; }
.tr-chips { display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; }
.tr-chip {
  font-size: .82rem; font-weight: 700; padding: 4px 12px; border-radius: 999px;
  background: var(--c-bg); color: #b6b0c8; border: 1px solid var(--c-border);
}
.tr-chip.lit { background: #e8e3ff; color: var(--c-primary-dark); border-color: #cdc4f5; }
.tr-banner {
  text-align: center; font-weight: 700; color: var(--c-primary);
  background: #efecff; border-radius: 999px; padding: 6px 16px;
  display: table; margin: 14px auto 0;
}

/* ---------- feedback page ---------- */
.feedback-page { max-width: 620px; margin: 0 auto; padding: 30px 0 50px; }
.feedback-form {
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 22px; display: flex; flex-direction: column; gap: 14px;
}
.rating-fieldset { border: 0; padding: 0; margin: 0; }
.rating-fieldset legend { font-weight: 700; margin-bottom: 6px; }
.rating-radios { display: inline-flex; flex-direction: row-reverse; gap: 4px; }
.rating-radios input { position: absolute; opacity: 0; width: 0; }
.rating-radios label { font-size: 2rem; color: #d6d3e8; cursor: pointer; transition: color .1s; }
.rating-radios input:checked ~ label, .rating-radios label:hover, .rating-radios label:hover ~ label { color: var(--c-accent); }
.rating-radios input:focus-visible + label { outline: 2px solid var(--c-primary); border-radius: 4px; }
.field-label { font-weight: 700; }
.feedback-form textarea, .fb-dialog textarea {
  width: 100%; border: 2px solid var(--c-border); border-radius: 12px; padding: 10px;
  font: inherit; resize: vertical;
}
.feedback-form textarea:focus, .fb-dialog textarea:focus { outline: none; border-color: var(--c-primary); }
.alert { border-radius: 12px; padding: 12px 16px; margin: 14px 0; font-weight: 600; }
.alert-success { background: #e9fbf5; color: #00795f; }
.alert-error { background: #ffe3e3; color: var(--c-error); }
.fb-note { color: var(--c-text-soft); font-size: .82rem; margin: 0; }
/* Spam honeypot: parked off-screen rather than display:none — bots that skip hidden inputs
   still fill this one, and no sighted or screen-reader user ever reaches it (aria-hidden +
   tabindex="-1" on the element). Anything typed here marks the submission as spam. */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

/* ---------- privacy page ---------- */
.privacy-page { max-width: 680px; margin: 0 auto; padding: 30px 0 50px; }
.privacy-page .lead { color: var(--c-text-soft); }
.privacy-page h2 { margin-top: 28px; font-size: 1.15rem; }
.privacy-page li { margin: 6px 0; }

/* floating feedback button + dialog */
.fb-fab {
  position: fixed; right: 16px; bottom: 16px; z-index: 60;
  border: 0; border-radius: 999px; padding: 11px 18px; cursor: pointer;
  background: var(--c-primary); color: #fff; font-weight: 700; font-size: .92rem;
  box-shadow: var(--shadow-lg);
}
.fb-fab:hover { background: var(--c-primary-dark); }
.fb-dialog {
  border: 0; border-radius: var(--radius); box-shadow: var(--shadow-lg);
  padding: 22px; width: min(420px, calc(100vw - 32px));
}
.fb-dialog::backdrop { background: rgba(30, 30, 60, .45); }
.fb-dialog h2 { margin-top: 0; font-size: 1.2rem; }
.fb-stars { display: flex; gap: 4px; margin-bottom: 12px; }
.fb-star { background: none; border: 0; font-size: 1.9rem; color: #d6d3e8; cursor: pointer; padding: 0 2px; }
.fb-star.sel { color: var(--c-accent); }
.fb-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }

/* ---------- footer ---------- */
.site-footer {
  border-top: 1px solid var(--c-border); background: var(--c-surface);
  margin-top: 40px; padding: 30px 0;
}
.footer-inner {
  display: flex; flex-wrap: wrap; gap: 18px 48px;
  justify-content: space-between; align-items: flex-start;
}
.footer-brand-col { max-width: 460px; }
.footer-brand { font-weight: 700; margin: 0 0 6px; }
.footer-tag { color: var(--c-text-soft); font-size: .9rem; margin: 0; }
/* The basis is two 150px tracks plus their gap, and it is what makes the nav drop onto its
   own full-width row instead of being squeezed beside the 460px brand block. With a zero
   basis this never wraps: it shrinks to one column that 32 links make taller than the
   window, and below 674px of window the 150px track minimum overflows the page as well. */
.footer-links {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px 24px; flex: 1 1 324px; min-width: 0;
}
.footer-links a { color: var(--c-text-soft); text-decoration: none; font-size: .92rem; }
.footer-links a:hover { color: var(--c-primary); }
.footer-bottom {
  border-top: 1px solid var(--c-border); margin-top: 20px; padding-top: 14px;
  color: var(--c-text-soft); font-size: .82rem; text-align: center;
}
.footer-bottom a { color: inherit; text-decoration: underline; }

/* ---------- responsive ---------- */
@media (min-width: 700px) {
  .game-info { grid-template-columns: 1fr 1fr; }
  .hero { padding-top: 60px; }
}
@media (max-width: 420px) {
  .site-nav { gap: 9px; }
  .site-nav > a { font-size: .85rem; }
  .fb-fab span { display: none; }
  .game-root { padding: 14px; }
}

/* ---------- homepage topic hub ---------- */
/* unify all homepage block widths so their left/right edges line up (full container) */
.home > section { max-width: none; margin-left: 0; margin-right: 0; }
.topics-section { padding: 14px 0 8px; }
.topics-section h2 { font-size: 1.45rem; }
.topics-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.topic-card {
  display: flex; flex-direction: column; gap: 8px;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius);
  padding: 26px 22px; text-decoration: none; color: var(--c-text);
  box-shadow: var(--shadow); transition: transform .15s ease, box-shadow .15s ease;
}
.topic-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.topic-icon { font-size: 2.6rem; line-height: 1; }
.topic-card h3 { margin: 4px 0 0; font-size: 1.3rem; }
.topic-card p { margin: 0; color: var(--c-text-soft); font-size: .95rem; flex: 1; }
.topic-meta { color: var(--c-primary); font-weight: 700; font-size: .9rem; margin-top: 6px; }

/* ---------- topic page: embedded featured game ---------- */
/* Anchor targets for the topic hero's CTA. The site header is sticky and 57px tall, so an
   in-page jump lands the section heading underneath it without this. */
.topic-featured, .games-section, .game-root, .more-games, .more-topics { scroll-margin-top: 68px; }

/* A topic flagged 'splitnav' (app/topics.php) hands the header band to the board: app.js
   sets .nav-split on the body while the board is behind it, and the bar sheds its paint
   so only the logo and the nav are left, riding as pills at the two edges.
   The bottom border keeps its 1px and loses only its colour, and .header-inner keeps its
   56px: app.js measures the header to decide the class, so a height that moved with it
   would flap the toggle at the boundary line. The stacked nav is taller than that and
   overflows the band on purpose — nothing here clips it. */
body.nav-split .site-header {
  background-color: transparent; border-bottom-color: transparent; backdrop-filter: none;
  /* The middle of the band is the board. A bar still catching clicks there would swallow
     a drag that starts at the top of the pond. */
  pointer-events: none;
}
/* Out of .container and onto the screen edges: the board is 1080px wide at most, so the
   page gutter is the only place the pills clear it. */
body.nav-split .header-inner { max-width: none; padding: 0 10px; align-items: flex-start; }
body.nav-split .logo, body.nav-split .site-nav {
  pointer-events: auto; padding: 5px 14px; border-radius: 999px;
  background: rgba(255, 255, 255, .88); backdrop-filter: blur(8px);
  box-shadow: 0 2px 12px rgba(24, 48, 44, .18);
}
body.nav-split .logo { display: inline-flex; align-items: center; height: 34px; margin-top: 11px; }
/* Only the split header has a menu to put this in; in the whole bar it would be a third
   sitewide nav entry pointing at an anchor most pages do not have. The rule above it and the
   chevron say it is not a third destination: it stays on the page and scrolls down. */
.nav-more { display: none; }
body.nav-split .nav-more {
  display: flex; justify-content: space-between; gap: 12px;
  margin-top: 6px; padding-top: 7px; border-top: 1px solid var(--c-border);
}
body.nav-split .nav-more::after { content: "⌄"; line-height: 1; }
body.nav-split .site-nav {
  flex-direction: column; align-items: stretch; gap: 4px; margin-top: 11px;
  border-radius: 16px; padding: 7px 14px; text-align: left;
}

.topic-featured { margin: 8px 0 0; text-align: center; }
.topic-featured h2 { font-size: 1.25rem; margin-bottom: 12px; }
/* Weight only — the text stays, it is the section's outline heading. */
.topic-featured-quiet h2 { font-size: 1.02rem; color: var(--c-text-soft); margin-bottom: 8px; }
/* Laid over the panel rather than in the flow, so the board keeps its position and its
   height. .sol-bar is 34px tall and starts at the panel's 22px padding, so matching that
   box puts this on the same line as the game's own "0/52 home · 0 moves". z-index stays
   below .sol-full's 200: in full screen the board covers the page and this goes with it. */
.game-howto-wrap { position: relative; }
.topic-howto {
  position: absolute; top: 22px; right: 22px; z-index: 1; height: 34px;
  margin-top: 0; display: inline-flex; align-items: center; gap: 5px;
  border: 0; background: none; border-radius: 0; padding: 0;
  font-size: .8rem; font-weight: 400; color: var(--c-text-soft);
  text-decoration: underline; text-underline-offset: 3px;
}
.topic-howto:hover { background: none; color: var(--c-primary); }
/* Needed: the rule above sets `display`, which beats the UA sheet's [hidden] { display: none }
   — without this the element stays on screen after app.js sets the property. */
.topic-howto[hidden] { display: none; }
/* A text question mark, not ❓: the emoji renders red on most platforms. Bolder than the
   label so the glyph still reads at the width where it stands alone. */
.topic-howto-icon { font-weight: 700; }
/* Phones: the solitaire panel sheds its card chrome and eats 8px into the gutter (see the
   solitaire .game-root rule in site.css), so the row starts at the wrapper's own top edge. The label is
   hidden — it would run into the status line, which is centred on the same row. */
@media (max-width: 640px) {
  .topic-howto { top: 0; right: 8px; }
  .topic-howto-text { display: none; }
}
.topic-featured-link { margin-top: 10px; }

/* Where the embedded game sits in the topic's series ("Game 2 of 3 …"). Shown at every
   width — the series should read as a series on phones too, where the cards below hide. */
.topic-series-pos { margin: -8px 0 14px; font-size: .9rem; color: var(--c-text-soft); }

/* Prev/next series cards beside the embedded board. They live entirely in the page
   gutter — nothing here may change the board's width or sit on top of it: a card over
   the play area would swallow a drag or a click that starts at the board's edge.
   .container is 1080px wide; the compact pill (56px + 20px gap) needs 76px free per
   side and the full card (132px + 20px) needs 152px, so each form only appears once
   the viewport can spare it. The column spans the whole section and the card sticks
   to the middle of the viewport inside it, so a tall board still has its flipper in
   reach mid-scroll. */
.topic-flip { display: none; }
@media (min-width: 1240px) {
  .topic-featured { position: relative; }
  .topic-flip { display: block; position: absolute; top: 0; bottom: 0; width: 56px; }
  .topic-flip-prev { left: -76px; }
  .topic-flip-next { right: -76px; }
  .topic-flip-btn {
    position: sticky; top: calc(50vh - 60px);
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    width: 100%; padding: 16px 0; border-radius: 999px;
    background: var(--c-surface); border: 1px solid var(--c-border); box-shadow: var(--shadow);
    color: var(--c-text); text-decoration: none;
    transition: border-color .15s, transform .15s;
  }
  .topic-flip-btn:hover { border-color: var(--c-primary); transform: translateY(-2px); }
  .topic-flip-icon { font-size: 1.55rem; line-height: 1; }
  .topic-flip-arrow { color: var(--c-primary); font-weight: 800; line-height: 1; }
  .topic-flip-word, .topic-flip-name { display: none; }
}
@media (min-width: 1400px) {
  .topic-flip { width: 132px; }
  .topic-flip-prev { left: -152px; }
  .topic-flip-next { right: -152px; }
  .topic-flip-btn { border-radius: var(--radius); padding: 18px 12px 14px; gap: 6px; top: calc(50vh - 92px); }
  .topic-flip-word {
    display: block; font-size: .68rem; font-weight: 800;
    letter-spacing: .09em; text-transform: uppercase; color: var(--c-primary);
  }
  .topic-flip-icon { font-size: 2rem; margin-top: 2px; }
  .topic-flip-name { display: block; font-weight: 700; font-size: .92rem; line-height: 1.3; }
  /* A wave runs through the letters of the next card's word every few seconds, so a
     player looking at the board notices the card without anything moving beside it.
     The stagger is one rule per letter because the CSP has no style-src exception:
     an inline style="--i:n" on each letter is blocked, so the delays live here and
     must cover the longest word the card can show (topic.flip_again, 10 characters). */
  .topic-flip-word > span { display: inline-block; }
  .topic-flip-next .topic-flip-word > span { animation: topic-flip-wave 6s ease-in-out infinite; }
  .topic-flip-next .topic-flip-word > span:nth-child(2)  { animation-delay: .055s; }
  .topic-flip-next .topic-flip-word > span:nth-child(3)  { animation-delay: .11s; }
  .topic-flip-next .topic-flip-word > span:nth-child(4)  { animation-delay: .165s; }
  .topic-flip-next .topic-flip-word > span:nth-child(5)  { animation-delay: .22s; }
  .topic-flip-next .topic-flip-word > span:nth-child(6)  { animation-delay: .275s; }
  .topic-flip-next .topic-flip-word > span:nth-child(7)  { animation-delay: .33s; }
  .topic-flip-next .topic-flip-word > span:nth-child(8)  { animation-delay: .385s; }
  .topic-flip-next .topic-flip-word > span:nth-child(9)  { animation-delay: .44s; }
  .topic-flip-next .topic-flip-word > span:nth-child(10) { animation-delay: .495s; }
}
@keyframes topic-flip-wave {
  0%, 12%, 100% { transform: none; }
  4%            { transform: translateY(-3px) scale(1.12); }
}

/* Cross-topic nav at the very bottom of a topic page — compact one-line cards, kept
   lighter than the homepage topic cards so they never compete with the page's own games. */
.more-topics { margin: 56px 0 4px; }
.more-topics h2 { font-size: 1.2rem; margin: 0 0 12px; }
.topic-minis { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }
.topic-mini {
  display: flex; align-items: flex-start; gap: 10px; padding: 12px 16px;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius);
  box-shadow: var(--shadow); text-decoration: none; color: var(--c-text);
}
.topic-mini:hover { border-color: var(--c-primary); }
.topic-mini-icon { font-size: 1.6rem; line-height: 1; }
.topic-mini-body { display: flex; flex-direction: column; }
.topic-mini-name { font-weight: 700; font-size: .98rem; }
.topic-mini-short { font-size: .85rem; color: var(--c-text-soft); margin: 2px 0 4px; }
.topic-mini-count { font-size: .82rem; color: var(--c-primary); }
.topic-featured-link a { color: var(--c-primary); font-weight: 700; text-decoration: none; }
.topic-featured-link a:hover { text-decoration: underline; }
.topic-browse-all { display: none; margin: 16px 0 0; text-align: center; }
.topic-browse-all a { color: var(--c-primary); font-weight: 700; text-decoration: none; }
@media (max-width: 640px) { .topic-browse-all { display: block; } }
