:root {
  --primary: #9c1c17;
  --gold: #f7941d;
  --bg: #f6f7f9;
  --card: #ffffff;
  --text: #1c2430;
  --muted: #667085;
  --border: #e3e7ed;
  --danger: #b3261e;
}

* { 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);
}

header.topbar {
  background: var(--primary);
  color: #fff;
  padding: 10px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 3px solid var(--gold);
  position: relative;
}

header.topbar a { color: #fff; text-decoration: none; }
header.topbar .brand { font-weight: 700; font-size: 1.1rem; display: flex; align-items: center; gap: 10px; }
header.topbar .brand-link { display: block; flex: none; }
header.topbar .brand-logo { height: 40px; width: auto; display: block; }
/* The version sits small under the title, inside the logo's height, so it
   adds nothing to the bar's width or its depth. */
header.topbar .brand-text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15; white-space: nowrap; }
header.topbar .brand-version { font-weight: 400; font-size: 0.68rem; opacity: 0.75; }
header.topbar .brand-version:hover { opacity: 1; text-decoration: underline; }
header.topbar nav { display: flex; gap: 18px; align-items: center; font-size: 0.92rem; }
header.topbar nav a { opacity: 0.85; }
header.topbar nav a:hover { opacity: 1; text-decoration: underline; }
header.topbar form { margin: 0; }
/* Log out is a quiet button in the bar's own white. */
header.topbar button.quiet { color: #fff; opacity: 0.85; text-decoration: none; }
header.topbar button.quiet:hover { opacity: 1; text-decoration: underline; }

/* From the hamburger's breakpoint up the bar wraps rather than squeezing
   (Club Volunteers' rule; Season setup, JPI-39, made an admin's bar long
   enough to matter). No link ever breaks across two lines: where there isn't
   room beside the title the links go onto a second row, and wrap among
   themselves below that. */
@media (min-width: 641px) {
  header.topbar { flex-wrap: wrap; gap: 6px 18px; }
  header.topbar nav { flex-wrap: wrap; justify-content: flex-end; row-gap: 4px; margin-left: auto; }
  header.topbar nav > a, header.topbar nav summary, header.topbar nav button { white-space: nowrap; }
}

/* Below the breakpoint, the nav collapses into a dropdown opened by a
   hamburger button - a checkbox drives it so it works with no JS. */
.nav-toggle { display: none; }
.nav-toggle-btn { display: none; cursor: pointer; font-size: 1.5rem; line-height: 1; padding: 2px 4px; }

.admin-group { margin-bottom: 28px; }
.admin-group > h2 { color: var(--primary); border-bottom: 2px solid var(--gold); padding-bottom: 6px; display: inline-block; margin-bottom: 14px; }
/* In case of Emergency: a closed <details>, so no JS; its heading reads like
   the others, with the marker kept so it plainly opens. */
.admin-group.emergency > summary { cursor: pointer; }
.admin-group.emergency > summary > h2 { display: inline-block; color: var(--primary); border-bottom: 2px solid var(--gold); padding-bottom: 6px; margin: 0 0 14px; }

.admin-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.admin-cards a.card { display: block; text-decoration: none; color: inherit; transition: border-color 0.15s; }
.admin-cards a.card:hover { border-color: var(--primary); }
.admin-cards h2 { color: var(--primary); margin-bottom: 6px; }
.admin-cards p { margin: 0; color: var(--muted); font-size: 0.88rem; }

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 32px 24px 64px;
}

/* Team sheets and admin pages carry wide tables (rosters, uniform lists,
   audit logs) that truncate or wrap awkwardly inside the standard reading
   width - let them use the full window instead. Narrow windows still work:
   .table-scroll scrolls its table horizontally rather than the page. */
main.main-wide {
  max-width: none;
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 24px;
  margin-bottom: 20px;
}

h1 { font-size: 1.5rem; margin: 0 0 4px; }
h2 { font-size: 1.15rem; margin: 0 0 12px; }
p.subtitle { color: var(--muted); margin: 0 0 20px; }

label { display: block; font-weight: 600; font-size: 0.85rem; margin-bottom: 6px; }
input[type="text"], input[type="email"], input[type="tel"], input[type="file"], input[type="date"], input[type="search"], input[type="number"], select, textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 1rem;
  font-family: inherit;
  margin-bottom: 16px;
  background: var(--card);
  color: var(--text);
}

/* Four kinds of button, one look - the same in all four apps (Jira JPI-4,
   the president, 3 October 2026: some were white with red text and some red
   with white, with nothing to say why). Primary is the one action a form or
   section is for - red, white text - and there is at most one in a form.
   Secondary is everything else: going to another page, a download, a copy,
   cancel, clear. Danger removes, reverses or unpublishes, and always asks
   first. Quiet is a small action on a table row or in the top bar, drawn as
   underlined text with no box. A <button> or .btn with no kind is drawn grey
   on purpose, so a missed one reads as plainly wrong rather than quietly
   primary - buttons.test.js fails it too. Both <button> and <a class="btn">
   take them, and .small is the size for a page head, a table row or a
   filter panel. */
button, .btn {
  display: inline-block;
  padding: 10px 18px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #eef1f6;
  color: var(--text);
  font-size: 0.95rem;
  line-height: normal;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}
button.primary, .btn.primary { background: var(--primary); color: #fff; border-color: var(--primary); }
button.secondary, .btn.secondary { background: var(--card); color: var(--primary); border-color: var(--border); }
button.danger, .btn.danger { background: var(--card); color: #7a1510; border-color: #f4c7c3; }
button.quiet, .btn.quiet {
  background: none; border: none; border-radius: 0; padding: 0;
  color: var(--primary); text-decoration: underline; font: inherit; text-align: left;
}
button.small, .btn.small { padding: 5px 12px; font-size: 0.85rem; }
button.quiet.small, .btn.quiet.small { padding: 0; font-size: 0.85rem; }
/* The same hover, focus ring and disabled look whatever the kind. */
button:hover, .btn:hover { opacity: 0.92; }
button.secondary:hover, .btn.secondary:hover, button.danger:hover, .btn.danger:hover { background: var(--bg); opacity: 1; }
button.quiet:hover, .btn.quiet:hover { opacity: 1; text-decoration-thickness: 2px; }
button:focus-visible, .btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
button:disabled, button:disabled:hover, .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }

/* A page's head (views/_page-head.ejs): the heading and its key numbers
   always shown, and under them a folded "About this page and actions"
   holding the description and the buttons in their four groups - Go to,
   WhatsApp, Download, Send - each a row with its label at the left. */
.page-head { margin: 0 0 16px; }
.page-head > summary { cursor: pointer; color: var(--primary); font-size: 0.92rem; }
.page-head > summary .muted { margin-left: 4px; font-size: 0.85rem; }
.page-head .subtitle { margin: 10px 0 12px; max-width: 760px; }
.page-head-numbers { color: var(--muted); margin: 0 0 6px; }
.page-head-groups { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.page-head-group { display: flex; gap: 6px 8px; align-items: center; flex-wrap: wrap; }
.page-head-group-label { flex: none; min-width: 84px; font-size: 0.78rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.page-head-group-name { text-transform: none; letter-spacing: 0; font-weight: 400; }
.page-head-group .inline-form { display: inline; margin: 0; }

/* A yes/no question needing real, explicitly-labelled buttons rather than
   a native confirm()'s ambiguous OK/Cancel - see the scoring roster's
   coach question in team-sheet.ejs for why. */
.confirm-dialog { max-width: 380px; border: 1px solid var(--border); border-radius: 10px; padding: 20px; box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18); }
.confirm-dialog::backdrop { background: rgba(28, 36, 48, 0.5); }
.confirm-dialog p { margin: 0 0 18px; }
.confirm-dialog-actions { display: flex; justify-content: flex-end; gap: 10px; }

.error { color: var(--danger); background: #fdecea; border: 1px solid #f4c7c3; padding: 10px 14px; border-radius: 6px; margin-bottom: 16px; font-size: 0.9rem; }
.notice { background: #fff8e6; border: 1px solid #f0dfa8; padding: 10px 14px; border-radius: 6px; margin-bottom: 16px; font-size: 0.9rem; }
.success { background: #eaf7ee; border: 1px solid #bfe6cb; padding: 10px 14px; border-radius: 6px; margin-bottom: 16px; font-size: 0.9rem; }

.season-group { margin-bottom: 24px; }
.season-group h2 { color: var(--primary); border-bottom: 2px solid var(--gold); padding-bottom: 6px; display: inline-block; }

table { width: 100%; border-collapse: collapse; margin-top: 8px; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); font-size: 0.92rem; }
th { color: var(--muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }

/* Sortable column headers - see sortable-tables.js, which adds/toggles
   these classes on click. */
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--primary); }
th.sortable.sort-asc::after { content: "\25B2"; margin-left: 5px; font-size: 0.7em; }
th.sortable.sort-desc::after { content: "\25BC"; margin-left: 5px; font-size: 0.7em; }

.team-list { list-style: none; padding: 0; margin: 0; }
.team-list li { padding: 12px 0; border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; }
.team-list li:last-child { border-bottom: none; }
.team-list a { color: var(--primary); font-weight: 600; text-decoration: none; }
.team-list a:hover { text-decoration: underline; }

/* A row on My Team Sheets that can carry the team's games under it
   (views/_team-games.ejs): the row as it always was, then the games across
   its full width, so nothing is added to the row's own line. */
.team-list li.team-item { display: block; }
.team-list .team-row { display: flex; justify-content: space-between; align-items: center; }
/* The next game, in the Game day look of Club Volunteers' rosters: a soft
   block with a coloured left edge - blue for boys, pink for girls - and the
   grade as its heading, in capitals after a dot. Every line wraps, so it is
   never wider than the row. */
.team-games { margin-top: 8px; min-width: 0; }
.game-block { background: #f3f4f6; border: 1px solid var(--border); border-left: 4px solid var(--muted); border-radius: 8px; padding: 8px 12px; overflow-wrap: anywhere; }
.game-block.boys { background: #eef3fc; border-color: #b9cdf0; border-left-color: #1f5bc7; }
.game-block.girls { background: #fdeef0; border-color: #f1bcc3; border-left-color: #cc2638; }
.game-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.game-grade { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; color: var(--muted); }
.game-grade::before { content: ''; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: currentColor; margin-right: 5px; vertical-align: 1px; }
.game-block.boys .game-grade { color: #1f5bc7; }
.game-block.girls .game-grade { color: #cc2638; }
.game-team, .game-opponent, .game-line { display: block; }
.game-team { font-size: 0.95rem; margin-top: 2px; }
.game-opponent { font-size: 0.9rem; }
.game-line { font-size: 0.85rem; color: var(--muted); margin-top: 2px; }
.game-score { color: var(--text); font-weight: 700; }
.game-block .btn { margin-top: 8px; }
.team-list a.btn { font-weight: 400; }
.team-list a.btn:hover { text-decoration: none; }
.game-tag { flex: none; padding: 1px 8px; border-radius: 999px; font-size: 0.7rem; font-weight: 600; background: #fff; border: 1px solid var(--border); color: var(--muted); }
.game-tag.home { background: #e7f6ec; border-color: #b7e0c4; color: #1e7a3c; }
/* The games before it, folded; a line a game, which wraps on a phone. */
.game-history { margin-top: 6px; font-size: 0.85rem; }
.game-history summary { cursor: pointer; color: var(--muted); }
.game-history ul { list-style: none; margin: 4px 0 0; padding: 0; }
.team-list .game-history li { display: flex; flex-wrap: wrap; justify-content: flex-start; align-items: baseline; gap: 2px 10px; padding: 6px 0; border-bottom: 1px solid var(--border); overflow-wrap: anywhere; }
.team-list .game-history li:last-child { border-bottom: none; }
.game-round { font-weight: 600; }

.badge { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 0.72rem; font-weight: 600; background: #eef1f6; color: var(--primary); }
.badge.player { background: #eaf2ff; color: #1d4ed8; }
.badge.coach { background: #fff1e0; color: #b45309; }
.badge.team_manager { background: #f1e8ff; color: #6d28d9; }
.badge.hidden-status { background: #fff8e6; color: #92620a; }

/* Count cards (views/_count-cards.ejs) - the same as Club Volunteers', so
   the apps' admin pages read the same way. A plain .stat-card is just a
   fact; a.stat-link is a switch on the page's own filter, lit (.stat-on) in
   red through, its labels white, while that filter is what is set. */
.stat-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-bottom: 20px; }
.stat-card { background: var(--card); border: 1px solid var(--border); border-top: 3px solid var(--primary); border-radius: 8px; padding: 12px 14px; display: flex; flex-direction: column; }
.stat-label { color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; font-weight: 600; line-height: 1.25; min-height: 2.5em; }
.stat-value { font-size: 1.6rem; font-weight: 700; margin-top: 4px; }
a.stat-link { color: inherit; text-decoration: none; cursor: pointer; }
a.stat-link:hover, a.stat-link:focus-visible { border-color: var(--primary); background: #fbf4f3; }
a.stat-on { background: var(--primary); color: #fff; border-color: var(--primary); }
a.stat-on:hover, a.stat-on:focus-visible { background: var(--primary); }
a.stat-on .stat-label { color: #fff; }

.muted { color: var(--muted); }
/* A team sheet's per-parent lines (see team-sheet.ejs) - never wrapped, so
   parent 2's name, mobile and email stay level with each other across the
   row's cells. */
.parent-lines div { white-space: nowrap; }
.code-input { letter-spacing: 0.4em; font-size: 1.3rem; text-align: center; }

.admin-season { margin-bottom: 18px; }
.admin-season summary { cursor: pointer; font-weight: 600; color: var(--primary); padding: 8px 0; }

/* A row's actions collapsed behind a single "..." toggle instead of a run
   of buttons - <details> so it needs no JS to open/close; the small script
   next to its markup only repositions the list as position:fixed once
   open, so it escapes a scrolling table's clipping instead of getting cut
   off (a plain position:absolute would be clipped by .table-scroll's
   overflow), and closes any other open menu so only one shows at a time. */
.action-menu { position: relative; display: inline-block; }
.action-menu > summary {
  list-style: none;
  cursor: pointer;
  padding: 3px 9px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 1.1rem;
  line-height: 1.4;
  color: var(--muted);
  user-select: none;
}
.action-menu > summary::-webkit-details-marker { display: none; }
.action-menu > summary:hover, .action-menu[open] > summary { border-color: var(--primary); color: var(--primary); }
.action-menu-list {
  /* position:fixed (and hidden) from the very first frame it exists, not
     just once the script below repositions it - otherwise it briefly
     renders in normal flow inside the table cell (widening the row) before
     JS can move it out, which is exactly the glitch/jump this was for. */
  position: fixed;
  visibility: hidden;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
  min-width: 180px;
  z-index: 30;
  padding: 4px;
}
.action-menu-list form { margin: 0; }
.action-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 8px 10px;
  border-radius: 6px;
  font: inherit;
  font-size: 0.88rem;
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
}
.action-menu-item:hover { background: var(--bg); }
.action-menu-item.danger { color: var(--danger); }

.people-toolbar { display: flex; gap: 10px; margin-bottom: 16px; }
.people-toolbar input, .people-toolbar select { margin-bottom: 0; }

.fixtures-table { max-width: 620px; margin: 0 auto; border: 1px solid var(--border); }
.fixtures-table td { padding: 9px 14px; border-bottom: 1px solid var(--border); }
.fixtures-table tr:last-child td { border-bottom: none; }
.fixtures-table td:last-child { text-align: right; }
/* writing-mode + rowspan is inconsistently supported by print/PDF
   rendering engines (the year text can vanish in a printed/saved PDF while
   still showing fine on screen) - a plain 2D rotate on an inline-block
   span, independent of the cell's own text flow, renders reliably in
   both. */
.fixtures-year {
  text-align: center;
  vertical-align: middle;
  border-right: 1px solid var(--border);
  width: 1px;
  white-space: nowrap;
  position: relative;
}
.fixtures-year span {
  display: inline-block;
  transform: rotate(-90deg);
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--primary);
  white-space: nowrap;
}

.impersonation-banner {
  background: var(--gold);
  color: #1c2430;
  padding: 8px 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  font-size: 0.9rem;
  font-weight: 600;
}
.impersonation-banner form { margin: 0; }
.environment-banner {
  padding: 8px 24px;
  text-align: center;
  font-size: 0.9rem;
  font-weight: 600;
}
.environment-green { background: #1f7a4d; color: #fff; }
.environment-read-only { background: #b3261e; color: #fff; }
.impersonation-banner button.quiet { color: inherit; }

.sheet-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; flex-wrap: wrap; }
/* The sheet's own buttons (Print, Email All, the vCard, the calendar, the
   scoring roster) wrap a row at a time on a phone's narrow card; a little
   room under each keeps the rows from touching. */
.sheet-top-name > button, .sheet-top-name > .btn { margin-bottom: 6px; }
.sheet-top-right { display: flex; flex-direction: column; align-items: flex-end; gap: 12px; }
.team-name { text-transform: uppercase; font-style: italic; }

.training-box { width: auto; min-width: 320px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; margin-top: 0; }
.training-box thead tr th[colspan] { background: var(--primary); color: #fff; text-align: left; padding: 8px 12px; text-transform: none; font-size: 0.85rem; letter-spacing: 0; }
.training-box tbody tr:first-child th { background: #eef1f6; }
.training-box th, .training-box td { border-bottom: none; padding: 8px 12px; }

.training-form { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; margin-top: 10px; }
.training-form > div { flex: 1; min-width: 140px; }
.training-form input { margin-bottom: 0; }
.training-form button { height: 41px; }

.filter-grid { display: grid; grid-template-columns: repeat(4, minmax(140px, 1fr)); gap: 12px 16px; margin-top: 10px; }
.filter-grid label { margin-bottom: 4px; }
.filter-grid input, .filter-grid select { margin-bottom: 0; }
.filter-grid-actions { display: flex; gap: 10px; margin-top: 16px; }
@media (max-width: 640px) {
  .filter-grid { grid-template-columns: repeat(2, 1fr); }

  .nav-toggle-btn { display: block; }
  header.topbar nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
    padding: 16px 24px;
    background: var(--primary);
    border-bottom: 3px solid var(--gold);
    z-index: 20;
  }
  .nav-toggle:checked ~ nav { display: flex; }
}

.table-scroll { overflow-x: auto; }
.table-scroll table { min-width: 640px; }

/* No forced size here on purpose - it used to say `size: landscape`
   unconditionally, which only the wide team sheet table actually needs. Every
   other printed page (Scoring Roster, Season Fixtures) is a narrow, tall
   table that's naturally portrait-shaped, and browsers don't agree on what to
   do when the CSS demands landscape for content like that: some rotate it
   onto whatever paper the print dialog has, some ignore the hint and fall
   back to the printer's own default. Same page, different browsers, visibly
   different (and for one of them, sideways) results. The team sheet's own
   print button adds `size: landscape` just for that print instead - see
   logPrintThenPrint in team-sheet.ejs. */
@page {
  margin: 1.2cm;
}

.print-logo { display: none; }
.print-only { display: none; }

@media print {
  header.topbar, .no-print { display: none !important; }
  .print-only { display: inline; }
  body { background: #fff; }
  main { max-width: 100%; padding: 0; }
  .card { border: none; box-shadow: none; padding: 0; margin-bottom: 20px; }
  /* On screen, .sheet-top-right stacks the training box above the (hidden)
     logo in a right-hand column next to the team name. For print, the logo
     becomes visible and the training box needs to sit centered between the
     name and the logo on one row instead - `display: contents` un-nests
     .sheet-top-right so its children become direct grid items of
     .sheet-top, which a 1fr/auto/1fr grid then lays out as name (left),
     training (centered) and logo (right). */
  .sheet-top { break-inside: avoid; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 16px; }
  .sheet-top-name { grid-column: 1; }
  .sheet-top-right { display: contents; }
  .training-box { grid-column: 2; justify-self: center; margin: 0; }
  .print-logo { grid-column: 3; justify-self: end; display: block; height: 3cm; width: auto; }
  .table-scroll { overflow-x: visible; }
  .table-scroll table { min-width: 0; }
  a { color: inherit; text-decoration: none; }

  table { break-inside: auto; }
  tr { break-inside: avoid; break-after: auto; }
  thead { display: table-header-row-group; }
  h1, h2 { break-after: avoid; }

  /* The Players table can legitimately span pages (large squads), but Staff
     is only ever a handful of rows - keep it as one unbroken block, pushed
     to the next page whole rather than split, if it doesn't fit under the
     Players table on the first page. */
  .staff-card { break-inside: avoid; }
}

.club-message-dialog { width: min(480px, 92vw); max-width: none; }
.club-message-dialog select, .club-message-dialog textarea { width: 100%; box-sizing: border-box; }

/* Help Desk: a member's tickets and the conversation on one. The club's
   messages sit on the left in the club's colours, the member's own on the
   right, so it reads as a back-and-forth. */
.help-textarea { width: 100%; padding: 10px 12px; border: 1px solid var(--border); border-radius: 6px; font: inherit; font-size: 1rem; margin-bottom: 12px; background: var(--card); color: var(--text); }
.help-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.help-list { list-style: none; padding: 0; margin: 0; }
.help-list li { border-bottom: 1px solid var(--border); }
.help-list li:last-child { border-bottom: none; }
.help-list-link { display: block; padding: 12px 0; color: inherit; text-decoration: none; }
.help-list-link:hover strong { color: var(--primary); text-decoration: underline; }
.help-list-top { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: 0.92rem; }
.help-when { margin-left: auto; font-size: 0.85rem; }
.help-list-desc { margin-top: 4px; font-size: 0.9rem; overflow-wrap: anywhere; }
.badge.help-status.new { background: #eaf2ff; color: #1d4ed8; }
.badge.help-status.open { background: #fff1e0; color: #b45309; }
.badge.help-status.closed { background: #eef1f6; color: var(--muted); }
/* A refund's status, from the Help Desk. */
.badge.refund-status { background: #eef1f6; color: var(--muted); }
.badge.refund-status.approved { background: #e7f6ec; color: #15803d; }
.badge.refund-status.paid { background: #e7f6ec; color: #15803d; }
.badge.refund-status.denied { background: #fdecec; color: #b91c1c; }
.badge.refund-status.submitted { background: #eaf2ff; color: #1d4ed8; }
.help-refund { border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; margin: 12px 0; }
.help-refund .badge { margin-left: 6px; }
.badge.help-replied { background: var(--primary); color: #fff; }
/* BlazerBot: Help > Ask BlazerBot, and Questions asked for the club. */
.help-bot { max-width: 760px; }
.help-bot-docs { font-size: 0.85rem; }
.help-bot-docs .badge { margin: 2px 2px 2px 0; text-decoration: none; }
.help-bot-note { font-size: 0.85rem; margin-top: -4px; }
.help-bot-actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-top: 8px; }
.help-bot-sources { font-size: 0.88rem; margin-top: 0; padding-left: 1.2em; }
.help-bot-asked { padding: 12px 0; }
.help-bot-asked details { font-size: 0.9rem; }
.badge.help-bot-down { background: #fdecec; color: #b91c1c; }
.badge.help-bot-up { background: #e7f6ec; color: #15803d; }
.badge.help-bot-warn { background: #fff1e0; color: #b45309; }
/* Ask BlazerBot as a chat (JPI-83): the member's messages on the right,
   BlazerBot's answers on the left, then the box for the next message. */
.help-chat-turn { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.help-chat-me { align-self: flex-end; max-width: 85%; background: var(--primary); color: #fff; border-radius: 14px 14px 4px 14px; padding: 8px 14px; }
.help-chat-me p { margin: 0; white-space: pre-line; }
.help-chat-bot { align-self: flex-start; max-width: 92%; background: var(--bg); border: 1px solid var(--border); border-radius: 14px 14px 14px 4px; padding: 10px 14px; }
.help-chat-bot > p:last-child, .help-chat-bot > ul:last-child, .help-chat-bot > form:last-child { margin-bottom: 0; }
.help-chat-who { margin: 0 0 4px; font-size: 0.8rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.help-chat-form { border-top: 1px solid var(--border); padding-top: 14px; }
.help-chat > p.muted + .help-chat-form { border-top: 0; padding-top: 0; }
@media (max-width: 640px) { .help-chat-bot { max-width: 100%; } }
.help-msg { max-width: 85%; margin: 0 0 12px; padding: 10px 14px; border-radius: 10px; border: 1px solid var(--border); }
.help-msg.club { background: #fff8e6; border-color: #f0dfa8; margin-right: auto; }
.help-msg.member { background: #eaf2ff; border-color: #c9dcff; margin-left: auto; }
.help-msg-meta { font-size: 0.78rem; color: var(--muted); margin-bottom: 4px; }
.help-msg-body { white-space: pre-wrap; overflow-wrap: anywhere; }
/* Files on a ticket's message - a screenshot from the club, say. */
.help-files { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; }
.help-file-thumb img { display: block; max-width: 220px; max-height: 160px; border: 1px solid var(--border, #ddd); border-radius: 6px; }
.help-reply { margin-top: 20px; }

/* The top bar's Help menu - the guides and the Help Desk under one name. The
   summary reads like the links beside it, with a caret; the menu opens as a
   card below it, from the right so it stays on screen. Venue Hire's pattern. */
header.topbar details.nav-menu { position: relative; }
header.topbar details.nav-menu summary {
  list-style: none; cursor: pointer; opacity: 0.85; display: flex; align-items: center; gap: 5px;
}
header.topbar details.nav-menu summary::-webkit-details-marker { display: none; }
header.topbar details.nav-menu summary::after {
  content: ''; width: 0; height: 0; margin-top: 2px;
  border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid currentColor;
}
header.topbar details.nav-menu[open] summary::after { transform: rotate(180deg); }
header.topbar details.nav-menu summary:hover,
header.topbar details.nav-menu[open] summary,
header.topbar details.nav-menu summary.active { opacity: 1; }
header.topbar .nav-menu-items {
  position: absolute; top: calc(100% + 12px); right: -12px; z-index: 30; min-width: 230px;
  display: flex; flex-direction: column; padding: 6px;
  background: var(--card); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
}
header.topbar .nav-menu-items a {
  color: var(--text); opacity: 1; padding: 8px 10px; border-radius: 6px; font-size: 0.92rem;
}
header.topbar .nav-menu-items a:hover { background: #f4f1f0; text-decoration: none; }
header.topbar .nav-menu-items a span { display: block; color: var(--muted); font-size: 0.78rem; font-weight: 400; }
/* In the collapsed phone menu, it opens in place under its name. */
@media (max-width: 640px) {
  header.topbar .nav-menu-items {
    position: static; box-shadow: none; border: none; background: transparent; padding: 6px 0 0 12px; min-width: 0;
  }
  header.topbar .nav-menu-items a { color: #fff; padding: 6px 0; }
  header.topbar .nav-menu-items a:hover { background: transparent; }
  header.topbar .nav-menu-items a span { color: rgba(255, 255, 255, 0.7); }
}

/* Adding a passkey. The name box is a name, not an essay, and the buttons sit
   in a row under it with the lesser choice as a secondary - two full-strength
   buttons stacked read as two equal decisions, which they are not. */
.passkey-name, .passkey-code { max-width: 260px; }
.passkey-code { max-width: 140px; letter-spacing: 0.12em; }
.passkey-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 10px 0 0; }
.passkey-actions form { display: inline; margin: 0; }
.passkey-actions button { margin: 0; }

/* The honeypot on the "can't log in" form: off the page for people, present
   for a robot that fills in every box. Without this the box is visible, and a
   real question typed into it is thanked and thrown away. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* "Request Refund" sits in a card heading and in the team sheet's button row -
   in a heading it must not take the heading's size. */
h2 > .btn { font-size: 0.8rem; padding: 5px 12px; margin-left: 10px; vertical-align: middle; }

/* The club's banner across the top of My Team Sheets (banner.js, _banner.ejs):
   a headline, a line and a link, with a × that puts it away on this device.
   The × is only shown once the page's script has marked the banner, so with
   scripts off there is no button that does nothing. */
.site-banner {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  background: #fff8e6;
  border: 1px solid #f0dfa8;
  border-left: 5px solid var(--gold);
  border-radius: 8px;
  padding: 14px 18px;
  margin: 0 0 24px;
}
.site-banner[hidden] { display: none; }
.site-banner-text { flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.site-banner-headline { font-size: 1.05rem; }
.site-banner-line { font-size: 0.95rem; }
.site-banner-link { font-weight: 600; }
/* A quiet button, but drawn as a bare x: scoped to the banner so it
   outweighs button.quiet's underline and inherited font. */
.site-banner .site-banner-close {
  display: none;
  background: none;
  border: 0;
  padding: 0 4px;
  margin: -4px -6px 0 0;
  color: var(--muted);
  font-size: 1.6rem;
  line-height: 1;
  text-decoration: none;
}
.site-banner.has-script .site-banner-close { display: block; }
.site-banner .site-banner-close:hover { color: var(--text); opacity: 1; }
.site-banner-preview { margin-top: 4px; }

/* Admin > Banner's choices: a radio with its words beside it, and the
   write-your-own boxes indented under their radio. */
fieldset.choices { border: 1px solid var(--border); border-radius: 8px; padding: 12px 16px 4px; margin: 0 0 20px; }
fieldset.choices legend { font-weight: 600; font-size: 0.85rem; padding: 0 6px; }
.choice { display: flex; align-items: flex-start; gap: 10px; font-weight: 400; font-size: 0.95rem; margin-bottom: 10px; }
.choice input[type="radio"] { margin: 3px 0 0; flex: none; }
.choice-block { padding: 8px 0; }
.choice-fields { margin: 0 0 12px 28px; }

/* The filters every list page shares (Jira JPI-3, ported from Club
   Volunteers; views/_filters.ejs, filters.js). The search box on its own
   line; what is on as chips with "Showing N of M"; the rest folded under
   More filters, tick lists in a grid, Apply and Clear all bottom right. */
.filters { padding: 14px 20px; }
.filters-scope { display: flex; gap: 8px 12px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.filters-scope label { margin: 0; }
.filters-scope select { margin: 0; width: auto; max-width: 100%; }
.filters-search { display: flex; gap: 8px; align-items: center; max-width: 560px; }
.filters-search input { margin: 0; flex: 1 1 auto; width: 100%; }
.filters-search noscript { display: flex; }
.filters-state { display: flex; gap: 6px 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; min-height: 1.4em; }
.filters-state:empty { display: none; }
.filters-showing { font-size: 0.88rem; }
.chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px 3px 10px; border: 1px solid var(--primary); border-radius: 999px; background: #fbf4f3; color: var(--primary); font-size: 0.82rem; text-decoration: none; line-height: 1.3; }
.chip:hover, .chip:focus-visible { background: var(--primary); color: #fff; }
.chip-x { font-weight: 700; font-size: 1rem; line-height: 1; }
.filters-more { margin-top: 10px; }
.filters-more > summary { cursor: pointer; color: var(--primary); font-size: 0.92rem; }
.filters-on { color: var(--muted); }
.filters-groups { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px 20px; margin-top: 12px; }
.filters-group { border: 0; margin: 0; padding: 0; min-width: 0; }
.filters-group legend, .filters-field label { padding: 0; margin: 0 0 4px; font-size: 0.78rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.filters-field input { margin: 0; }
.filters-group .tick { display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 4px 0; font-weight: 400; font-size: 0.92rem; cursor: pointer; }
.filters-group .tick input { margin: 3px 0 0; flex: none; }
/* A long list of ticks (Activity's kinds of event) scrolls inside itself. */
.filters-group .filters-ticks { max-height: 260px; overflow-y: auto; }
.filters-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.stat-note { text-transform: none; letter-spacing: 0; font-weight: 400; margin-top: 2px; }
.stat-card > .stat-label.stat-note { min-height: 0; }
/* While the server is asked again for a search, the list fades a little. */
[data-swap].swapping { opacity: 0.55; transition: opacity 0.15s; }
/* A download link the search took away (no teams left to export). */
.btn[hidden] { display: none; }
@media (max-width: 640px) {
  .filters { padding: 12px 14px; }
  .filters-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 12px; }
  .filters-actions { justify-content: stretch; }
  .filters-actions > * { flex: 1; text-align: center; }
}

/* The x that empties a search box (public/search-clear.js, ported from Club
   Volunteers): inside the box at its right, in the iPhone's shape, in place
   of the browser's own (hidden, so there are never two). Its height is set
   by the script, to the box's middle. */
.search-clear-wrap { position: relative; vertical-align: middle; }
.search-clear-wrap > input { padding-right: 40px; }
input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; display: none; }
.search-clear { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); width: 22px; height: 22px; min-height: 0; padding: 0; margin: 0; border: 0; border-radius: 50%; background: var(--text); color: var(--card); font-size: 16px; font-weight: 700; line-height: 22px; text-align: center; cursor: pointer; box-shadow: none; }
.search-clear:hover { background: var(--text); color: var(--card); }
/* Shown once something is typed - a button's own display would otherwise
   win over the hidden attribute. */
.search-clear[hidden] { display: none; }
/* A bigger target than it looks, for a thumb. */
.search-clear::before { content: ''; position: absolute; inset: -10px; }

/* Page links under a long list (views/_pager.ejs). */
.pager { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 14px; }
.pager a:not(.btn), .pager-current { min-width: 2em; padding: 4px 8px; text-align: center; border-radius: 6px; }
.pager-current { background: var(--primary); color: #fff; font-weight: 600; }

.people-toolbar button { white-space: nowrap; }

.badge.tentative { background: #fff1e0; color: #b45309; }

/* Training roster grid (views/training-roster.ejs, public/training-roster.js). */
.tg-table { table-layout: fixed; min-width: 980px; }
.tg-table th, .tg-table td { vertical-align: top; }
.tg-hour { width: 92px; white-space: nowrap; font-size: 0.8rem; color: var(--muted); }
.tg-cell { padding: 4px; border-radius: 6px; margin-bottom: 4px; border: 1px dashed transparent; font-size: 0.82rem; }
.tg-cell .tg-input { width: 100%; margin: 0; padding: 4px 6px; font-size: 0.82rem; }
.tg-cell select { width: 100%; margin: 2px 0; padding: 3px; font-size: 0.78rem; }
.tg-st-linked { border-color: #bfe3cb; background: #f2fbf5; }
.tg-st-open { border-color: #f3cf9b; background: #fff8ec; }
.tg-st-other { background: var(--soft, #f6f7f9); border-color: var(--line, #d6dae1); }
.tg-conflict { border-color: #e9a3a0; background: #fdf0ef; }
.tg-saving { opacity: 0.6; }
.tg-error { outline: 2px solid #b91c1c; }
.tg-note { font-size: 0.75rem; margin-top: 2px; color: var(--muted); }
.tg-ok { color: #15803d; }
/* A team that trains in more than one slot (JPI-28): said quietly on each of its boxes. */
.tg-slots { color: #64748b; font-weight: 400; white-space: nowrap; }
.tg-wait { color: #b45309; }
.tg-bad { color: #b91c1c; }
.tg-text { display: block; word-break: break-word; }
.tg-summary { margin: 10px 0; }
.tg-announce { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 8px; }
/* The season's stage (JPI-29): three steps, the current one lit. */
.tg-stage { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 8px; }
.tg-stage-steps { display: inline-flex; border: 1px solid #cbd5e1; border-radius: 999px; overflow: hidden; font-size: 0.8rem; font-weight: 600; }
.tg-stage-step { padding: 3px 12px; color: #94a3b8; background: #fff; }
.tg-stage-step + .tg-stage-step { border-left: 1px solid #cbd5e1; }
.tg-stage-step.tg-stage-now { background: #8b1c1c; color: #fff; }
.tg-stage > .muted { flex: 1 1 320px; }
.tg-stage form { flex: none; }
.tg-stage button { white-space: nowrap; }
/* The season and the page's own buttons on one row; the filter ticks on theirs
   (JPI-80). The two shared a row that couldn't wrap, so a label broke in two
   and each button was a different shape. A button is never narrower than its
   words: the row wraps between buttons, never inside one. */
.tg-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; margin-bottom: 16px; }
.tg-top-season { display: flex; gap: 10px; align-items: center; flex: 0 1 300px; }
.tg-top-season select { margin-bottom: 0; }
.tg-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.tg-actions form { display: flex; margin: 0; }
.tg-actions button, .tg-actions .btn { white-space: nowrap; flex: none; }
.tg-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; margin: 12px 0 16px; }
.tg-filters .tick { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-weight: 400; font-size: 0.88rem; white-space: nowrap; cursor: pointer; }
.tg-filters .tick input { margin: 0; flex: none; width: auto; }
.tg-only-open .tg-cell:not(.tg-st-open) { opacity: 0.25; }
/* The court times the club has booked (court-times.js): an hour the court
   isn't ours is hatched, one that is only partly ours is tinted, and a team's
   box outside what is booked has an amber edge. An hour we know nothing
   about (.tg-slot-unknown) looks as the grid always has. */
.tg-slot-none, .tg-key-none { background: repeating-linear-gradient(135deg, #f3f4f6, #f3f4f6 6px, #e5e7eb 6px, #e5e7eb 12px); }
.tg-slot-part { background: #f6f7f9; }
.tg-slot-none .tg-input, .tg-slot-part .tg-input { background: rgba(255, 255, 255, 0.85); }
.tg-nocourt { border-style: solid; border-color: #f3cf9b; box-shadow: inset 3px 0 0 #d97706; }
.tg-slot-note { font-size: 0.72rem; color: var(--muted); margin: 0 4px 4px; }
.tg-court { margin: -4px 0 8px; font-size: 0.85rem; }
.tg-court-times { margin: 6px 0; font-size: 0.88rem; }
.tg-key { display: inline-block; width: 14px; height: 14px; vertical-align: -2px; border: 1px solid var(--line, #d6dae1); border-radius: 3px; }
/* The teams that aren't in a box (JPI-30). */
.tg-teams > summary { cursor: pointer; }
.tg-teams-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 8px 28px; margin-top: 10px; }
.tg-teams-cols h3 { margin: 0 0 4px; font-size: 1rem; }
.tg-team-list { margin: 0 0 10px; padding-left: 18px; font-size: 0.9rem; }
.tg-team-list li { margin: 2px 0; }

/* The planning controls on a box (JPI-26). */
.tg-badges { display: flex; flex-wrap: wrap; gap: 3px; }
.tg-badges .badge { font-size: 0.68rem; padding: 1px 6px; }
.tg-badge-confirmed { background: #dcfce7; color: #166534; }
.tg-badge-locked { background: #e0e7ff; color: #3730a3; }
.tg-badge-flagged { background: #fff1e0; color: #b45309; }
.tg-box-note { font-style: italic; word-break: break-word; }
.tg-m-flagged { border-top: 3px solid #d97706; }
.tg-held { color: var(--text, #222); }
/* A team on the grid more than once (JPI-48): amber down the box's left edge
   and a badge, until the club says it is meant. Not the red of a clash. */
.tg-dupe-check { box-shadow: inset 5px 0 0 #d97706; background: #fffbeb; border-color: #f3d38a; }
.tg-badge-dupe { background: #fde9c8; color: #92400e; white-space: nowrap; }
.tg-same-team { outline: 2px solid #d97706; outline-offset: -1px; }
.tg-several-check { font-weight: 600; }
.tg-several-check .muted, .tg-several-check form { font-weight: 400; }
.tg-several-slots { display: block; font-size: 0.85rem; margin: 2px 0; }
.tg-several-slots a { color: inherit; }
.tg-only-dupe .tg-cell:not(.tg-dupe),
.tg-only-held .tg-cell:not(.tg-m-held),
.tg-only-unconfirmed .tg-cell:not(.tg-m-unconfirmed),
.tg-only-flagged .tg-cell:not(.tg-m-flagged),
.tg-only-locked .tg-cell:not(.tg-m-locked) { opacity: 0.25; }

/* When a slot runs, and booked by hand (JPI-37). A hand-booked hour isn't
   hatched; the spare half hour is. */
.tg-slot-time { font-size: 0.75rem; font-weight: 700; color: #1d4ed8; margin: 0 4px 2px; }
.tg-slot-gap { background: repeating-linear-gradient(135deg, #f3f4f6, #f3f4f6 6px, #e5e7eb 6px, #e5e7eb 12px); }
.tg-slot-gap .tg-slot-time { color: var(--muted); font-weight: 600; }
.tg-slot-hand { background: #f7fbf8; }
/* The slot's own menu: a quiet "..." at the foot of the slot, so three hundred
   of them don't read as three hundred labels. */
.tg-slot-menu { font-size: 0.72rem; margin: -2px 4px 2px; color: var(--muted); }
.tg-slot-menu summary { cursor: pointer; list-style: none; text-align: right; line-height: 1; font-size: 0.95rem; opacity: 0.6; }
.tg-slot-menu summary::-webkit-details-marker { display: none; }
.tg-slot-menu summary:hover, .tg-slot-menu[open] summary { opacity: 1; }
.tg-slot-menu button { display: block; margin: 2px 0; text-align: left; }
.tg-slot-note button { font-size: inherit; }

/* The grid in the roster sheet's colours (JPI-32), while the planning tools
   are on (.tg-sheet on each court's card): a team's box white with its text
   blue for boys and red for girls, a booked half court with nothing in it
   yellow, Dribblers red, the Open Mens Competition orange, another club in
   the colour kept for it, and a court not used this season under a light
   green band. A box still to be linked, a clash and a team entered more than
   once keep their own colours over these: they are what needs looking at. */
.tg-sheet .tg-cell .tg-input { background: rgba(255, 255, 255, 0.75); }
.tg-sheet .tg-g-boys .tg-input, .tg-sheet .tg-g-boys .tg-text, .tg-key-boys { color: #1d4ed8; font-weight: 600; }
.tg-sheet .tg-g-girls .tg-input, .tg-sheet .tg-g-girls .tg-text, .tg-key-girls { color: #b91c1c; font-weight: 600; }
.tg-key-free { background: #fff59d; border-color: #e6d33c; border-style: solid; }
.tg-key-dribblers { background: #fca5a5; border: 1px solid #dc2626; }
.tg-key-omc { background: #fdba74; border: 1px solid #ea580c; }
/* A club's colour, as the key's swatch and the Venues and courts picker draw
   it. Swatches only: a box takes its club's colour from the sheet's own rules
   below, which is what keeps the older tint for a club's box (JPI-25, further
   down) from being painted over it - on live it was, and every club's boxes
   came out the same pale blue (Jira JPI-64). */
.tg-key.tg-club-blue { background: #bfdbfe; border-color: #60a5fa; }
.tg-key.tg-club-green { background: #bbf7d0; border-color: #4ade80; }
.tg-key.tg-club-pink { background: #fbcfe8; border-color: #f472b6; }
.tg-key.tg-club-purple { background: #ddd6fe; border-color: #a78bfa; }
.tg-key.tg-club-teal { background: #99f6e4; border-color: #2dd4bf; }
.tg-key.tg-club-brown { background: #e7d3c1; border-color: #b08968; }
/* Every box is its half court: drawn the full height of its row (Jira
   JPI-61 for the yellow free half, JPI-63 for the rest), so the two halves
   of an hour and the same half across the week are one size whatever is in
   them. A row is as tall as its tallest box, and a box's own element is only
   as tall as its text - so **the box is painted behind its content, across
   its table cell**: one ::before on the cell, placed against the td (which
   is the half court), reading what to paint from variables each kind of box
   sets. The cell itself paints nothing here. Never a percentage height
   inside a table cell - Safari and Firefox disagree about those. The td is
   its own stacking context so the painted layer sits under everything in
   it without anything else having to be positioned. */
.tg-sheet td.tg-slot { position: relative; z-index: 0; }
.tg-sheet .tg-cell { background: transparent !important; border-color: transparent !important; box-shadow: none !important; outline: none !important; }
.tg-sheet .tg-cell::before {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  left: 10px; right: 10px; top: 0; bottom: 4px;
  border-radius: 6px;
  background: var(--tg-bg, transparent);
  border: var(--tg-bw, 1px) var(--tg-bs, solid) var(--tg-bc, transparent);
  border-top: var(--tg-top, var(--tg-bw, 1px) var(--tg-bs, solid) var(--tg-bc, transparent));
  box-shadow: var(--tg-edge, none);
  outline: var(--tg-ring, none); outline-offset: -1px;
}
/* The row's own paddings: the top half starts under the td's top padding,
   the bottom half ends above its bottom padding, and the two keep the small
   gap between them that the boxes always had. */
.tg-sheet tr.tg-row-0 .tg-cell::before { top: 8px; }
.tg-sheet tr.tg-row-1 .tg-cell::before { bottom: 8px; }
/* What each kind of box paints. Later lines win: what needs looking at -
   no court, entered more than once, a clash - goes over what the box is. */
.tg-sheet .tg-slot-booked .tg-st-empty, .tg-sheet .tg-slot-part .tg-st-empty, .tg-sheet .tg-slot-hand .tg-st-empty { --tg-bg: #fff59d; --tg-bc: #e6d33c; }
.tg-sheet .tg-st-taken { --tg-bg: repeating-linear-gradient(135deg, #fafafa, #fafafa 5px, #f0f1f4 5px, #f0f1f4 10px); --tg-bc: var(--line, #d6dae1); --tg-bs: dashed; }
.tg-sheet .tg-st-linked { --tg-bg: #fff; --tg-bc: #cbd5e1; }
.tg-sheet .tg-st-open { --tg-bg: #fff8ec; --tg-bc: #f3cf9b; --tg-bs: dashed; }
.tg-sheet .tg-st-other { --tg-bg: var(--soft, #f6f7f9); --tg-bc: var(--line, #d6dae1); --tg-bs: dashed; }
.tg-sheet .tg-use-dribblers { --tg-bg: #fca5a5; --tg-bc: #dc2626; --tg-bs: solid; }
.tg-sheet .tg-use-omc { --tg-bg: #fdba74; --tg-bc: #ea580c; --tg-bs: solid; }
/* Another club's box: its club's colour, which has to come after every colour
   a box gets for what it is. */
.tg-sheet .tg-use-club { --tg-bs: solid; }
.tg-sheet .tg-club-blue { --tg-bg: #bfdbfe; --tg-bc: #60a5fa; }
.tg-sheet .tg-club-green { --tg-bg: #bbf7d0; --tg-bc: #4ade80; }
.tg-sheet .tg-club-pink { --tg-bg: #fbcfe8; --tg-bc: #f472b6; }
.tg-sheet .tg-club-purple { --tg-bg: #ddd6fe; --tg-bc: #a78bfa; }
.tg-sheet .tg-club-teal { --tg-bg: #99f6e4; --tg-bc: #2dd4bf; }
.tg-sheet .tg-club-brown { --tg-bg: #e7d3c1; --tg-bc: #b08968; }
.tg-sheet .tg-whole { --tg-bw: 2px; --tg-bs: solid; }
.tg-sheet .tg-nocourt { --tg-bc: #f3cf9b; --tg-bs: solid; --tg-edge: inset 3px 0 0 #d97706; }
.tg-sheet .tg-dupe-check { --tg-bg: #fffbeb; --tg-bc: #f3d38a; --tg-edge: inset 5px 0 0 #d97706; }
.tg-sheet .tg-conflict { --tg-bg: #fdf0ef; --tg-bc: #e9a3a0; }
.tg-sheet .tg-m-flagged { --tg-top: 3px solid #d97706; border-top: 1px solid transparent; }
.tg-sheet .tg-same-team { --tg-ring: 2px solid #d97706; }
.tg-sheet .tg-error { --tg-ring: 2px solid #b91c1c; }
.tg-only-room .tg-sheet .tg-slot-booked .tg-st-empty, .tg-only-room .tg-sheet .tg-slot-part .tg-st-empty, .tg-only-room .tg-sheet .tg-slot-hand .tg-st-empty { --tg-ring: 2px solid #15803d; }
.tg-sheet .tg-use-dribblers .tg-note, .tg-sheet .tg-use-omc .tg-note, .tg-sheet .tg-use-club .tg-note { color: #1f2937; }
.tg-badge-lent { background: #1f2937; color: #fff; }
.tg-sheet details.tg-unused, .tg-key-unused { background: #ecfdf3; border-color: #a7e3bd; }
.tg-key-team { background: #fff; border-color: #cbd5e1; }
.tg-key-open { border: 1px dashed #f3cf9b; background: #fff8ec; }
.tg-key-clash { border: 1px dashed #e9a3a0; background: #fdf0ef; }
.tg-key-dupe { background: #fffbeb; border-color: #f3d38a; box-shadow: inset 4px 0 0 #d97706; }
.tg-key-card h2 { margin: 0 0 6px; font-size: 1rem; }
.tg-key-list { list-style: none; display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 0; padding: 0; font-size: 0.88rem; }
.tg-key-marks { margin: 10px 0 0; font-size: 0.85rem; }
.tg-key-marks .badge { font-size: 0.68rem; padding: 1px 6px; }
.tg-sheet-summary > summary { cursor: pointer; }
/* Starting an empty grid from an earlier season (JPI-34). */
.tg-start .tg-summary-table { min-width: 620px; background: #fff; }
.tg-summary-table { min-width: 760px; }
.tg-summary-table td, .tg-summary-table th { text-align: right; }
.tg-summary-table th:first-child { text-align: left; }
.tg-summary-table tfoot th, .tg-summary-table tfoot td { font-weight: 700; border-top: 2px solid var(--line, #d6dae1); }
.tg-summary-table .tg-free { background: #fff59d; font-weight: 600; }
.tc-colour { display: flex; align-items: center; gap: 6px; }
.tc-colour select { width: auto; margin: 0; }
/* Printed in the same colours: a browser drops backgrounds unless told. */
@media print {
  .tg-sheet, .tg-sheet *, .tg-key-card, .tg-key-card *, .tg-summary-table, .tg-summary-table * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .tg-sheet .tg-cell .tg-input { border: 0; padding: 0; background: transparent; }
  .tg-key-card { break-inside: avoid; }
}

/* What a box is for (JPI-25). Tints only - the full colour scheme is JPI-32's. */
.tg-use-dribblers { border-color: #f1b0aa; background: #fdecea; }
.tg-use-omc { border-color: #f2c28b; background: #fff0df; }
.tg-use-club { border-color: #b3cdf0; background: #eaf2fd; }
.tg-use-said { color: var(--text, #222); font-weight: 600; }
.tg-whole { border-style: solid; border-width: 2px; }
.tg-badge-whole { background: #e8eaf0; color: #333; }
.tg-st-taken { border-color: var(--line, #d6dae1); background: repeating-linear-gradient(135deg, #fafafa, #fafafa 5px, #f0f1f4 5px, #f0f1f4 10px); min-height: 2.2rem; display: flex; align-items: center; }
.tg-more { margin-top: 3px; font-size: 0.75rem; }
.tg-more summary { cursor: pointer; color: var(--muted); }
.tg-more .tg-field { display: block; margin-top: 3px; color: var(--muted); }
.tg-more .tg-field select, .tg-more .tg-field input { width: 100%; margin: 1px 0 0; padding: 3px; font-size: 0.78rem; }
.tg-more .tick { margin-top: 4px; }
.tg-error[title]::after { content: attr(title); display: block; color: #b91c1c; font-size: 0.75rem; margin-top: 2px; }
details.tg-unused { opacity: 0.75; }
details.tg-unused > summary { cursor: pointer; font-size: 1rem; }
details.tg-unused[open] > summary { margin-bottom: 8px; }
@media print { .tg-more { display: none; } details.tg-unused { display: none; } }

/* Venues and courts (views/training-courts.ejs). */
.tc-table input[type="text"] { margin: 0; width: 100%; min-width: 21rem; }
.tc-table .tc-new input[name="new_code"] { min-width: 11rem; }
.tc-table .tick { white-space: nowrap; }
.tc-table .tc-order { width: 4.5rem; margin: 0; }
.tc-table td { vertical-align: middle; }
/* "Only booked slots with room": everything fades but the free half courts
   in an hour the court is ours. */
.tg-only-room .tg-cell { opacity: 0.25; }
.tg-only-room .tg-slot-booked .tg-st-empty, .tg-only-room .tg-slot-part .tg-st-empty, .tg-only-room .tg-slot-hand .tg-st-empty { opacity: 1; outline: 2px solid #15803d; outline-offset: -2px; }
.tg-only-room .tg-slot-booked .tg-st-empty.tg-ro::after, .tg-only-room .tg-slot-part .tg-st-empty.tg-ro::after, .tg-only-room .tg-slot-hand .tg-st-empty.tg-ro::after { content: 'Free'; color: #15803d; font-size: 0.78rem; }
@media print {
  .tg-cell { border-color: var(--line, #d6dae1); background: #fff; }
  .tg-cell .tg-input { border: 0; padding: 0; }
  .tg-cell select, .tg-cell button { display: none; }
  .tg-block { break-inside: avoid; }
}

.tg-table tr.tg-row-0 td { padding-bottom: 0; }
.tg-table tr.tg-row-1 td { border-top: 0; padding-top: 0; }
.tg-table tr.tg-row-1 th, .tg-table tr.tg-row-0 th { border-bottom: 1px solid var(--line, #d6dae1); }

/* ---- The Training Roster on half-hour rows (Jira JPI-66) ----------------
   Time down the page, a column for each end of the court, five days across
   at the width the hour grid had. Everything here hangs off .th-sheet and
   uses its own class names (th-...), so no rule written for the hour grid's
   boxes can reach one of these, and none of these can reach those. */
.th-grid { display: grid; grid-template-columns: 44px repeat(10, minmax(0, 1fr)); column-gap: 1px; row-gap: 1px; min-width: 900px; --th-row: 28px; }
.th-day { text-align: center; font-weight: 600; font-size: 0.85rem; padding-bottom: 1px; }
.th-end { text-align: center; font-size: 0.7rem; color: var(--muted); padding-bottom: 2px; }
.th-time { font-size: 0.72rem; color: var(--text, #222); text-align: right; padding-right: 5px; line-height: 1; margin-top: -1px; }
.th-time-half { color: #94a3b8; font-size: 0.66rem; }
/* The gap between one day and the next. */
.th-end-0, .th-day { margin-left: 4px; }
.th-day { margin-right: 0; }
/* A half hour of an end: what the court is then. */
.th-bg { min-width: 0; }
.th-bg.th-hour { box-shadow: 0 -1px 0 #94a3b8; }
.th-s-booked, .th-s-part, .th-s-hand { background: #fff59d; }
.th-s-part { background: repeating-linear-gradient(135deg, #fff59d, #fff59d 6px, #fdfbe1 6px, #fdfbe1 12px); }
.th-s-hand { outline: 1px dotted #b08d00; outline-offset: -1px; }
.th-s-none { background: repeating-linear-gradient(135deg, #f3f4f6, #f3f4f6 5px, #e2e5ea 5px, #e2e5ea 10px); }
.th-s-unknown { background: var(--soft, #f6f7f9); }
.th-sheet[data-edit] .th-bg[data-start] { cursor: pointer; }
.th-sheet[data-edit] .th-bg[data-start]:hover { filter: brightness(0.94); }
/* A box, over the half hours it runs. */
.th-box { position: relative; z-index: 1; min-width: 0; overflow: hidden; cursor: pointer; background: #fff; border: 1px solid #94a3b8; border-radius: 4px; padding: 1px 4px 1px 4px; font-size: 0.72rem; line-height: 1.22; color: #1f2937; }
.th-box:focus-visible { outline: 2px solid #2563eb; outline-offset: 1px; }
.th-text { display: inline; word-break: break-word; }
.th-g-boys .th-text { color: #1d4ed8; font-weight: 600; }
.th-g-girls .th-text { color: #b91c1c; font-weight: 600; }
.th-st-open { background: #fff8ec; border: 1px dashed #d9a441; }
.th-st-other { background: var(--soft, #f6f7f9); border: 1px dashed #94a3b8; }
.th-use-dribblers { background: #fca5a5; border: 1px solid #dc2626; }
.th-use-omc { background: #fdba74; border: 1px solid #ea580c; }
.th-use-club { border-style: solid; }
.th-club-blue { background: #bfdbfe; border-color: #60a5fa; }
.th-club-green { background: #bbf7d0; border-color: #4ade80; }
.th-club-pink { background: #fbcfe8; border-color: #f472b6; }
.th-club-purple { background: #ddd6fe; border-color: #a78bfa; }
.th-club-teal { background: #99f6e4; border-color: #2dd4bf; }
.th-club-brown { background: #e7d3c1; border-color: #b08968; }
.th-whole { border-width: 2px; text-align: center; }
/* What needs looking at goes over what the box is, so it comes after. */
.th-nocourt { box-shadow: inset 3px 0 0 #d97706; }
.th-dupe-check { background: #fffbeb; border-color: #f3d38a; box-shadow: inset 4px 0 0 #d97706; }
.th-m-flagged { border-top: 3px solid #d97706; }
.th-clash, .th-overlap { background: #fdf0ef; border: 1px solid #b91c1c; box-shadow: inset 0 0 0 1px #b91c1c; }
/* The later of two boxes that overlap on an end is set in, so both show. */
.th-over { margin-left: 38%; z-index: 4; }
/* A day with nothing on it and nothing booked, folded to a thin strip
   (JPI-68): its columns are narrow (set on the grid itself), its name short.
   A click opens it. */
.th-day.th-idle { font-size: 0.64rem; font-weight: 500; color: var(--muted); overflow: hidden; padding-top: 3px; }
.th-end.th-idle { visibility: hidden; }
.th-idle { cursor: pointer; }
/* An opened day's heading folds it away again (JPI-71). */
.th-day.th-refold { cursor: pointer; }
.th-refold-word { font-size: 0.68rem; font-weight: 400; color: var(--muted); text-decoration: underline; margin-left: 4px; }
.th-day.th-refold:hover .th-refold-word { color: var(--text, #222); }
.th-grid:has(.th-idle:hover) .th-idle { filter: brightness(0.93); }
.th-on { outline: 2px solid #2563eb; outline-offset: 1px; z-index: 3; }
.th-bg.th-on { outline-offset: -2px; }
.th-same-team { outline: 2px solid #d97706; outline-offset: 1px; }
/* The box's marks: a dot each, top right. */
.th-marks { float: right; display: flex; gap: 2px; margin: 2px 0 0 3px; }
.th-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; border: 1px solid rgba(0, 0, 0, 0.25); vertical-align: middle; }
.th-dot-confirmed { background: #16a34a; }
.th-dot-locked { background: #4338ca; }
.th-dot-flagged { background: #f59e0b; }
.th-dot-held { background: #0e7490; }
/* The filters across the top fade everything but what each is looking for. */
.tg-only-open .th-box:not(.th-st-open),
.tg-only-dupe .th-box:not(.th-dupe),
.tg-only-held .th-box:not(.th-m-held),
.tg-only-unconfirmed .th-box:not(.th-m-unconfirmed),
.tg-only-flagged .th-box:not(.th-m-flagged),
.tg-only-locked .th-box:not(.th-m-locked),
.tg-only-overlap .th-box:not(.th-overlap):not(.th-clash),
.tg-only-room .th-box { opacity: 0.25; }
.tg-only-room .th-bg:not(.th-free) { opacity: 0.35; }
.tg-only-room .th-bg.th-free { outline: 2px solid #15803d; outline-offset: -2px; }
/* The open box's panel, under its court. */
.th-panel { margin-top: 12px; padding: 10px 12px; border: 1px solid #93c5fd; border-radius: 8px; background: #f8fbff; max-width: 560px; }
.th-panel-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 6px; }
.th-panel-warn, .th-panel-error { color: #b91c1c; font-size: 0.85rem; margin: 4px 0 8px; }
.th-panel-note { font-size: 0.8rem; margin: 8px 0 0; }
.th-panel .tg-cell { margin: 0; }
.th-panel .tg-more { margin-top: 6px; }
.th-when { display: flex; flex-wrap: wrap; gap: 8px 12px; margin-top: 10px; padding-top: 10px; border-top: 1px solid #dbe4f0; }
.th-when .tg-field { display: flex; flex-direction: column; font-size: 0.78rem; color: var(--muted); }
.th-panel-foot { margin: 10px 0 0; }
.th-when select { width: auto; min-width: 84px; padding: 3px 6px; font-size: 0.85rem; height: auto; }
.th-add { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; }
.th-add .tg-field { flex: 1 1 240px; display: flex; flex-direction: column; font-size: 0.78rem; color: var(--muted); }
.tg-unused .th-grid { opacity: 0.6; }
@media print {
  .th-sheet, .th-sheet * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .th-grid { min-width: 0; --th-row: 22px; }
}

/* BlazerBot's face beside each of its answers (JPI-82): the avatar is the
   head in a cream badge, so it reads on a light bubble and in dark mode. */
.help-chat-who { display: flex; align-items: center; gap: 8px; }
.help-chat-avatar { width: 40px; height: 40px; flex: none; }
/* Faded where the documents didn't hold the answer. */
.help-chat-avatar.is-faded { opacity: 0.45; filter: grayscale(0.6); }
/* On Questions asked: the face beside what BlazerBot said. */
.help-asked-answer { display: flex; align-items: flex-start; gap: 12px; margin-top: 8px; }
.help-asked-answer > div { flex: 1; min-width: 0; }
.help-asked-answer > div > p:first-child { margin-top: 0; }

/* The full mascot on Ask BlazerBot: at the right of the title, standing down
   over the top edge of the chat's card below - only as far as that card's
   own padding, so it never covers the first message. Smaller beside the
   title on a phone. */
.help-bot-head { display: flex; align-items: flex-end; gap: 24px; }
.help-bot-head-text { flex: 1; min-width: 0; }
.help-bot-head-text > p:last-child { margin-bottom: 0; }
/* It comes first in the page so that on a phone it can float beside the
   title with the words running round it; on a wider screen it is put last. */
.help-bot-mascot { order: 2; width: 150px; height: auto; flex: none; margin-bottom: -62px; position: relative; z-index: 1; }
@media (max-width: 640px) {
  .help-bot-head { display: block; }
  .help-bot-head::after { content: ''; display: block; clear: both; }
  .help-bot-mascot { float: right; width: 96px; margin: 0 0 6px 10px; }
}

/* The top bar's shortcut to BlazerBot (JPI-82): a pill with its face and
   "Ask BlazerBot", right after the app's name, outlined in gold on the Ask
   page. It is the logo's height, so the bar is no deeper for it. The words
   drop before the bar would wrap - sooner on the long bar - and on a phone
   it is the face alone, a 44px round button beside the menu button. */
header.topbar .ask-bot {
  display: flex; align-items: center; gap: 8px; flex: none; box-sizing: border-box;
  height: 40px; padding: 3px 14px 3px 3px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.12); border: 1px solid rgba(255, 255, 255, 0.4);
  color: #fff; font-weight: 600; font-size: 0.92rem; white-space: nowrap;
}
header.topbar .ask-bot:hover { background: rgba(255, 255, 255, 0.22); text-decoration: none; }
header.topbar .ask-bot[aria-current] { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold); }
header.topbar .ask-bot img { width: 32px; height: 32px; display: block; }
/* Measured as a club admin with Season setup in the bar: the long bar has
   room for the words from about 1140px; everybody else's bar is short. */
@media (max-width: 1139px) {
  header.topbar .ask-bot-long { padding: 3px; }
  header.topbar .ask-bot-long .ask-bot-words { display: none; }
}
@media (max-width: 899px) {
  header.topbar .ask-bot { padding: 3px; }
  header.topbar .ask-bot .ask-bot-words { display: none; }
}
/* It stays beside the app's name when the links drop to a row of their own:
   the links are pushed right by their own margin, so the bar needn't spread
   its items. */
@media (min-width: 641px) {
  header.topbar { justify-content: flex-start; }
}
@media (max-width: 640px) {
  header.topbar .ask-bot { width: 44px; height: 44px; justify-content: center; margin: 0 12px 0 auto; }
  header.topbar .ask-bot img { width: 38px; height: 38px; }
}
/* Its face beside the Help menu's own entry. */
header.topbar .nav-menu-items a.nav-bot { display: flex; align-items: center; gap: 10px; font-weight: 600; }
header.topbar .nav-menu-items a.nav-bot img { width: 30px; height: 30px; flex: none; }

/* The page's one Back (views/_back.ejs), at its head. */
p.back-line { margin: 0 0 10px; font-size: 0.92rem; }

/* Find a team or coach on the Training Roster (6 October 2026): the
   matches stay lit and ringed, everything else fades. */
.tg-find { margin: 10px 0 6px; max-width: 520px; }
.tg-find-said { margin: 6px 0 2px; }
.tg-find-list { margin: 4px 0 0; padding-left: 18px; max-height: 260px; overflow-y: auto; }
.tg-find-list li { margin: 2px 0; }
.tg-finding .tg-cell[data-id]:not(.tg-found),
.tg-finding .th-box:not(.tg-found) { opacity: 0.2; }
.tg-finding .tg-found { outline: 3px solid #d4a017; outline-offset: -1px; opacity: 1; z-index: 2; }
@keyframes tg-found-go { 0%, 100% { box-shadow: none; } 50% { box-shadow: 0 0 0 6px rgba(212, 160, 23, 0.6); } }
.tg-found-go { animation: tg-found-go 0.6s ease 2; }
