/* Broomstack design system.
 *
 * Ported from the Claude Design project "Broomstack league platform design"
 * (Broomstack.html — nine high-fidelity screen mockups plus a component
 * inventory). Tokens, chrome, and components are taken from that file
 * verbatim so the built screens and the mockups stay the same design.
 *
 * The mockup expresses per-screen layout as inline styles on a fixed
 * 1440px frame. Those repeated patterns are lifted into real classes here
 * (.page, .weekgrid, .iceblock, .banner, …) so templates stay readable and
 * the layout is responsive rather than frame-width.
 *
 * Colors, per the design's own legend:
 *   Slate #2D3042 — chrome and headings
 *   Gold  #C9A54A — the single primary action per view, used sparingly
 *   Blue  #5B7B9A — secondary actions, info, the home club
 *   Paper #F7F7F2 — page background
 * Stone red and yellow are team colors only, never chrome.
 */

:root {
  --slate: #2d3042;
  --slate-70: rgba(45, 48, 66, 0.7);
  --slate-45: rgba(45, 48, 66, 0.45);
  --slate-surface: #363a4f;
  --gold: #c9a54a;
  --gold-dk: #b08f36;
  --gold-tint: #f6efdc;
  --paper: #f7f7f2;
  --blue: #5b7b9a;
  --blue-dk: #4a677f;
  --blue-tint: #eaf0f5;
  --ink: #2d3042;
  --muted: #6f7280;
  --faint: #9a9ca6;
  --line: #e4e4de;
  --line-2: #d6d6cf;
  --white: #fff;
  --red: #c0392b;
  --red-tint: #fbe9e6;
  --yellow: #e5b028;
  --yellow-tint: #fbf1d6;
  --green: #4b8a5a;
  --green-tint: #e7f1e9;
  --r: 5px;
  --r-lg: 8px;
  --sh: 0 1px 2px rgba(45, 48, 66, 0.06), 0 1px 3px rgba(45, 48, 66, 0.05);
  --sh-md: 0 4px 14px rgba(45, 48, 66, 0.09);
}

* { box-sizing: border-box; }
html, body { margin: 0; }

body {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  background: var(--paper);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  line-height: 1.45;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

h1, h2, h3, h4, h5 { margin: 0; font-weight: 700; line-height: 1.2; }
p { margin: 0; }
a { color: var(--blue); text-decoration: none; }
a:hover { color: var(--blue-dk); text-decoration: underline; }

/* A team name linking to its roster (#1163). Keeps the colour of whatever
   it sits in: these appear in standings rows, draw cells and bracket
   tables, and the generic blue above turns a standings table into a wall
   of links. The hover underline is the affordance instead. */
a.team-link { color: inherit; }

/* The "previewing as a member" bar (#1206). Gold and full width at the
   top of every affected page, never dismissible: the whole point is
   that you cannot leave it on without noticing, because the honest 403s
   it produces are otherwise indistinguishable from a bug. */
.previewbar {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 18px; padding: 10px 14px;
  border: 1px solid var(--gold); border-left-width: 4px;
  border-radius: 8px; background: #fdf7e6; color: var(--slate);
  font-size: 13px;
}
.previewbar svg { color: var(--gold-dk); flex: none; }
.previewbar-body { flex: 1; }
@media (max-width: 34rem) {
  .previewbar { flex-wrap: wrap; }
}
a.team-link:hover { color: inherit; text-decoration: underline; }

svg.i {
  width: 18px; height: 18px;
  stroke: currentColor; fill: none;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  flex: none;
}

/* Every icon in the sprite is drawn as an outline — strokes, no areas — so
   `stroke: currentColor; fill: none` is not decoration, it is the
   difference between the icon existing and not.
 *
 * That treatment used to require `class="i"`, and 137 of the 141 call
 * sites did not carry it. Those icons fell back to the SVG default of
 * `fill: black; stroke: none`, which renders a path with no enclosed area
 * as *nothing at all* — the icon still occupied its box, so buttons like
 * "Add season" showed a 16px gap where the plus should be. Symbols that do
 * enclose an area (#ic-stone) came out as a black blob instead, which is
 * what the SEASON card kick was.
 *
 * Keyed on the icon being a sprite reference rather than on a class, so a
 * new `<svg><use href="#ic-…">` is correct by default and cannot
 * reintroduce this by forgetting something. The context rules further down
 * still own size and colour; this only sets paint.
 *
 * `.mark` is excluded deliberately. The brand mark is now an `<img>` of
 * the roundel so this rule cannot reach it either way, but any future
 * inline `.mark` would carry its own `fill`/`stroke` presentation
 * attributes, and CSS outranks those — without the exclusion this would
 * repaint the logo in currentColor. */
svg:has(use):not(.mark) {
  stroke: currentColor; fill: none;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.i-14 { width: 14px; height: 14px; }
.i-16 { width: 16px; height: 16px; }
.i-20 { width: 20px; height: 20px; }
.i-24 { width: 24px; height: 24px; }

/* ---------- app chrome ---------- */

.appnav {
  display: flex; align-items: center; gap: 26px;
  background: var(--slate); color: #eceef4;
  padding: 0 24px; height: 58px;
  flex-wrap: wrap;
}
.brand {
  display: flex; align-items: center; gap: 10px;
  font-weight: 700; font-size: 17px; letter-spacing: -0.01em; color: #fff;
}
.brand:hover { color: #fff; text-decoration: none; }
.brand .mark {
  width: 28px; height: 28px; flex: none;
  background: var(--paper); border-radius: 50%;
}
.navlinks { display: flex; gap: 4px; height: 100%; }
.navlinks a {
  color: rgba(255, 255, 255, 0.72);
  font-size: 14px; font-weight: 500;
  display: flex; align-items: center;
  padding: 0 13px; height: 100%;
  border-bottom: 2px solid transparent;
}
.navlinks a:hover { color: #fff; text-decoration: none; }
.navlinks a.on { color: #fff; border-bottom-color: var(--gold); }
.navspacer { flex: 1; }
.navtools { display: flex; align-items: center; gap: 10px; }
.navbtn {
  width: 36px; height: 36px; border-radius: var(--r);
  display: flex; align-items: center; justify-content: center;
  color: rgba(255, 255, 255, 0.75); background: transparent;
  border: 0; cursor: pointer;
}
.navbtn:hover { background: rgba(255, 255, 255, 0.1); color: #fff; }
.navlink-quiet { color: rgba(255, 255, 255, 0.72); font-size: 13.5px; }
.navlink-quiet:hover { color: #fff; text-decoration: none; }
/* Sign out is a POST — `LogoutView` refuses GET — so it is a button that
   has to pass for one of the anchors beside it.

   Qualified with `[type='submit']`, for the third instance of the trap
   the two comments further down describe: the bare `button[type='submit']`
   rule is (0,1,1) and comes later in this file, so a plain
   `button.navlink-quiet` at (0,1,1) loses the tie and Sign out renders as
   a gold pill in the middle of the nav. Colour is restated here for the
   same reason. Caught in a browser, because a stylesheet tie is invisible
   to a view test. */
.navlink-form { display: flex; margin: 0; }
button[type='submit'].navlink-quiet {
  font: inherit;
  background: none; border: 0; padding: 0;
  font-size: 13.5px; font-weight: 400; cursor: pointer;
  color: rgba(255, 255, 255, 0.72);
}
button[type='submit'].navlink-quiet:hover { color: #fff; }
.avatar {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--blue); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 13px; flex: none;
}
.avatar-gold { background: var(--gold); color: var(--slate); }
.adminpill {
  font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--slate);
  background: var(--gold); padding: 3px 9px; border-radius: 20px;
}

/* page title bar */
.pagebar {
  display: flex; align-items: center; gap: 14px;
  padding: 20px 32px; background: var(--white);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.pagebar h3 { font-size: 20px; color: var(--slate); }
.pagebar .sub { color: var(--muted); font-size: 13px; }
.pagebar .spacer { flex: 1; }

/* ---------- page shell ---------- */

.page { padding: 28px 32px 64px; flex: 1; }
.page-narrow { max-width: 720px; margin-left: auto; margin-right: auto; }
.page-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; margin-bottom: 22px; flex-wrap: wrap;
}
.page-head h1 { font-size: 24px; color: var(--slate); }
.page-head .sub { color: var(--muted); font-size: 14px; margin-top: 2px; }
.section-title {
  font-size: 16px; color: var(--slate);
  margin: 28px 0 12px;
}

.sitefoot {
  color: var(--faint); font-size: 12.5px;
  padding: 20px 32px 32px; text-align: center;
}

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

.btn {
  font-family: inherit; font-size: 14px; font-weight: 600;
  border-radius: var(--r); padding: 9px 16px;
  display: inline-flex; align-items: center; gap: 8px;
  cursor: pointer; border: 1px solid transparent;
  line-height: 1; white-space: nowrap;
}
.btn:hover { text-decoration: none; }
.btn svg { width: 16px; height: 16px; }
.btn-gold { background: var(--gold); color: var(--slate); border-color: var(--gold-dk); }
.btn-gold:hover { background: var(--gold-dk); color: #fff; }
.btn-slate { background: var(--slate); color: #fff; }
.btn-slate:hover { background: #3c4058; color: #fff; }
.btn-ghost { background: transparent; color: var(--slate); border-color: var(--line-2); }
.btn-ghost:hover { background: var(--white); border-color: var(--slate-45); color: var(--slate); }
.btn-blue { background: var(--blue); color: #fff; }
.btn-blue:hover { background: var(--blue-dk); color: #fff; }
.btn-sm { padding: 6px 11px; font-size: 12.5px; }
.btn-lg { padding: 14px 22px; font-size: 16px; }
.btn-block { width: 100%; justify-content: center; }
.btn:disabled, .btn.disabled { opacity: 0.45; cursor: not-allowed; }
.btnrow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ---------- tags ---------- */

.tag {
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.02em;
  padding: 3px 9px; border-radius: 20px;
  display: inline-flex; align-items: center; gap: 5px; line-height: 1.3;
}
.tag-gold { background: var(--gold-tint); color: var(--gold-dk); }
.tag-blue { background: var(--blue-tint); color: var(--blue-dk); }
.tag-slate { background: #e7e8ec; color: var(--slate); }
.tag-green { background: var(--green-tint); color: var(--green); }
.tag-red { background: var(--red-tint); color: var(--red); }
.tag-draft {
  background: repeating-linear-gradient(45deg, #f0ede3, #f0ede3 5px, #e8e4d4 5px, #e8e4d4 10px);
  color: #8a7a45; border: 1px dashed #cbbf94;
}
.tag-out { background: transparent; border: 1px solid var(--line-2); color: var(--muted); }

/* stone dots — team color only, never chrome */
.stone {
  width: 14px; height: 14px; border-radius: 50%; flex: none;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.55), inset 0 -2px 3px rgba(0, 0, 0, 0.18);
}
.stone-red { background: var(--red); }
.stone-yel { background: var(--yellow); }
.stone.sm { width: 11px; height: 11px; }
.stone.lg { width: 18px; height: 18px; }

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

.card {
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--sh);
}
.card-pad { padding: 20px; }
.card-kick {
  font-family: 'IBM Plex Mono', monospace; font-size: 11px;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; margin-bottom: 10px;
  display: flex; align-items: center; gap: 7px;
}
.card-kick svg { width: 14px; height: 14px; color: var(--blue); }
.card-head {
  display: flex; align-items: center; gap: 8px;
  background: var(--slate); color: #fff; padding: 11px 20px;
  font-weight: 600; font-size: 13px;
  letter-spacing: 0.04em; text-transform: uppercase;
}
.card-head svg { color: var(--gold); }
.card h2 { font-size: 16px; color: var(--slate); }
/* A linked card heading keeps the heading's colour (#1204). Without this
   the generic `a { color: var(--blue) }` wins: inheritance loses to any
   matching rule, however unspecific, so `.card h2`'s --slate never
   reaches the anchor inside it. `league_list.html` and
   `bonspiel_list.html` had been rendering their headings in the lighter
   blue since they were first linked, and nobody had placed why.

   This also holds on hover: (0,1,2) beats `a:hover` at (0,1,1), so the
   colour stays put while `a:hover`'s underline still lands. That
   underline is the whole affordance. */
.card h2 a { color: inherit; }
.cards {
  display: grid; gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

/* ---------- banners and messages ---------- */

.banner {
  display: flex; align-items: center; gap: 14px;
  background: var(--gold-tint); border: 1px solid #e6d6a3;
  border-radius: var(--r-lg); padding: 14px 18px; margin-bottom: 24px;
  font-size: 13.5px; color: var(--slate); flex-wrap: wrap;
}
.banner svg { color: var(--gold-dk); flex: none; }
.banner .banner-body { flex: 1; min-width: 14rem; }
.banner-blue { background: var(--blue-tint); border-color: #c9d8e5; }
.banner-blue svg { color: var(--blue-dk); }
.banner-green { background: var(--green-tint); border-color: #c3ddca; }
.banner-green svg { color: var(--green); }
.banner-red { background: var(--red-tint); border-color: #f0cbc5; }
.banner-red svg { color: var(--red); }

.messages { list-style: none; margin: 0 0 20px; padding: 0; }
.messages li {
  border-radius: var(--r-lg); padding: 11px 16px; margin-bottom: 8px;
  font-size: 13.5px; border: 1px solid var(--line);
  background: var(--white); color: var(--slate);
}
.messages li.success { background: var(--green-tint); border-color: #c3ddca; }
.messages li.warning { background: var(--gold-tint); border-color: #e6d6a3; }
.messages li.error { background: var(--red-tint); border-color: #f0cbc5; }
.messages li.info { background: var(--blue-tint); border-color: #c9d8e5; }

.note {
  background: var(--white); border: 1px solid var(--line);
  border-left: 3px solid var(--blue); border-radius: var(--r);
  padding: 10px 14px; font-size: 13px; color: var(--slate);
  margin-bottom: 16px;
}
.note b { color: var(--blue-dk); }
.note.flag { border-left-color: var(--gold); }
.note.flag b { color: var(--gold-dk); }

/* ---------- forms ---------- */

.field { margin-bottom: 18px; }
.field label, .field .field-heading {
  display: block; font-size: 13px; font-weight: 600;
  color: var(--slate); margin-bottom: 6px;
}
.field .hint, .field .help {
  font-size: 12px; color: var(--muted); font-weight: 400; margin-top: 5px;
}
.input, .select, .textarea,
.field input[type='text'], .field input[type='email'], .field input[type='password'],
.field input[type='date'], .field input[type='time'], .field input[type='number'],
/* `tel` too: the phone field became one for the number pad, and this
   list is keyed on the input type — so it silently dropped out of the
   form's styling and rendered as a bare narrow box beside eight fields
   that looked right. */
.field input[type='tel'],
.field select, .field textarea {
  width: 100%; font-family: inherit; font-size: 14px; color: var(--ink);
  background: var(--white); border: 1px solid var(--line-2);
  border-radius: var(--r); padding: 10px 12px;
}
.input:focus, .select:focus, .textarea:focus,
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-tint);
}
/* A select that sits in a table row rather than a form column. `.select`
   above is full-width by design; in a cell that stretches it across the
   column and it stops reading as a control. This is the same skin at the
   width of its content. */
.select-inline {
  font-family: inherit; font-size: 13.5px; color: var(--ink);
  background: var(--white); border: 1px solid var(--line-2);
  border-radius: var(--r); padding: 6px 10px;
}
.select-inline:hover { border-color: var(--slate-45); }
.select-inline:focus {
  outline: none; border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-tint);
}

.input::placeholder { color: var(--faint); }

/* Show/Hide on a password box — the button is injected by password.js,
   so none of this renders without it. `padding-right` on the input makes
   room for the button; without it a long password runs underneath. The
   type flips to `text` when revealed, which the type-keyed input styling
   above already covers. */
.pw { position: relative; display: block; }
.pw-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  font-family: inherit; font-size: 12px; font-weight: 600;
  color: var(--slate); background: transparent;
  border: 0; border-radius: var(--r);
  padding: 5px 8px; cursor: pointer;
}
.pw-toggle:hover { color: var(--ink); background: var(--paper); }
.pw-toggle:focus-visible {
  outline: none; box-shadow: 0 0 0 3px var(--blue-tint);
}
.pw > input { padding-right: 4rem; }
.textarea, .field textarea { resize: vertical; min-height: 76px; }

/* Django renders multiple-choice widgets as a bare <ul>. */
.field ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.field ul li { font-size: 14px; }
.field ul label {
  display: flex; align-items: center; gap: 9px;
  font-weight: 400; margin: 0; cursor: pointer;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--white); padding: 9px 12px;
}
.field ul label:hover { border-color: var(--slate-45); }
.field input[type='checkbox'], .field input[type='radio'] { width: auto; margin: 0; }
.field-inline label { display: inline-flex; align-items: center; gap: 8px; }

.errorlist {
  list-style: none; margin: 6px 0 0; padding: 0;
  color: var(--red); font-size: 12.5px; display: grid; gap: 3px;
}
.errorlist li { border: 0; background: none; padding: 0; }

/* A field that failed validation, marked on the control and not only in
   the message underneath it.

   Keyed off `aria-invalid`, which Django renders on an errored widget by
   itself, rather than a class the templates have to remember to add: one
   rule then covers every form in the app and cannot fall out of step with
   what the server actually rejected.

   Before this the message was the only signal, and on the account form it
   was not even red — `_account_fields.html` asked for `.help.is-error`,
   which was never defined here, so "that does not look like a phone
   number" rendered in exactly the same grey as the hint above it. A
   tester filled the form in, was refused, and could not see why. */
.field input[aria-invalid='true'],
.field select[aria-invalid='true'],
.field textarea[aria-invalid='true'],
.input[aria-invalid='true'],
.select[aria-invalid='true'],
.textarea[aria-invalid='true'] {
  border-color: var(--red);
  background: var(--red-tint);
}
.field input[aria-invalid='true']:focus,
.field select[aria-invalid='true']:focus,
.field textarea[aria-invalid='true']:focus,
.input[aria-invalid='true']:focus,
.select[aria-invalid='true']:focus,
.textarea[aria-invalid='true']:focus {
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(192, 57, 43, .16);
}

/* Help text is a block, not a paragraph, because the password field's is
   Django's own `<ul>` of the rules its validators enforce — and a `<ul>`
   inside a `<p>` closes the paragraph early and indents nothing.

   The reset of `list-style` and `display` is undoing `.field ul` above,
   which strips markers and lays out as a grid for the checkbox widgets.
   That is right for a column of tickable options and wrong for four
   sentences about what a password may not be. */
.field .help ul {
  display: block; list-style: disc;
  margin: 4px 0 0; padding-left: 1.15em;
}
.field .help li { font-size: inherit; margin: 0; }

.stacked { max-width: 40rem; }
.stacked .btn, .stacked button[type='submit'] { margin-right: 8px; }
button[type='submit'] {
  font-family: inherit; font-size: 14px; font-weight: 600;
  border-radius: var(--r); padding: 9px 16px; cursor: pointer;
  border: 1px solid var(--gold-dk); background: var(--gold); color: var(--slate);
}
button[type='submit']:hover { background: var(--gold-dk); color: #fff; }

/* A submit button that names a variant has to get it. The bare
   `button[type='submit']` rule above is an element+attribute selector
   (0,1,1), which outranks a plain `.btn-ghost` class (0,1,0) — so every
   submit button asking to be quiet came out gold instead. That put
   "Decline" on rink invitations and the pending banner in the same loud
   gold as the "Accept" next to it, which is the opposite of what the
   markup asked for. Re-stated here at (0,2,1) so the variant wins. */
button[type='submit'].btn-ghost {
  background: transparent; color: var(--slate); border-color: var(--line-2);
}
button[type='submit'].btn-ghost:hover {
  background: var(--white); border-color: var(--slate-45); color: var(--slate);
}
button[type='submit'].btn-blue { background: var(--blue); border-color: var(--blue-dk); color: #fff; }
button[type='submit'].btn-blue:hover { background: var(--blue-dk); color: #fff; }
button[type='submit'].btn-slate { background: var(--slate); border-color: var(--slate); color: #fff; }

/* Size is the same trap as colour, and was missed when colour was fixed.
   `button[type='submit']` sets font-size and padding at (0,1,1), which
   outranks `.btn-sm` at (0,1,0) — so a small submit button came out
   full-sized, next to an `<a class="btn btn-sm">` that obeyed. That is
   why Archive and Remove on the league list were visibly different
   sizes; 29 buttons across 16 templates were asking for a size they
   never got. */

button[type='submit'].btn-sm { padding: 6px 11px; font-size: 12.5px; }
button[type='submit'].btn-lg { padding: 14px 22px; font-size: 16px; }
.link-button {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--blue); font: inherit; font-size: 12.5px;
}
.link-button:hover { color: var(--blue-dk); text-decoration: underline; }
/* A submit button asking to look like a link has to get it. The bare
   `button[type='submit']` rule is an element+attribute selector (0,1,1)
   and outranks `.link-button` (0,1,0), so all four of its properties —
   background, border, padding, colour — were being overridden and every
   one of these rendered as a full gold primary button. Eighteen of them,
   across eight templates. Re-stated at (0,2,1) so the class wins. */
button[type='submit'].link-button {
  background: none; border: 0; padding: 0;
  color: var(--blue); font: inherit; font-size: 12.5px;
}
button[type='submit'].link-button:hover {
  background: none; color: var(--blue-dk); text-decoration: underline;
}
.cancel { font-size: 13.5px; color: var(--muted); }

/* Wraps rather than overflowing: the calendar's segmented control grows
   as facility-admin screens are added (Displays, Webhooks), and a phone
   is 390px wide. */
.seg {
  display: inline-flex; flex-wrap: wrap; max-width: 100%;
  background: #edece6; border-radius: var(--r); padding: 3px; gap: 2px;
}
.seg a, .seg button {
  font-family: inherit; font-size: 13px; font-weight: 600;
  border: 0; background: transparent; color: var(--muted);
  padding: 7px 14px; border-radius: 4px; cursor: pointer;
}
.seg a:hover { text-decoration: none; color: var(--slate); }
.seg a.on, .seg button.on { background: var(--white); color: var(--slate); box-shadow: var(--sh); }

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

.tablewrap { overflow-x: auto; border-radius: var(--r-lg); border: 1px solid var(--line); }
.table { width: 100%; border-collapse: collapse; font-size: 13.5px; background: var(--white); }
.table th {
  text-align: left; font-size: 11.5px; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--muted); font-weight: 600;
  padding: 11px 14px; border-bottom: 2px solid var(--line-2); white-space: nowrap;
}
.table td { padding: 12px 14px; border-bottom: 1px solid var(--line); }
.table tbody tr:hover { background: #faf9f4; }
.table .num { text-align: right; font-variant-numeric: tabular-nums; font-family: 'IBM Plex Mono', monospace; }
.table .rk { font-family: 'IBM Plex Mono', monospace; color: var(--faint); font-weight: 600; }
/* A checkbox column: only as wide as the box, and centred on the row
   rather than sitting on the text baseline beside two lines of name. */
.table .selcell { width: 1%; white-space: nowrap; vertical-align: middle; }
.table .selcell input { width: 15px; height: 15px; accent-color: var(--gold-dk); }

/* definition lists — league format, request detail */
.detail { display: grid; grid-template-columns: 11rem 1fr; gap: 0; margin: 0; }
.detail dt {
  font-family: 'IBM Plex Mono', monospace; font-size: 11px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
  padding: 11px 0; border-bottom: 1px solid var(--line);
}
.detail dd {
  margin: 0; padding: 11px 0; font-size: 14px;
  border-bottom: 1px solid var(--line); color: var(--slate);
}
@media (max-width: 40rem) {
  .detail { grid-template-columns: 1fr; }
  .detail dt { padding-bottom: 0; border-bottom: 0; }
}

/* ---------- ice calendar ---------- */

.weeknav {
  display: flex; align-items: center; gap: 16px;
  margin-bottom: 14px; font-size: 13.5px;
}
.weeknav strong { color: var(--slate); }

.weekgrid {
  display: grid; grid-template-columns: repeat(7, minmax(9rem, 1fr));
  background: var(--white); border: 1px solid var(--line-2);
  border-radius: var(--r-lg); overflow: hidden;
}
.weekgrid .day { border-left: 1px solid var(--line); min-height: 9rem; padding: 0 0 8px; }
.weekgrid .day:first-child { border-left: 0; }
.weekgrid .day.today { background: var(--gold-tint); }
.dayhead { padding: 10px 8px; text-align: center; border-bottom: 1px solid var(--line-2); }
.dayhead .dow {
  font-family: 'IBM Plex Mono', monospace; font-size: 11px;
  color: var(--muted); text-transform: uppercase;
}
.dayhead .dom { font-weight: 700; color: var(--slate); }
.day-add {
  display: block; text-align: center; font-size: 12px;
  color: var(--faint); padding: 6px;
}
.day-add:hover { color: var(--blue-dk); }

.blocks { list-style: none; margin: 0; padding: 6px; display: grid; gap: 6px; }

/* An ice block. Blue is the club you are looking as; gold is the other
   club. Matches the mockup's two-club legend. */
.iceblock {
  background: #efe7d6; border: 1px solid var(--gold-dk);
  border-left: 3px solid var(--gold-dk);
  border-radius: var(--r); padding: 7px 8px; font-size: 11.5px;
}
.iceblock .who { font-weight: 700; color: var(--gold-dk); }
.iceblock.own { background: var(--blue-tint); border-color: var(--blue); border-left-color: var(--blue); }
.iceblock.own .who { color: var(--blue-dk); }
.iceblock .when { color: var(--slate); }
.iceblock .meta { color: var(--muted); }
.iceblock .block-actions {
  display: flex; gap: 8px; align-items: center; margin-top: 5px;
  font-size: 11.5px;
}
.iceblock .block-actions form { display: inline; }
.iceblock.pending {
  background: repeating-linear-gradient(45deg, #fff, #fff 5px, #f6efdc 5px, #f6efdc 10px);
  border: 1.5px dashed var(--gold-dk);
}

.seasonday { margin-bottom: 20px; }
.seasonday h2 {
  font-size: 14px; color: var(--slate);
  padding-bottom: 6px; border-bottom: 1px solid var(--line-2); margin-bottom: 8px;
}
.seasonday .blocks { padding: 0; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }

/* ---------- lists ---------- */

.requests { list-style: none; margin: 0 0 20px; padding: 0; }
.requests li {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 14px; background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r);
  margin-bottom: 6px; font-size: 13.5px;
}
.requests .req-club { color: var(--muted); font-size: 12.5px; }
.requests .req-sheets { font-family: 'IBM Plex Mono', monospace; font-size: 12.5px; color: var(--muted); }

.roster { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 6px; }
.roster li {
  display: flex; justify-content: space-between; gap: 10px;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r); padding: 8px 11px; font-size: 13px;
}
.roster .pos { color: var(--muted); font-size: 11px; text-transform: uppercase; }

.status { font-size: 11.5px; font-weight: 600; padding: 3px 9px; border-radius: 20px; }
.status-pending { background: var(--gold-tint); color: var(--gold-dk); }
.status-approved { background: var(--green-tint); color: var(--green); }
.status-declined { background: var(--red-tint); color: var(--red); }
.status-withdrawn { background: #e7e8ec; color: var(--slate); }

/* ---------- utilities ---------- */

.muted { color: var(--muted); }
.mono { font-family: 'IBM Plex Mono', monospace; }
.empty { color: var(--faint); font-size: 13px; }
.divider { height: 1px; background: var(--line); border: 0; margin: 16px 0; }
.iconpair { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: 13px; }
.iconpair svg { width: 15px; height: 15px; color: var(--faint); }

/* ---------- auth ---------- */

.authwrap {
  flex: 1; display: flex; align-items: center; justify-content: center;
  padding: 48px 24px;
}
.authcard { width: 100%; max-width: 25rem; }
.authcard .card-pad { padding: 28px; }
.authcard h1 { font-size: 22px; color: var(--slate); margin-bottom: 4px; }

/* ---------- responsive ---------- */

@media (max-width: 60rem) {
  .weekgrid { grid-template-columns: repeat(2, 1fr); }
  .weekgrid .day { border-left: 1px solid var(--line); border-top: 1px solid var(--line); }
}
@media (max-width: 40rem) {
  .appnav { height: auto; padding: 10px 16px; gap: 12px; }
  .navlinks { flex-wrap: wrap; height: auto; }
  .navlinks a { padding: 6px 10px; border-bottom: 0; }
  .navlinks a.on { color: var(--gold); }
  .page, .pagebar { padding-left: 16px; padding-right: 16px; }
  .weekgrid { grid-template-columns: 1fr; }
}

/* ---------- team formation board (#638) ---------- */

/* Dense by design — the registrar is comparing preferences across a
   pool of forty-odd names, so this screen trades whitespace for how
   much fits on one laptop screen. */

.page-formation { padding: 0; }
.page-formation .messages, .page-formation > .note { margin: 16px 24px 0; }

.formation {
  display: grid;
  grid-template-columns: 22rem 1fr;
  align-items: start;
  min-height: 40rem;
  margin-top: 16px;
  border-top: 1px solid var(--line-2);
}
.formation.is-saving { opacity: 0.6; pointer-events: none; }

.tray {
  border-right: 1px solid var(--line-2);
  background: var(--white);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  max-height: 100vh;
}
.tray-head { padding: 14px 16px; border-bottom: 1px solid var(--line); }
.tray-title {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px; font-size: 14px; color: var(--slate);
}
.tray-search input { font-size: 13px; }
.tray-list { list-style: none; margin: 0; padding: 10px 12px; overflow: auto; flex: 1; }

.tray-card {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  padding: 9px 11px;
  margin-bottom: 8px;
  background: var(--white);
  cursor: grab;
}
.tray-card:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.tray-card.dragging {
  border: 2px solid var(--gold);
  background: var(--gold-tint);
  cursor: grabbing;
  box-shadow: var(--sh-md);
}
.tray-card .grip { color: var(--faint); }
.tray-name { font-weight: 600; font-size: 13.5px; }
.tray-meta { color: var(--muted); font-size: 11.5px; }
.tray-move { grid-column: 1 / -1; display: flex; gap: 6px; align-items: center; }
.tray-move select {
  flex: 1; font-family: inherit; font-size: 12px;
  border: 1px solid var(--line-2); border-radius: var(--r); padding: 4px 6px;
  background: var(--white); color: var(--ink);
}
.tray-empty { padding: 16px 4px; }
[data-drop-tray].drop-target { background: var(--blue-tint); }

.teams { padding: 16px 20px; background: var(--paper); }
.teamgrid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
}

.teamcard.complete { border-color: var(--green); border-width: 1.5px; }
.teamcard-head {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 15px; border-bottom: 1px solid var(--line);
}
.teamcard.complete .teamcard-head {
  background: var(--green-tint);
  border-bottom-color: #cfe3d3;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.teamname { flex: 1; min-width: 0; }
.teamname-input {
  width: 100%; border: 0; padding: 2px 0; background: transparent;
  font-family: inherit; font-weight: 700; font-size: 14px; color: var(--slate);
}
.teamname-input:focus {
  outline: none; border-bottom: 1px solid var(--blue); background: var(--white);
}
.teamcard-remove { color: var(--muted); font-size: 12px; }
.teamcard-remove:hover { color: var(--red); }

.seats { list-style: none; margin: 0; padding: 10px 12px; display: grid; gap: 6px; }
.seat {
  display: flex; align-items: center; gap: 9px;
  font-size: 13px; border-radius: var(--r); padding: 6px 8px;
}
.seat-pos { width: 3.5rem; justify-content: center; }
.seat-name { font-weight: 600; flex: 1; }
.seat.open {
  border: 1px dashed var(--line-2);
  color: var(--faint); font-size: 12.5px;
}
.seat.open .seat-empty { flex: 1; }
.seat.drop-target {
  border: 2px dashed var(--blue);
  background: var(--blue-tint);
  color: var(--blue-dk);
  font-weight: 600;
}
/* Removing somebody is a real action, so the control has to be legible.
   --faint on white was already thin; on the gold background it was
   getting from the rule above, it was almost invisible. */
.seat-clear button, .seat-clear button[type='submit'] {
  color: var(--muted); font-size: 17px; line-height: 1; font-weight: 600;
}
.seat-clear button:hover, .seat-clear button[type='submit']:hover {
  color: var(--red); text-decoration: none;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The design flags this screen as desktop-only: dragging across two
   dense panels does not work on a phone. The panels stack and the
   "Move to…" select carries the interaction instead. */
@media (max-width: 60rem) {
  .formation { grid-template-columns: 1fr; }
  .tray { position: static; max-height: none; border-right: 0; border-bottom: 1px solid var(--line-2); }
  .tray-card { cursor: default; }
  .tray-card .grip { display: none; }
}

/* ---------- draw grid (#639) ---------- */

.drawlegend {
  display: flex; gap: 16px; flex-wrap: wrap;
  font-size: 12.5px; color: var(--muted); margin-bottom: 14px;
}
.swatch-draft {
  width: 12px; height: 12px; border-radius: 3px;
  background: repeating-linear-gradient(45deg, #f0ede3, #f0ede3 4px, #e8e4d4 4px, #e8e4d4 8px);
  border: 1px dashed #cbbf94;
}

.drawgrid { width: 100%; border-collapse: separate; border-spacing: 8px; table-layout: fixed; }
.drawgrid th {
  font-size: 12px; background: var(--slate); color: #fff;
  padding: 7px; border-radius: var(--r); font-weight: 600;
}
.drawgrid th.draw-when, .drawgrid td.draw-when {
  background: transparent; color: var(--muted);
  text-align: left; width: 7rem; font-size: 11.5px;
}
.drawgrid td.draw-when { color: var(--slate); font-weight: 600; vertical-align: middle; }
.drawgrid td { vertical-align: top; }

.drawcell {
  background: var(--white); border: 1px solid var(--line-2);
  border-radius: var(--r); padding: 9px 10px;
  font-size: 12.5px; position: relative; min-height: 3.2rem;
}
.drawcell.draft {
  background: repeating-linear-gradient(45deg, #f7f5ec, #f7f5ec 5px, #f0ecdd 5px, #f0ecdd 10px);
  border: 1px dashed #cbbf94;
}
.drawcell.pinned { border-color: var(--gold-dk); }
.drawcell.placeholder { background: var(--blue-tint); border: 1px dashed var(--blue); }
.drawcell.empty {
  border: 1px dashed var(--line-2); color: var(--faint);
  text-align: center; background: transparent;
}
/* An empty slot the chair can fill (#1286). A link, so it reads as
   somewhere to go rather than decoration, and it fills the cell so the
   target is the whole box rather than a plus sign to aim at. */
a.drawcell-add {
  display: flex; align-items: center; justify-content: center;
  color: var(--faint); text-decoration: none; font-size: 16px;
  min-height: 3.2rem;
}
a.drawcell-add:hover,
a.drawcell-add:focus-visible {
  border-style: solid; border-color: var(--gold-dk);
  color: var(--gold-dk); background: var(--white); text-decoration: none;
}
/* A cell you type two names into (#1286). The inputs fill the cell so
   the click target is the whole box, and they carry no border of their
   own — the cell's dashed outline is the affordance, and a hundred
   bordered inputs on one page reads as a form rather than a grid. */
.drawcell-fill {
  display: flex; flex-direction: column; gap: 2px; justify-content: center;
}
.drawcell-fill .cellname {
  border: 0; background: transparent; padding: 1px 2px;
  font: inherit; color: var(--slate); width: 100%; min-width: 0;
}
.drawcell-fill .cellname::placeholder { color: var(--faint); }
.drawcell-fill .cellname:focus {
  outline: 2px solid var(--gold-dk); outline-offset: 1px; background: var(--white);
}
.drawcell-fill.is-filled {
  border-style: solid; border-color: var(--gold-dk); background: var(--white);
}
.drawcell-fill.is-filled .cellname:first-child { font-weight: 600; }
/* Enter jumps a row, so the landing cell must not sit under the
   sticky header. */
.drawcell-fill .cellname { scroll-margin-block: 5rem; }
.drawgrid.is-typable .drawcell-add { display: none; }

/* Ice this row's block does not cover. The column has to stay aligned
   with the sheet it is headed by, so the cell stays and goes quiet
   rather than vanishing. */
.drawcell-noice {
  border-style: dotted; color: var(--line-2); background: transparent;
  display: flex; align-items: center; justify-content: center;
}

/* The team list beside the grid. */
.drawlayout { display: flex; gap: 18px; align-items: flex-start; }
.drawlayout-grid { flex: 1; min-width: 0; }
.drawteams {
  flex: none; width: 13rem; position: sticky; top: 12px;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--white); padding: 10px 12px;
}
.drawteams-head { font-size: 13px; margin-bottom: 6px; }
.drawteams-list { list-style: none; margin: 0; padding: 0; font-size: 12.5px; }
.drawteams-list li {
  display: flex; justify-content: space-between; gap: 8px;
  padding: 3px 0; border-bottom: 1px solid var(--line-1);
}
.drawteams-list li:last-child { border-bottom: 0; }
.drawteams-list .is-short .mono { color: var(--gold-dk); }
.drawteams-list .linkish {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--slate); cursor: pointer; text-align: left;
}
.drawteams-list .linkish:hover { text-decoration: underline; }
@media (max-width: 60rem) {
  .drawlayout { flex-direction: column; }
  .drawteams { width: 100%; position: static; }
}

.drawcell-home { font-weight: 600; }
.drawcell-tbd { font-weight: 600; color: var(--blue-dk); }
.drawcell-pin { position: absolute; top: 5px; right: 5px; }
.drawcell-pin button { color: var(--line-2); }
.drawcell.pinned .drawcell-pin button { color: var(--gold-dk); }
.drawcell-pin button:hover { color: var(--gold-dk); }

/* The filter control over a list of registrations (#1118).
   Collapsed by default: a secondary control should not be the loudest
   band on the screen. Opens itself whenever a filter is on, and names
   the active values in the summary, so a narrowed table is never
   explained only by a shut box. */
.filterbar {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--white); margin: 0 0 12px;
}
.filterbar[open] { background: var(--paper); }
.filterbar-summary {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  padding: 8px 12px; cursor: pointer; list-style: none;
  font-size: 13px; color: var(--muted);
}
/* The default triangle sits oddly against a row of tags, and the chevron
   below carries the same meaning in this design's own language. */
.filterbar-summary::-webkit-details-marker { display: none; }
.filterbar-summary::after {
  content: ''; margin-left: auto;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 5px solid var(--faint);
}
.filterbar[open] .filterbar-summary::after { transform: rotate(180deg); }
.filterbar-summary:hover { color: var(--ink); }
.filterbar-summary svg { color: var(--faint); flex: none; }
.filterbar-title { font-weight: 600; color: var(--ink); }
.filterbar-hint { color: var(--faint); }
.filterbar-body { padding: 2px 12px 10px; }
/* Inside the control the label no longer needs to fight for attention —
   the summary above it already says what this band is. */
.filterbar-body .chiprow { margin-bottom: 6px; }
.filterbar-body .chiprow:last-child { margin-bottom: 0; }

/* Filter chips over the placement table. Each carries its count, so the
   row answers "how many for each night" as well as narrowing to one. */
.chiprow {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: 0 0 10px;
}
.chiprow-label {
  font-size: 11px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--faint); margin-right: 2px;
}
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 20px;
  border: 1px solid var(--line-2); background: var(--white);
  color: var(--slate); text-decoration: none; font-size: 12.5px;
}
.chip:hover, .chip:focus-visible { border-color: var(--slate-45); text-decoration: none; }
.chip.on { background: var(--gold-tint); border-color: var(--gold-dk); color: var(--gold-dk); font-weight: 600; }
.chip-count { font-variant-numeric: tabular-nums; color: var(--faint); font-size: 11.5px; }
.chip.on .chip-count { color: var(--gold-dk); }
.chip-clear { font-size: 12.5px; color: var(--muted); margin-left: 2px; }

/* "Already have an account?" and its one button, on one line. */
.signin-inline {
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
  margin: 26px 0 10px;
}

/* Required / optional markers beside a field label. Quiet: the label is
   what you read, this is what you check. */
.req, .opt {
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; margin-left: 7px; vertical-align: 1px;
}
.req { color: var(--gold-dk); }
.opt { color: var(--faint); font-weight: 500; }

/* The photo control. The native file input is an unlabelled grey button
   in a column of bordered fields; this gives it the same skin as its
   neighbours. The input is moved off-screen rather than hidden, so it
   keeps its place in the tab order. */
.filedrop {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px; border: 1px dashed var(--line-2);
  border-radius: var(--r); background: var(--surface-2);
  color: var(--muted); cursor: pointer; font-size: 13.5px;
}
.filedrop:hover { border-color: var(--gold-dk); color: var(--slate); }
.filedrop:focus-within {
  border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-tint);
}
.filedrop-text { font-weight: 600; color: var(--slate); }
.filedrop-hint { margin-left: auto; font-size: 11.5px; color: var(--faint); }
.filedrop input[type='file'] {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* A label/value list — a pending member's profile, read before deciding
   on them. Two columns on anything but a phone. */
.factlist {
  display: grid; grid-template-columns: 10rem 1fr;
  gap: 4px 14px; margin: 0; font-size: 14px;
}
.factlist dt { color: var(--muted); font-size: 12.5px; padding-top: 2px; }
.factlist dd { margin: 0; }
@media (max-width: 560px) {
  .factlist { grid-template-columns: 1fr; }
  .factlist dd { margin-bottom: 8px; }
}

/* Sortable column headings. A link, so a sorted view has a URL the
   chair can send to the other chair, and so it works without JS. */
.sorthead {
  display: inline-flex; align-items: center; gap: 3px;
  color: inherit; text-decoration: none; white-space: nowrap;
}
.sorthead:hover, .sorthead:focus-visible { color: var(--slate); text-decoration: underline; }
.sorthead svg { color: var(--gold-dk); }
/* A question heading is a sentence with the question mark taken off, so
   it needs to wrap rather than stretch the column to fit one line. */
.table th.qcol { white-space: normal; min-width: 7rem; max-width: 11rem; }

/* Hand-edit controls (#844). Shown, not revealed on hover: a control
   nobody can see is a control nobody uses, and there is no hover on the
   tablet these grids get read on at the rink. The hover-reveal this
   replaced is why the buttons were reported missing. */
.drawcell-actions { display: flex; gap: 4px; margin-top: 8px; }
.cellbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: var(--r);
  color: var(--muted); border: 1px solid var(--line);
  background: var(--white); text-decoration: none;
}
.cellbtn:hover, .cellbtn:focus-visible {
  color: var(--slate); border-color: var(--slate-45); text-decoration: none;
}
.cellbtn-danger:hover, .cellbtn-danger:focus-visible {
  color: var(--red); border-color: var(--red);
}

/* Dragging a game from one cell to another. Only offered where the
   browser can actually do it — draw-grid.js adds the class. */
.is-draggable .drawcell[data-game] { cursor: grab; }
.is-draggable .drawcell.is-dragging {
  opacity: .5; border-style: dashed; border-color: var(--gold-dk); cursor: grabbing;
}
.drawgrid td.is-drop-target { outline: 2px dashed var(--gold); outline-offset: -2px; }
.cellstatus {
  display: block; margin-top: 4px; font-size: 11.5px;
  color: var(--green); font-weight: 600;
}
.cellstatus.is-error { color: var(--red); }

.byerow td { font-size: 12px; color: var(--muted); padding-top: 0; }

/* ---------- score entry (#640) — mobile-first ---------- */

/* This screen is used standing on the ice with a phone, so targets are
   large and typing is kept to a minimum. */
.scoreform .scoremode { width: 100%; margin-bottom: 18px; }
.scoreform .scoremode button { flex: 1; }
.scorerow {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 12px;
}
.scorerow label { flex: 1; font-weight: 600; font-size: 15px; margin: 0; }
.scorebox {
  width: 5.5rem; flex: none;
  font-size: 22px; font-weight: 700; text-align: center;
  padding: 12px 8px; font-variant-numeric: tabular-nums;
}
.endstable input { padding: 6px 8px; text-align: center; }
.endstable td { padding: 6px 8px; }

/* ---------- live draft board (#642) ---------- */

.draftbar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 16px; margin-bottom: 16px;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.draftbar-turn { display: flex; align-items: center; gap: 12px; }
.draftbar-round {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted);
}
.draftbar-onclock { font-size: 17px; color: var(--slate); }

.draftgrid { display: grid; grid-template-columns: 22rem 1fr; gap: 20px; align-items: start; }
.draftpool {
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden;
  position: sticky; top: 12px; max-height: 80vh; display: flex; flex-direction: column;
}
.draftpool .tray-list { max-height: none; }
.draftpool .tray-card { cursor: default; grid-template-columns: 1fr auto; }

.teamcard.onclock { border-color: var(--gold); border-width: 2px; }
.teamcard.onclock .teamcard-head { background: var(--gold-tint); }

.draftorder { padding-left: 1.4rem; }
.draftorder li { margin-bottom: 6px; }

/* The projected board: legible from across a room. */
.bigboard .draftbar-onclock { font-size: 28px; }
.bigboard .teamcard { font-size: 16px; }
.bigboard .seat { font-size: 16px; }
.bigboard .tray-name { font-size: 16px; }
.bigboard .draftpool { position: static; max-height: none; }

@media (max-width: 60rem) {
  .draftgrid { grid-template-columns: 1fr; }
  .draftpool { position: static; max-height: none; }
}

/* ---------- display screens (#643) ---------- */

/* A wall screen is read from across a room, so everything is larger and
   nothing is interactive. */
body.display { background: var(--slate); color: #eceef4; }

.displayhead {
  display: flex; align-items: center; gap: 14px;
  padding: 18px 32px; background: var(--slate-surface);
  border-bottom: 3px solid var(--gold);
}
.displayhead .mark {
  width: 36px; height: 36px; flex: none;
  background: var(--paper); border-radius: 50%;
}
.displayhead-title { font-size: 24px; font-weight: 700; color: #fff; }
.displayhead-sub { font-size: 15px; color: rgba(255, 255, 255, 0.7); }
.displayclock { font-size: 26px; font-weight: 600; color: var(--gold); }

.displaybody { padding: 28px 32px; }
.displaysplit {
  display: grid; gap: 28px;
  grid-template-columns: repeat(auto-fit, minmax(28rem, 1fr));
  align-items: start;
}
.panel-title {
  font-size: 22px; color: #fff; margin-bottom: 14px;
}
.panel-title .muted { color: rgba(255, 255, 255, 0.55); font-weight: 400; }

.displaytable { background: transparent; color: #eceef4; font-size: 18px; }
.displaytable th {
  color: rgba(255, 255, 255, 0.6); font-size: 13px;
  border-bottom-color: rgba(255, 255, 255, 0.25);
}
.displaytable td { border-bottom-color: rgba(255, 255, 255, 0.12); padding: 12px 14px; }
.displaytable tbody tr:hover { background: transparent; }
.displaytable .muted { color: rgba(255, 255, 255, 0.55); }
.displaytable .rk { color: rgba(255, 255, 255, 0.45); }
.display .empty { color: rgba(255, 255, 255, 0.5); font-size: 18px; }
.display .iceblock { background: var(--slate-surface); border-color: rgba(255,255,255,.2); }
.display .iceblock .who { color: var(--gold); }
.display .iceblock .when { color: #eceef4; }
.display .iceblock .meta { color: rgba(255, 255, 255, 0.55); }

.displaylinks { display: flex; gap: 16px; flex-wrap: wrap; font-size: 17px; }
.displaylinks a { color: var(--gold); }

/* ---------- display portal rotation editor ---------- */

.panelrota li { gap: 10px; }
.panelrota form { display: flex; align-items: center; gap: 4px; margin: 0; }
.panelseconds .input-sm { width: 4.5rem; }
.input-sm { padding: 4px 7px; font-size: 12.5px; }
.panelrota .link-button[disabled] { opacity: .3; cursor: default; }

/* Visible to a screen reader, not on screen: the per-panel seconds box
   is labelled by its row, which a sighted person reads and a screen
   reader would otherwise meet as a bare number field. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ---------- ice-entrance kiosk (#644) ---------- */

/* Sized for a tablet at the door, tapped by someone in gloves on their
   way onto the ice. Nothing here is smaller than a thumb, and the whole
   board is legible from arm's length. */

.kiosk { padding: 20px 22px 40px; }
.kiosk-board {
  display: grid; gap: 18px;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}
.kiosk-sheet {
  background: rgba(255, 255, 255, .04);
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 14px; padding: 14px 16px 18px;
}
.kiosk-sheetname {
  font-size: 15px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 12px;
}
.kiosk-game {
  background: rgba(255, 255, 255, .05);
  border: 1px solid rgba(255, 255, 255, .09);
  border-radius: 12px; padding: 14px; margin-bottom: 12px;
}
.kiosk-game.is-live { border-color: rgba(201, 165, 74, .55); }
.kiosk-game.is-over { opacity: .55; }
.kiosk-when {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 14px; color: rgba(255, 255, 255, .62);
}
.kiosk-league { font-size: 13px; }
.kiosk-teams {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  font-size: 21px; font-weight: 600; margin: 8px 0 12px; color: #fff;
}
.kiosk-vs { font-size: 13px; color: rgba(255, 255, 255, .45); }
.kiosk-state { font-size: 14px; color: rgba(255, 255, 255, .6); margin: 6px 0; }
.kiosk-free { color: rgba(255, 255, 255, .45); font-size: 14px; }

.kiosk-actions { display: grid; gap: 10px; }
.kiosk-btn {
  width: 100%; min-height: 60px; padding: 0 18px;
  font-size: 17px; font-weight: 600; cursor: pointer;
  border-radius: 12px; border: 1px solid rgba(255, 255, 255, .22);
  background: rgba(255, 255, 255, .09); color: #fff;
}
.kiosk-btn:active { transform: translateY(1px); }
.kiosk-btn-go { background: var(--gold); border-color: var(--gold); color: #14202b; }
.kiosk-btn-stop { background: rgba(190, 70, 60, .85); border-color: rgba(190, 70, 60, .9); }
.kiosk-finish { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.kiosk-finish .kiosk-btn { grid-column: 1 / -1; }
.kiosk-score {
  /* A grid item defaults to min-width:auto, and an <input>'s intrinsic
     min-content width is its `size` attribute — about 20 characters. So
     on a four-sheet board the two score boxes refuse to shrink into
     their 1fr columns and burst out of the card, over the next sheet.
     Only shows up once the columns are narrow enough to matter, which
     is exactly the wall-mounted four-sheet case this screen is for. */
  min-width: 0;
  min-height: 60px; font-size: 22px; text-align: center;
  border-radius: 12px; border: 1px solid rgba(255, 255, 255, .22);
  background: rgba(0, 0, 0, .25); color: #fff;
}

.kiosk-offline, .kiosk-toast {
  border-radius: 12px; padding: 12px 16px; margin-bottom: 14px; font-size: 15px;
}
.kiosk-offline { background: rgba(190, 140, 40, .2); border: 1px solid rgba(201, 165, 74, .5); color: #f2e2bb; }
.kiosk-toast { background: rgba(255, 255, 255, .1); color: #fff; }
.kiosk-toast.is-error { background: rgba(190, 70, 60, .3); }

.codeblock {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r);
  padding: 14px 16px; overflow-x: auto; font-size: 12.5px;
  font-family: 'IBM Plex Mono', monospace;
}

/* ---------- bonspiels (#645) ---------- */

.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 560px) { .row2 { grid-template-columns: 1fr; } }

/* The bracket scrolls sideways in its own box — a 16-rink double
   elimination is simply wider than a laptop, and the page itself must
   never scroll horizontally. */
.bracketwrap { overflow-x: auto; padding-bottom: 8px; }
.bracket { display: flex; gap: 26px; align-items: flex-start; min-width: min-content; }
.bracket-round { min-width: 15rem; }
.bracket-round h3 {
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 8px;
}
.bracket-match {
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r); padding: 9px 11px; margin-bottom: 10px; font-size: 13px;
}
.bracket-match.is-done { border-left: 3px solid var(--green); }
.bracket-match.is-bye { opacity: .55; font-style: italic; }
.bracket-side { display: flex; justify-content: space-between; gap: 10px; }
.bracket-side.won { font-weight: 700; }
.bracket-side .muted { font-weight: 400; }
.bracket-round-title {
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 0 0 8px;
}

/* League playoff seeding (#845) — read far more often than edited. */
.seedlist { margin: 0 0 12px; padding-left: 1.4em; }
.seedlist li { padding: 2px 0; }
.seedlist .tag { margin-left: 6px; }
.bracket-meta { color: var(--muted); font-size: 11.5px; margin-top: 5px; }

.entryrow .tag { margin-left: 4px; }
.pooltable { margin-bottom: 20px; }
.displaytable .num { text-align: right; font-variant-numeric: tabular-nums; }
.bracket-score { display: flex; align-items: center; gap: 4px; margin: -4px 0 12px; }
.bracket-score .input-sm { width: 3.5rem; }

/* ---------- copy-to-clipboard toast (#691) ---------- */

.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translate(-50%, 12px);
  background: var(--slate); color: #fff; font-size: 13.5px; font-weight: 500;
  padding: 11px 18px; border-radius: var(--r); box-shadow: var(--sh-lg);
  z-index: 60; opacity: 0; pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.toast.is-on { opacity: 1; transform: translate(-50%, 0); }
.toast.is-error { background: var(--red); }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }

.signuplink { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; }
.signuplink-url {
  font-size: 12.5px; margin-top: 8px; word-break: break-all; color: var(--slate);
}

/* ---- Modal ---------------------------------------------------------------
 * A page shown in place rather than navigated to (see js/modal.js). Native
 * <dialog>, so the browser handles focus trapping, Escape, and the top
 * layer; this only has to dress it.
 */
.modal {
  position: relative;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 0; background: var(--paper); color: var(--ink);
  width: min(720px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  box-shadow: 0 18px 50px rgba(45, 48, 66, 0.28);
  overflow: hidden;
}
.modal::backdrop { background: rgba(45, 48, 66, 0.45); }
.modal-body {
  padding: 24px; overflow-y: auto; max-height: calc(100vh - 64px);
}
/* The modal already frames its content, so a page's own outer chrome would
   read as a box inside a box. */
.modal-body .page-narrow { max-width: none; }
.modal-body .page-head { margin-top: 0; }
.modal-body .page-head .btnrow { display: none; }
.modal-body h1 { font-size: 22px; }
/* A page's card is its own frame. Inside a dialog that frame is already
   there, so keeping it draws a box inside a box — which is what the
   `.page-narrow` and `.page-head` rules above are already about. */
.modal-body .card {
  background: none; border: 0; box-shadow: none; border-radius: 0;
}
.modal-body .card-pad { padding: 0; }
.modal-body .stacked { max-width: none; }
/* Room for the dismiss in the corner, so a long title does not run under it. */
.modal-body h1 { padding-right: 36px; }

.modal-x { position: absolute; top: 12px; right: 12px; margin: 0; z-index: 1; }
.modal-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 999px;
  border: 1px solid transparent; background: transparent;
  color: var(--muted); cursor: pointer;
}
.modal-close:hover, .modal-close:focus-visible {
  background: var(--white); border-color: var(--line-2); color: var(--slate);
}

/* ---- Sheet ordering list -------------------------------------------------
 * Rows are the order: top to bottom is left to right in the building.
 * Dragging is an enhancement (js/sheet-order.js); the arrows in each row
 * are the control it sits on top of.
 */
.sheetlist { list-style: none; margin: 0; padding: 0; }
.sheetrow {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border: 1px solid var(--line);
  border-radius: var(--r); background: var(--white); margin-bottom: 8px;
}
/* Wide enough for the longest thing a facility actually calls a sheet
   ("Championship"), rather than the input's default ~20 characters. */
.sheetrow .sheetlabel { width: 16rem; flex: none; }
.sheetrow .grip {
  display: none; color: var(--faint); cursor: grab;
}
/* Only offered where dragging actually works — the class is added by the
   script, so a browser it does not run in never shows a handle it cannot
   use. */
.is-draggable .grip { display: inline-flex; }
.is-draggable .sheetrow:hover { border-color: var(--slate-45); }
.rowstatus {
  font-size: 12px; color: var(--green); font-weight: 600;
  white-space: nowrap; min-width: 4.5rem;
}
.rowstatus.is-error { color: var(--red); white-space: normal; }
.sheetrow.is-dragging {
  opacity: 0.55; border-style: dashed; border-color: var(--gold-dk);
  cursor: grabbing;
}

/* ---------- league registration questions (#841) ---------- */

/* Same shape as the sheet list, which is the other drag-to-order screen —
   a grip that only appears where dragging works, and a per-row status
   that is the only confirmation there is once saving happens on its own. */
.questionlist { margin: 0 0 18px; }
.questionrow {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 12px; border: 1px solid var(--line);
  border-radius: var(--r); background: var(--white); margin-bottom: 8px;
}
.questionrow .grip { display: none; color: var(--faint); cursor: grab; padding-top: 2px; }
.is-draggable .questionrow .grip { display: inline-flex; }
.is-draggable .questionrow:hover { border-color: var(--slate-45); }
.questionrow.is-dragging {
  opacity: 0.55; border-style: dashed; border-color: var(--gold-dk);
  cursor: grabbing;
}
.questionrow-body { flex: 1; min-width: 0; }
.questionrow-meta { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.questionrow-actions {
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap; flex: none;
}

/* A field that does not apply to the question type currently chosen.
   Dimmed rather than hidden: it stays where it was so the form does not
   jump under the cursor every time the type changes. */
.field.is-disabled { opacity: .5; }
.field.is-disabled textarea,
.field.is-disabled input { cursor: not-allowed; background: var(--paper); }

@media (max-width: 640px) {
  .questionrow { flex-wrap: wrap; }
  .questionrow-actions { width: 100%; }
}

/* Small forms that sit inline in a list row — the role select, and the
   single-button actions beside it. Without this each <form> takes a line
   of its own and the row falls apart. */
.inline-form {
  display: inline-flex; align-items: center; gap: 6px; margin-left: 8px;
}
.inline-form .input { font-size: 13px; padding: 6px 10px; }

/* ---------- error pages ---------- */
/* Shared by the 404 and the 403. The 500 cannot use this file: it renders
   with no request and no database, so it carries its own copy of what it
   needs inline. See `sites/broomstack/errors.py` for why. */

.errpage { max-width: 34rem; margin: 6vh auto 0; text-align: center; }

/* The house is the zero in the numeral. Four rings, the real colours:
   12-foot blue, 8-foot white, 4-foot red, button white. */
.errmark {
  display: flex; align-items: center; justify-content: center;
  gap: 10px; margin-bottom: 26px;
}
.errmark span {
  font-size: 92px; font-weight: 700; line-height: 1;
  color: var(--slate); letter-spacing: -.02em;
}
.errmark svg { width: 92px; height: 92px; display: block; }

/* A stone on a sheet. On the 404 it comes down and stops short of the far
   hog line, which is what "hogged" means and therefore what the picture
   has to show — an earlier version slid it clean off the right-hand edge,
   which is a stone through the house, the opposite mistake. */
.errsheet {
  position: relative; height: 66px; max-width: 24rem; margin: 0 auto 26px;
}
.errsheet::before {
  content: ''; position: absolute; left: 0; right: 0; top: 40px;
  border-top: 2px solid var(--line);
}
.errhog {
  position: absolute; left: 80%; top: 4px; height: 42px;
  border-left: 2px dashed var(--slate-45);
}
.errhog span {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  padding-top: 5px; white-space: nowrap;
  font-size: 9.5px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--faint);
}
/* `top` seats the stone *on* the sheet line rather than letting the line
   cut through it: the drawing's base is at 89 of a 100-unit viewBox,
   which at this width lands 37px down. */
.errstone {
  position: absolute; left: -50px; top: 3px; width: 50px;
  animation: errslide 4.6s cubic-bezier(.1,.72,.28,1) infinite;
}
@keyframes errslide {
  0%, 4%    { left: -50px; }
  55%, 100% { left: 44%; }
}
@media (prefers-reduced-motion: reduce) {
  /* Parked where the animation ends, so the picture still reads. */
  .errstone { animation: none; left: 44%; }
}

/* The 403 shows the same stone standing still — no sheet line, no hog
   line, no travel. Nothing about "that is not your rock" is about
   distance, and a lone stone on a stub of line read as a smudge. Bigger
   too, because here it is the whole picture rather than one end of it. */
.errsolo { margin: 0 auto 26px; }
.errsolo .errstone {
  position: static; display: block; width: 76px; margin: 0 auto;
  animation: none;
}

.errpage h1 { font-size: 34px; margin: 0 0 12px; letter-spacing: -.01em; }
.errpage p { color: var(--muted); font-size: 15px; margin: 0 auto 14px; }
.errbtns {
  display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;
  margin-top: 26px;
}
.errpath {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 12.5px; color: var(--faint);
  word-break: break-all; margin-top: 26px;
}

/* Role guide on the access screen (#1131). A definition list rather than
   a paragraph: a chair is comparing four options against each other, and
   four things compare better stacked than in prose. Two columns once
   there is room, one below that so the wording never gets squeezed into
   a gutter. */
.rolelist { margin: 10px 0 0; display: grid; gap: 2px 14px; }
.rolelist dt { font-weight: 600; font-size: 13px; color: var(--slate); }
.rolelist dd { margin: 0 0 6px; font-size: 13px; color: var(--muted); }
@media (min-width: 560px) {
  .rolelist { grid-template-columns: max-content 1fr; }
  .rolelist dt { grid-column: 1; }
  .rolelist dd { grid-column: 2; margin-bottom: 2px; }
}
