/* Club Contacts additions on top of the shared Team Sheets stylesheet (style.css). */

header.topbar nav a.active { opacity: 1; text-decoration: underline; }
header.topbar .muted-light { color: #fff; opacity: 0.7; }

.subnav-row { background: var(--card); border-bottom: 1px solid var(--border); display: flex; align-items: flex-end; }
.subnav { flex: 1; min-width: 0; padding: 0 24px; display: flex; gap: 2px; flex-wrap: wrap; }
.subnav-season { display: flex; gap: 8px; align-items: center; align-self: center; margin: 0; padding: 4px 24px 4px 8px; white-space: nowrap; }
.subnav-season select { margin: 0; width: auto; padding: 4px 8px; font-size: 0.9rem; }
.subnav a { padding: 11px 12px 9px; color: var(--muted); text-decoration: none; font-size: 0.9rem; border-bottom: 3px solid transparent; }
.subnav a:hover { color: var(--text); }
.subnav a.active { color: var(--primary); border-bottom-color: var(--gold); font-weight: 600; }

/* A group's own pages, as a row under the tabs on each of them. */
.groupnav { background: #fafbfc; border-bottom: 1px solid var(--border); padding: 6px 24px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.groupnav a { padding: 4px 11px; border-radius: 999px; color: var(--muted); text-decoration: none; font-size: 0.85rem; border: 1px solid transparent; }
.groupnav a:first-child { color: var(--primary); padding-left: 0; }
.groupnav a:hover { color: var(--text); }
.groupnav a.active { color: var(--primary); border-color: var(--border); background: var(--card); font-weight: 600; }

.report-head { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
.report-head .subtitle { max-width: 760px; }
/* 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; }
/* The description runs the page's width, as a page's own does: capped at
   760px a short one broke onto a second line with half the row empty. */
.page-head .subtitle { margin: 10px 0 12px; }
.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; }
.report-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.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; }
/* The heading takes two lines' worth whether it needs them or not, so the
   numbers sit on one line across the row - "Not in PlayHQ" next to
   "Dependent, missing contact" used to leave them at different heights. A
   longer heading than that pushes its own number down, which is honest: the
   alternative is truncating what the card is counting. */
.stat-card > .stat-label:first-child { min-height: 2.5em; }
a.stat-link { color: inherit; text-decoration: none; cursor: pointer; }
/* A colour change and nothing else: the shadow it used to gain read as the
   card lifting off the page when pressed (the president, 1 October 2026). */
a.stat-link:hover, a.stat-link:focus-visible { border-color: var(--primary); background: #fbf4f3; }
a.stat-on:hover, a.stat-on:focus-visible { background: var(--primary); }
a.stat-on { background: var(--primary); color: #fff; border-color: var(--primary); }
.stat-value { font-size: 1.6rem; font-weight: 700; margin-top: 4px; }

.report-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; margin-bottom: 24px; }
a.report-card { text-decoration: none; color: inherit; margin-bottom: 0; transition: border-color 0.15s; }
a.report-card:hover { border-color: var(--primary); }
a.report-card h2 { color: var(--primary); }
.mini-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; margin: 0; }
.mini-stats dt { color: var(--muted); font-size: 0.75rem; }
.mini-stats dd { margin: 2px 0 0; font-size: 1.15rem; font-weight: 700; }

.badge-good { background: #eaf7ee; color: #1e7a3c; }
.badge-bad { background: #fdecea; color: var(--danger); }
.badge-warn { background: #fff8e6; color: #92620a; }

.filter-bar { padding: 16px 20px; }
/* The buttons that act on a whole page, above its filters. */
.season-picker { display: flex; gap: 8px; align-items: center; margin: -6px 0 16px; }
.season-picker label { margin: 0; }
.season-picker select { margin: 0; width: auto; }
/* On a phone the page's buttons wrap under the heading, and the picker's
   pull-up (there to tuck it under a subtitle) laid it over them. It gets a
   full-width select of its own there instead, with room above it. */
@media (max-width: 640px) {
  .season-picker { margin: 12px 0 16px; }
  .season-picker select { flex: 1; min-width: 0; }
  .report-actions { margin-bottom: 4px; }
}
.page-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; }
/* A button and the field it acts on, side by side. */
.inline-form { display: flex; gap: 8px; align-items: center; }
.inline-form select { margin-bottom: 0; width: auto; }
.filter-bar .filter-grid { margin-top: 0; }

.report-meta { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 10px; }
/* A count with a button beside it: the two sit on the same line, and the
   button is sized to the text rather than to a page action. */
.report-meta.meta-with-action { align-items: center; gap: 14px; }
.email-box { font-size: 0.88rem; }
.email-box summary { cursor: pointer; color: var(--primary); }
.email-box textarea { display: block; width: min(640px, 100%); font: inherit; font-size: 0.85rem; padding: 8px; border: 1px solid var(--border); border-radius: 6px; margin: 8px 0; }

.table-card { padding: 0; overflow: hidden; }
.table-card .table-scroll { max-height: 72vh; overflow: auto; }
.table-card table { margin-top: 0; }
.table-card thead th { position: sticky; top: 0; background: var(--card); z-index: 1; box-shadow: inset 0 -1px 0 var(--border); }
.report-table td { white-space: nowrap; }
/* A column of tags wraps within a readable width instead of stretching to its longest row.
   The width goes on an inner block because browsers ignore max-width on table cells. */
.report-table td.wrap { white-space: normal; }
.report-table td.wrap .cell-wrap { min-width: 12rem; max-width: 22rem; }

/* A row's actions behind a single "⋮" menu, as in the Team Sheets app. The list is
   position:fixed and hidden from its first frame, so it never widens the row or gets
   clipped by the scrolling table; the script in report.ejs places and shows it. */
.row-menu-cell { width: 1%; text-align: left; white-space: nowrap; }
.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; 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-item { display: block; padding: 8px 10px; border-radius: 6px; font-size: 0.88rem; color: var(--text); text-decoration: none; white-space: nowrap; }
.action-menu-item:hover { background: var(--bg); }
/* A form's button in a menu looks like the menu's links. */
.action-menu-list { text-align: left; }
.action-menu-list form { margin: 0; }
button.action-menu-item { width: 100%; margin: 0; background: none; border: none; text-align: left; font: inherit; font-size: 0.88rem; color: var(--text); cursor: pointer; }
button.action-menu-item:hover { background: var(--bg); }
/* A menu item that opens a file picker is a <label>, which forms style bold. */
label.action-menu-item { font-weight: normal; margin: 0; cursor: pointer; }
.action-menu-item.danger { color: var(--danger); }
tr.editing td { background: #fff8e6; }

.group-card summary { cursor: pointer; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.group-card summary code { font-size: 0.8rem; }
.group-counts { margin-left: auto; display: flex; gap: 6px; }
.invite-link { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; word-break: break-all; margin: 14px 0; }

.import-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
.import-grid .card { margin-bottom: 0; }
.import-grid form { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.import-grid input[type="file"] { margin-bottom: 4px; flex: 1 1 100%; }
.columns-help { margin-top: 14px; font-size: 0.85rem; }
.columns-help summary { cursor: pointer; color: var(--muted); }
.columns-help ul { padding-left: 18px; margin: 8px 0 0; }
.columns-help code, .small code { font-size: 0.8rem; word-break: break-all; }
.small { font-size: 0.85rem; }

.row-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 0 16px; }
/* A checkbox in the row of fields, lined up with the boxes rather than their labels. */
.row-form-check { align-self: end; display: flex; align-items: center; flex-wrap: wrap; gap: 6px 20px; min-height: 40px; margin-bottom: 16px; }
.row-form-check label.checkbox { flex-wrap: wrap; white-space: nowrap; column-gap: 8px; row-gap: 2px; }
.row-form-actions { grid-column: 1 / -1; display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
/* A form's buttons under its table, with room above them. */
.table-scroll + .row-form-actions, .table-scroll + .page-actions { margin-top: 10px; }
label.checkbox { display: flex; gap: 8px; align-items: center; font-weight: 400; margin: 0; }
.row-actions { display: flex; gap: 12px; align-items: center; white-space: nowrap; }
.row-actions form { margin: 0; }
.inline-form { display: inline; margin: 0 0 0 6px; }
.action-link { margin-left: 6px; font-size: 0.8rem; white-space: nowrap; }
.cell-note { display: block; font-size: 0.72rem; color: var(--muted); }
.tag-input { display: flex; margin-bottom: 16px; }
.tag-input input { margin-bottom: 0; border-radius: 0 6px 6px 0; }
.tag-prefix { padding: 10px 10px; background: #eef1f6; border: 1px solid var(--border); border-right: none; border-radius: 6px 0 0 6px; color: var(--muted); font-size: 1rem; white-space: nowrap; }
.tag-prefix:empty { display: none; }
.detail-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px 16px; margin: 0; }
.detail-list dt { color: var(--muted); font-size: 0.78rem; }
.detail-list dd { margin: 2px 0 0; font-weight: 600; word-break: break-word; }
label.plain-label { font-weight: 400; font-size: inherit; margin: 0; cursor: pointer; }

@media (max-width: 640px) {
  /* One swipeable row of report tabs instead of several wrapped rows. */
  .subnav-row { flex-direction: column; align-items: stretch; }
  .subnav-season { order: -1; align-self: stretch; padding: 8px 12px 0; }
  .subnav-season select { flex: 1; min-width: 0; }
  .subnav { padding: 0 12px; flex-wrap: nowrap; overflow-x: auto; }
  .subnav a { white-space: nowrap; }
  .groupnav { padding: 6px 12px; }
  .group-counts { margin-left: 0; }
}


/* My details: the WhatsApp groups someone belongs in, joined or not. */
/* What someone does, one role to a line with the teams it is for. */
.plain-list { list-style: none; margin: 10px 0 0; padding: 0; }
.plain-list li { padding: 4px 0; }

.group-list { list-style: none; margin: 10px 0 0; padding: 0; }
.group-list li { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 8px 0; border-top: 1px solid var(--border); }
.group-list li:first-child { border-top: none; }

/* Availability: a tick per person per game time, and the tick boxes on the
   form someone fills in for a round. */
.grid-table th.slot, .grid-table td.slot { text-align: center; white-space: nowrap; }
.grid-table td.slot .tick { color: var(--good, #157347); font-weight: 700; }
.check-row { display: flex; flex-wrap: wrap; gap: 8px 18px; margin: 8px 0 4px; }
.check-row .checkbox { white-space: nowrap; }

/* The availability form's two notes fields: one each side, meeting in the
   middle, with their boxes lined up however the labels wrap. */
.notes-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; align-items: end; margin-top: 12px; }
@media (max-width: 640px) { .notes-row { grid-template-columns: 1fr; } }
/* What somebody wrote is read here, not edited: a box tall enough to read a
   few lines in, scrolling for the rest, rather than one line cut off. */
.availability-sheet .notes-row textarea { width: 100%; min-height: 110px; max-height: 220px; overflow-y: auto; resize: vertical; }
.availability-sheet .notes-row { align-items: start; }
.availability-sheet .sheet-note > summary { cursor: pointer; margin-bottom: 6px; }
/* The ticks in columns: left to wrap, each line broke somewhere different and
   no two times sat under each other. */
.availability-sheet .check-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 8px 12px; }
.availability-sheet .notes-row { align-items: stretch; }
.availability-sheet .notes-row > div { display: flex; flex-direction: column; }
.availability-sheet .notes-row label { flex: 1 0 auto; display: flex; align-items: flex-end; }
/* A browser draws a disabled tick pale grey on pale grey, and the ticks are
   the whole of what this sheet says - so it draws its own, in the club's red. */
.availability-sheet input[type="checkbox"]:disabled { appearance: none; -webkit-appearance: none; flex: none; width: 16px; height: 16px; margin: 0; border: 1.5px solid #c5ccd6; border-radius: 4px; background: #fff; display: inline-grid; place-content: center; }
.availability-sheet input[type="checkbox"]:disabled:checked { background: var(--primary); border-color: var(--primary); }
.availability-sheet input[type="checkbox"]:disabled:checked::after { content: ""; width: 4px; height: 8px; margin-top: -2px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); }

/* The rounds someone has already sent in, so they can go back and change one. */
.sent-rounds { list-style: none; margin: 0 0 14px; padding: 0; }
.sent-rounds li { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; padding: 6px 0; border-top: 1px solid var(--border); }
.sent-rounds li:first-child { border-top: none; }

/* My details: the sections someone rarely changes open on a click, so the
   page opens on what they're here for. */
.page-section > summary { cursor: pointer; font-size: 1.15rem; font-weight: 700; display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; list-style: none; }
.page-section > summary::-webkit-details-marker { display: none; }
/* A chevron that turns as the section opens, so it reads as something to open. */
.page-section > summary::before { content: '\25B8'; color: var(--primary); font-size: 0.9em; transition: transform 0.15s ease; }
.page-section[open] > summary::before { transform: rotate(90deg); }
.page-section > summary:hover { color: var(--primary); }
.page-section > summary span { font-weight: 400; }
.page-section[open] > summary { margin-bottom: 12px; }
/* A section inside another - About you's - reads as part of it: a rule above
   rather than a card of its own, and a step smaller. */
.sub-section { border-top: 1px solid var(--border); padding: 12px 0 0; margin-top: 12px; }
.sub-section > summary { font-size: 1rem; }
.page-section[open] > .sub-section:first-of-type { margin-top: 0; }

/* The rounds already played, out of the way until someone wants them. */
.sent-past { margin: -6px 0 12px; }
.sent-past > summary { cursor: pointer; color: var(--muted); font-size: 0.88rem; }

/* The tag and invite link at the top of a group, so they're there to copy
   without opening it. */
.group-head { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 10px 0 4px; }
.group-head a { word-break: break-all; }

/* A report's own role tag, beside its heading. */
h1 .heading-tag { font-size: 0.55em; vertical-align: middle; }

/* Joining a report's WhatsApp group: the invite link as two buttons rather
   than a long URL across the intro. */
.whatsapp-line { margin: 14px 0 8px; }
.whatsapp-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }

/* The top bar never makes the page scroll sideways. It was one unbreakable
   row: at 1024 wide an admin's bar ran 50px off the page once Payments joined
   it, and on a phone the title and version pushed the menu button off the
   right edge, out of reach without scrolling across. (The version has since
   gone under the title, small - style.css - so it takes no width at all.)
   From the hamburger's breakpoint up, the links drop to a second row when
   there is no room beside the title, and wrap among themselves below that.
   The long bar - the people who run the club - gives up the signed-in name
   first, which keeps it to one row down to about 950px. */
header.topbar { gap: 8px 20px; }
header.topbar .brand { min-width: 0; }
header.topbar .nav-toggle-btn { flex: none; }
@media (min-width: 641px) {
  header.topbar { flex-wrap: wrap; }
  header.topbar nav { margin-left: auto; max-width: 100%; flex-wrap: wrap; justify-content: flex-end; row-gap: 8px; }
  header.topbar nav > a, header.topbar button.quiet { white-space: nowrap; }
  header.topbar nav .muted-light { max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
@media (min-width: 641px) and (max-width: 1200px) {
  header.topbar nav.nav-long .muted-light { display: none; }
}

/* The setting-up pages behind one top bar item, so the bar stays short. A
   <details> so it works with no JavaScript; /nav-menu.js only closes it when
   you click away. */
header.topbar .nav-menu { position: relative; }
header.topbar .nav-menu > summary {
  cursor: pointer; opacity: 0.85; list-style: none; display: inline-flex; align-items: center; gap: 4px;
}
/* A two-word menu ("Game day") stays on one line with its arrow. */
header.topbar .nav-menu > summary { white-space: nowrap; }
header.topbar .nav-menu > summary::-webkit-details-marker { display: none; }
header.topbar .nav-menu > summary::after { content: '\25be'; font-size: 0.8em; }
header.topbar .nav-menu[open] > summary, header.topbar .nav-menu > summary:hover { opacity: 1; }
header.topbar .nav-menu > summary.active { opacity: 1; text-decoration: underline; }
header.topbar .nav-menu-items {
  position: absolute; top: calc(100% + 12px); left: 0; z-index: 30; min-width: 170px;
  display: flex; flex-direction: column; gap: 12px;
  padding: 14px 18px; background: var(--primary);
  border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 6px;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
}

@media (max-width: 640px) {
  /* The bar is already a column behind the hamburger, so the menu opens in
     place rather than floating over what's under it. */
  header.topbar .nav-menu-items {
    position: static; border: none; box-shadow: none; background: none;
    padding: 12px 0 0 14px; gap: 14px;
  }
}

/* The coach survey's player cards: a form per player, opened one at a time,
   with the ratings as a compact grid rather than five rows of radios. */
.player-card { margin: 10px 0; }
.player-card > summary { display: flex; align-items: center; gap: 10px; }
.player-card textarea { min-height: 60px; }
.player-card input[type="text"], .player-card select, .player-card textarea { margin-bottom: 10px; }
table.ratings { width: auto; margin: 4px 0 14px; }
table.ratings td, table.ratings th { padding: 4px 10px; }
table.ratings input[type="radio"] { margin: 0; }

/* A sortable column heading: the whole label is the link, and an arrow
   shows which column the rows are in the order of. */
table thead th > a { color: inherit; text-decoration: none; white-space: nowrap; }
table thead th > a:hover { text-decoration: underline; }
table thead th.sorted { color: var(--primary); }

/* What a coach said about one player, in the survey's drill-down: enough
   room to read a sentence, not so much that the page scrolls sideways. */
td.player-said { max-width: 420px; white-space: normal; }
td.player-said .badge { font-size: 0.72rem; margin: 0 2px 3px 0; display: inline-block; }
td.player-said div + div { margin-top: 3px; }

/* The coach survey list. A response is two rows: what the coach said, and
   the players underneath it, so opening the players doesn't widen a column
   and shove the rest of the table about. The pair reads as one row. */
tr.survey-row > td { border-bottom: none; padding-bottom: 4px; }
tr.survey-players > td { padding-top: 0; }
tr.survey-players summary { cursor: pointer; color: var(--muted); font-size: 0.85rem; }
td.nowrap { white-space: nowrap; }
/* Room for a sentence, not for the whole width of the table. */
td.team-notes { max-width: 460px; white-space: normal; }

/* Text a button copies, kept in the page but out of sight. Not display:none
   - a hidden field's value can't always be read for the clipboard. */
.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;
}

/* The age groups an Age Group Coordinator looks after: the season's own
   brackets, ticked rather than typed. */
fieldset.age-picker { border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; margin: 0 0 12px; }
fieldset.age-picker legend { font-size: 0.85rem; color: var(--muted); padding: 0 6px; }
fieldset.age-picker label.checkbox { display: inline-flex; align-items: center; gap: 5px; margin: 0 14px 4px 0; }
/* A status badge explains its own rule on hover. */
.report-table span[title] { cursor: help; }

/* A filter that takes several values at once - role on Staff, user type on
   Users. Boxed in as a fieldset it stood taller than the plain dropdowns
   beside it and threw the whole filter row out of line, so it's a <details>
   sized exactly like a select that drops its tick boxes over the page. It
   opens and closes on its own; /nav-menu.js only closes it on a click away. */
.tick-menu { position: relative; }
.tick-menu > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  list-style: none;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
  font-size: 1rem;
  cursor: pointer;
}
.tick-menu > summary::-webkit-details-marker { display: none; }
.tick-menu > summary::after { content: '\25be'; color: var(--muted); font-size: 0.8em; }
.tick-menu[open] > summary { border-color: var(--primary); }
.tick-menu-items {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0;
  min-width: 100%;
  max-height: 300px;
  overflow-y: auto;
  padding: 8px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(28, 36, 48, 0.14);
}
.tick-menu-items .tick {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 6px 8px;
  border-radius: 4px;
  white-space: nowrap;
  font-weight: 400;
  cursor: pointer;
}
.tick-menu-items .tick:hover { background: var(--bg); }
.tick-menu-items .tick input { margin: 0; }
.tick-menu-items .muted { display: block; padding: 6px 8px 2px; }
/* Roles ticked straight off the Staff table, behind a dropdown so seven of
   them don't stretch the column. */
.role-menu { position: relative; }
.role-menu > summary { cursor: pointer; list-style: none; }
/* The column is as wide as the widest single role and no wider: each role
   can't break inside itself, so min-content settles on "Team Manager" and
   anyone holding several wraps to a line each. */
.role-list { display: inline-block; max-width: min-content; }
.role-name { white-space: nowrap; }
.role-position { display: block; font-size: 0.85rem; }
.role-menu > summary::-webkit-details-marker { display: none; }
/* The caret rides along with the last role rather than being an item of its
   own, so it can't be the thing that wraps: the column is then wide enough
   for the widest role and its caret side by side. */
.role-menu .role-list .role-name:last-child::after { content: ' \25BE'; color: var(--muted); }
.role-menu[open] > summary { font-weight: 500; }
.role-menu-items {
  /* /nav-menu.js pins this to the viewport on open, so .table-scroll's
     horizontal scrolling can't clip it. Absolute is the no-JavaScript
     fallback. */
  position: absolute; z-index: 60; left: 0; top: 100%; min-width: 200px;
  background: var(--card); border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 12px; box-shadow: 0 6px 20px rgba(0,0,0,0.12);
}
.role-menu-items label { display: flex; align-items: center; gap: 6px; padding: 3px 0; white-space: nowrap; }
.role-menu-position { display: block; margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--border); font-size: 0.85rem; }
.role-menu-position select { display: block; margin-top: 4px; width: 100%; }

/* Two views of the Staff list. The chosen one reads as pressed, so it is the
   button that says where you are rather than only where you can go. */
.view-switch { display: inline-flex; }
.view-switch .btn { border-radius: 0; }
.view-switch .btn:first-child { border-top-left-radius: 6px; border-bottom-left-radius: 6px; }
.view-switch .btn:last-child { border-top-right-radius: 6px; border-bottom-right-radius: 6px; margin-left: -1px; }
.view-switch .btn.active { background: var(--primary); color: #fff; border-color: var(--primary); position: relative; z-index: 1; }

/* A tick per role per person. The role columns are narrow and centred so the
   eye can run down one of them, which is how allocations are actually set. */
.role-grid th.role-grid-col, .role-grid td.role-grid-col { text-align: center; width: 1%; white-space: nowrap; }
.role-grid td.role-grid-col { padding-left: 6px; padding-right: 6px; }
.role-grid td.role-grid-col input[type="checkbox"] { margin: 0; }
.role-grid-name { white-space: nowrap; }
/* No content-visibility here. It was added with the rest of the speed-up and
   measured as a win, but the measurement was wrong - it timed the window,
   which barely scrolls, while the table scrolls inside .table-scroll. Timed
   properly it is slower every way round (first scroll 11ms against 5ms), and
   it is what made the first scroll stutter: a table cannot skip much layout
   anyway, since the columns have to be sized from every row. */
.role-grid-note { display: block; }
/* Ticked here, cancelled in PlayHQ: the club still wants them in the job, so
   the tick stays and the cross says what PlayHQ has done to it. */
.role-grid-x { color: var(--danger); margin-left: 3px; }
.role-grid td.role-grid-cancelled { background: #fdecea; }

/* A star beside a name: this person is on somebody else's PlayHQ account. A
   solid star means the club has their own details and reaches them directly;
   a hollow one means it has nothing of theirs and is writing to the account
   holder - an empty star for a gap still to fill. */
.on-parent { color: var(--gold); margin-left: 4px; cursor: help; }

/* ---------- Game day: the round's fixture and rosters (views/game-day*.ejs,
   views/_my-roster.ejs). Boys' grades blue and girls' red, as the printed
   roster has always coloured them. ---------- */
.gd { --boys: #1f5bc7; --girls: #cc2638; --split: #3f6fd6; --split-bg: #e3ebfb; --hit: #ffe9b8; }
.badge-split { background: var(--split-bg, #e3ebfb); color: var(--split, #3f6fd6); }
.gd-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin: 4px 0 12px; overflow-x: auto; }
.gd-tabs a { padding: 10px 14px 8px; color: var(--muted); text-decoration: none; font-weight: 600; border-bottom: 3px solid transparent; white-space: nowrap; }
.gd-tabs a.active { color: var(--primary); border-bottom-color: var(--gold); }
.gd-layouts, .gd-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.gd-scroll { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; }
.gd-layouts a, .gd-chip { border: 1px solid var(--border); background: var(--card); border-radius: 999px; padding: 6px 12px; font-size: 0.85rem; font-weight: 600; color: var(--text); text-decoration: none; white-space: nowrap; }
.gd-layouts a.active, .gd-chip.active { background: var(--primary); border-color: var(--primary); color: #fff; }
.gd-chip span { opacity: 0.65; font-weight: 400; margin-left: 3px; }
.gd-search { width: 100%; max-width: 520px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 16px; margin-bottom: 14px; background: var(--card); }
/* The × that empties a search box (public/search-clear.js): 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; }
.search-clear-wrap:has(> .gd-search) { max-width: 520px; }
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); }
/* A bigger target than it looks, for a thumb. */
.search-clear::before { content: ''; position: absolute; inset: -10px; }
.gd-note { margin-top: 12px; }

.gd-grade { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; display: inline-flex; align-items: center; gap: 5px; }
.gd-grade::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.gd-grade.boys { color: var(--boys, #1f5bc7); }
.gd-grade.girls { color: var(--girls, #cc2638); }
.gd-list { padding: 0; overflow: hidden; }
.gd-slot { margin-bottom: 14px; }
.gd-slot-h, .gd-age-h { position: sticky; top: 0; z-index: 2; background: var(--bg); margin: 0; padding: 8px 2px 6px; font-size: 1.35rem; display: flex; gap: 10px; align-items: baseline; font-variant-numeric: tabular-nums; }
.gd-slot-h small, .gd-time small, .gd-when small { font-size: 0.6em; text-transform: uppercase; margin-left: 1px; }
.gd-slot-h span, .gd-age-h span { font-size: 0.85rem; font-weight: 400; color: var(--muted); }
.gd-game, .gd-duty { display: grid; grid-template-columns: 72px 1fr; gap: 12px; padding: 11px 14px; border-top: 1px solid var(--border); }
.gd-list > .gd-game:first-child, .gd-list > .gd-court-h + .gd-game { border-top: 0; }
.gd-loc { font-weight: 700; line-height: 1.1; }
.gd-loc small { display: block; font-weight: 400; color: var(--muted); font-size: 0.8rem; }
.gd-time { font-weight: 700; font-size: 1.1rem; font-variant-numeric: tabular-nums; }
.gd-home { font-weight: 600; }
.gd-away { color: var(--muted); }
.gd-courts { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.gd-court-h { margin: 0; padding: 10px 14px; background: var(--bg); border-bottom: 1px solid var(--border); font-size: 1rem; display: flex; justify-content: space-between; align-items: baseline; }
.gd-court-h span { font-size: 0.8rem; font-weight: 400; color: var(--muted); }
.gd-team { display: grid; grid-template-columns: 1fr auto; gap: 4px 12px; padding: 12px 14px; border-top: 1px solid var(--border); align-items: center; }
.gd-list > .gd-team:first-child { border-top: 0; }
.gd-when { font-weight: 700; font-size: 1.25rem; font-variant-numeric: tabular-nums; }
.gd-where { grid-column: 1 / -1; color: var(--muted); font-size: 0.88rem; }
.gd-where strong { color: var(--text); font-weight: 600; }

.gd-cards { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.gd-person { padding: 0; overflow: hidden; margin: 0; }
.gd-person-h { display: flex; justify-content: space-between; gap: 10px; padding: 12px 14px 10px; border-bottom: 1px solid var(--border); }
.gd-person-h h3 { margin: 0; font-size: 1.1rem; }
.gd-count { flex: none; min-width: 42px; height: 42px; border-radius: 10px; background: var(--primary); color: #fff; display: grid; place-items: center; font-weight: 700; font-size: 1.15rem; line-height: 1; padding-top: 4px; }
.gd-count small { display: block; font-size: 0.55rem; text-transform: uppercase; font-weight: 600; letter-spacing: 0.05em; }
.gd-person .gd-duty:first-of-type { border-top: 0; }
.gd-break { padding: 5px 14px; font-size: 0.78rem; color: var(--muted); border-top: 1px dashed var(--border); background: var(--bg); }

.gd-grid-card .table-scroll { max-height: 78vh; }
table.gd-grid { border-collapse: separate; border-spacing: 0; font-size: 0.82rem; }
.gd-grid th, .gd-grid td { border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); padding: 6px 8px; text-align: left; vertical-align: top; }
.gd-grid thead th { position: sticky; top: 0; background: var(--primary); color: #fff; z-index: 3; white-space: nowrap; }
.gd-grid thead tr:nth-child(2) th { top: 31px; background: #7d1612; font-weight: 600; }
.gd-grid th.gd-t { position: sticky; left: 0; z-index: 4; white-space: nowrap; }
.gd-grid tbody th.gd-t { background: var(--bg); color: var(--text); z-index: 2; font-variant-numeric: tabular-nums; }
.gd-grid td { min-width: 130px; }
.gd-grid td.gd-none { background: repeating-linear-gradient(135deg, transparent 0 6px, var(--bg) 6px 12px); }
.gd-n { display: block; font-weight: 600; white-space: nowrap; }
.gd-grid td.gd-hit { background: var(--hit, #ffe9b8); }
.gd-grid td.gd-dim { opacity: 0.35; }
/* The referees' grid by venue: a name typed lights the games it is on. */
.gd-blk.gd-ref-game .gd-n { display: block; font-size: 0.74rem; font-weight: 600; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gd-blk.gd-ref-game.gd-hit { box-shadow: 0 0 0 3px var(--hit, #ffcf5c); }
.gd-blk.gd-ref-game.gd-dim { opacity: 0.35; }
.gd-cov td { min-width: 56px; text-align: center; font-weight: 700; }
.gd-cov td small { display: block; font-size: 0.68rem; font-weight: 600; opacity: 0.8; }
.gd-cov td.gd-ok { background: #eaf7ee; color: #1e7a3c; }
.gd-cov td.gd-warn { background: #fff3d6; color: #92620a; }
.gd-cov td.gd-bad { background: #fdecea; color: var(--danger); }
.gd-cov td.gd-supers { border-left: 2px solid var(--border); font-weight: 600; }
.gd-cov td.gd-zero { color: var(--muted); font-weight: 400; }

.gd-venue { padding: 0; margin-bottom: 16px; }
.gd-venue-h { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 12px 14px; border-bottom: 1px solid var(--border); }
.gd-venue-h h3 { margin: 0; }
.gd-lanes { display: grid; padding: 10px 12px 14px 0; }
.gd-lane-h { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); text-align: center; padding: 0 4px 6px; }
.gd-axis, .gd-lane { position: relative; }
.gd-axis span { position: absolute; right: 8px; transform: translateY(-50%); font-size: 0.72rem; color: var(--muted); white-space: nowrap; }
.gd-lane { border-left: 1px solid var(--border); margin-left: 6px; }
.gd-rule { position: absolute; left: 0; right: 0; border-top: 1px dashed var(--border); }
/* The games beside the roster, a column per court, coloured by grade. Past
   two columns the venue scrolls sideways inside itself, never the page. */
.gd-lanes-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.gd-lane-h.gd-court-col { color: var(--text, #222); }
.gd-blk.gd-gm { background: #eef3fc; border-color: #b9cdf0; border-left: 3px solid var(--boys, #1f5bc7); padding: 3px 6px; }
.gd-blk.gd-gm.girls { background: #fdeef0; border-color: #f1bcc3; border-left-color: var(--girls, #cc2638); }
.gd-blk.gd-gm .gd-grade { font-size: 0.64rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; }
/* A supervisor's shift: a box in their colour around the games it covers,
   their name across the top. The games sit inside it, inset. */
.gd-lane-h small { display: block; font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--muted); }
.gd-sup-box { position: absolute; left: 2px; right: -2px; border: 2px solid #d98a3d; background: #fff1e3; border-radius: 9px; z-index: 0; }
.gd-sup-box.gd-split { border-color: var(--split, #3f6fd6); background: var(--split-bg, #e3ebfb); }
.gd-sup-name { padding: 1px 7px; font-size: 0.74rem; line-height: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #7a3d0c; }
.gd-sup-box.gd-split .gd-sup-name { color: var(--split, #3f6fd6); }
.gd-sup-name small { color: var(--muted); font-size: 0.66rem; }
.gd-blk.gd-gm { z-index: 1; }
.gd-blk.gd-gm.in-shift { left: 8px; right: 4px; }
/* The times stay put while a wide venue scrolls sideways. */
.gd-lanes-scroll .gd-axis, .gd-lanes-scroll .gd-lanes > div:first-child { position: sticky; left: 0; z-index: 2; background: var(--card, #fff); }
.gd-blk.gd-gm small { color: var(--text, #333); font-size: 0.68rem; line-height: 1.15; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gd-blk { position: absolute; left: 4px; right: 0; border-radius: 7px; padding: 4px 7px; background: #fdf0e6; border: 1px solid #f3c8a5; overflow: hidden; line-height: 1.2; font-size: 0.82rem; }
.gd-blk strong, .gd-blk small { display: block; }
/* Where each game starts inside a shift, so a long stint reads as its games. */
.gd-blk-rule { position: absolute; left: 0; right: 0; height: 0; border-top: 1px dashed #b8bec7; pointer-events: none; }
.gd-blk strong, .gd-blk small { position: relative; }
.gd-blk small { color: var(--muted); font-size: 0.72rem; }
/* The roster by venue (the president, 4 October 2026): green is somebody
   rostered, amber is a game time nobody is rostered for. A split shift keeps
   its blue edge on the green. */
.gd-blk.gd-on { background: #e9f6ec; border-color: #9fd3ad; color: #14532d; }
.gd-blk.gd-on small { color: #2f6b45; }
.gd-blk.gd-open { background: #fff4d6; border: 1px dashed #d99a1c; color: #7a4b00; }
.gd-blk.gd-open small { color: #8a5a0a; }
.gd-blk.gd-split { background: var(--split-bg, #e3ebfb); border-color: var(--split, #3f6fd6); }
.gd-blk.gd-on.gd-split { background: #e9f6ec; border-color: var(--split, #3f6fd6); border-left-width: 3px; }
.gd-blk.gd-oc { background: repeating-linear-gradient(135deg, #eaf7ee 0 5px, transparent 5px 10px); border: 1px dashed #1e7a3c; color: #1e7a3c; font-size: 0.66rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; display: grid; place-items: center; padding: 0; }

.gd-shiftrow { display: grid; grid-template-columns: 84px 1fr; gap: 12px; padding: 12px 14px; border-top: 1px solid var(--border); }
.gd-list > .gd-shiftrow:first-child { border-top: 0; }
.gd-arrive { font-weight: 700; font-size: 1.2rem; line-height: 1.1; font-variant-numeric: tabular-nums; }
.gd-arrive small { font-size: 0.6em; text-transform: uppercase; }
.gd-arrive i { display: block; font-style: normal; font-size: 0.65rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin-bottom: 2px; }
.gd-shiftrow h4, .gd-prow h4 { margin: 0 0 2px; }
.gd-ln { font-size: 0.88rem; color: var(--muted); display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; margin-top: 2px; }
.gd-ln strong { color: var(--text); font-variant-numeric: tabular-nums; }

.gd-duty-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.gd-od { padding: 0; margin: 0; overflow: hidden; }
.gd-od.gd-closed { opacity: 0.6; }
.gd-od-h { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; padding: 11px 14px; border-bottom: 1px solid var(--border); }
.gd-od-h h3 { margin: 0; font-size: 1rem; }
.gd-od-row { display: flex; justify-content: space-between; gap: 10px; padding: 9px 14px; border-top: 1px solid var(--border); }
.gd-od-row:first-of-type { border-top: 0; }
.gd-od-r { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
.gd-od-p { text-align: right; font-weight: 600; }
.gd-od-p small { display: block; font-weight: 400; color: var(--muted); }

.gd-info { margin-top: 20px; }
.gd-info h3 { margin-top: 0; }
.gd-contacts { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); margin-bottom: 12px; }
.gd-rules p { margin: 0 0 8px; max-width: 65ch; }

.gd-issue { display: grid; grid-template-columns: 56px 1fr; gap: 10px; padding: 10px 0; border-top: 1px solid var(--border); align-items: start; }
.gd-issue p { margin: 0; }
.gd-issue .badge { justify-self: start; }
.gd-prow { display: grid; grid-template-columns: 1fr auto; gap: 8px 12px; padding: 12px 16px; border-top: 1px solid var(--border); align-items: center; }
.gd-list > .gd-prow:first-child { border-top: 0; }

/* Your roster, on My details. */
/* Three small boxes side by side, even on a phone - the report cards'
   two-line heading and wide minimum would stack them one to a row. */
#roster .gd-me-stats { margin-bottom: 12px; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr)); gap: 8px; }
#roster .gd-me-stats .stat-card { padding: 8px 10px; }
#roster .gd-me-stats .stat-label { min-height: 0; }
#roster .gd-me-stats .stat-value { font-size: 1.2rem; }
.gd-strip { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px; }
.gd-strip span { font-size: 0.8rem; padding: 4px 8px; border-radius: 6px; border: 1px solid var(--border); color: var(--muted); font-variant-numeric: tabular-nums; }
.gd-strip span.on { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.gd-myduty { display: grid; grid-template-columns: 96px 1fr; gap: 12px; padding: 12px 0; border-top: 1px solid var(--border); }
.gd-mytime { font-weight: 700; font-variant-numeric: tabular-nums; }
.gd-mytime small { display: block; font-weight: 400; color: var(--muted); font-size: 0.8rem; }
.gd-myduty h4 { margin: 0 0 2px; }
.gd-myduty p { margin: 2px 0 0; font-size: 0.9rem; }
.gd-mynotes { background: var(--bg); border-radius: 8px; padding: 10px 12px; margin: 12px 0; font-size: 0.9rem; }
.gd-mynotes p { margin: 0 0 6px; }
.gd-mynotes p:last-child { margin-bottom: 0; }

@media (max-width: 600px) {
  .gd-game, .gd-duty { grid-template-columns: 60px 1fr; }
  .gd-myduty { grid-template-columns: 76px 1fr; }
  .gd-shiftrow { grid-template-columns: 70px 1fr; }
  .gd-prow { grid-template-columns: 1fr; }
}
/* Their own teams, at the top of Find a team. */
.gd-mine .card { border-left: 3px solid var(--gold); }
.gd-mine { margin-bottom: 8px; }

/* My details, grouped by job: a heading per role group, and the one-line
   facts (teams, age groups) that sit under it before its sections. */
.role-group { margin-top: 20px; }
.role-group-heading { font-size: 1.05rem; margin: 0 0 8px; display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.role-group-line { margin: 0 0 12px; }
.register-first .btn { display: inline-block; }

/* Report a problem: a member's tickets and the conversation on one. The
   club's messages sit on the left and theirs on the right, as a chat does. */
.help-tickets { display: grid; gap: 10px; }
.help-ticket-row { display: block; color: inherit; text-decoration: none; }
.help-ticket-row:hover { border-color: currentColor; }
.help-ticket-top { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.help-ticket-desc { margin-top: 6px; }
.help-conversation { display: flex; flex-direction: column; gap: 10px; margin: 16px 0 20px; max-width: 720px; }
.help-message { border-radius: 10px; padding: 10px 14px; max-width: 85%; border: 1px solid rgba(0, 0, 0, 0.12); }
.help-message-club { align-self: flex-start; background: #fff4e5; }
.help-message-member { align-self: flex-end; background: #eef3fb; }
.help-message-head { margin-bottom: 4px; }
.help-message-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; }

/* A ticket's status beside its number, sized to the heading. */
h1 .help-status { font-size: 0.8rem; vertical-align: middle; margin-left: 6px; }

/* 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; }

/* Activity's kinds of event, in the Staff page's tick menu. It sits beside the
   search box rather than under it, so the row reads as one set of filters. */
.event-pick { display: flex; flex-direction: column; gap: 2px; flex: 0 1 220px; }
.event-pick .tick-menu > summary { padding: 9px 12px; }

/* The roster editor: times across, a row for each venue's duty. Each cell is a
   picker, so the columns are wide enough for a name and narrow enough that
   five venues still fit on a laptop. A cell the club has to sort out is
   coloured rather than badged - at a glance, the grid is the report. */
.roster-grid td { vertical-align: top; min-width: 170px; }
/* A time's heading is a button: pressing it lists who said they could do it,
   above the grid, and tints that time's column. */
.roster-grid .roster-time { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 0; text-decoration: none; font: inherit; font-weight: 600; color: inherit; text-transform: inherit; letter-spacing: inherit; white-space: nowrap; cursor: pointer; }
.roster-grid .roster-time:hover, .roster-grid .roster-time[aria-expanded="true"] { color: var(--primary); }
.roster-grid .roster-time .small { text-transform: none; letter-spacing: 0; font-weight: 400; }
.roster-grid .at-time { box-shadow: inset 2px 0 0 var(--primary), inset -2px 0 0 var(--primary); }
/* The grid drawn as the roster reads (the president, 5 October 2026): one
   person's run along a row is one box, its first block carrying the name in
   full weight and the rest lighter; a block with nobody is amber; a time the
   job isn't on is hatched. The page's script sets the classes, so without it
   the cells are the plain pickers they were. */
.roster-grid.is-boxed td { padding: 5px 0; min-width: 124px; width: 124px; max-width: 124px; }
.roster-grid.is-boxed .cell-name { padding-left: 8px; font-size: 0.92rem; }
.roster-grid.is-boxed .cell-copy { padding: 0 5px; }

.roster-grid .cell-box { display: flex; align-items: center; height: 40px; border: 2px solid transparent; box-sizing: border-box; }
.roster-grid .cell-name { flex: 1; min-width: 0; width: auto; height: 100%; margin: 0; padding: 0 2px 0 10px; border: 0; border-radius: 0; background: transparent; box-shadow: none; font: inherit; color: inherit; text-overflow: ellipsis; }
.roster-grid .cell-name:focus { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; background: #fff; color: var(--text, #1f2937); }
.roster-grid .cell-name::placeholder { color: #7a6400; opacity: 0.8; }
/* The arrow at a block's edge: the same person at the next time. */
.roster-grid .cell-copy { flex: none; align-self: stretch; padding: 0 7px; margin: 0; font-size: 1.15rem; line-height: 1; text-decoration: none; color: #94a3b8; }
.roster-grid .cell-copy:hover, .roster-grid .cell-copy:focus-visible { color: var(--primary); background: #f1f5f9; }
.roster-grid.is-boxed td.is-on .cell-box { border-color: #94a3b8; border-left-width: 0; border-right-width: 0; background: #fff; color: #64748b; }
.roster-grid.is-boxed td.is-on.run-start .cell-box { border-left-width: 2px; border-radius: 8px 0 0 8px; font-weight: 700; color: var(--primary); }
.roster-grid.is-boxed td.is-on.run-end .cell-box { border-right-width: 2px; border-top-right-radius: 8px; border-bottom-right-radius: 8px; }
.roster-grid.is-boxed td.is-on.run-start { padding-left: 4px; }
.roster-grid.is-boxed td.is-on.run-end { padding-right: 4px; }
.roster-grid.is-boxed td.is-open { background: #fdf3a7; padding-left: 4px; padding-right: 4px; }
.roster-grid.is-boxed td.is-open .cell-box { border: 1px dashed #c9a400; border-radius: 8px; color: #7a6400; }
.roster-grid.is-boxed td.is-out { background: repeating-linear-gradient(135deg, #eef0f4 0 8px, #e3e6ec 8px 16px); }
.roster-grid.is-boxed td.is-out > span { visibility: hidden; }
.roster-grid.is-boxed td.cell-bad.is-on .cell-box { border-color: #c0392b; background: #fdecea; }
.roster-grid.is-boxed td.cell-warn.is-on .cell-box { border-color: #d9a400; background: #fff8e6; }
.roster-grid td[data-note]::after { content: attr(data-note); display: block; padding: 2px 8px 0; font-size: 0.75rem; line-height: 1.25; color: #b42318; }
/* Why somebody the draft placed was taken off: asked under the block. */
.roster-grid .cell-why-ask { display: block; width: calc(100% - 8px); margin: 4px 4px 0; padding: 4px 8px; font-size: 0.8rem; border: 1px solid #d9a400; border-radius: 6px; background: #fffdf2; }
.roster-grid .cell-why-kept { padding: 2px 8px 0; font-size: 0.75rem; color: #157347; }
/* One person's day along the top of the grid: what they said for each time. */
/* It stays under the times as the grid scrolls: --head-h is the time row's
   height, measured by the page's script. */
.roster-grid tr.roster-who th, .roster-grid tr.roster-who td { position: sticky; top: var(--head-h, 44px); z-index: 1; background: var(--card); padding: 6px 10px; font-size: 0.8rem; text-transform: none; letter-spacing: 0; white-space: nowrap; border-bottom: 2px solid var(--border); }
.roster-grid tr.roster-who th.slot { left: 0; z-index: 3; box-shadow: inset -1px 0 0 var(--border); }
.roster-grid tr.roster-who .roster-who-name { font-weight: 700; color: var(--primary); }
.roster-grid tr.roster-who td.who-yes { background: #e9f6ec; color: #157347; font-weight: 600; }
.roster-grid tr.roster-who td.who-warn { background: #fff8e6; color: #7a4b00; }
/* Their own game then (JPI-137): the availability form's amber, with the venue. */
.roster-grid tr.roster-who td.who-game { background: #fdf3a7; color: #7a4b00; font-weight: 600; white-space: normal; line-height: 1.25; }
.roster-grid tr.roster-who td.who-no { background: #f1f3f6; color: #98a2b3; }
.roster-grid tr.roster-who td.who-none { color: var(--muted); font-style: italic; }
/* The matches under the block being typed in. */
.cell-suggest { position: fixed; z-index: 40; width: 300px; max-width: calc(100vw - 16px); margin: 0; padding: 4px; list-style: none; overflow-y: auto; background: var(--card); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18); }
.cell-suggest li { padding: 4px 10px; border-radius: 6px; }
.cell-suggest li[data-value] { cursor: pointer; }
.cell-suggest-top { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
/* Where they already are that day, under the name. */
.cell-suggest-on { font-size: 0.76rem; line-height: 1.3; color: var(--muted); }
.cell-suggest-on.is-on { color: #7a4b00; }
.cell-suggest li[data-value]:hover, .cell-suggest li.is-lit { background: #f1f5f9; }
.cell-suggest-tag { font-size: 0.78rem; color: var(--muted); text-align: right; }
.cell-suggest-level { display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 999px; background: #eef1f6; color: #33415c; font-size: 0.74rem; font-weight: 600; vertical-align: 1px; }
.cell-suggest-head { font-size: 0.75rem; color: var(--muted); border-top: 1px solid var(--border); border-radius: 0; margin-top: 4px; }
.cell-suggest-head:first-child { border-top: 0; margin-top: 0; }
.cell-suggest-anyway { color: #7a4b00; }
.cell-suggest-none { color: var(--muted); }
.cell-suggest-move .cell-suggest-tag { color: #7a4b00; }
.roster-grid .cell-said { padding: 2px 8px 0; }
/* Why the draft put somebody there: folded under the shift's first block.
   Opened it runs the width of the shift - a percentage width adds nothing to
   the cell's own width, so it lies across the blocks after it, which hold
   nothing under their pickers. */
.roster-grid .cell-why { padding: 2px 8px 0; font-size: 0.8rem; color: var(--muted, #64748b); }
.roster-grid .cell-why > summary { cursor: pointer; width: max-content; }
.roster-grid .cell-why > div { width: calc(var(--span, 1) * 100%); min-width: 100%; padding: 2px 0 4px; line-height: 1.35; }
.roster-at { margin: 12px 0; padding: 12px 14px; border: 1px solid var(--border); border-left: 4px solid var(--primary); border-radius: 10px; background: var(--card); }
.roster-at p { margin: 0 0 6px; }
.roster-at-list { display: flex; flex-wrap: wrap; gap: 4px 18px; margin: 0 0 10px; }
.roster-at-list li { margin: 0; }
.roster-grid select { width: 100%; max-width: 240px; }
.roster-grid th.slot { white-space: nowrap; font-weight: 600; text-align: left; }
/* The banner on a game-day page showing the draft: it prints, so a page
   printed from the draft says so. */
.notice-draft { border-left: 4px solid #d9a400; }
/* The build row: which past round to start from, then the buttons. The site's
   selects are full width, which would take the row to itself. */
.build-draft { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.build-draft select { width: auto; max-width: 100%; }
/* Games per person, under the grid: the counts line up down the column. */
#games-per-person th.num, #games-per-person td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
#games-per-person td { vertical-align: top; }
/* The time stays put while the columns scroll under it: with five venues the
   grid is far wider than the window, and half way across nobody could tell
   which block a cell was. The corner sits above both the header row, which
   already sticks to the top, and the times. */
.roster-grid tbody th.slot { position: sticky; left: 0; z-index: 1; background: var(--card); box-shadow: inset -1px 0 0 var(--border); }
.roster-grid thead th:first-child { left: 0; z-index: 2; box-shadow: inset 0 -1px 0 var(--border), inset -1px 0 0 var(--border); }
.roster-grid td.cell-bad { background: #fdecea; }
.roster-grid td.cell-warn { background: #fff8e6; }

/* The message the club posts to the supervisors' group when a roster goes out:
   wide enough to read as it will be read, and editable in place. */
.roster-message { width: 100%; max-width: 640px; font: inherit; padding: 10px 12px; }

/* A money box with its dollar sign beside it rather than floating above: the
   sign is part of the field, so they sit on one line and the boxes down a
   column line up with each other. */
.money-field { display: inline-flex; align-items: center; gap: 4px; }
.money-field input { width: 7em; text-align: right; }
.money-field > span { color: var(--muted, #667); }

/* Somebody paid their own rate: who, then how much, on one line. */
.override-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.override-row select { min-width: 16em; }

/* One of Claude's suggestions on a draft roster: what it would do, and the
   button that does it. Set apart so a proposal never reads as a change that
   has already happened. */
.suggestion { border-left: 3px solid var(--line, #dde); padding: 2px 0 2px 12px; margin: 12px 0; }
.suggestion p { margin: 0 0 4px; }
.suggestion form { margin-top: 4px; }

/* A job on the day, with who does it and what it is worth beside it, so
   naming somebody is a dropdown rather than deleting the job and typing it
   back in. */
.duty-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 6px 0; }
/* One fixed column for the job's name, whatever its length, so every row's
   controls line up; on a narrow screen the row wraps under it instead. */
.duty-row > div:first-child { flex: 0 0 24em; max-width: 100%; }
/* Who, how much and Save wrap onto a second line where the card is narrow:
   as one unbreakable row they were 438px wide, and made the whole roster
   page scroll sideways on a phone. */
.duty-edit { display: flex; align-items: center; gap: 8px; margin: 0; flex-wrap: wrap; max-width: 100%; }
/* Its own width, not the form's: at 100% it took a line to itself once the
   form could wrap. */
.duty-edit select { width: auto; min-width: 14em; max-width: 100%; flex: 0 1 auto; }

/* Reminder emails from To chase: a ticked list of people, then a card per draft. */
.remind-list { list-style: none; margin: 8px 0 16px; padding: 0; }
.remind-list li { padding: 8px 0; border-top: 1px solid var(--border); }
.remind-list li:first-child { border-top: none; }
.remind-list label.checkbox { align-items: flex-start; }
.remind-list label.checkbox input { margin-top: 4px; }
.remind-list label.checkbox > span { display: flex; flex-direction: column; gap: 3px; }
.remind-jobs { display: flex; flex-wrap: wrap; gap: 4px; }
.remind-draft label.checkbox { margin-bottom: 10px; }
.remind-draft textarea { width: 100%; font: inherit; }
/* New volunteers on Staff: the role picker under each person, full width on
   a phone, beside them on a wider screen. */
.new-volunteers li { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; justify-content: space-between; }
.new-volunteers label.checkbox { flex: 1 1 260px; }
.new-volunteers select { flex: 0 1 220px; margin: 0; }
@media (max-width: 640px) { .new-volunteers select { flex: 1 1 100%; } }
.flash-link { font-weight: 600; white-space: nowrap; }

/* Paging a long table. Buttons rather than bare links, so the row reads as one
   control; the page you are on is filled in the way the view switch fills its
   own current button (that rule is scoped to .view-switch, so this repeats it
   rather than sharing it). */
.pager { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 12px 0 4px; }
.pager .btn { padding: 6px 12px; }
.pager .btn.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.pager-off { opacity: 0.45; pointer-events: none; }
.pager-gap { padding: 0 2px; color: var(--muted); }
.pager-note { margin: 0 0 12px; }

/* What somebody typed on their PlayHQ registration, under their name on the
   Roles grid. Gold rather than grey: it is the one thing on that row telling
   the club which box to tick, so it has to be findable down a long list. */
.role-grid-said { color: var(--gold); }

/* A Roles cell with a tick in it that hasn't been saved. The summary is
   rewritten as you tick (nav-menu.js), and this says the row is waiting on
   Save roles - otherwise a changed cell looks identical to a saved one. */
.role-menu-unsaved > summary { color: var(--gold); font-weight: 600; }
.role-menu-unsaved > summary::after { content: " · unsaved"; font-weight: 400; }

/* Import & lists: the imports kept for a rainy day, folded away at the foot. */
/* The season fixture's venues, under the imports: a venue, its address and
   its two buttons - a row that wraps on a narrow screen, never widens it. */
.fixture-venues { margin-top: 24px; }
.fixture-venues > summary { cursor: pointer; }
.fixture-venues[open] > summary { margin-bottom: 8px; }
.venue-address { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.venue-address .venue-name { flex: 0 1 300px; }
.venue-address input[type="text"] { flex: 1 1 280px; width: auto; margin: 0; }
.emergency-imports { margin-top: 24px; }
.emergency-imports > summary { cursor: pointer; }
.emergency-imports[open] > summary { margin-bottom: 8px; }

/* A small form inside a row's action menu: a label, a choice and its button. */
.action-menu-form { display: flex; flex-direction: column; gap: 4px; padding: 6px 10px; border-top: 1px solid var(--border); }
.action-menu-form select { max-width: 240px; }

/* The To do inbox (views/todo.ejs) and its badge in the header. */
.nav-badge { display: inline-block; min-width: 1.4em; padding: 0 6px; border-radius: 999px; background: var(--gold, #f5b400); color: #1c1c1c; font-size: 0.72rem; font-weight: 700; line-height: 1.5; text-align: center; vertical-align: 1px; }
.todo-group h2 { margin: 0 0 8px; font-size: 1.1rem; }
.todo-list { list-style: none; margin: 0; padding: 0; }
.todo-item { display: flex; align-items: baseline; gap: 12px; padding: 6px 0; border-top: 1px solid var(--line, #e6e9ef); }
.todo-item:first-child { border-top: 0; }
.todo-count { min-width: 2.5em; text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }
.staff-seen { margin-left: 6px; }
.training-said { margin-top: 2px; }

/* Supervisor training on My details: the three ways in, and the video. */
.training-way { margin: 18px 0 6px; font-size: 1rem; }
.training-video { position: relative; width: 100%; max-width: 720px; aspect-ratio: 16 / 9; margin: 8px 0 12px; background: #000; border-radius: 6px; overflow: hidden; }
.training-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.quiz-question { margin: 0 0 18px; }
.quiz-question legend { font-weight: 600; margin-bottom: 6px; }
.quiz-question label { display: block; padding: 4px 0; }
.quiz-question input { margin-right: 8px; }
.question-bank td { vertical-align: top; }
/* The question is the long text; the topic and the time are short. */
.question-bank th:first-child, .question-bank td:first-child { width: 62%; }
.question-bank td:nth-child(2) input[type="text"]:first-child { width: 100%; }
.question-bank .answers { margin: 4px 0 0; padding-left: 18px; }
/* Save over Remove, the same width, a gap between - not two buttons wrapping
   however the column happens to fall. */
.question-bank td:last-child { white-space: nowrap; }
.question-bank td:last-child button { display: block; width: 100%; margin: 0 0 6px; }

/* The tick column on a role report, and the bar that acts on the ticked. */
.report-table .tick-cell { width: 1.5em; padding-right: 0; }
/* The ticked rows' buttons: the label above, the two buttons side by side at
   one width, the hint below - lined up at any width (the president, 2 October
   2026), rather than wrapping one button onto a line of its own. */
.bulk-bar { display: grid; grid-template-columns: repeat(2, minmax(0, 12rem)); gap: 10px; align-items: center; margin-top: 12px; }
.bulk-bar > .muted { grid-column: 1 / -1; margin: 0; }
.bulk-bar > button { width: 100%; margin: 0; }
@media (max-width: 640px) { .bulk-bar { grid-template-columns: 1fr 1fr; } }

/* An email as it would arrive, on the Emails page. */
.email-preview { border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; background: #fafbfc; }
.email-preview pre { white-space: pre-wrap; font: inherit; margin: 0; }

/* Ask for availability: the people to tick beside the email as it will go,
   one under the other on a phone. */
.ask-layout { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 16px; align-items: start; }
.ask-layout .card { margin-top: 0; }
.ask-layout .table-scroll table.ask-table { min-width: 0; }
.ask-table th:first-child, .ask-table td:first-child { width: 1%; }
.ask-table td:nth-child(2), .ask-table td:nth-child(3) { white-space: nowrap; }
.ask-table th label.checkbox { margin: 0; }
/* What the sender types to add to this send, and where it shows in the
   email beside it. */
.remind-add textarea { width: 100%; }
/* What came across from the picker, said back on the drafts page (JPI-89). */
.remind-carried p { margin: 8px 0 4px; }
.remind-quote { margin: 0 0 6px; padding: 8px 12px; border-left: 3px solid var(--border); background: #f8fafc; white-space: pre-wrap; font-size: 0.92rem; }
.ask-note { margin-bottom: 14px; }
.ask-note textarea { width: 100%; }
.ask-note-preview { background: #fff4c2; border-radius: 3px; }
@media (max-width: 900px) { .ask-layout { grid-template-columns: 1fr; } }

/* The note typed for a denial, in a row's action menu. */
.action-menu-note { display: block; padding: 6px 10px 2px; border-top: 1px solid var(--border); margin-top: 4px; }
.action-menu-note input { display: block; width: 100%; min-width: 220px; margin-top: 3px; }

/* One go at the quiz, question by question (the club's view). */
.quiz-result { border-left: 4px solid var(--border); }
.quiz-result.quiz-right { border-left-color: #1e7a3c; }
.quiz-result.quiz-wrong { border-left-color: #b3261e; }
.quiz-answers li { padding: 2px 0; }
.quiz-answers li.quiz-correct { font-weight: 600; }

/* Shift sheets: a supervisor's start, checklist and end of shift, built for
   a phone held in one hand - big targets, one column. */
.shift-sheet { max-width: 560px; }
.shift-sheet h2 { font-size: 1rem; margin: 0 0 6px; }
.shift-sheet h2 .muted, .shift-sheet label .muted { font-weight: 400; }
.shift-big { display: block; width: 100%; padding: 14px; font-size: 1.05rem; }
.shift-ok { background: #eaf7ee; color: #1e7a3c; border-radius: 8px; padding: 10px 14px; margin-bottom: 12px; font-weight: 600; }
.shift-remind { background: #eef2f7; border-radius: 8px; padding: 10px 14px; margin-bottom: 12px; font-size: 0.92rem; }
.shift-section { padding-bottom: 8px; }
.shift-tick { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; border-top: 1px solid var(--border); font-weight: 400; cursor: pointer; }
.shift-tick input[type="checkbox"] { width: 22px; height: 22px; flex: none; margin: 1px 0 0; }
.shift-tick small { display: block; color: var(--muted); }
.shift-tick.done-by, .shift-tick.read { cursor: default; }
.shift-mark { flex: none; width: 22px; height: 22px; border: 1px solid var(--border); border-radius: 4px; text-align: center; line-height: 20px; font-size: 0.85rem; }
.shift-mark.on { background: var(--primary); border-color: var(--primary); color: #fff; }
.shift-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.shift-tag { margin: 0; font-weight: 400; cursor: pointer; }
.shift-tag input { position: absolute; opacity: 0; width: 0; height: 0; }
.shift-tag span { display: inline-block; border: 1px solid var(--border); border-radius: 999px; padding: 6px 14px; font-size: 0.9rem; background: var(--card); }
.shift-tag input:checked + span { background: #fdecea; border-color: var(--danger); color: var(--danger); font-weight: 600; }
.shift-tag input:focus-visible + span { outline: 2px solid var(--primary); outline-offset: 2px; }
.shift-sheet textarea, .shift-sheet input[type="text"], .shift-sheet input[type="time"] { width: 100%; font-size: 1rem; }
.shift-late { margin-top: 12px; }
.shift-late > summary { cursor: pointer; color: var(--primary); }
.shift-saved { min-height: 1.2em; margin: 0 0 8px; }
.shift-note { white-space: pre-wrap; margin: 0; }
.shift-venue > summary { cursor: pointer; font-weight: 600; }
.shift-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 0; border-top: 1px solid var(--border); }
.shift-row .btn { flex: none; }
.checklist-table input[type="text"] { width: 100%; min-width: 220px; }
.checklist-table td.center { text-align: center; }
.shift-club { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; align-items: start; }
.shift-club .shift-sheet { max-width: none; }
.kv-table th { text-align: left; font-weight: 600; color: var(--muted); padding: 6px 12px 6px 0; vertical-align: top; white-space: nowrap; }
.kv-table td { padding: 6px 0; }
.shift-claim .inline-form { flex-wrap: wrap; }

/* The page's message about what just happened, when the reader has been kept
   where they were rather than sent to the top (keep-scroll.js): it stays at
   the top of the window until it is put away. */
.flash-floating { position: sticky; top: 8px; z-index: 60; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18); padding-right: 40px; }
.flash-close { position: absolute; top: 4px; right: 6px; background: none; border: none; color: inherit; font-size: 1.3rem; line-height: 1; padding: 4px 8px; cursor: pointer; opacity: 0.7; }
.flash-close:hover { opacity: 1; background: none; }

/* A job's 'Locked to Roster' tick sits in the row of controls and wraps with it. */
.duty-edit .duty-always { display: inline-flex; align-items: center; gap: 4px; font-size: 0.85rem; white-space: nowrap; margin: 0; }
.duty-edit .duty-always input { width: auto; margin: 0; }

/* A heading that sorts its table in the page (public/table-sort.js). */
th .sort-head { all: unset; cursor: pointer; display: inline; font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit; }
th .sort-head:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Build the roster's buttons: two to a row, every one the same width, the
   Start from choice across the top. The forms take no box of their own. */
.roster-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; align-items: stretch; margin: 12px 0; }
.roster-actions > form { display: contents; }
.roster-actions select { grid-column: 1 / -1; width: 100%; margin: 0; }
.roster-actions button { width: 100%; margin: 0; }
.roster-actions > a.btn { width: 100%; margin: 0; text-align: center; box-sizing: border-box; }
/* The 'keep what's on the draft' tick has the row to itself, above the two build buttons. */
.roster-actions .build-keep { grid-column: 1 / -1; margin: 0; }
.roster-actions-note { grid-column: 1 / -1; }

/* A page's explanations in a pop-up, opened by a "How these work" button. */
.help-dialog { max-width: min(560px, calc(100vw - 32px)); border: 1px solid var(--border); border-radius: 12px; padding: 18px 20px; box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25); }
.help-dialog::backdrop { background: rgba(20, 24, 31, 0.45); }
.help-dialog h2 { margin-top: 0; font-size: 1.1rem; }
.help-dialog p { margin: 0 0 12px; line-height: 1.45; }
.roster-actions .roster-actions-note { justify-self: start; }

/* The grid inside Edit the draft's fold: the fold is the card, so the
   table only needs its edge. */
.draft-grid-table { margin-top: 0.75rem; border: 1px solid var(--border); border-radius: 8px; }

/* Publish's Test run tick sits on its own line under the buttons. */
.publish-test { flex-basis: 100%; margin: 4px 0 0; }
/* Wipe this round: a tick a line, each saying what it takes under its name. */
.wipe-ticks label.checkbox { align-items: flex-start; }
.wipe-ticks label.checkbox input { margin-top: 4px; flex: 0 0 auto; }
.wipe-ticks label.checkbox > span > span { display: block; }
.wipe-ticks .wipe-sheets { margin: 6px 0 0 26px; }

/* The top bar and the row of tabs stay put while the page scrolls under them
   (the president, 2 October 2026). Only where there is the room: on a phone,
   or a window too short to spare it, they scroll away as before. --top-h is
   the bar's height, measured by the script beside it in _header.ejs, and is
   where everything else that sticks or stops has to start from - none of
   which applies while the bar itself isn't stuck. */
@media screen and (min-width: 641px) and (min-height: 620px) {
  .page-top { position: sticky; top: 0; z-index: 50; }
  html { scroll-padding-top: calc(var(--top-h, 0px) + 8px); }
  .flash-floating { top: calc(var(--top-h, 0px) + 8px); }
  .gd-slot-h, .gd-age-h { top: var(--top-h, 0px); }
}

/* Coordinator hours: ticks changed and not saved. The figures have been
   re-worked on the page, so it says they aren't kept yet, and the send
   button - which would send the saved amount - steps back until they are. */
.coordinator-unsaved-note { display: none; color: var(--gold); font-weight: 600; font-size: 0.85rem; }
.coordinator-unsaved .coordinator-unsaved-note { display: inline; }
.coordinator-unsaved .coordinator-send { display: none; }
.coordinator-unsaved .stat-value[data-ch] { color: var(--gold); }

/* Your hours: the coordinator's own page. One column, narrow enough to read
   on a phone held in one hand, and the form's boxes big enough to hit. */
.my-hours { max-width: 760px; }
.hours-form { display: grid; grid-template-columns: 1.3fr 1fr 1fr; gap: 0 14px; align-items: end; }
.hours-form input { width: 100%; font-size: 1rem; }
/* A time box isn't in the site's own list of inputs, so it is dressed here to
   match the date beside it - same height, same edge. */
.hours-form input[type="time"], .hours-form input[type="date"] { padding: 10px 12px; border: 1px solid var(--border); border-radius: 6px; margin-bottom: 16px; background: var(--card); color: var(--text); min-height: 44px; box-sizing: border-box; font-family: inherit; }
.my-hours .stat-cards { grid-template-columns: repeat(3, 1fr); gap: 10px; }
.my-hours .stat-card { padding: 10px 12px; }
.my-hours .stat-card > .stat-label:first-child { min-height: 0; margin-bottom: 4px; }
.hours-form .hours-note, .hours-form .hours-go { grid-column: 1 / -1; }
.hours-form .hours-go button { width: 100%; padding: 12px; font-size: 1rem; margin-top: 6px; }
@media (max-width: 520px) { .hours-form { grid-template-columns: 1fr 1fr; } .hours-form > div:first-of-type { grid-column: 1 / -1; } }
/* The All my details button and its caption on the week's page: on a phone
   the caption wraps under the button as a line of its own rather than
   starting beside it and running on underneath. */
.me-all { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; }
.me-all .muted { flex: 1 1 16em; }

/* The filters every list page shares (JPI-3; views/_filters.ejs,
   lib/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; cursor: pointer; }
.filters-group .tick input { margin: 3px 0 0; flex: none; }
.filters-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
/* A lit card is red through: its labels go white too, not grey on red. */
a.stat-on .stat-label { color: #fff; }
.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; }
@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; }
}
/* A coach's or team manager's teams on My details, drawn as Team Sheets
   draws a team on My Team Sheets: the name as the link to its sheet, the
   grade, the counts as badges in Team Sheets' own colours, and the training
   slot on a line under them. */
.team-rows { list-style: none; padding: 0; margin: 0 0 12px; }
.team-rows li { padding: 10px 0; border-bottom: 1px solid var(--border); }
.team-rows li:last-child { border-bottom: none; }
.team-rows a.team-sheet-link { color: var(--primary); font-weight: 600; text-decoration: none; }
.team-rows a.team-sheet-link:hover { text-decoration: underline; }
.team-badge-player { background: #eaf2ff; color: #1d4ed8; }
.team-badge-coach { background: #fff1e0; color: #b45309; }
.team-badge-tm { background: #f1e8ff; color: #6d28d9; }
.team-row-training { display: block; font-size: 0.85rem; margin-top: 2px; }

/* Where somebody's teams play, above the availability ticks (JPI-12), and the
   note beside a tick one of their own games takes. */
.team-games { margin: 12px 0 4px; padding: 10px 14px; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; }
.team-games h3 { margin: 0 0 6px; }
.team-games ul { margin: 0 0 6px; padding-left: 18px; }
.team-games li { margin: 2px 0; }
.slot-note { color: #92620a; background: #fff8e6; border-radius: 4px; padding: 1px 6px; font-weight: 600; cursor: help; }
/* The time itself is what is coloured (the president, 3 October 2026): a
   note beside or under it made the row of ticks ragged. What the game is, is
   in the list above and in the tooltip. */

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

/* 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 over
   the top edge of the chat's card - only as far as the 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; max-width: 760px; }
.help-bot-head-text { flex: 1; min-width: 0; }
.help-bot-head-text > p:last-child { margin-bottom: 14px; }
/* 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: -22px; 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: the long bar has room for the words above
   1240px, and again from 1080 to 1200 where it has already given up the
   signed-in name. Everybody else's bar is short and keeps them to 1000px. */
@media (max-width: 1079px), (min-width: 1201px) and (max-width: 1239px) {
  header.topbar .ask-bot-long { padding: 3px; }
  header.topbar .ask-bot-long .ask-bot-words { display: none; }
}
@media (max-width: 999px) {
  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-left: 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: 8px; }
header.topbar .nav-menu-items a.nav-bot img { width: 22px; height: 22px; flex: none; }

/* Staff > Details: a badge per role for the season, each with what it is
   for - teams, age groups, a position (JPI-102). One role to a line, so a
   coach of three teams reads down rather than across. */
.role-tags { min-width: 220px; }
.role-tag { margin: 2px 0; line-height: 1.5; }
.role-tag .badge { margin-right: 6px; }
.staff-mobile { white-space: nowrap; }

/* Supervisor levels: somebody with no level yet stands out until one is picked. */
select.level-not-set { border-color: #d9a400; background-color: #fff8e6; font-weight: 600; }

/* Build the roster's grid: each venue's rows share a shade (the president, 5
   October 2026) - the row heading in the stronger tone, its blocks in the
   lighter. An empty block's amber and an off time's hatching sit over it. */
.roster-grid tr.venue-tint-0 { --tint: #e8f0fb; --tint-soft: #f4f8fd; }
.roster-grid tr.venue-tint-1 { --tint: #e6f4ea; --tint-soft: #f3faf5; }
.roster-grid tr.venue-tint-2 { --tint: #f3e9f7; --tint-soft: #faf5fc; }
.roster-grid tr.venue-tint-3 { --tint: #fdebdc; --tint-soft: #fef6ee; }
.roster-grid tr.venue-tint-4 { --tint: #e2f3f4; --tint-soft: #f1f9fa; }
.roster-grid tr.venue-tint-5 { --tint: #f7e8ec; --tint-soft: #fcf4f6; }
.roster-grid tr.venue-tint-none { --tint: #eef0f3; --tint-soft: #f7f8fa; }
.roster-grid tbody tr[class*="venue-tint"] th.slot { background: var(--tint); }
.roster-grid tbody tr[class*="venue-tint"] td { background: var(--tint-soft); }
.roster-grid.is-boxed tbody tr[class*="venue-tint"] td.is-open { background: #fdf3a7; }
.roster-grid.is-boxed tbody tr[class*="venue-tint"] td.is-out { background: repeating-linear-gradient(135deg, var(--tint-soft) 0 8px, var(--tint) 8px 16px); }
.roster-grid tbody tr[class*="venue-tint"] td.cell-bad { background: #fdecea; }
.roster-grid tbody tr[class*="venue-tint"] td.cell-warn { background: #fff8e6; }
.roster-grid.is-boxed tbody tr[class*="venue-tint"] td.is-open.cell-warn { background: #fdf3a7; }

/* A pay run's actions as two stacks: the files over the bill's Xero mark,
   and the paid mark with what became of the bank file. */
.run-actions { display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: flex-start; }
.run-actions-col { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.run-actions-col .inline-form { margin: 0; }
.run-actions-row { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; }

/* The draft's reason belongs to who it put there: gone once the block is emptied. */
.roster-grid.is-boxed td.is-open .cell-why-because { display: none; }
.roster-grid.is-boxed td.is-open .cell-why:not(:has(.cell-why-issues, .cell-why-ask)) { display: none; }

/* A filled block's clear button - a small dark circle with a white x, as the
   search boxes have - and the saved draft's word under a block, which is put
   away once the block has been changed (JPI-135). */
.roster-grid .cell-clear { flex: none; width: 18px; height: 18px; padding: 0; margin: 0 2px; border-radius: 50%; background: #98a2b3; color: #fff; font-size: 0.85rem; line-height: 18px; text-align: center; text-decoration: none; font-weight: 700; }
.roster-grid .cell-clear:hover, .roster-grid .cell-clear:focus-visible { background: var(--primary); color: #fff; }
.roster-grid td.is-changed .cell-said { display: none; }
/* ...and so is its colour: a block changed since the page was drawn no longer
   wears the saved draft's warning. */
.roster-grid.is-boxed tbody tr[class*="venue-tint"] td.is-changed.is-on { background: var(--tint-soft); }
.roster-grid.is-boxed td.is-changed.is-on .cell-box { border-color: #94a3b8; background: #fff; }

/* Narrow times, short names (JPI-136): a block shows "Alice C", and opens
   out over the times after it while it is being edited, so the full name and
   the matches can be read. Its place in the row is kept by the cell's own
   height, so nothing below moves. At the foot of the file, so it wins over
   every state a block can be in (run start, changed, warned). */
.roster-grid.is-boxed td.is-editing { position: relative; z-index: 4; }
.roster-grid.is-boxed td.is-editing::before { content: ''; display: block; height: 40px; }
.roster-grid.is-boxed td.is-editing .cell-box.cell-box { position: absolute; top: 5px; left: 0; width: 190px; z-index: 5; background: #fff; border: 2px solid var(--primary); border-radius: 8px; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18); color: var(--text, #1f2937); font-weight: 400; }
.roster-grid.is-boxed td.is-editing.opens-left .cell-box.cell-box { left: auto; right: 0; }
.roster-grid.is-boxed td.is-editing .cell-name:focus { outline: none; }

/* Edit the draft: how many there are to roster from, and the way to what each said. */
.draft-available { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; margin: 8px 0 4px; }

/* Build the roster, smaller (JPI-146, the president, 6 October 2026: "like
   used on the training roster so I can fit more on"): the grid's type and
   its blocks come down a size, so more of the day is on screen. */
.roster-grid { font-size: 0.8rem; }
.roster-grid.is-boxed td { padding: 3px 0; min-width: 104px; width: 104px; max-width: 104px; }
.roster-grid .cell-box { height: 30px; }
.roster-grid.is-boxed .cell-name { padding-left: 6px; font-size: 0.8rem; }
.roster-grid .cell-copy { padding: 0 4px; font-size: 1rem; }
.roster-grid .cell-clear { width: 15px; height: 15px; line-height: 15px; font-size: 0.72rem; margin: 0 1px; }
.roster-grid tbody th.slot, .roster-grid thead th { padding-top: 4px; padding-bottom: 4px; }
.roster-grid .cell-why, .roster-grid .cell-said { font-size: 0.72rem; }
.roster-grid.is-boxed td.is-editing .cell-box.cell-box { top: 3px; height: 34px; }

/* Who can do the time being worked on: a fold above the grid, each person's
   whole day a row, that time's column outlined, and where they already are
   written in the cell. Type as small as the grid's. */
details.draft-available { margin: 8px 0 6px; }
details.draft-available > summary { cursor: pointer; }
.avail-at-table { font-size: 0.76rem; margin-top: 6px; border-collapse: collapse; }
.avail-at-table th, .avail-at-table td { padding: 3px 6px; white-space: nowrap; border-bottom: 1px solid var(--border); text-align: center; }
.avail-at-table thead th { text-transform: none; letter-spacing: 0; font-weight: 600; }
.avail-at-table tbody th, .avail-at-table thead th:first-child { text-align: left; position: sticky; left: 0; background: var(--card); z-index: 1; }
.avail-at-table td.who-yes { background: #e9f6ec; color: #157347; }
.avail-at-table td.who-warn { background: #fff8e6; color: #7a4b00; }
.avail-at-table td.who-no { background: #f4f5f7; }
.avail-at-table td.who-none { color: var(--muted); }
.avail-at-table td.is-alloc { background: #dbe7fb; color: #1d3f8a; font-weight: 600; }
.avail-at-table .at-time { box-shadow: inset 2px 0 0 var(--primary), inset -2px 0 0 var(--primary); }
.avail-at-table tr.is-placed tbody th, .avail-at-table tr.is-placed th { color: var(--muted); font-weight: 400; }
.avail-at-table tbody th { text-transform: none; letter-spacing: 0; font-weight: 600; font-size: inherit; }

/* The who-can-do-this-time list as rows of the grid itself, above its time
   row (JPI-147): the same columns as the grid, so the times line up. They
   are plain cells, so they scroll away under the time row, which sticks. */
.avail-table tr.avail-row td { padding: 2px 6px; font-size: 0.74rem; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-bottom: 1px solid var(--border); background: var(--card); min-width: 0; }
.avail-table tr.avail-row td.avail-name { text-align: left; font-weight: 600; position: sticky; left: 0; z-index: 1; box-shadow: inset -1px 0 0 var(--border); }
.avail-table tr.avail-row.is-placed td.avail-name { font-weight: 400; color: var(--muted); }
.avail-table tr.avail-row td.who-yes { background: #e9f6ec; color: #157347; }
.avail-table tr.avail-row td.who-warn { background: #fff8e6; color: #7a4b00; }
.avail-table tr.avail-row td.who-no { background: #f4f5f7; }
.avail-table tr.avail-row td.who-none { color: var(--muted); }
.avail-table tr.avail-row td.is-alloc { background: #dbe7fb; color: #1d3f8a; font-weight: 600; }

/* The list's level buttons, in the fold. */
.avail-levels { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0; }
.avail-levels button { margin: 0; }

/* The list: a game of their own in amber with where it is, and the person
   being looked at marked - the last row, nearest the times. */
.avail-table tr.avail-row td.who-game { background: #fdf3a7; color: #7a4b00; font-weight: 600; }
.avail-table tr.avail-row.is-looking td { border-top: 2px solid var(--primary); border-bottom: 2px solid var(--primary); }
.avail-table tr.avail-row.is-looking td.avail-name { background: var(--primary); color: #fff; font-weight: 700; }
.avail-table tr.avail-row.is-looking td.avail-name .muted { color: #fff; opacity: 0.85; }
/* A game's venue is longer than a block is wide: it wraps rather than being cut. */
.avail-table tr.avail-row td.who-game { white-space: normal; line-height: 1.2; text-overflow: clip; }

/* The roster grid full screen (JPI-148): its card fills the window, above
   the bar, and the grid's own box is the one thing that scrolls - the
   page's script sizes it. The list of names under a block sits above it. */
.grid-tools { margin: 6px 0; }
html.grid-full, html.grid-full body { overflow: hidden; }
details#draft-grid.is-full { position: fixed; inset: 0; z-index: 70; margin: 0; border-radius: 0; padding: 8px 14px; overflow: auto; background: var(--card); }
details#draft-grid.is-full .draft-grid-table { margin-top: 4px; }
details#draft-grid.is-full #avail-at > p.muted { display: none; }
details#draft-grid.is-full .page-actions .muted { display: none; }
details#draft-grid.is-full .page-actions { margin-top: 6px; }
html.grid-full .cell-suggest { z-index: 80; }
/* The page's message - Saved, or why not - is over the full-screen card, not
   behind it: top centre, with its x to put it away. */
html.grid-full main > .success:first-child, html.grid-full main > .error:first-child { position: fixed; top: 8px; left: 50%; transform: translateX(-50%); z-index: 90; margin: 0; max-width: min(720px, calc(100vw - 96px)); padding-right: 40px; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18); }

/* The venue buttons beside Full screen, and the level buttons: one row each. */
.grid-tools { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; }
.grid-tools .grid-venues .avail-levels { margin: 0; }

/* Availability's list: a time one of their own teams plays is amber. */
td.slot.slot-game { background: #fff8e6; }
td.slot .slot-game-said { display: block; font-size: 0.7rem; font-weight: 600; color: #92620a; line-height: 1.1; }

/* Full screen is the editor and nothing else: no headings, and the venue and
   level buttons on one line with the way out. */
details#draft-grid.is-full > summary, details#draft-grid.is-full #draft-available { display: none; }
/* Full screen is an icon at the right-hand end of that line - the top right
   of the window once it is on - which turns into the way back. */
.grid-tools .grid-full-button { order: 99; margin-left: auto; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; }
.grid-full-button .icon-shrink, .grid-full-button[aria-pressed="true"] .icon-expand { display: none; }
.grid-full-button[aria-pressed="true"] .icon-shrink { display: block; }
.chips-label { font-size: 0.8rem; font-weight: 600; color: var(--muted); align-self: center; }
.grid-tools .avail-levels { margin: 0; }

/* The supervisor list as a pane under the grid, and the line above it to
   drag (JPI-150; under rather than above since 7 October 2026). The two are
   one dock held at the foot of the window as the page scrolls. The pane
   scrolls on its own; its table takes the grid's column widths from the
   page's script, and carries the times at its head. */
.avail-dock { position: sticky; bottom: 0; z-index: 4; background: var(--card); box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.06); }
.avail-pane { overflow: auto; border-bottom: 1px solid var(--border); background: var(--card); scrollbar-width: thin; }
.avail-table thead th { position: sticky; top: 0; z-index: 2; background: var(--card); font-size: 0.72rem; text-transform: none; letter-spacing: 0; padding: 2px 6px; border-bottom: 1px solid var(--border); text-align: left; }
.avail-table thead th:first-child { left: 0; z-index: 3; }
.avail-table { table-layout: fixed; border-collapse: collapse; font-size: 0.74rem; }
.avail-table .at-time { box-shadow: inset 2px 0 0 var(--primary), inset -2px 0 0 var(--primary); }
.grid-split { height: 9px; cursor: row-resize; touch-action: none; background: var(--border); border-top: 1px solid #c5cad3; border-bottom: 1px solid #c5cad3; position: relative; }
.grid-split::after { content: ""; position: absolute; left: 50%; top: 2px; width: 46px; height: 3px; margin-left: -23px; border-radius: 2px; background: #8a94a6; }
.grid-split:hover, .grid-split:active { background: #cfd5df; }

/* The roster grid's words at the size of the supervisor list under it (the
   president, 7 October 2026): job names, times, names in the blocks, Why. */
.draft-grid-table .roster-grid, .draft-grid-table .roster-grid th, .draft-grid-table .roster-grid th *, .draft-grid-table .roster-grid td, .draft-grid-table .roster-grid .roster-time { font-size: 0.74rem; }
.draft-grid-table .roster-grid tbody th { padding-top: 4px; padding-bottom: 4px; }
.draft-grid-table .roster-grid.is-boxed .cell-name, .draft-grid-table .roster-grid .cell-name { font-size: 0.74rem; }
.draft-grid-table .roster-grid .cell-box { height: 30px; }
.draft-grid-table .roster-grid .cell-why summary, .draft-grid-table .roster-grid .cell-said { font-size: 0.7rem; }

/* The venue rows as short as a block (the president, 7 October 2026: "as
   short input height as possible"). Nothing is written under a block: its
   colour says there is something to look at, and the small i at its left
   edge opens a card over the grid with what, and why the draft put the
   shift there. */
.draft-grid-table .roster-grid.is-boxed td { padding: 1px 0; position: relative; }
.draft-grid-table .roster-grid .cell-box { height: 24px; }
.draft-grid-table .roster-grid.is-boxed td.is-editing::before { height: 24px; }
.draft-grid-table .roster-grid.is-boxed td.is-editing .cell-box.cell-box { top: -4px; height: 32px; }
.draft-grid-table .roster-grid tbody th.slot { padding-top: 2px; padding-bottom: 2px; white-space: nowrap; vertical-align: middle; }
.draft-grid-table .roster-grid .cell-why { position: absolute; top: 50%; left: 2px; margin-top: -7px; padding: 0; z-index: 3; color: var(--text, #1f2937); }
.draft-grid-table .roster-grid .cell-why > summary { list-style: none; width: 14px; height: 14px; border-radius: 50%; background: #98a2b3; color: #fff; font: italic 700 10px/14px Georgia, serif; text-align: center; }
.draft-grid-table .roster-grid .cell-why > summary::-webkit-details-marker { display: none; }
.draft-grid-table .roster-grid .cell-why.is-warn > summary { background: #b7791f; }
.draft-grid-table .roster-grid .cell-why.is-bad > summary { background: #c0392b; }
.draft-grid-table .roster-grid .cell-why[open] > summary { outline: 2px solid var(--primary); outline-offset: 1px; }
.draft-grid-table .roster-grid .cell-why > div { position: fixed; z-index: 80; width: max(220px, calc(var(--span, 1) * 104px)); min-width: 0; max-width: 360px; padding: 8px 10px; background: #fff; border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18); font-size: 0.74rem; line-height: 1.35; white-space: normal; color: var(--text, #1f2937); }
.draft-grid-table .roster-grid .cell-why-issues { margin: 0 0 4px; padding-left: 16px; color: #7a4b00; }
.draft-grid-table .roster-grid .cell-why-issues.bad { color: #b42318; font-weight: 600; }
.draft-grid-table .roster-grid .cell-why-because { margin: 0; color: var(--muted, #64748b); }
.draft-grid-table .roster-grid td.is-changed .cell-why-issues:not(.is-fresh) { display: none; }

/* The first column's width, set by dragging the edge of its heading (the
   president, 7 October 2026). The list under the grid takes the same width. */
.draft-grid-table .roster-grid tbody th.slot, .draft-grid-table .roster-grid thead th:first-child { width: var(--where-w, 200px); min-width: var(--where-w, 200px); max-width: var(--where-w, 200px); overflow: hidden; text-overflow: ellipsis; }
.avail-table td.avail-name, .avail-table thead th:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.draft-grid-table .roster-grid { width: auto; min-width: 0; }

/* Nothing under a block, whatever it has to say (the president, 7 October
   2026): the box asking why somebody was taken off, and its "Kept", are in
   the i's card; the i is gold while a reason is wanted. A refused copy's
   word floats over the block for a moment. */
.draft-grid-table .roster-grid .cell-why > div .cell-why-ask { width: 100%; margin: 6px 0 0; }
.draft-grid-table .roster-grid .cell-why > div .cell-why-kept { padding: 2px 0 0; }
.draft-grid-table .roster-grid .cell-why.is-ask > summary { background: #d4a017; }
.draft-grid-table .roster-grid td.is-changed .cell-why-because { display: none; }
.draft-grid-table .roster-grid td[data-note]::after { position: absolute; left: 2px; right: 2px; top: 1px; z-index: 6; padding: 4px 8px; border-radius: 6px; background: #b42318; color: #fff; font-size: 0.7rem; line-height: 1.2; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); }
/* A changed block's old conflicts and reason no longer hold: its i stays
   only while it is asking why. */
.draft-grid-table .roster-grid td.is-changed > .cell-why:not(:has(.cell-why-ask, .is-fresh)) { display: none; }

/* The grip on the first column's heading: its right-hand edge, a wide
   target for a finger, a line that shows on hover or while dragged. */
.draft-grid-table .roster-grid thead th:first-child { position: sticky; overflow: visible; }
.where-grip { position: absolute; top: 0; bottom: 0; right: -8px; width: 16px; cursor: col-resize; touch-action: none; z-index: 4; }
.where-grip::after { content: ""; position: absolute; top: 20%; bottom: 20%; left: 6px; width: 4px; border-radius: 2px; background: #c5cad3; }
.where-grip:hover::after, .where-grip.is-dragging::after { background: var(--primary); top: 0; bottom: 0; }

/* No mark to press on a block (the president, 7 October 2026): the card
   opens when the block is tapped into. A hard conflict gets a corner mark,
   like a spreadsheet's comment; a reason still wanted outlines the block in
   gold. */
.draft-grid-table .roster-grid .cell-why { position: static; margin: 0; }
.draft-grid-table .roster-grid .cell-why > summary { display: none; }
.draft-grid-table .roster-grid td.cell-bad .cell-box { background-image: linear-gradient(225deg, #c0392b 0, #c0392b 7px, transparent 7px); }
.draft-grid-table .roster-grid td:has(> .cell-why.is-ask) .cell-box { outline: 2px solid #d4a017; outline-offset: -2px; border-radius: 6px; }

/* Room round each block for the venue's colour to show (the president,
   7 October 2026: at one line high the rows were too squashed to see it).
   The block keeps its own height; the cell around it is the venue's tint,
   the stronger one, so the band reads at a glance. */
.draft-grid-table .roster-grid.is-boxed td { padding: 5px 0; }
.draft-grid-table .roster-grid.is-boxed tbody tr[class*="venue-tint"] td:not(.is-open):not(.is-out):not(.cell-bad):not(.cell-warn) { background: var(--tint); }
.draft-grid-table .roster-grid.is-boxed td.is-editing .cell-box.cell-box { top: 1px; }

/* Saved as it goes, and who is editing (7 October 2026). */
.grid-tools .draft-status { font-size: 0.8rem; color: var(--muted, #64748b); display: inline-flex; gap: 8px; align-items: center; }
.grid-tools .draft-status.is-mine, .grid-tools .draft-status.is-saved { color: #157347; }
.grid-tools .draft-status.is-warn { color: #7a4b00; }
.grid-tools .draft-status.is-error, .grid-tools .draft-status.is-locked { color: #b42318; font-weight: 600; }
.roster-grid.is-locked .cell-box { opacity: 0.75; }

/* The one portal's menu (JPI-124, portal/menu.js in the one repository):
   the Admin area opens to its eight groups in two columns, each headed. */
header.topbar .nav-menu-items.portal-admin { min-width: 560px; display: grid; grid-template-columns: 1fr 1fr; gap: 14px 24px; }
header.topbar .portal-group { display: flex; flex-direction: column; gap: 8px; }
header.topbar .portal-group-name { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; opacity: 0.7; }
@media (max-width: 640px) {
  header.topbar .nav-menu-items.portal-admin { min-width: 0; display: flex; }
}

/* The one portal's Home (JPI-124): a card an area. */
.portal-home .portal-card h2 { margin-top: 0; display: flex; align-items: baseline; gap: 8px; }
.portal-home .portal-card h2 a { text-decoration: none; color: var(--primary); }
.portal-count { font-size: 0.8rem; font-weight: 700; color: #fff; background: var(--primary); border-radius: 999px; padding: 2px 9px; }
.portal-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 0.95rem; }
.portal-n { font-weight: 700; color: var(--muted); }

/* People and access: one person's grid (JPI-123). */
.logins-grid td, .logins-grid th { vertical-align: top; }
.logins-tick { display: inline-block; margin: 0 14px 6px 0; white-space: nowrap; }
