/* WhichBeats — one stylesheet, no build step.
 *
 * Cache-busted by ?v={{ css_version }} (md5 of this file, see app/templates.py),
 * so it can be served with a long max-age and still update instantly.
 *
 * Light and dark are both defined here via prefers-color-scheme. There is no
 * theme toggle: it would need either a cookie read on every render or a flash of
 * the wrong theme on first paint, and neither is worth it yet.
 *
 * The look, stated once so it does not drift: neutral surfaces, one warm accent,
 * hairline borders instead of heavy shadows, and every number that a reader will
 * compare against another number set in a tabular mono face. A leaderboard is a
 * table of claims about the world, so it should read like a document rather than
 * like a dashboard. The accent is spent on two things only: what you can click,
 * and what just changed. */

/* --- fonts --------------------------------------------------------------
 *
 * Self-hosted, and the reason is measurable rather than ideological. The
 * Google Fonts stylesheet was a render-blocking request to a third-party host
 * in <head>: two DNS lookups and two TLS handshakes before this file could even
 * be parsed, and the font files themselves a third hop after that. Served from
 * our own origin they are same-connection, cacheable on our terms, and one less
 * outage that can make the site look broken.
 *
 * Latin and Latin-Extended only, of the seven subsets Google ships. The UI is
 * English and the catalog is Latin-script; latin-ext is what carries é, ü, ñ, ø
 * and the rest that services/slug.py explicitly normalises. Cyrillic, Greek and
 * Vietnamese are dropped — a name in one falls back to the system stack, which
 * is a rendering difference on a rare row rather than a broken page, and keeping
 * them would have tripled the bytes for glyphs nothing in the catalog uses.
 *
 * `font-display: swap` matters more than usual here: the whole design
 * degrades to a system stack on purpose (see --font below), so swapping in late
 * costs layout polish and never a blank page.
 *
 * Variable fonts, one file per family per subset — `font-weight` is a range,
 * so every weight the design uses comes out of the same download. */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/static/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/static/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/static/fonts/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/static/fonts/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- tokens -------------------------------------------------------------- */

:root {
  /* Surfaces run white → zinc-100. Nothing in the neutral ramp is warm, so the
     accent is the only colour on the page and lands harder for it. */
  --bg: #ffffff;
  --bg-alt: #fafafa;
  --surface: #ffffff;
  --surface-2: #f4f4f5;
  --surface-3: #e9e9ec;
  --text: #09090b;
  --text-dim: #52525b;
  --text-muted: #71717a;
  --border: #e4e4e7;
  --border-strong: #d0d0d5;

  /* The lightest orange that still clears 4.5:1 on white — which it has to,
     because body links are set in it. */
  --accent: #c2410c;
  --accent-hover: #9a3412;
  --accent-soft: #fff3ec;
  --accent-line: #f6d0b8;
  --accent-text: #ffffff;

  --up: #15803d;
  --down: #b91c1c;
  --danger: #b91c1c;
  --gold: #a16207;
  --silver: #52525b;
  --bronze: #9a5b2d;

  --r-sm: 6px;
  --r: 10px;
  --r-lg: 14px;
  --r-xl: 20px;
  --radius: var(--r);

  --shadow-sm: 0 1px 2px rgba(9, 9, 11, 0.04), 0 1px 1px rgba(9, 9, 11, 0.03);
  --shadow-md: 0 4px 12px rgba(9, 9, 11, 0.06), 0 1px 3px rgba(9, 9, 11, 0.04);
  --shadow-lg: 0 12px 32px rgba(9, 9, 11, 0.10), 0 2px 8px rgba(9, 9, 11, 0.05);
  --ring: 0 0 0 3px rgba(194, 65, 12, 0.25);

  --maxw: 70rem;
  --font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", "Cascadia Mono", Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #09090b;
    --bg-alt: #0d0d10;
    --surface: #131317;
    --surface-2: #1b1b20;
    --surface-3: #26262c;
    --text: #fafafa;
    --text-dim: #c2c2ca;
    --text-muted: #9797a1;
    --border: #27272d;
    --border-strong: #3b3b43;

    --accent: #fb8b4c;
    --accent-hover: #fda571;
    --accent-soft: #23150d;
    --accent-line: #5b3018;
    --accent-text: #1a0c04;

    --up: #4ade80;
    --down: #f87171;
    --danger: #f87171;
    --gold: #eab308;
    --silver: #a1a1aa;
    --bronze: #c98a5c;

    /* Shadows do almost nothing on near-black. The elevation cue in dark mode is
       a lighter surface plus a brighter border; these exist only to stop
       floating panels from merging into the page behind them. */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 4px 14px rgba(0, 0, 0, 0.5);
    --shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.6);
    --ring: 0 0 0 3px rgba(251, 139, 76, 0.28);
  }
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; text-underline-offset: 2px; }

/* One focus treatment for everything, and it is never removed. The primary
   interaction on this site is two buttons and a skip link, so a missing focus
   ring makes the site unusable by keyboard rather than merely awkward. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm); }

h1, h2, h3 { line-height: 1.15; letter-spacing: -0.022em; font-weight: 700; }
h1 { font-size: clamp(1.85rem, 4.6vw, 2.75rem); margin: 0 0 0.6rem; letter-spacing: -0.032em; font-weight: 800; }
h2 { font-size: 1.3rem; margin: 2.5rem 0 0.9rem; }
h3 { font-size: 1.05rem; margin: 1.5rem 0 0.5rem; }

code { font-family: var(--mono); font-size: 0.88em; }

.num, .mono, .delta, .rank, .kpi strong, .tag-count, .stats strong,
.page-meta, .card-meta { font-variant-numeric: tabular-nums; }

.muted, .card-meta, small { color: var(--text-muted); }
small { font-size: 0.85rem; display: block; }
.lede { font-size: 1.09rem; color: var(--text-dim); max-width: 46rem; line-height: 1.62; }
.footnote {
  color: var(--text-muted); font-size: 0.9rem; margin-top: 2.5rem; max-width: 46rem;
  padding-left: 0.95rem; border-left: 2px solid var(--border);
}
.empty { color: var(--text-muted); padding: 1.5rem 0; }

/* --- layout ------------------------------------------------------------- */

main {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 2.25rem 1.25rem 5rem;
}

footer {
  border-top: 1px solid var(--border);
  background: var(--bg-alt);
  padding: 2.5rem 1.25rem;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
}
footer p { margin: 0; }
.footer-links {
  margin: 0.6rem 0 0;
  display: flex; gap: 0.85rem; justify-content: center; flex-wrap: wrap;
}
.footer-links a { color: var(--text-dim); }
.footer-links a:hover { color: var(--accent); }
.footer-sep { color: var(--border-strong); }

/* A heading with its "see everything" link pulled onto the same baseline. Used
   wherever a page shows a slice of a longer list. */
.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  margin: 2.75rem 0 1.1rem;
  padding-bottom: 0.65rem;
  border-bottom: 1px solid var(--border);
}
.section-head h2 { margin: 0; }
.section-head a { font-size: 0.9rem; font-weight: 500; white-space: nowrap; }

/* --- nav ---------------------------------------------------------------- */

header {
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 20;
}
/* Translucent only where the blur actually works, so the bar never degrades to
   "page text visible through the nav". */
@supports (backdrop-filter: blur(1px)) {
  header {
    background: color-mix(in srgb, var(--bg) 78%, transparent);
    backdrop-filter: saturate(1.6) blur(12px);
    -webkit-backdrop-filter: saturate(1.6) blur(12px);
  }
}

nav {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0.7rem 1.25rem;
  display: flex;
  align-items: center;
  gap: 1.1rem;
}

.logo { display: flex; align-items: center; gap: 0.5rem; color: var(--text); font-weight: 700; }
.logo:hover { text-decoration: none; color: var(--text); }
.logo-mark {
  display: grid; place-items: center;
  width: 1.65rem; height: 1.65rem;
  border-radius: var(--r-sm);
  background: var(--accent); color: var(--accent-text);
  /* Two lowercase letters ('vs'), so it needs weight and tightening to read
     as a mark rather than as small text in a box. */
  font-size: 0.72rem; line-height: 1; font-weight: 800; letter-spacing: -0.03em;
  box-shadow: var(--shadow-sm);
  transition: background 0.14s, transform 0.14s;
}
.logo-text { letter-spacing: -0.035em; font-size: 1.2rem; font-weight: 800; }
.logo:hover .logo-mark { background: var(--accent-hover); transform: translateY(-1px); }

/* min-width: 0 on both, and it is load-bearing on a phone: a text input has an
   intrinsic min-content width of roughly twenty characters, a flex item will not
   shrink below its min-content size, and the nav therefore pushed the whole
   document wider than the viewport — hamburger included, so the menu could not
   be reached to fix it. */
.nav-search { position: relative; flex: 1 1 auto; min-width: 0; max-width: 22rem; }
.nav-search input {
  width: 100%;
  min-width: 0;
  padding: 0.48rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: 0.92rem;
  transition: border-color 0.14s, box-shadow 0.14s, background 0.14s;
}
.nav-search input::placeholder { color: var(--text-muted); }
.nav-search input:focus {
  outline: none;
  background: var(--surface);
  border-color: var(--accent);
  box-shadow: var(--ring);
}
.suggestions {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  padding: 0.25rem;
  z-index: 30;
}
.suggestion { display: block; padding: 0.45rem 0.6rem; border-radius: var(--r-sm); color: var(--text); }
.suggestion:hover { background: var(--surface-2); text-decoration: none; color: var(--text); }
.suggestion strong { display: block; font-weight: 600; font-size: 0.93rem; }
.suggestion span { color: var(--text-muted); font-size: 0.82rem; }

#nav-menu {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  font-size: 0.92rem;
}
#nav-menu > li > a {
  display: block;
  color: var(--text-dim);
  padding: 0.35rem 0.65rem;
  border-radius: var(--r-sm);
  font-weight: 500;
}
#nav-menu > li > a:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }
#nav-menu > li > a.active { color: var(--accent); background: var(--accent-soft); font-weight: 600; }
.nav-dd-end { margin-left: auto; }

.nav-toggle { display: none; }

.nav-dd { position: relative; }
.nav-avatar-btn {
  display: flex; align-items: center; gap: 0.3rem;
  background: none; border: 0; cursor: pointer; padding: 0;
  border-radius: 50%;
}
.nav-avatar {
  display: grid; place-items: center;
  width: 2rem; height: 2rem;
  border-radius: 50%;
  background: var(--accent); color: var(--accent-text);
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.02em;
  transition: filter 0.14s;
}
.nav-avatar-btn:hover .nav-avatar { filter: brightness(1.1); }
.nav-dd-panel {
  display: none;
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 15rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 0.35rem;
}
.nav-dd.open .nav-dd-panel { display: block; }
.nav-dd-panel a, .nav-dd-form button {
  display: block; width: 100%; text-align: left;
  padding: 0.45rem 0.55rem; border-radius: var(--r-sm);
  background: none; border: 0; cursor: pointer;
  color: var(--text-dim); font: inherit; font-size: 0.92rem;
}
.nav-dd-panel a:hover, .nav-dd-form button:hover {
  background: var(--surface-2); text-decoration: none; color: var(--text);
}
.nav-dd-head { padding: 0.55rem; border-bottom: 1px solid var(--border); margin-bottom: 0.3rem; }
.nav-dd-head strong { display: block; font-size: 0.93rem; }
.nav-dd-head span { color: var(--text-muted); font-size: 0.8rem; word-break: break-all; }
.nav-inline-form { display: inline; }
.link-button { background: none; border: 0; padding: 0; color: var(--accent); cursor: pointer; font: inherit; }
.link-button:hover { color: var(--accent-hover); text-decoration: underline; }

@media (max-width: 400px) {
  .nav-search { display: none; }
}

@media (max-width: 820px) {
  .nav-toggle {
    display: flex; flex-direction: column; justify-content: center; gap: 4px;
    width: 2.3rem; height: 2.3rem; margin-left: auto;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--r-sm); cursor: pointer;
  }
  .nav-toggle span { display: block; height: 1.5px; background: var(--text); margin: 0 auto; width: 1.05rem; }
  #nav-menu {
    display: none;
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch;
    background: var(--surface); border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-md);
    padding: 0.6rem 1.25rem 1rem; gap: 0.15rem;
  }
  nav.menu-open #nav-menu { display: flex; }
  #nav-menu > li > a { padding: 0.55rem 0.6rem; }
  .nav-dd-end { margin-left: 0; }
  /* No nested toggles on a phone: the panel renders inline instead. */
  .nav-avatar-btn { display: none; }
  .nav-dd-panel {
    display: block; position: static; border: 0; box-shadow: none;
    padding: 0.4rem 0 0; min-width: 0; margin-top: 0.4rem;
    border-top: 1px solid var(--border);
  }
}

/* --- buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  padding: 0.55rem 1rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-weight: 550;
  font-size: 0.95rem;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform 0.1s, border-color 0.14s, box-shadow 0.14s, background 0.14s;
}
.btn:hover {
  text-decoration: none; color: var(--text);
  border-color: var(--text-muted); box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}
.btn:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-text); }
.btn-danger { border-color: var(--danger); color: var(--danger); }
.btn-danger:hover { border-color: var(--danger); color: var(--danger); }
.btn-lg { padding: 0.72rem 1.4rem; font-size: 1.02rem; }
.btn-small { padding: 0.28rem 0.65rem; font-size: 0.82rem; border-radius: var(--r-sm); box-shadow: none; }
.btn-small:hover { transform: none; box-shadow: none; }

/* --- hero / stats ------------------------------------------------------- */

/* The front page opens on a wash rather than flat white: a radial tint in the
   accent, bled past the content column and clipped to the top of the page.
   Enough to make the headline feel placed; not enough to become a colour the
   rest of the page has to answer to. */
.hero { position: relative; padding: 2.5rem 0 1.5rem; }
.hero::before {
  content: "";
  position: absolute;
  inset: -7rem 0 auto;
  height: 26rem;
  background: radial-gradient(58rem 20rem at 50% 0%, var(--accent-soft), transparent 72%);
  z-index: -1;
  pointer-events: none;
}
/* The wash wants to bleed past the content column, which means overflowing the
   viewport — so it only does so where `overflow-x: clip` can contain it. `clip`
   and not `hidden`: hidden would make body a scroll container and break the
   sticky header. Older browsers get the same gradient, stopped at the column
   edge, rather than a horizontal scrollbar. */
@supports (overflow-x: clip) {
  body { overflow-x: clip; }
  .hero::before { inset: -7rem -50vw auto; }
}
.hero h1 { max-width: 34rem; }
.hero-note { color: var(--text-muted); font-size: 0.9rem; margin-top: 0.85rem; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.5rem; }

/* An eyebrow above the headline: what this page is, before the pitch for it. */
.eyebrow {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-size: 0.74rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-radius: 999px;
  padding: 0.28rem 0.75rem;
  margin: 0 0 1.15rem;
}
.eyebrow .dot { width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--accent); flex: none; }

.stats {
  display: grid; grid-template-columns: repeat(3, 1fr);
  margin: 1.25rem 0 0;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.stats div {
  display: flex; flex-direction: column; gap: 0.15rem;
  padding: 1rem 1.25rem;
  border-left: 1px solid var(--border);
}
.stats div:first-child { border-left: 0; }
.stats strong {
  font-family: var(--mono);
  font-size: clamp(1.2rem, 3vw, 1.55rem);
  font-weight: 600;
  letter-spacing: -0.04em;
}
.stats span {
  color: var(--text-muted); font-size: 0.72rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.08em;
}
/* Three columns is the right shape for the strip and it stays three on a phone —
   the numbers are short and splitting them into rows loses the comparison. Only
   the padding and the label tracking give way. */
@media (max-width: 540px) {
  .stats div { padding: 0.8rem 0.65rem; }
  .stats span { font-size: 0.64rem; letter-spacing: 0.04em; }
}

/* --- cards -------------------------------------------------------------- */

.card-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0.85rem;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}
.card {
  position: relative;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); padding: 1rem 1.1rem;
  display: flex; flex-direction: column; gap: 0.4rem;
  box-shadow: var(--shadow-sm);
  transition: transform 0.14s, border-color 0.14s, box-shadow 0.14s;
}
.card:hover { transform: translateY(-2px); border-color: var(--border-strong); box-shadow: var(--shadow-md); }
.card > a { color: var(--text); display: flex; flex-direction: column; gap: 0.25rem; }
.card > a:hover { text-decoration: none; color: var(--text); }
.card > a strong { font-size: 1.02rem; font-weight: 650; letter-spacing: -0.015em; }
.card:hover > a strong { color: var(--accent); }
.card-desc {
  color: var(--text-muted); font-size: 0.88rem; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.card-meta { font-size: 0.8rem; }
.card-action {
  font-size: 0.84rem; font-weight: 600; margin-top: auto;
  padding-top: 0.65rem; border-top: 1px solid var(--border);
}

/* The trending strip at the head of /c's community section. Accent is spent here
   under the rule at the top of this file — this is "what just changed" — and it
   is the only thing that separates these cards from the identical ones listed
   below them, so it has to carry the whole distinction. Same soft/line pair as
   .eyebrow rather than a new colour. */
.trending-grid > .card { border-color: var(--accent-line); background: var(--accent-soft); }
.card-trend {
  font-size: 0.78rem; font-weight: 600; color: var(--accent);
  font-variant-numeric: tabular-nums; letter-spacing: -0.005em;
}

/* --- tables / leaderboard ---------------------------------------------- */

.table-wrapper {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
table { width: 100%; border-collapse: collapse; background: var(--surface); }
th, td { padding: 0.62rem 0.9rem; text-align: left; border-bottom: 1px solid var(--border); }
th {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.075em;
  color: var(--text-muted); font-weight: 600;
  background: var(--surface-2);
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background 0.1s; }
tbody tr:hover { background: var(--surface-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

.leaderboard td { vertical-align: middle; }
.leaderboard .rank {
  width: 3.4rem;
  font-family: var(--mono); font-size: 0.9rem; font-weight: 500;
  color: var(--text-muted);
}
/* Top three get a filled disc. It is the only decoration on the board and it is
   doing work: the eye needs somewhere to land in a fifty-row table. */
.rank-medal {
  display: inline-grid; place-items: center;
  width: 1.75rem; height: 1.75rem;
  border-radius: 50%;
  border: 1px solid currentColor;
  font-family: var(--font); font-size: 0.8rem; font-weight: 700;
}
.rank-1 { color: var(--gold); background: color-mix(in srgb, var(--gold) 13%, transparent); }
.rank-2 { color: var(--silver); background: color-mix(in srgb, var(--silver) 13%, transparent); }
.rank-3 { color: var(--bronze); background: color-mix(in srgb, var(--bronze) 13%, transparent); }

.name-cell { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.name-cell a { color: var(--text); font-weight: 550; }
.name-cell a:hover { color: var(--accent); }
.leaderboard .score { font-family: var(--mono); font-weight: 600; letter-spacing: -0.02em; }
.leaderboard tr.provisional td { color: var(--text-muted); }
/* A young category is provisional in every row, and fifty bordered chips down
   one column is a fence across the board. Here the badge is an annotation, not a
   status pill: same words, no frame. */
.leaderboard .badge {
  border-color: transparent; background: none; padding-inline: 0;
  font-size: 0.62rem; letter-spacing: 0.09em;
}
.leaderboard tr.provisional .name-cell a { color: var(--text-dim); font-weight: 500; }

/* Win rate is a proportion, so it is drawn as one: the number, and a hairline
   bar under it scaled to the same value. Far cheaper to scan down a column than
   fifty two-digit percentages. */
.meter { display: inline-flex; flex-direction: column; align-items: flex-end; gap: 0.25rem; }
.meter-track { width: 3.2rem; height: 4px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.meter-fill { display: block; height: 100%; border-radius: 999px; background: var(--accent); }
tr.provisional .meter-fill { background: var(--border-strong); }

table.compact th, table.compact td { padding: 0.35rem 0.65rem; font-size: 0.85rem; }

/* The board is five columns and the wrapper scrolls, but a phone that has to
   scroll sideways to see the vote count barely shows a board at all. Tighten the
   cells first; scrolling stays as the fallback. */
@media (max-width: 560px) {
  th, td { padding: 0.55rem 0.55rem; }
  .leaderboard .rank { width: 2rem; }
  .meter-track { width: 2.4rem; }
}

/* --- pager -------------------------------------------------------------- */

.pager { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 1.5rem 0; align-items: center; }
.pager a, .pager .current, .pager .gap {
  min-width: 2.1rem; height: 2.1rem;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 0.5rem; border-radius: var(--r-sm); font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
}
.pager a { border: 1px solid var(--border); color: var(--text-dim); background: var(--surface); }
.pager a:hover { border-color: var(--border-strong); color: var(--text); background: var(--surface-2); text-decoration: none; }
.pager .current { background: var(--accent); border: 1px solid var(--accent); color: var(--accent-text); font-weight: 600; }
.pager .gap { color: var(--text-muted); }

/* --- the duel ----------------------------------------------------------- */

/* The front page's duel. Sits between the hero copy and the stats, so it needs
   its own vertical rhythm and a boundary — without one it reads as part of the
   marketing copy rather than as something to answer. */
.hero-duel {
  margin: 0 auto 2.5rem;
  padding: 1.75rem 1.25rem 1.5rem;
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-md);
}
.hero-duel .duel-question { margin-top: 0; }

.duel-question {
  text-align: center; margin: 1.5rem 0 0.2rem;
  font-size: clamp(1.35rem, 3.4vw, 1.8rem);
  font-weight: 750; letter-spacing: -0.03em;
}
.duel-context {
  text-align: center; color: var(--text-muted); margin: 0 0 1.5rem;
  font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.1em; font-weight: 600;
}

/* The themed-session marker, on the same line as the category. Not a separate
   row: it is a qualifier on "in Films", not a second fact — and the way out of
   the theme has to be reachable without leaving the duel. */
.duel-theme { color: var(--accent); }
.duel-theme a {
  color: var(--text-muted); text-decoration: underline; margin-left: 0.35rem;
  text-transform: none; letter-spacing: 0; font-weight: 500;
}
.duel-theme a:hover { color: var(--text); }

/* The theme picker under the duel. Quiet by design: the unfiltered pair is the
   default and this is the deliberate narrowing, so it reads as an aside rather
   than as the main control. */
.vote-themes {
  margin-top: 2.5rem; padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}
.vote-themes h2 { font-size: 1rem; margin: 0 0 0.35rem; }
.vote-themes .footnote { margin-top: 0; margin-bottom: 1rem; }

.duel { position: relative; display: grid; gap: 1rem; grid-template-columns: 1fr 1fr; }

/* The "or" sits in the gutter between the two cards rather than above them: it
   belongs to the pair, and putting it in the flow would push the two choices
   apart at exactly the moment the reader is weighing them against each other. */
.duel::after {
  content: "or";
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 2.1rem; height: 2.1rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--surface); border: 1px solid var(--border);
  color: var(--text-muted); font-size: 0.7rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  box-shadow: var(--shadow-sm);
  pointer-events: none;
  z-index: 2;
}
@media (max-width: 640px) {
  .duel { grid-template-columns: 1fr; }
  .duel::after { display: none; }
}

.duel form { display: flex; }
.duel-card {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.55rem;
  /* A floor, not a height: most pairs carry no image and no description, and two
     one-line cards read as a form field rather than as a choice worth making. */
  min-height: 9.5rem;
  padding: 1.6rem 1.1rem;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); color: var(--text);
  font: inherit; text-align: center; cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: border-color 0.12s, transform 0.12s, box-shadow 0.12s, background 0.12s;
}
.duel-card:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
}
.duel-card:active { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.duel-card strong { font-size: 1.14rem; font-weight: 650; letter-spacing: -0.02em; line-height: 1.25; }
.duel-card img {
  max-width: 100%; max-height: 12rem;
  border-radius: var(--r); border: 1px solid var(--border);
  object-fit: contain;
}
.duel-desc {
  color: var(--text-muted); font-size: 0.88rem; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.duel-skip { text-align: center; margin-top: 1.4rem; font-size: 0.88rem; }
.duel-skip a { color: var(--text-muted); }
.duel-skip a:hover { color: var(--text); }

/* The verdict, after a vote. It is replaced by the next swap, so it has to read
   in the second it is on screen: one line, centred, the delta in the direction
   colour. */
.result {
  display: flex; align-items: center; justify-content: center; gap: 0.5rem;
  flex-wrap: wrap;
  width: fit-content; max-width: 100%;
  margin: 0 auto 0.5rem;
  padding: 0.55rem 1rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
  font-size: 0.93rem;
  text-align: center;
}
.delta { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 600; font-size: 0.92em; }
/* The vote card's score movement: "1500 → 1502". No label and no sentence — the
   two numbers and the arrow between them are the whole message, and the new
   score is the only coloured thing on the card so the eye lands on it. One
   nowrap unit because .result wraps and a movement split across lines reads as
   two unrelated scores. */
.score-move {
  display: inline-flex; align-items: baseline; gap: 0.34rem;
  white-space: nowrap;
}
.score-move .delta { color: var(--text-muted); }
.score-move .delta.up { color: var(--up); font-size: 1.05em; }
.score-arrow { color: var(--text-muted); font-size: 0.85em; }

/* Available to a screen reader, absent from the page. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.delta.up, .outcome.won { color: var(--up); }
.delta.down, .outcome.lost { color: var(--down); }

.empty-state { text-align: center; padding: 3.5rem 1rem; }
.empty-state h1 { max-width: 34rem; margin-inline: auto; }
.empty-state p { max-width: 34rem; margin-inline: auto; color: var(--text-dim); }
.empty-state .btn { margin: 0.25rem; }

/* --- item page ---------------------------------------------------------- */

.breadcrumbs {
  font-size: 0.83rem; color: var(--text-muted); margin-bottom: 1.25rem;
  display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center;
}
.breadcrumbs a { color: var(--text-muted); }
.breadcrumbs a:hover { color: var(--accent); }
.breadcrumbs span { color: var(--border-strong); }

.page-head { margin-bottom: 1.75rem; }
.page-meta { color: var(--text-muted); font-size: 0.88rem; margin: 0.4rem 0 1rem; }
.item-head { display: flex; gap: 1.75rem; align-items: flex-start; flex-wrap: wrap; }
.item-head > div { flex: 1 1 20rem; }
.item-image {
  max-width: 13rem; border-radius: var(--r-lg); border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
}

/* Flex rather than grid: the number of KPIs changes with the item (four when it
   is ranked, a full-width explanation plus two when it is not), and flex lets the
   survivors share the row instead of sitting in a quarter-width track next to
   empty space. */
.kpis { display: flex; flex-wrap: wrap; gap: 0.7rem; margin: 1.75rem 0; }
.kpi {
  flex: 1 1 8.5rem;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); padding: 0.9rem 1.1rem;
  box-shadow: var(--shadow-sm);
}
.kpi strong {
  display: block;
  font-family: var(--mono); font-size: 1.45rem; font-weight: 600;
  letter-spacing: -0.04em; line-height: 1.25;
}
.kpi span {
  color: var(--text-muted); font-size: 0.72rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.08em;
}
/* "Not ranked yet" is a sentence, not a number, so it drops the mono face and
   the uppercase label and looks like the prose it is. */
.kpi-wide { flex-basis: 100%; }
.kpi-wide strong { font-family: var(--font); font-size: 1.05rem; letter-spacing: -0.02em; }
.kpi-wide span { text-transform: none; letter-spacing: 0; font-size: 0.88rem; }

.attributes {
  display: grid; grid-template-columns: max-content 1fr; gap: 0 1.5rem; margin: 0;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); padding: 0.35rem 1.1rem;
}
.attributes dt { color: var(--text-muted); font-size: 0.88rem; padding: 0.5rem 0; border-top: 1px solid var(--border); }
.attributes dd { margin: 0; padding: 0.5rem 0; border-top: 1px solid var(--border); }
.attributes dt:first-of-type, .attributes dd:first-of-type { border-top: 0; }

.duel-log {
  list-style: none; margin: 0; padding: 0.2rem 1.1rem;
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--surface);
}
.duel-log li {
  display: flex; gap: 0.7rem; align-items: baseline;
  padding: 0.55rem 0; border-bottom: 1px solid var(--border);
}
.duel-log li:last-child { border-bottom: 0; }
/* The opponent is the row's subject, not a call to action — reading a column of
   orange link text as a list of names is harder than it needs to be. */
.duel-log a { color: var(--text); font-weight: 500; }
.duel-log a:hover { color: var(--accent); }
.outcome {
  font-size: 0.68rem; font-weight: 700; min-width: 4.3rem;
  text-transform: uppercase; letter-spacing: 0.07em;
}
.duel-log .delta { margin-left: auto; }

/* --- chips: the tag filter ----------------------------------------------
 *
 * A chip is "jump sideways from here": one row of them is `.chip-row`, used by
 * the admin pages to switch category. It was `.subcategories` while a browse
 * heading was a category with children — headings are `Group` rows now, so the
 * name described nothing. Tag chips share the shape and add a count and a group
 * heading, because a filter is a promise about how many rows are behind it and
 * an unlabelled row of twenty chips is unreadable. */

.chip-row { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-bottom: 1.5rem; }
.chip-row a, .tag-chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  border: 1px solid var(--border); border-radius: 999px;
  padding: 0.25rem 0.75rem; font-size: 0.86rem; color: var(--text-dim);
  background: var(--surface);
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.chip-row a:hover, .tag-chip:hover {
  text-decoration: none; border-color: var(--border-strong);
  color: var(--text); background: var(--surface-2);
}
.chip-row a.active, .tag-chip.active {
  border-color: var(--accent); color: var(--accent);
  background: var(--accent-soft); font-weight: 550;
}

.tag-filter { margin-bottom: 1.5rem; display: flex; flex-direction: column; gap: 0.55rem; }
.tag-group { display: flex; flex-wrap: wrap; gap: 0.45rem; align-items: center; }
.tag-group ul { list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0; padding: 0; }
.tag-group-label {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-muted); min-width: 4.5rem; font-weight: 600;
}
.tag-count { font-size: 0.74rem; color: var(--text-muted); }
.tag-chip.active .tag-count { color: var(--accent); }
.tag-clear { align-self: flex-start; color: var(--text-muted); border-style: dashed; }

.item-tags { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.6rem 0 0.9rem; }

/* The picker on the submission form and in the moderation queue. Checkboxes,
 * not a text input — the tag vocabulary is the moderator's, see admin.py. */
.tag-picker { border: 1px solid var(--border); border-radius: var(--r-lg); padding: 1rem 1.1rem; background: var(--surface); }
.tag-picker legend { font-weight: 600; padding: 0 0.35rem; font-size: 0.92rem; }
.tag-picker .tag-group { margin-bottom: 0.5rem; }
.tag-picker small, .queue-tags small { color: var(--text-muted); font-weight: 400; }
.tag-check {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-weight: 400; font-size: 0.88rem;
  border: 1px solid var(--border); border-radius: 999px;
  padding: 0.2rem 0.7rem 0.2rem 0.55rem;
  background: var(--surface); cursor: pointer;
}
.tag-check:hover { border-color: var(--border-strong); background: var(--surface-2); }
.tag-check input { margin: 0; accent-color: var(--accent); }
.tag-check:has(input:checked) { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

.queue-tags {
  display: flex; flex-direction: column; gap: 0.4rem; align-items: flex-start;
  margin-top: 0.7rem; padding-top: 0.7rem; border-top: 1px dashed var(--border);
}

/* --- forms, panels, flashes -------------------------------------------- */

.form { display: flex; flex-direction: column; gap: 1.1rem; max-width: 34rem; }
.form label { display: flex; flex-direction: column; gap: 0.35rem; font-weight: 550; font-size: 0.93rem; }
.form input, .form textarea, .form select, .search-form input {
  padding: 0.55rem 0.75rem; border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface); color: var(--text); font: inherit; font-weight: 400;
  transition: border-color 0.14s, box-shadow 0.14s;
}
.form input:focus, .form textarea:focus, .form select:focus, .search-form input:focus {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}
.form button { align-self: flex-start; }
.search-form { display: flex; gap: 0.5rem; margin: 1.5rem 0; max-width: 34rem; }
.search-form input { flex: 1; }

.panel {
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); padding: 1.2rem 1.35rem; margin: 1.25rem 0;
  box-shadow: var(--shadow-sm);
}
.panel h2 { margin-top: 0; font-size: 1.12rem; }
.panel-danger { border-color: var(--danger); }
.split { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }

.flash {
  padding: 0.75rem 1rem; border-radius: var(--r); font-size: 0.93rem;
  border: 1px solid var(--border); border-left: 3px solid var(--border-strong);
  background: var(--surface-2);
}
.flash-ok { border-left-color: var(--up); }
.flash-error { border-left-color: var(--danger); }
.notice {
  padding: 0.75rem 1rem; border-radius: var(--r);
  background: var(--surface-2); border: 1px solid var(--border);
  color: var(--text-dim); font-size: 0.93rem;
}
.section-cta { margin: 2rem 0; }

/* The viewer's own row on /voters. Accent, under the rule at the top of this
   file — on a board of fifty names the one thing a reader is looking for is
   their own, and "where am I" is the only reason they opened the page. */
.leaderboard tr.is-you { background: var(--accent-soft); }
.leaderboard tr.is-you td { font-weight: 600; }

.result-list { list-style: none; margin: 0; padding: 0; }
.result-list li {
  padding: 0.6rem 0; border-bottom: 1px solid var(--border);
  display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: baseline;
}
.result-list li:last-child { border-bottom: 0; }
.result-list a { font-weight: 550; }

.badge {
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.07em; font-weight: 600;
  padding: 0.14rem 0.45rem; border-radius: var(--r-sm);
  border: 1px solid var(--border); color: var(--text-muted);
  background: var(--surface-2);
  white-space: nowrap;
}
.badge-approved { border-color: var(--up); color: var(--up); background: transparent; }
.badge-rejected { border-color: var(--down); color: var(--down); background: transparent; }
.badge-upset { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

.dup-hint, .dup-warn {
  border: 1px solid var(--accent-line); border-left: 3px solid var(--accent);
  border-radius: var(--r);
  padding: 0.75rem 1rem; background: var(--accent-soft); font-size: 0.9rem;
}
.dup-hint ul { margin: 0.4rem 0; padding-left: 1.2rem; }

/* The row of actions under a board's heading: "Vote in Films" beside "Share",
   and "Add a film" on a board the visitor owns. Wraps rather than overflowing —
   item_noun is submitter-typed and can be long. */
.page-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }

/* The entry list on the create-a-category form: a numbered column of boxes with
   a remove button each, and a "+ Add another" under it. The counter next to that
   button is the only thing telling someone how far off the minimum they are. */
.entry-list { border: 1px solid var(--border); border-radius: var(--r); padding: 0.9rem 1rem; }
.entry-list legend { font-weight: 600; padding: 0 0.35rem; }
.entry-rows { list-style: decimal; margin: 0.6rem 0; padding-left: 1.8rem; }
.entry-rows li::marker { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.entry-row { display: flex; gap: 0.4rem; align-items: center; margin-bottom: 0.4rem; }
.entry-row input { flex: 1; min-width: 0; }
.entry-remove { line-height: 1; padding: 0.3rem 0.55rem; }
.entry-remove[disabled] { opacity: 0.35; cursor: default; }
#add-entry[disabled] { opacity: 0.5; cursor: default; }
.entry-count-ok { color: var(--up); }

/* The community marker. Deliberately quiet — it is a fact about who made a
   category, not a warning about it — but it must survive being dropped into a
   card title, an <h1> and a search row, so it never inherits font size. */
.origin-badge {
  display: inline-block; vertical-align: middle;
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.07em;
  font-weight: 600; white-space: nowrap;
  padding: 0.14rem 0.45rem; border-radius: var(--r-sm);
  border: 1px dashed var(--border); color: var(--text-muted);
  background: transparent;
}

/* One entry's edit form in /manage. The inputs go full width; the row's delete
   button is a sibling form, so .queue-row's own layout still applies. */
.manage-item { flex: 1; gap: 0.4rem; }
.manage-item input, .manage-item textarea { width: 100%; }

/* --- moderation queue -------------------------------------------------- */

.queue { list-style: none; margin: 0; padding: 0; }
.queue-row {
  display: flex; gap: 1rem; justify-content: space-between; align-items: flex-start;
  padding: 1.1rem; border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); margin-bottom: 0.75rem; flex-wrap: wrap;
  box-shadow: var(--shadow-sm);
}
.queue-row p { margin: 0.35rem 0; }
.queue-actions { display: flex; gap: 0.5rem; }

/* Reports. The count badge is the one place on the site the accent is spent on
   something bad rather than something clickable, which is the point: it is the
   only queue fed by visitors complaining. */
.badge-warn { border-color: var(--danger); color: var(--danger); }

.report-notes { list-style: none; margin: 0.6rem 0 0; padding: 0; }
.report-notes > li {
  border-left: 2px solid var(--border); padding: 0 0 0 0.7rem; margin-bottom: 0.6rem;
}
.report-notes p { margin: 0.15rem 0 0; }

/* Share. The URL is an input rather than text so it is selectable and
   tap-to-select on a phone, which is the fallback when neither clipboard nor
   share-sheet exists. Monospace because a URL is a string to be read character
   by character, not prose. */
.share { margin: 0.75rem 0 1.25rem; }
.share-row { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.share-url {
  flex: 1 1 18rem; min-width: 0;
  font-family: var(--mono, monospace); font-size: 0.85rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface-2); color: var(--text-dim);
}
.share-said { display: inline-block; margin-top: 0.4rem; font-size: 0.85rem; }
.share .footnote { margin-top: 0; }

/* Radio list on the report form: one choice per line, the whole line clickable. */
.choice { display: flex; align-items: baseline; gap: 0.5rem; font-weight: 400; }
.choice input { margin: 0; }
.queue-meta { font-size: 0.85rem; margin: 0.3rem 0; }
.queue-thumb { max-width: 8rem; border-radius: var(--r); margin-top: 0.5rem; border: 1px solid var(--border); }

/* --- motion ------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
  .card:hover, .duel-card:hover, .btn:hover { transform: none; }
}
