:root {
  --bg: #14161a;
  --panel: #1c1f26;
  --panel-2: #232733;
  --line: #2e3441;
  --ink: #e8eaf0;
  --ink-dim: #969db0;
  --accent: #6f9dff;
  --accent-ink: #0d1220;
  --warn: #f0a955;
  --ok: #63c98a;
  --err: #ef7a7a;
  --radius: 10px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { font-size: 1.6rem; margin: 0 0 1rem; }
h2 { font-size: 1.1rem; margin: 1.75rem 0 .6rem; }
code { background: var(--panel-2); padding: .1em .35em; border-radius: 4px; }

/* Layout ------------------------------------------------------------------ */

.topbar {
  display: flex; align-items: center; gap: 1.5rem;
  padding: .75rem 1.25rem;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.brand { font-weight: 700; letter-spacing: .04em; color: var(--ink); }
.topbar nav { display: flex; gap: 1rem; }
.topbar nav a, .who a { color: var(--ink-dim); }
.topbar nav a.on, .who a.on { color: var(--ink); border-bottom: 2px solid var(--accent); }
.who { margin-left: auto; display: flex; align-items: center; gap: .75rem; }

main { flex: 1; width: 100%; max-width: 1100px; margin: 0 auto; padding: 1.5rem 1.25rem 3rem; }
.narrow { max-width: 620px; }
.lead { margin: -.4rem 0 1.5rem; font-size: 1.05rem; }

footer {
  border-top: 1px solid var(--line);
  padding: 1rem 1.25rem;
  color: var(--ink-dim);
  font-size: .85rem;
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
}

/* Messages ---------------------------------------------------------------- */

.flash, .error, .ok {
  padding: .7rem .9rem; border-radius: var(--radius); margin: 0 0 1.25rem;
  border: 1px solid var(--line); background: var(--panel);
}
.error { border-color: var(--err); color: var(--err); }
.ok { border-color: var(--ok); color: var(--ok); }
.warn { color: var(--warn); }
.muted { color: var(--ink-dim); }
.small { font-size: .85rem; }
.empty { padding: 3rem 1rem; text-align: center; color: var(--ink-dim); }

/* Forms ------------------------------------------------------------------- */

label { display: block; margin-bottom: .35rem; }
input, select, textarea, button, .button-ish {
  font: inherit;
  border-radius: 8px;
  border: 1px solid var(--line);
  padding: .5rem .7rem;
}
input, select, textarea {
  width: 100%; background: var(--panel-2); color: var(--ink);
}
/* Block, or the inline baseline leaves a stray gap under it; vertical-only
   resize so dragging one cannot push the form wider than its column. */
textarea { display: block; resize: vertical; line-height: 1.5; }
input:focus, select:focus, textarea:focus, button:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
button, .button-ish {
  background: var(--accent); color: var(--accent-ink);
  font-weight: 600; cursor: pointer; white-space: nowrap;
}
button:hover { filter: brightness(1.1); }
button:disabled { opacity: .5; cursor: not-allowed; }
button.linkish {
  background: none; border: none; color: var(--ink-dim);
  padding: 0; font-weight: 400; text-decoration: underline;
}
.stack { display: grid; gap: .9rem; margin-bottom: 1rem; }
.stack button { justify-self: start; }
.inline {
  display: flex; gap: .6rem; align-items: end; flex-wrap: wrap; margin: .75rem 0;
}
.inline label { margin: 0; flex: 1 1 12rem; }
.search { max-width: 30rem; }

/* Chips ------------------------------------------------------------------- */

.chips { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.chip {
  padding: .3rem .7rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--ink-dim); font-size: .9rem;
}
.chip.on { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 600; }
.chip:hover { text-decoration: none; border-color: var(--accent); }
.chip .count { opacity: .7; }
.chip-gap { flex-basis: 100%; height: 0; }

/* Board ------------------------------------------------------------------- */

.grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
  display: flex; flex-direction: column;
}
.cover { display: block; background: var(--panel-2); aspect-ratio: 460 / 215; }
.cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cover-blank {
  display: flex; align-items: center; justify-content: center; height: 100%;
  padding: 1rem; text-align: center; color: var(--ink-dim); font-weight: 600;
}
/* Two columns: the title and ownership stack on the left, the score sits to
   their right spanning both, and the rating buttons run full width underneath.
   The third row takes the slack so buttons line up across cards whose titles
   wrap to different heights. */
/* Who is playing: the same chips as the filters, but a set of toggles rather
   than one-of-N, so the row reads as a line-up. */
.who-chips { align-items: center; }
.chips-label { color: var(--ink-dim); font-size: .9rem; margin-right: .1rem; }
.chip.clear { border-style: dashed; }
.playing-note {
  margin: 0 0 1rem; color: var(--ink-dim); font-size: .9rem;
}

.card-body {
  padding: .85rem; flex: 1;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto 1fr;
  gap: .4rem .75rem;
}
.card-body > h2      { grid-row: 1; grid-column: 1; margin: 0; font-size: 1rem; }
.card-body > .owners { grid-row: 2; grid-column: 1; }
.card-body h2 a { color: var(--ink); }
/* One line whatever the group size, with the names a click away. */
.owners { margin: 0; font-size: .9rem; }
details.owners > summary {
  cursor: pointer; color: var(--ink-dim);
  display: inline-flex; align-items: center; gap: .35rem;
  /* The native marker sits outside the summary's box, which pushed this text
     off the left edge the title sits on. Replaced with one that trails the
     text, so the two line up. */
  list-style: none;
}
details.owners > summary::-webkit-details-marker { display: none; }
details.owners > summary::after {
  content: "▸"; display: inline-block; font-size: .8em; color: var(--ink-dim);
  transition: transform .12s ease;
}
details.owners[open] > summary::after { transform: rotate(90deg); }
details.owners > summary:hover { color: var(--ink); }
details.owners > summary strong { color: var(--ink); }
details.owners > summary.all strong { color: var(--ok); }
details.owners > p { margin: .35rem 0 0; color: var(--ink-dim); }

/* Rating ------------------------------------------------------------------ */

.rating { margin: 0; display: flex; align-items: center; gap: .5rem; width: 100%; }
.levels { display: flex; gap: .25rem; flex: 1; }
.level {
  flex: 1; padding: .35rem .4rem;
  background: var(--panel-2); color: var(--ink-dim);
  border: 1px solid var(--line); border-radius: var(--radius);
  display: inline-flex; align-items: center; justify-content: center; gap: .3rem;
  font-size: .85rem; line-height: 1.2; cursor: pointer;
}
.level:hover { color: var(--ink); border-color: var(--ink-dim); }
.level-icon { font-size: 1rem; }
/* Each button carries its own tally, which is what let the separate count row
   under the control go away. No reserved width: an empty slot next to the icon
   left it sitting off-centre on every level nobody had picked. A level with no
   votes renders no count at all, so its icon centres properly. */
.level-count { font-variant-numeric: tabular-nums; }

/* The picked level is the only coloured one, so a glance at a card says where
   you stand without reading any of it. */
.level.on { color: var(--accent-ink); border-color: transparent; font-weight: 600; }
.level-dislike.on { background: var(--err); }
.level-neutral.on { background: var(--ink-dim); }
.level-like.on    { background: var(--ok); }
.level-love.on    { background: var(--accent); }

/* On a card there is no room for four labels; the icons and counts carry it,
   and the title attribute still names each one. */
.rating.compact .level-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.rating.compact .level { padding: .3rem .35rem; }

/* The form box disappears so its children join the card body's grid. They stay
   inside the form in the DOM, which is what keeps an HTMX rate swap refreshing
   the score along with the buttons — a score placed outside the form would go
   stale the moment anyone voted. Hidden inputs are display:none already, so
   they never become grid items. */
.rating.compact { display: contents; }
.rating.compact > .score  { grid-row: 1 / span 2; grid-column: 2; align-self: center; font-size: 2rem; }
.rating.compact > .levels { grid-row: 3; grid-column: 1 / -1; align-self: end; }

/* The score is the one number the board is ordered by, so it is the one thing
   on a card that should be readable across the room. */
/* No border or fill: boxed like the buttons, it read as a fifth one that could
   be clicked. A bare number sitting apart is both louder and honest about not
   being a control. */
.score {
  flex: 0 0 auto; min-width: 2ch; padding-left: .25rem;
  font-size: 1.5rem; font-weight: 700; line-height: 1; text-align: right;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
  color: var(--ink); cursor: default; user-select: none;
}
.score.good { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.score.bad  { color: var(--err);    border-color: color-mix(in srgb, var(--err) 45%, transparent); }
.score.none { color: var(--ink-dim); font-weight: 500; }

.t { color: var(--ink-dim); white-space: nowrap; }
.t-love { color: var(--accent); }
.t-like { color: var(--ok); }
.t-dislike { color: var(--err); }

.breakdown { list-style: none; padding: 0; display: grid; gap: .5rem; }
.breakdown li { display: flex; gap: .75rem; align-items: baseline; flex-wrap: wrap; }
.breakdown .t { min-width: 8rem; font-weight: 600; }

button.danger {
  background: var(--panel-2); color: var(--err); border: 1px solid var(--err);
}
button.danger:hover { background: var(--err); color: var(--accent-ink); }

/* Detail ------------------------------------------------------------------ */

.detail-head { display: flex; gap: 1.5rem; flex-wrap: wrap; align-items: start; }
.detail-head > div { flex: 1 1 20rem; }
.hero { width: min(100%, 400px); border-radius: var(--radius); }
.meta { display: flex; gap: 1rem; flex-wrap: wrap; color: var(--ink-dim); font-size: .9rem; }
.blurb { max-width: 55ch; }
.owners-list { list-style: none; padding: 0; display: grid; gap: .5rem; }
.owners-list li { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.pill {
  background: var(--panel-2); border: 1px solid var(--line);
  padding: .1rem .5rem; border-radius: 999px; font-size: .8rem; color: var(--ink-dim);
}

/* Search + profiles ------------------------------------------------------- */

.results { list-style: none; padding: 0; display: grid; gap: .5rem; margin: 1rem 0; }
.results li {
  display: flex; gap: .85rem; align-items: center;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .6rem .85rem;
}
.results img { width: 120px; border-radius: 6px; }
.result-body { flex: 1; display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.results form { margin: 0; }
.manual { margin-top: 2.5rem; border-top: 1px solid var(--line); padding-top: 1rem; }
/* Same measure as the edit form on a game page: full-width fields next to a
   30rem search box read as a different app. */
.manual form, .manual p { max-width: 46rem; }

.library, .profile-games { width: 100%; border-collapse: collapse; margin-top: 1rem; }
.library th, .library td, .profile-games th, .profile-games td {
  text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--line);
}
.library th, .profile-games th { color: var(--ink-dim); font-size: .85rem; font-weight: 600; }
.library form, .profile-games form { margin: 0; }
.profile-games .yes { color: var(--ok); font-weight: 700; }

/* A price is a fact about the game, not a call to action — same weight as the
   release date it sits beside, never styled like a buy button. */
.price { font-variant-numeric: tabular-nums; }

/* "Free to play" reads as good news wherever it appears: the detail page's
   meta row, and a board card in place of its ownership line. */
.free { color: var(--ok); }

/* Sits above the page title, so it reads as leaving rather than filtering. */
.back { margin-bottom: .5rem; font-size: .9rem; }
.back a { color: var(--ink-dim); }
.back a:hover { color: var(--ink); }

.profile-head { margin-bottom: 1.25rem; }
.profile-head h1 { margin-bottom: .35rem; }
.profile-head .tally { margin-top: .6rem; }
.stat { color: var(--ink-dim); }
.stat strong { color: var(--ink); font-size: 1.1rem; }

.members { list-style: none; padding: 0; display: grid; gap: .5rem; }
.members li {
  display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .75rem 1rem;
}
.member-name { font-weight: 600; font-size: 1.05rem; }

/* A plain <details>, so editing works with JavaScript off. */
.editor { margin-top: .75rem; }
.editor summary { cursor: pointer; color: var(--accent); width: max-content; }
.editor form { margin-top: .75rem; max-width: 46rem; }

@media (max-width: 560px) {
  .topbar { gap: .75rem; }
  .who { margin-left: 0; width: 100%; }
  .results img { display: none; }
}
