/*
  Palette sampled from the Phoenix Running Cambridgeshire franchisee logo
  (navy field, black phoenix, sage ribbon, gold "CAMBRIDGESHIRE" lettering):
  navy #033d9e, sage #8bb9af, near-black #211d1e, gold #f2ee33.
*/
:root {
  --bg: #f4f6fa;
  --surface: #ffffff;
  --surface-alt: #eaeff6;
  --border: #d8e0ea;
  --text: #171f2b;
  --text-muted: #5a6675;
  --brand: #0b3d8e;
  --brand-dark: #082c6b;
  --accent: #e0b900;
  --sage: #5c8c80;
  --bronze: #a1662f;
  --silver: #8a97a8;
  --header-bg: #0b3d8e;
  --male: #2f6fb0;
  --female: #b5468a;
  --success-bg: #e7f1ee;
  --success-text: #2f6b58;
  --error-bg: #fbe6e2;
  --error-text: #a3311a;
  --danger: #c0291c;
  --danger-dark: #8f1e14;
  --radius: 10px;
  --sidebar-w: 210px;
  --topbar-h: 64px;
  --shadow: 0 1px 3px rgba(11, 20, 35, 0.08), 0 1px 2px rgba(11, 20, 35, 0.06);
  --date-icon-filter: none;
}

/*
  Theme resolution order: an explicit choice (data-theme, set by theme.js
  from localStorage before first paint - see partials/head.ejs) always wins;
  with no explicit choice ("Auto"), it falls back to the OS/browser's
  prefers-color-scheme. The dark palette is authored once and reused by
  both paths.
*/
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0d1420;
    --surface: #16202e;
    --surface-alt: #1c2838;
    --border: #2c3b4e;
    --text: #eaf0f7;
    --text-muted: #9fb0c2;
    --brand: #6fa0e0;
    --brand-dark: #90bbee;
    --accent: #f2d400;
    --sage: #8fc0b2;
    --bronze: #c98a52;
    --silver: #c3cdd9;
    --header-bg: #0b3d8e;
    --male: #7fb3e0;
    --female: #e08ac0;
    --success-bg: #16332a;
    --success-text: #9fd6c4;
    --error-bg: #3a1e17;
    --error-text: #f0a08e;
    --danger: #e2574a;
    --danger-dark: #c23f34;
    --shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
    --date-icon-filter: invert(1);
  }
}
:root[data-theme="dark"] {
  --bg: #0d1420;
  --surface: #16202e;
  --surface-alt: #1c2838;
  --border: #2c3b4e;
  --text: #eaf0f7;
  --text-muted: #9fb0c2;
  --brand: #6fa0e0;
  --brand-dark: #90bbee;
  --accent: #f2d400;
  --sage: #8fc0b2;
  --bronze: #c98a52;
  --silver: #c3cdd9;
  --header-bg: #0b3d8e;
  --male: #7fb3e0;
  --female: #e08ac0;
  --success-bg: #16332a;
  --success-text: #9fd6c4;
  --error-bg: #3a1e17;
  --error-text: #f0a08e;
  --danger: #e2574a;
  --danger-dark: #c23f34;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  --date-icon-filter: invert(1);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brand-dark); }

.wrap {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 16px 48px;
}

header.topbar {
  background: var(--header-bg);
  color: #fff;
  margin-bottom: 24px;
  position: sticky;
  top: 0;
  z-index: 100;
}
header.topbar .bar {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  min-height: var(--topbar-h);
}
header.topbar .brand, .nav-brand {
  font-weight: 700;
  font-size: 1.05rem;
  color: #fff;
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
header.topbar .brand .nav-logo, .nav-brand .nav-logo {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid rgba(255, 255, 255, 0.85);
  vertical-align: middle;
  flex: none;
}
.nav-brand { padding: 0 6px; margin-bottom: 22px; }
.nav-brand b { font-size: 0.86rem; line-height: 1.2; }

#nav-toggle { display: none; }
.nav-toggle-label {
  display: none;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 34px;
  cursor: pointer;
  border: 1px solid rgba(255,255,255,0.4);
  border-radius: 6px;
}
.nav-ic { width: 17px; height: 17px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* nav.mainnav is a sibling of header.topbar (not nested inside it) so it
   can become a fixed full-height sidebar on desktop without dragging the
   top bar's box model along with it - see the two media queries below for
   how the same markup becomes a mobile dropdown vs. a desktop rail. */
nav.mainnav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--header-bg);
}
.nav-group { display: flex; flex-direction: column; }
.nav-group + .nav-group { margin-top: 14px; padding-top: 10px; border-top: 1px solid rgba(255,255,255,0.15); }
.nav-section-label {
  display: block;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: rgba(255,255,255,0.55);
  padding: 4px 12px;
}
nav.mainnav a {
  color: rgba(255,255,255,0.88);
  text-decoration: none;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 0.92rem;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 10px;
  position: relative;
}
nav.mainnav a:hover { background: rgba(255,255,255,0.1); color: #fff; }
nav.mainnav a.active { color: #fff; font-weight: 700; }
nav.mainnav a.active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  border-radius: 2px;
  background: var(--accent);
}
nav.mainnav form { margin: 0; }
nav.mainnav button.linklike {
  background: none;
  border: none;
  color: rgba(255,255,255,0.88);
  font: inherit;
  padding: 8px 12px;
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
}
nav.mainnav button.linklike:hover { background: rgba(255,255,255,0.1); color: #fff; }

.theme-row { display: flex; align-items: center; gap: 10px; padding: 4px 12px; }
.theme-row .nav-ic { color: rgba(255,255,255,0.7); }
.theme-select {
  background: rgba(255,255,255,0.1);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.3);
  border-radius: 6px;
  padding: 6px 8px;
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  flex: 1;
  min-width: 0;
  max-width: 150px;
}
.theme-select option { color: var(--text); background: var(--surface); }

@media (max-width: 720px) {
  .nav-toggle-label { display: inline-flex; }
  .nav-brand { display: none; }
  nav.mainnav {
    display: none;
    padding: 8px 16px 14px;
  }
  #nav-toggle:checked ~ nav.mainnav { display: flex; }
}

/* Desktop: the sidebar carries its own logo/wordmark at the top, so there's
   no separate top bar taking up space above it - the header is hidden
   entirely (rather than the sidebar being squeezed in underneath it). */
@media (min-width: 721px) {
  header.topbar { display: none; }

  nav.mainnav {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 90;
    width: var(--sidebar-w);
    padding: 20px 14px;
    overflow-y: auto;
  }
  .nav-theme-group { margin-top: auto; padding-top: 14px; border-top: 1px solid rgba(255,255,255,0.14); }
  /* Scoped to pages that actually render the nav (i.e. have a .wrap right
     after it) - error.ejs and login.ejs skip the nav partial and shouldn't
     get an empty gap on the left. Centers the page content in the space to
     the right of the sidebar (same 1100px column as before, just shifted
     over) instead of hugging the sidebar with all the leftover space
     dumped on the right. */
  nav.mainnav + .wrap {
    margin-left: calc(var(--sidebar-w) + max(0px, (100vw - var(--sidebar-w) - 1100px) / 2));
    margin-right: 0;
    padding-top: 32px;
  }
}

h1 { font-size: 1.4rem; margin: 0 0 4px; }
h2 { font-size: 1.15rem; margin: 32px 0 12px; }
.page-title { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.subtitle { color: var(--text-muted); font-size: 0.92rem; margin: 0; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 18px;
  margin-bottom: 20px;
}

/* Scoreboard strip: one continuous band instead of separate boxed cards,
   with hairline dividers between stats - reads like a finish-line clock. */
.stat-grid {
  display: flex;
  background: var(--header-bg);
  border-radius: var(--radius);
  padding: 16px 6px;
  margin-bottom: 24px;
  overflow-x: auto;
}
.stat-tile {
  flex: 1 1 0;
  min-width: 100px;
  text-align: center;
  padding: 0 10px;
  position: relative;
}
.stat-tile + .stat-tile::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: rgba(255,255,255,0.18);
}
.stat-tile .val {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.1;
  color: #fff;
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, "SF Mono", "Cascadia Code", Consolas, "Roboto Mono", monospace;
}
.stat-tile .label { color: rgba(255,255,255,0.65); font-size: 0.64rem; margin-top: 5px; text-transform: uppercase; letter-spacing: 0.06em; }

/* Below 600px a single-row strip doesn't fit 5 stats without side-scrolling,
   so it wraps into a small grid instead - the vertical hairline dividers
   only make sense in one row, so they're dropped here. */
@media (max-width: 600px) {
  .stat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px 6px; overflow-x: visible; }
  .stat-tile { min-width: 0; padding: 0 4px; }
  .stat-tile + .stat-tile::before { display: none; }
}

.table-scroll { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.mobile-break { display: none; }
table { border-collapse: collapse; width: 100%; font-size: 0.92rem; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--border); white-space: nowrap; }
thead th { background: var(--surface-alt); color: var(--text-muted); font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.03em; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-alt); }

.search-group {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 12px;
  overflow: hidden;
}
.search-group summary {
  list-style: none;
  cursor: pointer;
  padding: 12px 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  user-select: none;
}
.search-group summary::-webkit-details-marker { display: none; }
.search-group summary::before {
  content: '\25B8';
  color: var(--text-muted);
  display: inline-block;
  transition: transform 0.15s ease;
  font-size: 0.8em;
}
.search-group[open] summary::before { transform: rotate(90deg); }
.search-group summary:hover { background: var(--surface-alt); }
.search-group summary .small { font-weight: 400; }
.search-group .table-scroll { border: none; border-top: 1px solid var(--border); border-radius: 0; }
td.num, th.num { text-align: right; }

.pill { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 0.76rem; font-weight: 600; }
.pill.m { background: rgba(58,110,165,0.15); color: var(--male); }
.pill.f { background: rgba(181,70,138,0.15); color: var(--female); }
.pill.new { background: var(--success-bg); color: var(--success-text); }
.badge-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  height: 20px;
  padding: 0 8px;
  border-radius: 999px;
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  background: var(--accent);
  color: #1a1a1a;
  border: 1px solid transparent; /* reserved so .pending's visible border doesn't change the box size */
  margin: 0 3px 0 0;
  vertical-align: middle;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
}
.badge-pill.pending { background: var(--surface-alt); color: var(--text-muted); border-color: var(--border); border-style: dashed; }

/* Milestone tiers: 10 sage, 25 bronze, 50 silver, 100 gold. Awarded badges
   get a solid fill; waiting (reached but not yet given) badges get a
   tinted version of the same colour so the tier is visible either way. */
.badge-pill.tier-10 { background: var(--sage); color: #ffffff; }
.badge-pill.tier-25 { background: var(--bronze); color: #2b1c0d; }
.badge-pill.tier-50 { background: var(--silver); color: #1a1a1a; }
.badge-pill.tier-100 { background: var(--accent); color: #1a1a1a; }

.badge-pill.pending.tier-10 { background: rgba(92, 140, 128, 0.15); color: var(--sage); border-color: var(--sage); }
.badge-pill.pending.tier-25 { background: rgba(161, 102, 47, 0.15); color: var(--bronze); border-color: var(--bronze); }
.badge-pill.pending.tier-50 { background: rgba(138, 151, 168, 0.18); color: var(--silver); border-color: var(--silver); }
.badge-pill.pending.tier-100 { background: rgba(224, 185, 0, 0.15); color: var(--accent); border-color: var(--accent); }

form.inline { display: inline; }

.fix-choices { display: none; flex-direction: column; gap: 4px; margin-top: 6px; }
.fix-choices.open { display: flex; }
.fix-choices form { margin: 0; }
label { display: block; font-size: 0.85rem; font-weight: 600; margin: 12px 0 4px; }
input[type=text], input[type=password], input[type=date], input[type=file], input[type=search] {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font-size: 1rem;
}
input:focus { outline: 2px solid var(--brand); outline-offset: 1px; }

/* The native calendar-picker icon on <input type=date> is drawn black by
   the browser and doesn't follow our theme tokens on its own - it just
   disappears against a dark --surface. Invert it in dark mode instead. */
input[type=date]::-webkit-calendar-picker-indicator { filter: var(--date-icon-filter); }

.btn {
  display: inline-block;
  background: var(--brand);
  color: #fff;
  border: none;
  padding: 10px 16px;
  border-radius: 8px;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { background: var(--brand-dark); }
.btn.secondary { background: var(--surface-alt); color: var(--text); border: 1px solid var(--border); }
.btn.small { padding: 5px 10px; font-size: 0.78rem; }
.btn.danger { background: var(--danger); }
.btn.danger:hover { background: var(--danger-dark); }

.flash { padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 0.9rem; transition: opacity 0.4s ease; }
.flash.flash-hide { opacity: 0; }
.flash.success { background: var(--success-bg); color: var(--success-text); }
.flash.error { background: var(--error-bg); color: var(--error-text); }

.confirm-dialog {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow);
  padding: 20px 22px;
  max-width: 420px;
  width: calc(100% - 40px);
}
.confirm-dialog::backdrop { background: rgba(11, 20, 35, 0.45); }
.confirm-dialog p { margin: 0 0 18px; font-size: 0.95rem; line-height: 1.4; }
.confirm-dialog-actions { display: flex; justify-content: flex-end; gap: 10px; }

.login-shell { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
.login-card { width: 100%; max-width: 360px; }
.login-card h1 { text-align: center; margin-bottom: 4px; }
.login-card .subtitle { text-align: center; margin-bottom: 20px; }
.club-logo {
  display: block;
  width: 120px;
  height: 120px;
  margin: 0 auto 16px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: var(--shadow);
}

.checkbox-row { display: flex; align-items: center; gap: 8px; margin: 12px 0; }
.checkbox-row input { width: auto; }
.checkbox-row label { margin: 0; font-weight: 500; }

.dropzone {
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  padding: 14px;
  text-align: center;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.dropzone.drag-over { border-color: var(--brand); background: var(--surface-alt); }
.dropzone-text { margin: 0 0 8px; font-size: 0.82rem; color: var(--text-muted); }
.dropzone input[type=file] { width: 100%; }

.muted { color: var(--text-muted); }
.small { font-size: 0.82rem; }

.milestone-cols { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
/* Award/revoke pills are wrapped in a <form> for the POST action; without
   this the form (not the button) becomes the flex item, sizing/positioning
   it differently than the plain <span> pills next to it. */
.milestone-cols form { display: contents; }

.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.four-col { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
@media (max-width: 720px) {
  .two-col, .four-col { grid-template-columns: 1fr; }
}

/* Below 600px, data tables stop scrolling sideways and stack into one
   card per row instead, with each cell's column heading shown as a label
   above its value (via the data-label attribute set on each <td>). */
@media (max-width: 600px) {
  .wrap { padding: 0 10px 32px; }

  .table-scroll { overflow-x: visible; border: none; background: none; box-shadow: none; }

  table { display: block; }
  thead { display: none; }
  tbody { display: block; }
  tr {
    display: flex;
    flex-wrap: wrap;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 10px 12px;
    margin-bottom: 10px;
  }
  tr:last-child { margin-bottom: 0; }
  td {
    flex: 1 1 100%;
    padding: 6px 0;
    white-space: normal;
    text-align: left !important;
    font-size: 0.9rem;
    border-bottom: none;
  }
  td:empty:not(.mobile-break) { display: none; }
  input[name="new_password"] { width: 100%; margin-bottom: 6px; }

  /* Short, related fields (e.g. name + gender, or a cluster of stats) sit
     on one line instead of each taking a full-width row of its own: a
     100%-basis field (the default above) always starts a fresh flex line,
     so it's what separates one group from the next, and .mobile-break is
     an empty spacer for when two groups sit back to back with nothing
     full-width between them to force that break. */
  td.mobile-inline { flex: 0 1 auto; width: auto; padding: 6px 20px 6px 0; }
  .mobile-break { display: block; flex: 1 1 100%; height: 0; padding: 0; margin: 0; }
  td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
    font-weight: 600;
    margin-bottom: 2px;
  }

  /* Ranked lists (Top Stats): name length varies a lot, and letting it wrap
     /flow with its neighbours like the generic card layout above pushes the
     value into a different spot on every row. Force rank/name/value onto
     one line each time instead - name takes the flexible middle and gets
     truncated rather than wrapping, value stays pinned to the right. */
  .rank-table tr { flex-wrap: nowrap; align-items: baseline; }
  .rank-table td[data-label]::before { display: none; }
  .rank-table td:nth-child(1) { flex: 0 0 auto; padding-right: 8px; color: var(--text-muted); }
  .rank-table td:nth-child(2) {
    flex: 1 1 auto; min-width: 0; padding-right: 8px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .rank-table td:nth-child(3) { flex: 0 0 auto; margin-left: auto; }

  /* Dashboard "Attendance by person" cards: name length varies a lot, and
     the generic left-flow layout above lets Gender and the Events/Total/Avg
     trio drift to a different spot on every card depending on that length.
     Pin Gender to the card's right edge (Name grows to fill the rest of its
     line) and split Events/Total dist./Avg dist. into three equal-width
     columns - left/centre/right - so every card lines up the same way
     regardless of how long any name or value is. */
  .people-table td:nth-child(1) { flex: 1 1 auto; padding-right: 12px; }
  .people-table td:nth-child(2) { flex: 0 0 auto; }
  .people-table td:nth-child(4),
  .people-table td:nth-child(5),
  .people-table td:nth-child(6) {
    flex: 1 1 1%; padding-right: 0;
  }
  .people-table td:nth-child(4) { text-align: left !important; }
  .people-table td:nth-child(5) { text-align: center !important; }
  .people-table td:nth-child(6) { text-align: right !important; }
}
