/* ==========================================================================
   Sydney Huskies Basketball — registrations console
   The club's season-awards dashboard already established a house style for
   officials-only screens: winter sport-print, drawn from the club logo. Cold
   slate ground, the logo's neutral greys for type, condensed poster type, and
   the light blue of the husky's eye as the one highlight — outlined when it
   labels, filled when it acts. This console follows it, so an official moving
   between the two dashboards is looking at the same product.
   ========================================================================== */

:root {
  --ink:        #0b1014;
  --ink-raise:  #131b21;
  --ink-card:   #161f26;
  --ink-sunk:   #080c0f;

  /* Type: the logo's neutral cool greys, not a warm paper cream. */
  --bone:       #eaeef0;
  --bone-soft:  rgba(234, 238, 240, 0.66);
  --bone-faint: rgba(234, 238, 240, 0.38);

  /* Highlight: the light blue of the husky's eye — the only colour the logo
     carries, so the only colour on the page that is not a grey. --ice-deep is
     the eye's own shadow, for the quiet end of the same scale. */
  --ice:        #03e0ff;
  --ice-deep:   #237a87;

  /* Error and success stay red and green: they report state, they are not
     brand, and recolouring them to the highlight would lose the signal.
     --flagged is the third of the same kind: an age-rule exemption the club
     has to hold, which has to be told apart at a glance from a health note
     rather than sharing its red. */
  --alarm:      #d96b5c;
  --grow:       #7fb08a;
  --flagged:    #e0a33c;

  --line:       rgba(234, 238, 240, 0.13);
  --line-firm:  rgba(234, 238, 240, 0.28);

  --display: 'Anton', 'Haettenschweiler', 'Arial Narrow', sans-serif;
  --body:    'Sora', ui-sans-serif, sans-serif;
  --mono:    'DM Mono', ui-monospace, monospace;

  /* Wider than the ballot's 1180px: this shell holds a table of eleven
     columns, and the awards screens hold prose. */
  --shell: 1500px;
  /* The gutter every band of the page is padded by — masthead, shell and
     footer alike — so the rule under the navigation and the panels under it
     are measured from the same edge. The tables give it back to read wider. */
  --gutter: clamp(18px, 5vw, 52px);
  --r-sm: 4px;
  --r-md: 10px;
  --r-lg: 18px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  min-height: 100vh;
  background: var(--ink);
  color: var(--bone);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.6;
  font-weight: 300;
  letter-spacing: 0.005em;
  overflow-x: hidden;
}

/* Atmosphere: a cold light from the top-left, a deep teal pool bottom-right, and
   a fixed grain sheet so flat panels never read as plastic. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(1100px 620px at 8% -8%,  rgba(3, 224, 255, 0.10), transparent 62%),
    radial-gradient(820px 520px at 104% 108%, rgba(35, 122, 135, 0.18), transparent 60%),
    linear-gradient(178deg, transparent 40%, var(--ink-sunk) 100%);
}
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.038;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

.shell {
  position: relative;
  z-index: 2;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--gutter) 120px;
}

/* ---------- Type ---------------------------------------------------------- */

h1, h2, h3, .display {
  font-family: var(--display);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.012em;
  line-height: 0.92;
  margin: 0;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ice);
  margin: 0;
}

.lede { color: var(--bone-soft); max-width: 54ch; }

/* ---------- Masthead ----------------------------------------------------- */

/* Raised above the page rather than level with it. The masthead and .shell are
   both positioned and both carry a z-index, which makes each of them a stacking
   context of its own: everything inside the masthead paints within the
   masthead's, so the nav menu's own z-index sorts it against its neighbours in
   there and cannot lift it out. Level with the shell, the shell wins for being
   further down the document, and a hover menu hanging below the masthead was
   painted over by the first panel it reached.

   7 puts it above the page's own layers — .shell and .site-footer at 2 — so the
   menu floats over whatever it opens across, and below the drawer's scrim at 8,
   so opening a registration still dims the masthead along with everything else.
   It has to stay above 1 whatever happens, because body::after lays the grain
   over the page at 1. */
.masthead {
  position: relative;
  z-index: 7;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: clamp(16px, 3vw, 30px);
  align-items: end;
  max-width: var(--shell);
  margin: 0 auto;
  padding: clamp(30px, 6vw, 62px) var(--gutter) clamp(22px, 4vw, 34px);
  border-bottom: 1px solid var(--line-firm);
}

/* Logo and heading form one lockup, with the artwork centred against the
   heading's two lines so the two read as one block rather than as a tall
   image with a caption hanging off its foot. The logo's width is the lockup's
   one measurement: the heading is sized from it, which is what keeps the pair
   in proportion at every viewport width. */
.masthead-lockup {
  --lockup-logo: clamp(132px, 19vw, 268px);
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 30px);
  min-width: 0;
}

/* The club logo the registration form carries, scaled to masthead size. Only
   the width is set: the intrinsic width and height on the tag keep the artwork
   at its own 753:263 proportions, so the layout can never stretch it. */
.wordmark {
  width: var(--lockup-logo);
  height: auto;
  display: block;
  flex: none;
}

/* "Team Management" on one line beside the logo, rather than stacked over two.
   One line at this size comes to roughly a third of the logo's width, which is
   close enough to the artwork's own height that the pair still reads as a
   single lockup — and it holds that proportion as the logo grows and shrinks,
   because the heading is sized from the logo's width. */
.masthead h1 {
  font-size: calc(var(--lockup-logo) * 0.15);
  line-height: 1.04;
  white-space: nowrap;
}
.masthead h1 span { display: inline; }
/* The space between the two words, set here rather than left to the markup so
   it cannot be collapsed away by the source being reformatted. */
.masthead h1 span + span { margin-left: 0.32em; }
/* Two-tone wordmark: the subject in bone, the screen in outline. */
.masthead h1 .thin {
  -webkit-text-stroke: 1px var(--bone-faint);
  color: transparent;
}
.masthead .season {
  text-align: right;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bone-faint);
  line-height: 1.7;
  margin: 0;
}
.masthead .season b { color: var(--ice); font-weight: 500; }
/* An email address set in all caps reads as shouting, so the signed-in line
   steps out of the masthead's uppercase treatment. */
.masthead .season .who {
  text-transform: none;
  letter-spacing: 0.05em;
  color: var(--bone-soft);
}

.masthead-nav {
  display: flex;
  gap: 18px;
  justify-content: flex-end;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 6px;
}
.masthead-nav a,
.masthead-nav button {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
  text-decoration: none;
  background: none;
  border: 0;
  border-bottom: 1px solid transparent;
  padding: 0;
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.masthead-nav a:hover, .masthead-nav a:focus-visible,
.masthead-nav button:hover, .masthead-nav button:focus-visible {
  color: var(--ice);
  border-bottom-color: var(--ice);
}
.masthead-nav [hidden] { display: none; }

/* ---------- The one nav menu ---------------------------------------------- */

/* Everything the console does hangs off a single button, so the masthead holds
   one word however many doors the console grows. The list opens on hover, on a
   click and on the keyboard reaching it, and the two sections with more behind
   them open a second list beside their own line.

   Hover is the gesture the club asked for, so it is CSS rather than script:
   `:hover` on the row and `:focus-within` for the keyboard, with `.open` as
   the third way in for a click. Nothing here measures anything, which is what
   keeps a menu opening the instant the pointer arrives. */
.nav-menu { position: relative; }

.nav-menu-btn::after {
  content: '▾';
  margin-left: 7px;
  font-size: 0.8em;
  vertical-align: 1px;
}

/* The list itself: absolute rather than fixed, because it hangs off a masthead
   that does not scroll away under it, and right-aligned so it opens inwards
   from the edge of the page rather than off it. */
.nav-menu-list {
  position: absolute;
  z-index: 60;
  top: calc(100% + 10px);
  right: 0;
  min-width: 216px;
  margin: 0;
  padding: 8px;
  list-style: none;
  background: var(--ink-card);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
  box-shadow: 0 26px 50px -26px rgba(0, 0, 0, 0.86);
  text-align: left;
  /* Closed is invisible and unclickable, but still laid out, so the submenus
     can be measured and the whole thing can fade rather than snap. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.16s var(--ease), transform 0.16s var(--ease), visibility 0.16s;
}
.nav-menu:hover > .nav-menu-list,
.nav-menu:focus-within > .nav-menu-list,
.nav-menu.open > .nav-menu-list {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* A bridge across the gap between the button and the list, so the pointer
   travelling between them never leaves the menu and shuts it. */
.nav-menu-list::before {
  content: '';
  position: absolute;
  top: -12px;
  left: 0;
  right: 0;
  height: 12px;
}

.nav-item { position: relative; }
.nav-item[hidden] { display: none; }

/* Every line of the menu reads the same whether it is a link or a button, so
   the list looks like a list rather than a mixture. */
.masthead-nav .nav-item > a,
.masthead-nav .nav-item > button,
.masthead-nav .nav-sub a,
.masthead-nav .nav-sub button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 10px;
  border: 1px solid transparent;
  border-radius: var(--r-sm, 8px);
  color: var(--bone);
  white-space: nowrap;
}
.masthead-nav .nav-item > a:hover,
.masthead-nav .nav-item > a:focus-visible,
.masthead-nav .nav-item > button:hover,
.masthead-nav .nav-item > button:focus-visible,
.masthead-nav .nav-sub a:hover,
.masthead-nav .nav-sub a:focus-visible,
.masthead-nav .nav-sub button:hover,
.masthead-nav .nav-sub button:focus-visible {
  color: var(--ice);
  border-color: var(--ice-deep);
}

/* A line with a list behind it says so, the way a submenu does everywhere. */
.nav-item--sub > a::after {
  content: '›';
  float: right;
  margin-left: 14px;
  color: var(--bone-faint);
}

/* The second list, opening to the left of the first: the menu is already at
   the right edge of the page, so there is no room to the right of it. */
.nav-sub {
  position: absolute;
  z-index: 61;
  top: -8px;
  right: calc(100% + 6px);
  min-width: 196px;
  margin: 0;
  padding: 8px;
  list-style: none;
  background: var(--ink-card);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
  box-shadow: 0 26px 50px -26px rgba(0, 0, 0, 0.86);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.14s var(--ease), visibility 0.14s;
}
.nav-item--sub:hover > .nav-sub,
.nav-item--sub:focus-within > .nav-sub {
  opacity: 1;
  visibility: visible;
}
/* The same bridge again, across the gap to the parent line. */
.nav-sub::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  right: -8px;
  width: 8px;
}

/* Narrow screens have no room for a 268px logo and a heading side by side, so
   the lockup stacks. Nothing is being aligned against the logo any more, so the
   heading is set for reading rather than measured off the artwork — still on
   its two lines. */
@media (max-width: 720px) {
  .masthead { grid-template-columns: 1fr; gap: 14px; align-items: start; }
  .masthead-lockup { flex-direction: column; align-items: flex-start; gap: 14px; }
  .masthead .season { text-align: left; }
  .masthead h1 { font-size: clamp(1.2rem, 5.2vw, 2rem); }
  .masthead-nav { justify-content: flex-start; }
  /* Opened from the left edge now that the masthead is left-aligned, and the
     second list drops under its own line rather than beside it — there is no
     room beside anything at this width. */
  .nav-menu-list { right: auto; left: 0; }
  .nav-sub,
  .nav-item--sub:hover > .nav-sub {
    position: static;
    right: auto;
    min-width: 0;
    margin: 2px 0 2px 14px;
    padding: 0;
    border: 0;
    border-left: 1px solid var(--line-firm);
    border-radius: 0;
    box-shadow: none;
    /* There is no hovering on a touch screen, and tapping the parent line
       follows it — so the second list stands open under its own line instead
       of waiting for a pointer that will never arrive. */
    opacity: 1;
    visibility: visible;
  }
  .nav-sub::before { content: none; }
  .nav-item--sub > a::after { content: none; }
}

/* ---------- Panels ------------------------------------------------------- */

.panel {
  position: relative;
  background: linear-gradient(168deg, var(--ink-card), var(--ink-raise));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(22px, 3.4vw, 38px);
  box-shadow: 0 30px 60px -34px rgba(0, 0, 0, 0.85);
}

.panel--flush { padding: 0; overflow: hidden; }

.panel-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.panel-head h2 { font-size: clamp(1.15rem, 2.8vw, 1.6rem); }
.panel-head .hint {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bone-faint);
}

/* Reveal on load, staggered by --i.

   Filled backwards rather than both ways, which matters for more than tidiness.
   A panel holding the end of this animation holds a transform — an identity
   one, but a transform — and an element with a transform is the containing
   block for anything fixed inside it. Held forwards, every board on this page
   quietly became the frame of reference for the menus opened from it, and the
   two roster tables' Columns menus were placed off the bottom and the right of
   the screen. Backwards fill hides the panel through its own stagger delay and
   then lets go of it entirely, which is all this animation was ever for; the
   end of the animation is the panel's own resting style anyway. placePanel()
   measures its origin regardless, so neither half of this depends on the other.
*/
.rise {
  animation: rise 0.66s var(--ease) backwards;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes rise {
  from { opacity: 0; transform: translate3d(0, 16px, 0); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .rise { animation: none; }
  * { transition-duration: 0.01ms !important; }
}

/* ---------- Buttons ------------------------------------------------------ */

.btn {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 14px 26px;
  border-radius: 999px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.16s var(--ease), background 0.2s var(--ease),
              color 0.2s var(--ease), border-color 0.2s var(--ease), opacity 0.2s;
}
.btn:active { transform: translateY(1px) scale(0.995); }
.btn:focus-visible { outline: 2px solid var(--ice); outline-offset: 3px; }

.btn--go { background: var(--ice); color: #04191d; font-weight: 500; }
.btn--go:hover:not(:disabled) { background: #46e9ff; }
.btn--go:disabled { background: rgba(234, 238, 240, 0.09); color: var(--bone-faint); cursor: not-allowed; }

.btn--ghost { background: transparent; color: var(--bone-soft); border-color: var(--line-firm); }
.btn--ghost:hover:not(:disabled) { color: var(--bone); border-color: var(--ice); }
.btn--ghost:disabled { color: var(--bone-faint); cursor: not-allowed; opacity: 0.6; }

.btn--danger { background: transparent; color: var(--alarm); border-color: rgba(217, 107, 92, 0.45); }
.btn--danger:hover:not(:disabled) { background: rgba(217, 107, 92, 0.12); }

.btn--small { padding: 9px 16px; font-size: 0.66rem; }

/* A text button that reads as a link — "forgot my password", "back". */
.link-btn {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  background: none;
  border: 0;
  padding: 0;
  color: var(--bone-faint);
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.link-btn:hover, .link-btn:focus-visible { color: var(--ice); border-bottom-color: var(--ice); }

/* A link out to a form the club keeps elsewhere, wearing a button's clothes.
   An anchor is neither inline-block nor undecorated by default, so it is
   brought into line with whatever it is standing next to. */
a.btn, a.link-btn { display: inline-block; text-decoration: none; }

.actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 20px; align-items: center; }

/* ---------- Notices ------------------------------------------------------ */

.notice {
  margin: 20px 0 0;
  padding: 14px 18px;
  border-radius: var(--r-md);
  border-left: 3px solid var(--ice);
  background: rgba(3, 224, 255, 0.08);
  font-size: 0.92rem;
  color: var(--bone);
}
.notice--bad { border-left-color: var(--alarm); background: rgba(217, 107, 92, 0.11); }
.notice--good { border-left-color: var(--grow); background: rgba(127, 176, 138, 0.1); }
.notice:empty { display: none; }
.notice[hidden] { display: none; }

/* ---------- Fields ------------------------------------------------------ */

.field-label,
.field > span {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
  margin-bottom: 8px;
}

input[type='text'], input[type='email'], input[type='password'],
input[type='search'], input[type='date'], select, textarea {
  width: 100%;
  font-family: var(--body);
  font-size: 0.95rem;
  color: var(--bone);
  background: var(--ink-sunk);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
  padding: 13px 16px;
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
select {
  appearance: none;
  padding-right: 38px;
  /* The chevron is drawn in, because a native dark select arrow is unreliable. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23eaeef0' stroke-opacity='0.5' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  background-size: 11px;
  cursor: pointer;
}
/* The date picker's own indicator is black-on-black, so it is inverted. */
input[type='date']::-webkit-calendar-picker-indicator { filter: invert(1); opacity: 0.45; cursor: pointer; }
input[type='search']::-webkit-search-cancel-button { filter: invert(1); opacity: 0.4; cursor: pointer; }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--ice);
  box-shadow: 0 0 0 3px rgba(3, 224, 255, 0.16);
}
input::placeholder, textarea::placeholder { color: rgba(234, 238, 240, 0.26); }

.field { display: block; }
.field + .field { margin-top: 16px; }

/* ---------- Gate (password) --------------------------------------------- */

.gate {
  max-width: 520px;
  margin: clamp(40px, 9vw, 96px) auto 0;
}
.gate h2 { font-size: clamp(1.6rem, 4.4vw, 2.3rem); margin: 10px 0; }
.gate > p { color: var(--bone-soft); font-size: 0.93rem; margin: 0 0 24px; }
.gate-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.gate-row .field { flex: 1 1 200px; }
/* The sign-in panel has no intro line, so the form keeps the gap one would leave. */
.gate h2 + #signinForm { margin-top: 24px; }
.gate .fine {
  margin: 22px 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: 0.8rem;
  line-height: 1.65;
  color: var(--bone-faint);
}
.gate .fine b { color: var(--bone-soft); font-weight: 400; }
.gate-aside { margin: 18px 0 0; }

/* ---------- Skeletons & empty states ------------------------------------ */

.skeleton {
  border-radius: var(--r-md);
  background: linear-gradient(100deg,
    rgba(234, 238, 240, 0.05) 20%,
    rgba(234, 238, 240, 0.11) 42%,
    rgba(234, 238, 240, 0.05) 64%);
  background-size: 260% 100%;
  animation: sweep 1.5s linear infinite;
}
@keyframes sweep { from { background-position: 130% 0; } to { background-position: -130% 0; } }
.skeleton-row { height: 38px; margin-bottom: 11px; }
.skeleton-row:nth-child(2) { width: 82%; }
.skeleton-row:nth-child(3) { width: 64%; }

.empty {
  text-align: center;
  padding: clamp(34px, 7vw, 74px) 24px;
}
.empty h2 { font-size: clamp(1.3rem, 3.6vw, 1.9rem); margin-bottom: 12px; }
.empty p { color: var(--bone-soft); margin: 0 auto; max-width: 46ch; }
.empty[hidden] { display: none; }

/* ---------- Stats ------------------------------------------------------- */

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-top: clamp(24px, 4vw, 38px);
}
.stat {
  background: var(--ink-raise);
  padding: 20px clamp(16px, 2.4vw, 24px);
}
.stat .k {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--bone-faint);
  display: block;
  margin-bottom: 9px;
}
.stat .v {
  font-family: var(--display);
  font-size: clamp(1.7rem, 4.6vw, 2.5rem);
  line-height: 0.9;
  color: var(--bone);
  display: block;
}
/* A line under the figure that breaks it down — the male and female share of
   the players placed. It is deliberately quiet: the figure is the answer, and
   this is the detail behind it. */
.stat .s {
  display: block;
  margin-top: 9px;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.stat .s:empty { display: none; }

/* Health notes are the one figure somebody has to act on, so it carries the
   highlight while the rest of the row stays in bone. */
.stat--flag .v { color: var(--ice); }
/* And an exemption to flag is the other figure somebody has to act on. */
.stat--exempt .v { color: var(--flagged); }

/* A figure that is also the way in to what it counts. It keeps the shape of
   the plain figure beside it and takes an underline on hover, so the row still
   reads as a row of numbers rather than a row of buttons. */
.stat-open {
  display: block;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.stat-open .v { border-bottom: 1px solid transparent; display: inline-block; }
.stat-open:hover .v { border-bottom-color: currentColor; }
.stat-open:focus-visible { outline: 2px solid var(--ice); outline-offset: 3px; border-radius: var(--r-sm); }

/* ---------- Filter bar -------------------------------------------------- */

.filters {
  display: grid;
  grid-template-columns: minmax(220px, 2.2fr) repeat(7, minmax(130px, 1fr)) auto;
  gap: 14px;
  align-items: end;
  margin-top: clamp(18px, 3vw, 28px);
}
/* Below the width where all nine controls fit side by side at their minimum
   sizes, they wrap into as many even columns as there is room for, with the
   search box taking a row of its own. */
@media (max-width: 1480px) {
  .filters { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
  .filters .f-search { grid-column: 1 / -1; }
}
.filters .field > span { white-space: nowrap; }
.filters .btn { margin-bottom: 1px; }
/* Folded away until it is asked for, and given no top margin when it is
   hidden so the bar beneath closes right up under the heading. */
.filters[hidden] { display: none; }

.export-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.export-bar .spacer { flex: 1; }
.count-chip {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ice);
  border: 1px solid var(--ice-deep);
  background: rgba(3, 224, 255, 0.08);
  border-radius: 999px;
  padding: 7px 15px;
  white-space: nowrap;
}

/* ---------- How wide the two consoles read ------------------------------- */

/* A table is the whole point of both consoles, and a table of this many
   columns reads better the wider it is. Above the width where the roster's two
   tables sit side by side, each view gives the shell's gutter back, so its
   edges land on the ends of the rule under the navigation rather than inside
   it. Below that the gutter is kept: the tables have stacked by then, and a
   panel pressed against the edge of the screen reads worse than a narrow one. */
@media (min-width: 1181px) {
  #registrationsView,
  #rosterView {
    margin-left: calc(var(--gutter) * -1);
    margin-right: calc(var(--gutter) * -1);
  }
}

/* ---------- Data table -------------------------------------------------- */

.grid-wrap { margin-top: clamp(18px, 3vw, 28px); }

/* Scrolls in both directions so the column headings can stay put while a long
   season's worth of registrations moves under them. */
.table-scroll { overflow: auto; max-height: min(72vh, 880px); }

/* The scroll bars are drawn rather than left to the system, which on a
   trackpad hides them until the table is already moving: a table this wide
   needs to say it goes on. */
.table-scroll {
  scrollbar-width: auto;
  scrollbar-color: var(--ice-deep) var(--ink-sunk);
}
.table-scroll::-webkit-scrollbar { width: 12px; height: 12px; }
.table-scroll::-webkit-scrollbar-track { background: var(--ink-sunk); }
.table-scroll::-webkit-scrollbar-thumb {
  background: var(--ice-deep);
  border: 3px solid var(--ink-sunk);
  border-radius: 999px;
}
.table-scroll::-webkit-scrollbar-thumb:hover { background: var(--ice); }
.table-scroll::-webkit-scrollbar-corner { background: var(--ink-sunk); }

/* A shade on whichever side of the table has columns out of sight. */
.table-scroll.more-left { box-shadow: inset 18px 0 16px -14px rgba(3, 224, 255, 0.45); }
.table-scroll.more-right { box-shadow: inset -18px 0 16px -14px rgba(3, 224, 255, 0.45); }
.table-scroll.more-left.more-right {
  box-shadow: inset 18px 0 16px -14px rgba(3, 224, 255, 0.45),
    inset -18px 0 16px -14px rgba(3, 224, 255, 0.45);
}

/* The left and right buttons above a table that runs past its box. */
.sideways {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 8px clamp(12px, 2vw, 18px);
  border-bottom: 1px solid var(--line);
}
.sideways[hidden] { display: none; }
.sideways-hint {
  margin-right: auto;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.sideways .btn { padding: 6px 12px; }

table.grid {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}
table.grid[hidden] { display: none; }

table.grid th {
  position: sticky;
  top: 0;
  z-index: 3;
  text-align: left;
  white-space: nowrap;
  font-family: var(--mono);
  font-weight: 400;
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--bone-faint);
  background: var(--ink-sunk);
  border-bottom: 1px solid var(--line-firm);
  padding: 15px 16px;
  cursor: pointer;
  user-select: none;
  transition: color 0.2s var(--ease);
}
table.grid th:hover { color: var(--ice); }
table.grid th[aria-sort]:not([aria-sort='none']) { color: var(--ice); }
table.grid th .arrow { margin-left: 6px; font-size: 0.6rem; }

table.grid td {
  padding: 15px 16px;
  border-bottom: 1px solid var(--line);
  color: var(--bone-soft);
  vertical-align: top;
}
table.grid td.when { font-family: var(--mono); font-size: 0.78rem; white-space: nowrap; color: var(--bone-faint); }
/* A player is one line on every table that lists them: a name broken over two
   lines is the thing that makes a long list hard to scan down, and the column
   widens to hold it instead. */
table.grid td.name { color: var(--bone); font-weight: 400; white-space: nowrap; }

table.grid tbody tr {
  cursor: pointer;
  transition: background 0.16s var(--ease);
}
table.grid tbody tr:hover { background: rgba(234, 238, 240, 0.035); }
table.grid tbody tr.picked { background: rgba(3, 224, 255, 0.07); }
table.grid tbody tr.picked td.name { color: var(--ice); }
table.grid tbody tr:last-child td { border-bottom: 0; }

/* ---------- Ticking rows for a bulk action ------------------------------ */

/* Sits above the table and only appears once something is ticked, so the
   destructive action is never on screen without a selection to aim at. */
.select-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px clamp(16px, 2.4vw, 22px);
  border-bottom: 1px solid var(--line-firm);
  background: rgba(3, 224, 255, 0.045);
}
.select-bar[hidden] { display: none; }
.select-bar .spacer { flex: 1; }

.bulk-notice { margin: 16px clamp(16px, 2.4vw, 22px); }

/* The tick column. Kept as narrow as the control itself, and clicking it must
   not open the row's detail drawer — the handler in dashboard.js sees to that. */
table.grid th.pick,
table.grid td.pick {
  width: 1%;
  padding-right: 4px;
  cursor: default;
  white-space: nowrap;
}
/* The rep, player-flag, junior/senior and status columns. Like the tick column
   they hold a control rather than a reading, so they stay narrow, centre their
   box under the heading, and do not open the row's detail drawer when clicked. */
table.grid th.rep,
table.grid td.rep,
table.grid th.flag,
table.grid td.flag,
table.grid th.grade,
table.grid td.grade,
table.grid th.status,
table.grid td.status,
table.grid th.tickflag,
table.grid td.tickflag {
  width: 1%;
  text-align: center;
  cursor: default;
  white-space: nowrap;
}
/* On the registrations table every column, these included, sits flush left
   under its heading, so the rows read down one straight edge. */
#grid th,
#grid td { text-align: left; }
/* The standings menu, which is the whole of the column: one menu per row,
   holding the standing that row's player holds — Rep, DP, Train On, Rep PU —
   or sitting on the dash for a player who holds none. There is no box to tick
   first, so giving, changing and clearing a standing are all the same one
   action, read straight off the row. */
select.rep-status {
  display: inline-block;
  margin: 0 auto;
  padding: 3px 6px;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--ice);
  background: rgba(3, 224, 255, 0.08);
  border: 1px solid var(--ice-deep);
  border-radius: var(--r-sm);
  cursor: pointer;
}
/* The list itself is drawn by the browser, so its own colours are named or a
   dark-ground page ends up with light-ground options on some of them. */
select.rep-status option { color: var(--bone); background: var(--ink-card); }
select.rep-status:hover { border-color: var(--ice); }
select.rep-status:focus-visible { outline: 2px solid var(--ice); outline-offset: 1px; }
select.rep-status:disabled { opacity: 0.55; cursor: default; }
/* A standing a team's rep total does not count, in the neutral greys the same
   flag wears beside a player's name on the roster board. */
select.rep-status--uncounted {
  color: var(--bone-soft);
  background: rgba(234, 238, 240, 0.05);
  border-color: var(--line-firm);
}
select.rep-status--uncounted:hover { border-color: var(--bone-soft); }
/* No standing at all: the menu is there to be used, so it stays a control, but
   it is the quietest thing in the column — a row with nothing to say about rep
   should not draw the eye of somebody scanning for the rows that do. */
select.rep-status--none {
  color: var(--bone-faint);
  background: transparent;
  border-color: var(--line-firm);
}
select.rep-status--none:hover { color: var(--bone-soft); border-color: var(--bone-soft); }
/* The player-flag menu, the other writing cell on the registrations table: one
   menu per row holding Returning Player or New This Season, or sitting on the
   dash for a player the club has not said either way about. Grey throughout
   rather than in the club's ice, which is kept for the rep column where a
   colour means a player counted in a team's total: both flags here are plain
   facts about a player and neither is the notable one. */
select.flag-status {
  display: inline-block;
  margin: 0 auto;
  padding: 3px 6px;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--bone-soft);
  background: rgba(234, 238, 240, 0.05);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-sm);
  cursor: pointer;
}
select.flag-status option { color: var(--bone); background: var(--ink-card); }
select.flag-status:hover { border-color: var(--bone-soft); }
select.flag-status:focus-visible { outline: 2px solid var(--ice); outline-offset: 1px; }
select.flag-status:disabled { opacity: 0.55; cursor: default; }
/* No flag either way, held back the same way the rep column holds back a row
   with no standing, so a season's unmarked players are the ones that stand out
   least while still being one click from being marked. */
select.flag-status--none {
  color: var(--bone-faint);
  background: transparent;
  border-color: var(--line-firm);
}
select.flag-status--none:hover { color: var(--bone-soft); border-color: var(--bone-soft); }

/* A grade the club has set over the one the player's age gives. Brought up a
   shade rather than coloured: it is still a plain fact about a player, but it
   is the one grade on the table a person chose by hand, so a column of worked-
   out grades shows where somebody has stepped in. */
select.flag-status--override {
  color: var(--bone);
  border-color: var(--bone-soft);
}

/* A player the club has set aside, in the status column. Drawn the way the
   Inactive tag is drawn everywhere else — dashed and faint — so the state
   reads the same whether the row is being looked at by somebody who can change
   it or somebody who cannot, and a table sorted by status shows at a glance
   where the active list ends. */
select.flag-status--off {
  color: var(--bone-faint);
  background: transparent;
  border-style: dashed;
  border-color: var(--line-firm);
}
select.flag-status--off:hover { color: var(--bone-soft); border-color: var(--bone-soft); }

/* The jersey column, on the teams board only. Like the rep column it holds a
   control rather than a reading — but this one is typed into, so it is a box
   wide enough for three digits and no wider, and the row it sits on neither
   opens nor drags while it is being filled in. */
table.grid th.jersey,
table.grid td.jersey {
  width: 1%;
  text-align: center;
  cursor: default;
  white-space: nowrap;
}
.jersey-input {
  width: 4.2ch;
  padding: 5px 4px;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  color: var(--bone);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-sm);
  cursor: text;
}
.jersey-input::placeholder { color: var(--bone-faint); }
.jersey-input:hover { border-color: var(--ice-deep); }
.jersey-input:focus { outline: none; border-color: var(--ice); color: var(--ice); }
.jersey-input:disabled { opacity: 0.55; }
/* Two players in the same number on one team: allowed while a squad is being
   numbered, but never left unsaid. */
.jersey-input--clash {
  border-color: var(--flagged);
  color: var(--flagged);
  background: rgba(224, 163, 60, 0.1);
}
/* The same column for an account that reads the board rather than numbers it.
   A number nobody here can change is a reading and not a control, so it is set
   in the same mono figures without the box around them — and a number worn
   twice still says so, because that is worth knowing whoever is looking. */
.jersey-read {
  display: inline-block;
  min-width: 4.2ch;
  font-family: var(--mono);
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  color: var(--bone);
}
.jersey-read--clash { color: var(--flagged); }
/* How many of a team's players have a number, on the team's own row. */
.jersey-tally {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--bone-faint);
}

table.grid input[type='checkbox'],
.check input[type='checkbox'] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  margin: 0;
  display: block;
  border: 1px solid var(--line-firm);
  border-radius: var(--r-sm);
  background: transparent;
  cursor: pointer;
  position: relative;
  transition: border-color 0.16s var(--ease), background 0.16s var(--ease);
}
table.grid input[type='checkbox']:hover,
.check input[type='checkbox']:hover { border-color: var(--ice); }
table.grid input[type='checkbox']:focus-visible,
.check input[type='checkbox']:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }
table.grid input[type='checkbox']:checked,
.check input[type='checkbox']:checked {
  background: var(--ice);
  border-color: var(--ice);
}
table.grid input[type='checkbox']:checked::after,
.check input[type='checkbox']:checked::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid #04191d;
  border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}
/* Some of the filtered rows ticked, but not all of them. */
table.grid input[type='checkbox']:indeterminate {
  background: var(--ice-deep);
  border-color: var(--ice-deep);
}
table.grid input[type='checkbox']:indeterminate::after {
  content: '';
  position: absolute;
  left: 3px;
  top: 6px;
  width: 8px;
  height: 0;
  border-top: 2px solid var(--bone);
}
table.grid tbody tr.ticked { background: rgba(3, 224, 255, 0.055); }
table.grid tbody tr.ticked:hover { background: rgba(3, 224, 255, 0.085); }

.count-chip--pick { border-color: var(--ice); }

/* A tick box that sits in a tool bar rather than in a table column: the
   "Show inactive" switch above each of the three tables. */
.check {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  transition: color 0.2s var(--ease);
}
.check:hover { color: var(--ice); }
.check input[type='checkbox'] { flex: none; }
.check input[type='checkbox']:checked ~ span { color: var(--ice); }

/* A choice of one — the Filter panel's placement list — drawn as the tick
   boxes beside it are, round rather than square so the difference between
   "one of these" and "as many as you like" is visible before it is read. */
.check input[type='radio'] {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 16px;
  height: 16px;
  margin: 0;
  display: block;
  position: relative;
  border: 1px solid var(--line-firm);
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  transition: border-color 0.16s var(--ease);
}
.check input[type='radio']:hover { border-color: var(--ice); }
.check input[type='radio']:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }
.check input[type='radio']:checked { border-color: var(--ice); }
.check input[type='radio']:checked::after {
  content: '';
  position: absolute;
  left: 3px;
  top: 3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ice);
}
.check input[type='radio']:checked ~ span { color: var(--ice); }

/* Row-level tags. Outlined, because they label a state rather than act. */
.tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--bone-faint);
  white-space: nowrap;
}
.tag--review { border-color: rgba(217, 107, 92, 0.5); background: rgba(217, 107, 92, 0.12); color: #e79385; }
.tag--clear { border-style: dashed; }
.tag--src { border-color: var(--line-firm); color: var(--bone-soft); }
.tag--inactive {
  border-style: dashed;
  border-color: var(--line-firm);
  color: var(--bone-faint);
  letter-spacing: 0.16em;
}

/* A player the club has designated a representative player. A standing rather
   than a thing to be done about, so it takes the highlight — filled, to be
   picked out down a long list of names, and clear of the amber an exemption
   claims and the red a health note claims. */
.tag--rep {
  border-color: var(--ice-deep);
  background: rgba(3, 224, 255, 0.12);
  color: var(--ice);
}

/* A standing the club has flagged but a team's rep total does not count — a
   development player, one training on, one pulled up for a rep game. It is
   marked in the neutral greys rather than the club's highlight, so a team
   showing two reps beside four flagged players never reads as a miscount. */
.tag--rep-uncounted {
  border-color: var(--line-firm);
  background: rgba(234, 238, 240, 0.05);
  color: var(--bone-soft);
}

/* Which side of the club a player turns out for, as the club has graded them.
   A standing like the rep flag, but not one a team's tally counts, so it takes
   the same quiet greys an uncounted rep standing takes rather than the club's
   highlight — a name carrying both reads as one highlight and one fact. */
.tag--grade {
  border-color: var(--line-firm);
  background: rgba(234, 238, 240, 0.05);
  color: var(--bone-soft);
}
/* A grade resting on an age the console estimated from the declared group
   rather than read off a birthday. Dashed like every other unsettled thing
   here, so it reads as provisional without a second tag on the row. */
.tag--grade-est { border-style: dashed; }
/* A team's row reports both tallies at once, so they need parting. */
table.grid--board td.grade .tag + .tag { margin-left: 6px; }

/* ---------- The age rule ------------------------------------------------ */

/* The division a date of birth puts a player in, or a team plays. Read
   constantly while a roster is built, so it is the quiet outlined tag; it
   states a fact and asks for nothing. */
.tag--div { border-color: var(--ice-deep); color: var(--ice); }

/* A team named beside a player rather than under them. A name, not a label, so
   it keeps the club's own capitals and drops the wide letter-spacing the
   labelling tags carry. */
.tag--team {
  border-color: var(--line-firm);
  color: var(--bone-soft);
  text-transform: none;
  letter-spacing: 0.04em;
  font-size: 0.6rem;
}

/* Outside the rule by design, and outside it for want of a birthday: both are
   "no division here", so both are dashed and stay out of the way. */
.tag--senior,
.tag--age-none {
  border-style: dashed;
  border-color: var(--line-firm);
  color: var(--bone-faint);
}

/* An exemption the club has to hold. This is the one tag in the table that
   asks for something, so it is filled rather than outlined. */
.tag--exempt {
  border-color: rgba(224, 163, 60, 0.55);
  background: rgba(224, 163, 60, 0.14);
  color: var(--flagged);
}

/* The age cell: the years in figures, the division beside them. */
td.age { white-space: nowrap; }
td.age .age-years {
  font-family: var(--mono);
  font-size: 0.95em;
  color: var(--bone);
  margin-right: 8px;
}
td.age .age-none { color: var(--bone-faint); margin-right: 8px; }
/* An age the club worked out from the group a family declared, because the
   record carries no birthday it can read. Written with a `~` and set in the
   quieter colour, with the division beside it dashed the way every other
   "this is not settled" tag on the console is dashed — so the estimate reads
   as an estimate at a glance, without a second tag taking up the row. */
td.age .age-years--est { color: var(--bone-soft); }
.tag--div-est { border-style: dashed; }
table.grid--board td.age .tag { margin-top: 3px; }
table.grid--board td.age .tag + .tag { margin-left: 6px; }

/* A placement that needs an exemption is marked down the edge of its row, so
   it is found by scanning rather than by reading every tag. The rule applies
   to both tables and to a team's opened-up roster. */
table.grid tbody tr.needs-exemption td:first-child {
  box-shadow: inset 2px 0 0 var(--flagged);
}
/* The tint yields to a row the administrator is working on — one they have
   ticked, or opened in the drawer — because those say what is in hand right
   now. The edge marker above does not yield, so the flag never disappears. */
table.grid tbody tr.needs-exemption:not(.ticked):not(.picked) {
  background: rgba(224, 163, 60, 0.055);
}
table.grid tbody tr.needs-exemption:not(.ticked):not(.picked):hover {
  background: rgba(224, 163, 60, 0.09);
}

/* Only on screen because "Show inactive" is ticked, so the row says as much:
   it is dimmed and struck through the name, and still does everything a row
   does — it can be opened, exported, or flagged active again. */
table.grid tbody tr.inactive td { color: var(--bone-faint); }
table.grid tbody tr.inactive td.name {
  color: var(--bone-faint);
  text-decoration: line-through;
  text-decoration-color: var(--line-firm);
  text-decoration-thickness: 1px;
}

/* ---------- Detail drawer ----------------------------------------------- */

#scrim {
  position: fixed;
  inset: 0;
  z-index: 8;
  background: rgba(4, 7, 9, 0.72);
  backdrop-filter: blur(3px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s var(--ease);
}
#scrim.open { opacity: 1; pointer-events: auto; }

#drawer {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 9;
  width: min(540px, 100%);
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: linear-gradient(200deg, var(--ink-card), var(--ink));
  border-left: 1px solid var(--line-firm);
  box-shadow: -40px 0 80px -40px rgba(0, 0, 0, 0.9);
  transform: translateX(102%);
  transition: transform 0.34s var(--ease);
}
#drawer.open { transform: none; }

.drawer-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: clamp(22px, 3.6vw, 32px);
  border-bottom: 1px solid var(--line-firm);
}
.drawer-head h2 { font-size: clamp(1.4rem, 4vw, 2rem); margin-top: 8px; }
.drawer-head .eyebrow { color: var(--bone-faint); letter-spacing: 0.16em; }
.drawer-head .btn { flex: none; margin-left: auto; }

.drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: clamp(20px, 3.4vw, 30px) clamp(22px, 3.6vw, 32px) 60px;
}
.drawer-body h4, .player-card-body h4 {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 0.63rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ice);
  margin: 30px 0 12px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--line);
}
.drawer-body h4:first-child, .player-card-body h4:first-child { margin-top: 0; }

.kv {
  display: grid;
  margin: 0;
  grid-template-columns: minmax(120px, 38%) 1fr;
  gap: 8px 18px;
  padding: 8px 0;
  font-size: 0.9rem;
}
.kv dt {
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--bone-faint);
  padding-top: 3px;
}
.kv dd { margin: 0; color: var(--bone); overflow-wrap: anywhere; }
/* An answer a coach has to read before the player takes the court. */
.kv--alert {
  border-left: 2px solid var(--alarm);
  padding-left: 14px;
  margin-left: -16px;
  background: rgba(217, 107, 92, 0.07);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.kv--alert dd { color: #e79385; }

/* ---------- Import sheet ------------------------------------------------ */

/* The window itself is the sheet the rest of the console uses, so all this has
   to lay out is what stands inside it. */
.import-body p { margin: 0; font-size: 0.88rem; color: var(--bone-soft); max-width: 78ch; }
.import-body p + p { margin-top: 12px; }
.import-body label.btn { display: inline-block; }
#importStatus {
  margin-top: 16px;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--bone-soft);
}
#importStatus.bad { color: var(--alarm); }

/* Mapping a spreadsheet's columns onto the console's fields. Two columns on a
   wide screen, one on a phone, so a long sheet stays scannable either way. */
#importMap { margin-top: 24px; border-top: 1px solid var(--line); padding-top: 22px; }
#importMap .eyebrow {
  margin: 0 0 16px;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ice);
}

.map-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 12px 22px;
}
.map-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 12px;
  align-items: center;
}
.map-col { min-width: 0; }
.map-col b {
  display: block;
  font-size: 0.86rem;
  font-weight: 400;
  color: var(--bone);
  overflow-wrap: anywhere;
}
.map-col span {
  display: block;
  margin-top: 3px;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--bone-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.map-row select { padding: 9px 34px 9px 12px; font-size: 0.84rem; }

.map-preview { margin-top: 22px; }
#importMap .map-tally, #importMap .map-warn {
  margin: 0;
  max-width: none;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--bone-soft);
}
#importMap .map-warn { color: var(--alarm); }
#importMap .map-tally + .map-warn,
#importMap .map-warn + .map-tally { margin-top: 8px; }

/* The first few records as they would be stored, so a wrong column is obvious
   before it is written rather than after. */
.map-sample { list-style: none; margin: 14px 0 10px; padding: 0; display: grid; gap: 8px; }
.map-sample li {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  align-items: baseline;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--ink-raise);
}
.map-sample b { font-weight: 400; font-size: 0.9rem; color: var(--bone); }
.map-sample span { font-family: var(--mono); font-size: 0.7rem; color: var(--bone-faint); }

@media (max-width: 560px) {
  .map-row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
}

/* ---------- Manage Roster ----------------------------------------------- */

/* The nav link for the section being looked at is filled in the highlight, the
   same way the highlight is filled when a control acts rather than labels. */
.masthead-nav a.current {
  color: var(--ice);
  border-color: var(--ice-deep);
}

/* Season: the one being worked on, and the form the menu opens to add one. */
.season-bar {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-top: 20px;
}
.season-bar .field { flex: 0 1 320px; }
.season-bar .btn { margin-bottom: 1px; }
/* The bar gives way to the empty state when the club has named no season, and
   a flex container needs telling that `hidden` still means hidden. */
.season-bar[hidden] { display: none; }

/* No season named: the panel asks for the first one where the picker would be. */
.season-empty { margin-top: 20px; }

.season-new {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.season-new[hidden] { display: none; }
.season-new .field { flex: 1 1 260px; }
.season-new .btn, .season-new .link-btn { margin-bottom: 1px; }
.season-new .hint { flex-basis: 100%; margin: 0; }

/* Adding teams belongs with the teams themselves, so it sits on the Teams
   board: a menu beside the heading, and this strip under it for whatever that
   menu opened. The strip is only in the page while something is happening in
   it, which is why it carries its own rule rather than a permanent divider. */
.team-add {
  padding: 16px clamp(16px, 2.4vw, 22px) 18px;
  border-bottom: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.015);
}
.team-add[hidden] { display: none; }
.team-add-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}
.team-add-head .hint {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.team-add .field { margin-top: 14px; }
.team-add .actions { margin-top: 14px; }
.team-add .notice { margin-top: 14px; }

#rosterView .panel .fine,
#accessView .panel .fine {
  margin: 20px 0 0;
  font-size: 0.78rem;
  line-height: 1.7;
  color: var(--bone-faint);
  max-width: 84ch;
}

/* The team names read out of an uploaded list, before any of them are stored. */
.team-chips {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 16px 0 0;
  padding: 0;
  max-height: 220px;
  overflow: auto;
}
.team-chips li {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  padding: 7px 12px;
  border: 1px solid var(--line-firm);
  border-radius: 999px;
  color: var(--bone);
}
.team-chips li.known {
  border-style: dashed;
  border-color: var(--line);
  color: var(--bone-faint);
}

/* ---------- The two boards ---------------------------------------------- */

/* Players on the left, teams on the right, because a name is dragged from one
   into the other. Under 1180px there is not room for two tables side by side,
   so they stack — the drag still works, it is just a longer reach. */
.roster-board {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(14px, 2vw, 22px);
  margin-top: clamp(18px, 3vw, 28px);
  /* Both boards take the height of whichever of them is taller, rather than
     each ending wherever its own last row happens to fall. A name is dragged
     from one into the other, and two panels of different heights put the drop
     target and the name being dragged on different parts of the screen — quite
     apart from reading as an unfinished layout. The shorter board's table is
     what grows into the spare room, so its rows stay at the top where they
     were and the panel simply runs down to meet its neighbour. */
  align-items: stretch;
}
.roster-board > .panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/* The table takes whatever the head and the tools above it leave. */
.roster-board .table-scroll--board { flex: 1 1 auto; }
@media (max-width: 1180px) {
  .roster-board { grid-template-columns: minmax(0, 1fr); }
  /* Stacked, there is no neighbour to line up with, so a board is its own
     height again and the tables go back to sizing themselves. */
  .roster-board > .panel { display: block; }
  .roster-board .table-scroll--board { flex: none; }
}

/* The two boards' heading areas are held to the same height so the tables
   beneath them start on the same line: the eyebrow, the heading and the row of
   tools come to slightly different heights on the left and the right, and the
   columns landing a few pixels apart is exactly what makes a pair of tables
   side by side look misaligned. */
.board-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  min-height: 104px;
  padding: clamp(18px, 2.4vw, 24px) clamp(16px, 2.4vw, 22px) 16px;
  border-bottom: 1px solid var(--line);
}
.board-head h2 { font-size: clamp(1.1rem, 2.6vw, 1.45rem); margin-top: 8px; }
.board-head .hint {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.board-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
/* The line telling somebody a team opens on a click. It is a hint rather than
   a control, so it keeps out of the way of the two buttons beside it. */
.board-actions .hint--wrap { max-width: 22ch; line-height: 1.5; cursor: help; }

.board-tools {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: flex-end;
  min-height: 78px;
  padding: 14px clamp(16px, 2.4vw, 22px);
  border-bottom: 1px solid var(--line);
}
.board-tools .field { flex: 1 1 150px; }
.board-tools .field > span { white-space: nowrap; }
.board-tools .spacer { flex: 1; }
.board-tools .hint {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
}

.board-notice { margin: 16px clamp(16px, 2.4vw, 22px); }
.empty--board { padding: clamp(26px, 4vw, 44px) clamp(18px, 3vw, 30px); }
.empty--board h2 { font-size: clamp(1.1rem, 2.6vw, 1.4rem); }
.empty--board p { font-size: 0.88rem; }

/* Both boards scroll to the same height, so the two tables stay level. */
.table-scroll--board { max-height: min(64vh, 720px); min-height: 120px; }

table.grid--board { font-size: 0.82rem; }
table.grid--board th { padding: 13px 14px; cursor: default; }
table.grid--board th:hover { color: var(--bone-faint); }
table.grid--board td { padding: 12px 14px; }
table.grid--board th.grab, table.grid--board td.grab {
  width: 1%;
  padding-right: 0;
  color: var(--bone-faint);
}
table.grid--board th.act, table.grid--board td.act {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

/* A name that can be dragged says so: the grip, the cursor, and a lift while
   it is in the air. */
tr.player-row { cursor: grab; }
tr.player-row td.grab { font-size: 0.9rem; letter-spacing: -1px; }
tr.player-row.dragging { opacity: 0.4; }
tr.player-row.picked { background: rgba(3, 224, 255, 0.09); }
tr.player-row.picked td.name { color: var(--ice); }
tr.player-row.placed td.name { color: var(--bone-soft); }

/* Where a dragged name can land. */
#playersDrop.drop-on { box-shadow: inset 0 0 0 2px var(--ice); }

/* The whole row is the way into a team's details, so it says so. */
tr.team-row {
  cursor: pointer;
  background: rgba(234, 238, 240, 0.028);
}
tr.team-row td {
  border-bottom-color: var(--line-firm);
  vertical-align: middle;
  padding-top: 9px;
  padding-bottom: 9px;
}
/* Three actions is more than the half-width board can hold on one line, so a
   team's row wraps them rather than pushing the table into a sideways scroll. */
tr.team-row td.act { white-space: normal; }
tr.team-row td.act .icon-btn { margin: 2px 0; display: inline-block; }
tr.team-row td.name {
  font-family: var(--display);
  font-size: 0.76rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bone);
}
/* Held to one line, and cut with an ellipsis rather than allowed to push the
   table into a sideways scroll. The name is on the cell's title and in the
   row's label, so a team the club has given a long name is still readable. */
.team-title {
  display: inline-block;
  max-width: 26ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}
tr.team-row:hover { background: rgba(234, 238, 240, 0.045); }
tr.team-row.open { background: rgba(3, 224, 255, 0.055); }
tr.team-row.open td.name { color: var(--ice); }

/* The caret is the promise that the row opens, and turns when it has. */
.team-caret {
  display: inline-block;
  width: 8px;
  margin-right: 8px;
  color: var(--bone-faint);
  transition: transform 0.18s var(--ease), color 0.18s var(--ease);
}
.team-caret::before { content: '\25B8'; }
tr.team-row.open .team-caret { transform: rotate(90deg); color: var(--ice); }
.icon-btn--on { color: var(--ice); border-color: var(--ice-deep); }
tr.team-row.drop-on, tr.roster-row.drop-on {
  background: rgba(3, 224, 255, 0.14);
  box-shadow: inset 2px 0 0 var(--ice);
}
tr.team-row.awaiting { box-shadow: inset 2px 0 0 var(--ice-deep); }
tr.team-row.inactive { background: rgba(234, 238, 240, 0.014); }
tr.team-row.ticked { box-shadow: inset 2px 0 0 var(--ice); }
tr.team-row.inactive td.name { color: var(--bone-faint); }
tr.team-row.inactive .team-count { color: var(--bone-faint); border-color: var(--line-firm); }

tr.roster-row td { border-bottom-color: var(--line); }
tr.roster-row td.act { white-space: normal; }
tr.roster-row td.act .icon-btn { margin: 2px 0; display: inline-block; }
tr.roster-row td.name { padding-left: 30px; position: relative; color: var(--bone); }
/* The branch from the team's row into the player's, set against the middle of
   the one line their name now takes. */
tr.roster-row td.name::before {
  content: '';
  position: absolute;
  left: 16px;
  top: 22px;
  width: 8px;
  height: 1px;
  background: var(--line-firm);
}
/* A player who turns out for more than one team: the little branch into their
   name is drawn in the accent, so a shared player is picked out by scanning a
   roster rather than by reading every line's sub-text. */
tr.roster-row.shared td.name::before { background: var(--ice-deep); width: 10px; }

/* The name is the way into a player's registration, and the only part of their
   line that is — the jersey box beside it is typed in, so the click that opens
   the drawer is kept off it. Underlined on hover, so the one part of the line
   that opens something says which part it is. */
.name-open {
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.name-open:hover { color: var(--ice); border-bottom-color: var(--ice-deep); }

/* The controls that sit on a player's name rather than at the end of their
   line. Quieter than the pills in the action column, because they belong to the
   name they follow, and kept on that one line with it — the column widens to
   hold the pair rather than wrapping them. */
.line-btn {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 1px solid transparent;
  padding: 0;
  margin-left: 10px;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-faint);
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.line-btn:hover, .line-btn:focus-visible { color: var(--ice); border-bottom-color: var(--ice); }
.line-btn--on { color: var(--ice); border-bottom-color: var(--ice-deep); }

/* Choosing a second team for a player: a small form on a line of its own,
   directly under the player it is about. In the accent rather than the alarm
   colour of the two confirmations above it — nothing here is destructive, and
   the player keeps every team they already have. */
tr.roster-also-row td { background: rgba(3, 224, 255, 0.07); }
tr.roster-also-row td.none, tr.roster-also-row td[colspan] { padding-left: 30px; }

.also-play { max-width: 62ch; }
.also-play .field { max-width: 34ch; }
.also-play .field select { font-size: 0.88rem; padding: 10px 14px; padding-right: 34px; }
.also-play-actions {
  display: flex;
  gap: 14px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 12px;
}
#rosterView .panel .also-play .fine { margin: 12px 0 0; max-width: none; }

/* Scoped to the message cell, so the tick column beside it keeps the width
   and padding it has in every other row and the rows stay in line. */
tr.roster-empty td.none {
  color: var(--bone-faint);
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding-left: 30px;
}

/* ---------- An opened team ---------------------------------------------- */

/* Clicking a team drops its whole account of itself into the table: what the
   team amounts to this season, then every stored answer of every player on it.
   It sits inside one cell that spans the row, so the columns above it are
   undisturbed and the roster list a name is dragged onto stays put. */
tr.team-detail-row > td { padding: 0 14px 0 0; border-bottom-color: var(--line-firm); }
tr.team-detail-row.drop-on { background: rgba(3, 224, 255, 0.07); }

.team-detail {
  margin: 0 0 4px 16px;
  padding: 16px 0 4px 14px;
  border-left: 1px solid var(--ice-deep);
  animation: detail-open 0.26s var(--ease) both;
}
@keyframes detail-open {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .team-detail { animation: none; }
}

/* What the team amounts to, before any one player is read. */
.detail-chips {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 16px;
  padding: 0;
}
.detail-chip {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 7px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: rgba(234, 238, 240, 0.02);
}
.detail-chip .k {
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.detail-chip .v { font-size: 0.8rem; color: var(--bone); }
.detail-chip--alert { border-color: rgba(217, 107, 92, 0.45); background: rgba(217, 107, 92, 0.08); }
.detail-chip--alert .v { color: #e79385; }

/* One player, in full. */
.player-card {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: rgba(234, 238, 240, 0.022);
  margin-bottom: 12px;
  cursor: grab;
}
.player-card.dragging { opacity: 0.4; }
.player-card-head {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: baseline;
  padding: 13px 16px;
  border-bottom: 1px solid var(--line);
}
.player-card-head h5 {
  margin: 0;
  font-family: var(--display);
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--bone);
}
.player-card-sub {
  margin: 4px 0 0;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.player-card-acts { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.player-card-body { padding: 4px 16px 16px; }
.player-card-body h4 { margin-top: 22px; font-size: 0.6rem; }
.player-card-body .kv { font-size: 0.82rem; padding: 6px 0; }

.player-card-none {
  margin: 0 0 12px;
  font-size: 0.85rem;
  color: var(--bone-faint);
  max-width: 60ch;
}

/* Counts and the small round actions that sit at the end of a row. */
.team-count {
  display: inline-block;
  margin-left: 8px;
  font-family: var(--mono);
  font-size: 0.56rem;
  letter-spacing: 0.1em;
  color: var(--ice);
  border: 1px solid var(--ice-deep);
  border-radius: 999px;
  padding: 2px 7px;
  vertical-align: middle;
}

.icon-btn {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--bone-faint);
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  padding: 5px 11px;
  cursor: pointer;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease);
}
a.icon-btn { display: inline-block; text-decoration: none; }
.icon-btn:hover { color: var(--ice); border-color: var(--ice); }
.icon-btn:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }
.icon-btn--drop:hover { color: var(--alarm); border-color: rgba(217, 107, 92, 0.5); }


/* ---------- Club access ------------------------------------------------- */

/* The administrator's screen: a drop-down picks one coach (or a new one), and
   their teams and functions sit under it as boxes. It reuses the console's own
   furniture — panels, fields, tags — and adds only what the editor needs. */

.access-picker {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  flex-wrap: wrap;
}
.access-picker .field { flex: 1 1 320px; max-width: 58ch; }
.access-picker .count-chip { margin-bottom: 10px; }

/* Who is being worked on: the address box for a new coach, or the chosen
   coach's address with what is known about the account under it. */
.access-who {
  margin-top: 22px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--ice-deep);
  border-radius: var(--r-sm);
}
.access-who .field { max-width: 46ch; }
.access-who .access-email { font-size: 1.02rem; }
.access-who-tags { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.access-form .access-editor-body { margin-top: 24px; }

/* Save sits on the left where the eye finishes the boxes; the account's own
   actions — View as, Remove — are pushed to the far side. */
.access-form-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 24px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.access-form-actions .link-btn:disabled,
.access-form-actions .btn:disabled { opacity: 0.45; cursor: default; }
.access-actions-gap { flex: 1; }
.access-unsaved {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--ice);
}
#accessView .panel .notice { margin-top: 16px; }

/* The new-password form for a coach who has forgotten theirs. */
.access-reset {
  margin-top: 22px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--ice);
  border-radius: var(--r-sm);
}
.access-reset .field { max-width: 46ch; margin-top: 12px; }
.access-reset .access-check { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.access-reset-note { margin: 6px 0 0; color: var(--bone-faint); font-size: 0.9rem; max-width: 70ch; }
.access-reset .access-form-actions { margin-top: 16px; }

/* One box per team this season, in columns that fill the width they are
   given. A coach has a handful of teams and the club fields a few dozen, so
   they are all shown at once — nothing to scroll past and miss. */
.access-checks {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 6px 16px;
  margin-top: 12px;
}
.access-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 5px 0;
  font-size: 0.85rem;
  color: var(--bone);
  cursor: pointer;
}
.access-check input {
  flex: none;
  width: 16px;
  height: 16px;
  margin: 2px 0 0;
  accent-color: var(--ice);
  cursor: pointer;
}
.access-check em {
  font-style: normal;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  color: var(--bone-faint);
}
.access-editor .access-checks { margin-top: 0; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); }

/* The chips in a picker are removable, so they carry a cross; the ones in a
   row are being read, so they do not. Neither scrolls: a coach has a handful
   of teams, and hiding one behind a scrollbar would be hiding access. */
.team-chips--pick,
.team-chips--read { max-height: none; }
.team-chips--read { margin: 0; }
.team-chips--pick li { display: inline-flex; align-items: center; gap: 8px; }
.chip-x {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0 -2px 0 0;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--bone-faint);
  cursor: pointer;
  transition: color 0.18s var(--ease);
}
.chip-x:hover { color: var(--alarm); }
.chip-x:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }

/* Whether an account has been made private yet. "Starting" is the one worth
   noticing — that account cannot open the console until its holder changes
   the password the club handed them — so it is the lit tag and "Chosen",
   which is the ordinary state, is the quiet dashed one. */
.tag--starting { border-color: var(--ice-deep); background: rgba(3, 224, 255, 0.08); color: var(--ice); }

/* The address is the row's name, and what is known about the account goes
   under it in the small mono the console labels things in. */
.access-email { display: block; color: var(--bone); word-break: break-all; }
.access-added {
  display: block;
  margin-top: 5px;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  color: var(--bone-faint);
}

/* A coach with no teams, and the row that is mid-write: both are states rather
   than things, so both read as a note instead of a value. */
.access-none {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--bone-faint);
}
.access-confirm {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: flex-end;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--alarm);
}

/* Teams are rewritten in the row itself rather than in a window over it: the
   other rows are what the change is being made against, and a window would
   cover them. */
.access-editor {
  border-left: 2px solid var(--ice-deep);
  padding-left: 14px;
}
.access-editor .team-chips { margin-top: 0; }
.access-editor-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 14px;
}

/* The editor: teams first, then functions, each a numbered step so the order
   of the questions reads at a glance. */
.access-block + .access-block {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.access-block-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  margin-bottom: 12px;
}
.access-block-head .field-label { margin: 0; }
.access-step {
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid var(--ice-deep);
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--ice);
}
.access-quick { display: inline-flex; gap: 14px; margin-left: auto; }
.access-quick .link-btn:disabled { opacity: 0.4; cursor: default; }
.access-count {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--bone-faint);
}

/* "Assign to all teams": the broad answer, drawn as a card of its own above
   the boxes it makes moot. Ticked, the boxes dim and stop taking clicks. */
.access-all {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  margin-bottom: 12px;
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
  background: var(--ink-sunk);
  cursor: pointer;
  transition: border-color 0.18s var(--ease), background 0.18s var(--ease);
}
.access-all:hover { border-color: var(--bone-faint); }
.access-all.on { border-color: var(--ice-deep); background: rgba(3, 224, 255, 0.06); }
.access-all input { flex: none; width: 16px; height: 16px; margin: 2px 0 0; accent-color: var(--ice); }
.access-all b { display: block; color: var(--bone); font-weight: 500; }
.access-all small,
.access-fn small,
.access-preset small {
  display: block;
  margin-top: 3px;
  font-size: 0.74rem;
  line-height: 1.35;
  color: var(--bone-faint);
}
.access-checks.is-covered { opacity: 0.4; }
.access-checks.is-covered .access-check { cursor: default; }

/* The presets: one row of cards, the one the boxes currently match lit. */
.access-preset-label {
  margin: 0 0 8px;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.access-presets {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px;
}
.access-preset {
  appearance: none;
  text-align: left;
  padding: 10px 12px;
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
  background: transparent;
  color: var(--bone);
  font: inherit;
  cursor: pointer;
  transition: border-color 0.18s var(--ease), background 0.18s var(--ease);
}
.access-preset b { font-weight: 500; font-size: 0.86rem; }
.access-preset:hover { border-color: var(--bone-faint); }
.access-preset:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }
.access-preset.on,
.access-preset--none[aria-pressed="true"] {
  border-color: var(--ice);
  background: rgba(3, 224, 255, 0.08);
}
.access-preset.on b { color: var(--ice); }

/* The functions, grouped by what they act on, each group a card. */
.access-groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 12px;
  margin-top: 16px;
}
.access-group {
  margin: 0;
  padding: 12px 14px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--ink-raise);
  min-width: 0;
}
.access-group-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 0 0 8px;
  float: left;
}
.access-group-head b { display: block; color: var(--bone); font-weight: 500; }
.access-group-head small {
  display: block;
  margin-top: 2px;
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  color: var(--bone-faint);
}
.access-group-head .link-btn { flex: none; font-size: 0.72rem; }
.access-fn {
  clear: both;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 0;
  border-top: 1px solid var(--line);
  cursor: pointer;
}
.access-fn input { flex: none; width: 16px; height: 16px; margin: 2px 0 0; accent-color: var(--ice); cursor: pointer; }
.access-fn-text { flex: 1; min-width: 0; }
.access-fn b { font-weight: 400; font-size: 0.85rem; color: var(--bone-soft); }
.access-fn.on b { color: var(--bone); }
.access-fn--danger.on b { color: var(--alarm); }
.access-fn--danger input { accent-color: var(--alarm); }
.access-wide {
  flex: none;
  padding: 2px 6px;
  border: 1px solid var(--line-firm);
  border-radius: var(--r-sm);
  font-family: var(--mono);
  font-size: 0.54rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.access-fn.on .access-wide { border-color: var(--ice-deep); color: var(--ice); }

/* What somebody holds, as small chips (the View as preview reads these). */
.tag--preset { border-color: var(--ice-deep); color: var(--ice); margin-bottom: 8px; }
.fn-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.fn-chips li {
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: 0.72rem;
  color: var(--bone-soft);
}
.fn-chips li.chip-wide { border-style: dashed; }
.team-chips li.chip-all { border-color: var(--ice-deep); color: var(--ice); }

/* ---------- View as ----------------------------------------------------- */

/* The bar across the top while the console is being looked at as somebody
   else. Ice, full width and sticky, so it cannot be scrolled out of mind. */
.viewas-bar {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--ice);
  color: var(--ink);
}
.viewas-bar[hidden] { display: none; }
.viewas-bar-inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 18px;
  max-width: 1400px;
  margin: 0 auto;
  padding: 10px clamp(16px, 4vw, 40px);
}
.viewas-bar-say { margin: 0; flex: 1 1 320px; font-size: 0.88rem; }
.viewas-bar-say b { font-weight: 600; word-break: break-all; }
.viewas-bar-text {
  display: block;
  margin-top: 2px;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  opacity: 0.8;
}
.viewas-eye {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--ink);
  vertical-align: middle;
}
.viewas-switch { display: inline-flex; align-items: center; gap: 8px; font-size: 0.78rem; }
.viewas-switch select {
  max-width: 260px;
  padding: 6px 8px;
  border: 1px solid rgba(0, 0, 0, 0.35);
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, 0.35);
  color: var(--ink);
  font: inherit;
}
.viewas-stop.btn {
  background: var(--ink);
  color: var(--ice);
  border-color: var(--ink);
}
.viewas-stop.btn:hover { background: var(--ink-raise); }
.viewas-stop.btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.viewas-form select,
.viewas-form input { width: 100%; }
.viewas-form .notice { margin-top: 12px; }
.viewas-form .actions { display: flex; align-items: center; gap: 14px; margin-top: 16px; }
.viewas-preview {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 10px 16px;
  margin: 18px 0 6px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--ink-sunk);
}
.viewas-preview dt {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bone-faint);
  padding-top: 4px;
}
.viewas-preview dd { margin: 0; }

/* ---------- Footer ------------------------------------------------------ */

.site-footer {
  position: relative;
  z-index: 2;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 34px var(--gutter) 60px;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.site-footer a { color: var(--bone-faint); text-decoration: none; border-bottom: 1px solid transparent; }
.site-footer a:hover { color: var(--ice); border-bottom-color: var(--ice); }

.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;
}

/* ---------- Columns a table shows --------------------------------------- */

/* A small panel over the table holding one box per column, opened by
   right-clicking the table's own headings. What it is set to is kept in this
   browser, so a club that never reads the source column stops seeing it for
   good. `.menu-anchor` wraps the buttons that open the console's other panels
   — export, filter, add teams — which are still placed from their button. */
.menu-anchor { position: relative; display: inline-flex; }
/* The display above would otherwise outrank the `hidden` attribute, leaving a
   control on screen for an account whose capabilities took it away. */
.menu-anchor[hidden] { display: none; }

/* What stands where the "Columns" button used to. It says the right-click is
   there — a gesture nothing points at is a gesture nobody finds — and reports
   what the table has been narrowed to, which is the other thing the button's
   own label used to carry. It is a line of text, so it is not reachable by
   keyboard and does not want to be: Shift+F10 on a heading opens the menu. */
.cols-hint {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
  white-space: nowrap;
}
/* Narrowed or rearranged, the line is worth reading rather than glanced past,
   exactly as the button used to light up when it held a count. */
.cols-hint--on { color: var(--ice); }
.cols-menu,
.drop-menu {
  position: fixed;
  z-index: 40;
  top: auto;
  right: 0;
  width: max-content;
  min-width: 230px;
  max-width: min(92vw, 420px);
  max-height: min(60vh, 460px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 16px 18px 14px;
  background: var(--ink-card);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
  box-shadow: 0 26px 50px -26px rgba(0, 0, 0, 0.86);
  text-align: left;
}
.cols-menu[hidden], .drop-menu[hidden] { display: none; }
.cols-menu-head,
.cols-menu-foot,
.cols-menu-note {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.cols-menu-head { padding-bottom: 10px; border-bottom: 1px solid var(--line); margin-bottom: 10px; }
.cols-menu .check { display: flex; padding: 5px 0; font-size: 0.86rem; }
.cols-menu-foot {
  margin-top: 12px;
  padding-top: 10px;
  border-bottom: 0;
  border-top: 1px solid var(--line);
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}

/* The columns as a list rather than a stack of tick boxes, because the order
   they are in is now part of what the panel says: top to bottom here is left
   to right on the table. Each line can be dragged onto another, and carries a
   pair of arrows for the same move by keyboard. */
.col-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.col-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 1px 4px 1px 0;
  border-radius: var(--r-sm);
  cursor: grab;
  transition: background 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.col-item:hover { background: rgba(255, 255, 255, 0.035); }
.col-item .check { flex: 1; min-width: 0; cursor: pointer; }
.col-item .check span { white-space: nowrap; }
.col-grip {
  flex: none;
  font-size: 0.9rem;
  line-height: 1;
  letter-spacing: -1px;
  color: var(--bone-faint);
  opacity: 0.6;
}
/* The line in hand, and the line it is about to take the place of. */
.col-item--lifted { opacity: 0.45; }
.col-item.col-target { box-shadow: inset 0 -2px 0 var(--ice); background: rgba(3, 224, 255, 0.07); }

.col-moves { flex: none; display: flex; gap: 2px; }
.col-move {
  padding: 2px 5px;
  font-size: 0.62rem;
  line-height: 1;
  color: var(--bone-faint);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
}
.col-move:hover:not(:disabled) { color: var(--ice); border-color: var(--ice-deep); }
.col-move:focus-visible { outline: 2px solid var(--ice); outline-offset: 1px; }
.col-move:disabled { opacity: 0.3; cursor: default; }

/* Moving a column and sorting it are two different gestures, so they are put
   on two different things: the grip is what the browser drags, and the heading
   itself is left free to take a plain click. A heading that was draggable in
   its own right swallowed the click whenever the pointer moved a pixel while
   it was down, which is what made sorting feel like it worked only sometimes. */
.th-grip {
  display: inline-block;
  margin-right: 8px;
  font-size: 0.8rem;
  line-height: 1;
  letter-spacing: -1px;
  color: var(--bone-faint);
  opacity: 0.35;
  cursor: grab;
  transition: opacity 0.15s var(--ease), color 0.15s var(--ease);
}
table.grid th:hover .th-grip { opacity: 1; color: var(--ice); }
.th-grip:active { cursor: grabbing; }

/* A heading that can be moved by dragging it onto another, on all three
   tables. The registrations table's headings also sort, so the cursor is left
   alone there and it is the drag itself that says what is happening. */
table.grid th.th--movable.col-lifted { opacity: 0.45; }
table.grid th.th--movable.col-target {
  box-shadow: inset 2px 0 0 var(--ice);
  color: var(--ice);
}
table.grid--board th.th--movable { cursor: grab; }

/* Both board tables now sort on any heading that reports something, the same
   as the registrations table above them. A heading still drags to move its
   column, so the pointer says what a plain click will do and the drag itself
   says the rest. */
table.grid--board th.th--sortable { cursor: pointer; }
table.grid--board th.th--sortable:hover { color: var(--ice); }

/* The Filter panel: the columns panel's shape, holding a headed group of
   choices for each thing the registered players can be narrowed by. */
.filter-menu { min-width: 245px; }
.filter-group + .filter-group { margin-top: 14px; }
.filter-menu .check { display: flex; padding: 5px 0; font-size: 0.86rem; }
.filter-menu .cols-menu-note { margin-top: 6px; }
.cols-menu-note {
  margin-top: 8px;
  letter-spacing: 0.06em;
  text-transform: none;
  font-family: var(--body);
  font-size: 0.72rem;
  line-height: 1.5;
  max-width: 28ch;
}
/* A choice inside one of those panels — a file format to write, a way to get
   the team names in. Each reads as a line of the menu rather than a button in
   a row of buttons, so two or three of them stay a list. */
.drop-menu { min-width: 250px; }
.drop-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 0.88rem;
  color: var(--bone);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r-sm, 8px);
  padding: 9px 10px;
  margin: 2px 0;
  cursor: pointer;
}
.drop-menu-item:hover { color: var(--ice); border-color: var(--ice-deep); }
.drop-menu-item:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }
.drop-menu-item-label {
  display: block;
  font-family: var(--mono);
  letter-spacing: 0.06em;
}
.drop-menu-item-note {
  display: block;
  margin-top: 3px;
  font-size: 0.72rem;
  line-height: 1.45;
  color: var(--bone-faint);
}

/* A button whose panel is doing something — columns put away, a form open. */
.btn--on { color: var(--ice); border-color: var(--ice-deep); }

/* ---------- Overwriting a registration ---------------------------------- */

.drawer-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.drawer-acts .btn { flex: none; margin-left: 0; }

.edit-lede {
  margin: 0 0 22px;
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--bone-soft);
}
.edit-field + .edit-field { margin-top: 14px; }
.edit-field > span { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.edit-field input, .edit-field select, .edit-field textarea { padding: 10px 14px; font-size: 0.9rem; }
.edit-field select { padding-right: 38px; }
.edit-field textarea { resize: vertical; }
.edit-hint {
  font-family: var(--body);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--bone-faint);
}
.edit-form h4 { margin-top: 28px; }
.edit-form .notice { margin-top: 20px; }
.edit-actions {
  display: flex;
  gap: 14px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.edit-fine {
  margin: 16px 0 0;
  font-size: 0.72rem;
  color: var(--bone-faint);
  line-height: 1.6;
}


/* ---------- A row's Action menu ----------------------------------------- */

/* One menu, floated from whichever row's Action button was pressed. A team's
   row used to carry four buttons side by side, which made the widest column on
   the board the one that said the least; they are one button and a menu now.
   It is placed against the viewport from the button, because the boards clip
   what overflows them to keep their corners rounded. */
.row-menu {
  position: fixed;
  z-index: 44;
  min-width: 208px;
  max-width: min(92vw, 320px);
  max-height: min(60vh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 8px;
  background: var(--ink-card);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
  box-shadow: 0 26px 50px -26px rgba(0, 0, 0, 0.86);
  text-align: left;
}
.row-menu[hidden] { display: none; }
.row-menu-head {
  margin: 2px 8px 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bone-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row-menu-item {
  display: block;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-soft);
  text-align: left;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.row-menu-item:hover { background: rgba(234, 238, 240, 0.06); color: var(--ice); }
.row-menu-item:focus-visible { outline: 2px solid var(--ice); outline-offset: -2px; }
.row-menu-item--drop { color: #e79385; }
.row-menu-item--drop:hover { background: rgba(217, 107, 92, 0.12); color: var(--alarm); }
.row-menu-sep { height: 1px; margin: 6px 8px; background: var(--line); }

/* The button the menu hangs off. It names what it opens rather than what it
   will do, because what it will do is now the menu's business. */
.icon-btn--menu::after { content: ' ▾'; opacity: 0.6; }

/* ---------- A sheet over the page --------------------------------------- */

/* A panel that opens over everything — the exemptions the season has to hold,
   read across every team at once. It is its own layer rather than a pane of
   the board, because what it lists belongs to no single team. */
.sheet { position: fixed; inset: 0; z-index: 50; }
.sheet[hidden] { display: none; }
.sheet-scrim {
  position: absolute;
  inset: 0;
  background: rgba(4, 7, 9, 0.72);
  backdrop-filter: blur(3px);
}
.sheet-panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(760px, calc(100vw - 2 * clamp(14px, 4vw, 40px)));
  max-height: min(84vh, 860px);
  display: flex;
  flex-direction: column;
  background: linear-gradient(168deg, var(--ink-card), var(--ink-raise));
  border: 1px solid var(--line-firm);
  border-radius: var(--r-lg);
  box-shadow: 0 40px 80px -36px rgba(0, 0, 0, 0.9);
  overflow: hidden;
}
.sheet-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: clamp(18px, 3vw, 26px);
  border-bottom: 1px solid var(--line-firm);
}
.sheet-head h2 { font-size: clamp(1.2rem, 3vw, 1.7rem); margin-top: 8px; }
.sheet-body {
  padding: clamp(14px, 2.4vw, 22px) clamp(18px, 3vw, 26px) clamp(20px, 3vw, 28px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ---------- What the console asks before it cannot be taken back --------- */

/* A question put where the answers used to be reported reads as a report, and
   a strip above a table is easy to build a roster straight past. So each
   prompt stands over the console on its own layer, on the same scrim as the
   windows that read whole records, in a panel only as tall as the question. */
.confirm { position: fixed; inset: 0; z-index: 60; }
.confirm[hidden] { display: none; }
.confirm-scrim {
  position: absolute;
  inset: 0;
  background: rgba(4, 7, 9, 0.72);
  backdrop-filter: blur(3px);
}
.confirm-panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(560px, calc(100vw - 2 * clamp(14px, 4vw, 40px)));
  max-height: min(84vh, 680px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(20px, 3vw, 28px);
  background: linear-gradient(168deg, var(--ink-card), var(--ink-raise));
  border: 1px solid var(--line-firm);
  border-radius: var(--r-lg);
  box-shadow: 0 40px 80px -36px rgba(0, 0, 0, 0.9);
}
.confirm-panel p { margin: 0; font-size: 0.9rem; line-height: 1.7; color: var(--bone); }
.confirm-panel .eyebrow { margin-bottom: 12px; }
.confirm-actions {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 22px;
}

/* Taking something away is asked in the alarm colour. The age rule is asked in
   the flag colour instead: going ahead is permitted, it simply has to be
   agreed to, and it leaves an exemption for the club to hold. */
.confirm-panel--danger { border-color: rgba(217, 107, 92, 0.45); }
.confirm-panel--danger .eyebrow { color: var(--alarm); }
.confirm-panel--age { border-color: rgba(224, 163, 60, 0.45); }
.confirm-panel--age .eyebrow { color: var(--flagged); }

/* One player's exemption: who it is for, which placement asks for it, and
   whether the form has gone in. */
.exempt-list { list-style: none; margin: 0; padding: 0; }
.exempt-list li {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.exempt-list li:last-child { border-bottom: 0; }
.exempt-who {
  font-size: 0.98rem;
  color: var(--bone);
  min-width: 0;
  flex: 1 1 220px;
}
.exempt-who .exempt-why {
  display: block;
  margin-top: 5px;
  font-size: 0.78rem;
  color: var(--bone-soft);
}
.exempt-who .exempt-team {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bone-faint);
  display: block;
  margin-bottom: 6px;
}
.exempt-acts { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sheet-none { color: var(--bone-soft); margin: 0; }

/* The overrides, read under the list they are missing from, with a rule above
   them so the two lists are not mistaken for one. */
.exempt-heading {
  margin: 26px 0 8px;
  padding-top: 20px;
  border-top: 1px solid var(--line-firm);
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--bone-faint);
}

/* The exemption tag is the form link now: one thing to click rather than a
   label and a button that said the same word twice. */
a.tag--exempt { text-decoration: none; cursor: pointer; }
a.tag--exempt:hover { background: rgba(224, 163, 60, 0.26); color: #f2c477; }
a.tag--exempt:focus-visible { outline: 2px solid var(--flagged); outline-offset: 2px; }

/* Filed, it stops asking for anything and becomes a quiet statement of fact. */
.tag--exempt-done,
a.tag--exempt-done {
  border-color: rgba(3, 224, 255, 0.45);
  background: rgba(3, 224, 255, 0.1);
  color: var(--ice);
}
a.tag--exempt-done:hover { background: rgba(3, 224, 255, 0.2); color: var(--ice); }

/* ---------- Merging two records that are the same child ----------------- */

/* The merge window reads two registrations side by side, so it is given more
   room than the exemptions sheet: the comparison is four columns wide and
   holds addresses and health notes, which a 760px panel would wrap to
   ribbons. */
.sheet-panel--wide {
  width: min(1080px, calc(100vw - 2 * clamp(14px, 4vw, 40px)));
  max-height: min(90vh, 960px);
}

/* The record the window was opened on, above the list of what it might be a
   duplicate of. */
.merge-subject {
  margin-top: 18px;
  padding: 14px 16px;
  border: 1px solid var(--line-firm);
  border-left: 3px solid var(--ice);
  border-radius: var(--r-md);
  background: rgba(3, 224, 255, 0.05);
}
.merge-subject h4 { margin: 0; font-size: 0.95rem; color: var(--bone); }
.merge-subject p { margin: 6px 0 0; font-size: 0.78rem; color: var(--bone-soft); }

.merge-find { margin-top: 20px; max-width: 420px; }

/* Why this record is the one being kept. It leads the comparison because it
   is the whole of the rule, and reading it is what makes the rest legible. */
.merge-why { margin-bottom: 20px; max-width: 86ch; line-height: 1.65; }

/* The two records as a pair. The kept one is marked in the highlight, the
   duplicate is left quiet, and the only control on either is the one that
   swaps them over. */
.merge-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}
.merge-card {
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--ink-raise);
}
.merge-card--keep {
  border-color: var(--ice-deep);
  background: rgba(3, 224, 255, 0.06);
}
.merge-card h4 { margin: 10px 0 0; font-size: 1rem; color: var(--bone); }
.merge-card--keep h4 { color: var(--ice); }
.merge-card-sub {
  margin: 8px 0 0;
  font-size: 0.76rem;
  line-height: 1.6;
  color: var(--bone-soft);
}
.merge-card .link-btn { margin-top: 12px; }

.merge-toolbar {
  display: flex;
  justify-content: flex-end;
  margin: 18px 0 10px;
}

/* The comparison is long, so it scrolls inside the window rather than pushing
   the confirm button out of reach. */
.merge-scroll {
  max-height: min(46vh, 480px);
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
}
table.merge-table { font-size: 0.84rem; }
/* Nothing in this table sorts, so its headings are labels rather than
   controls and must not invite a click. */
table.merge-table thead th { cursor: default; }
table.merge-table thead th:hover { color: var(--bone-faint); }
table.merge-table td { vertical-align: top; overflow-wrap: anywhere; }

/* The question being compared, down the left. It is a row heading rather than
   a column heading, so it neither sticks nor sorts. */
table.merge-table tbody th {
  position: static;
  z-index: auto;
  text-align: left;
  cursor: default;
  white-space: normal;
  font-family: var(--body);
  font-size: 0.82rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--bone-soft);
  background: transparent;
  border-bottom: 1px solid var(--line);
  padding: 12px 16px;
  width: 22%;
}
table.merge-table tbody th:hover { color: var(--bone-soft); }
table.merge-table tbody tr { cursor: default; }
table.merge-table tbody tr:hover { background: transparent; }

/* Which section of the form the lines under it belong to. */
table.merge-table tr.merge-section th {
  position: sticky;
  top: 0;
  z-index: 2;
  width: auto;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--bone-faint);
  background: var(--ink-sunk);
  border-bottom: 1px solid var(--line-firm);
  padding: 11px 16px;
}

/* The one kind of line that is a change: a gap in the kept record, answered
   by the duplicate. It is marked so the merge can be read at a glance as the
   handful of lines it usually is. */
table.merge-table tr.merge-row--fill { background: rgba(127, 176, 138, 0.07); }
table.merge-table tr.merge-row--fill td:nth-child(3) { color: var(--bone); }
table.merge-table tr.merge-row--conflict { background: rgba(224, 163, 60, 0.07); }
table.merge-table tr.merge-row--blank td,
table.merge-table tr.merge-row--blank th { color: var(--bone-faint); }

.tag--merge-fill {
  border-color: rgba(127, 176, 138, 0.5);
  background: rgba(127, 176, 138, 0.12);
  color: #9ec9a8;
}
.tag--merge-clash {
  border-color: rgba(224, 163, 60, 0.5);
  background: rgba(224, 163, 60, 0.12);
  color: #f2c477;
}

/* What becomes of the record that gave up what it knew. Two choices, stacked
   and spelt out, because one of them cannot be undone. */
.merge-disposal {
  margin-top: 22px;
  padding: 16px;
  border: 1px solid var(--line-firm);
  border-radius: var(--r-md);
  background: var(--ink-sunk);
}
.merge-disposal .eyebrow { margin-bottom: 12px; }
.merge-disposal .check {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  white-space: normal;
  font-family: var(--body);
  font-size: 0.82rem;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.6;
  color: var(--bone-soft);
  padding: 8px 0;
}
.merge-disposal .check input[type='radio'] { margin-top: 3px; }
.merge-disposal .check input[type='radio']:checked ~ span { color: var(--bone); }

.merge-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 20px;
}

/* ---------- Quick submit: a player taken down by hand ------------------- */

/* The form asks five questions, so the window it opens in is narrower than the
   two that read whole records. */
.sheet-panel--narrow {
  width: min(520px, calc(100vw - 2 * clamp(14px, 4vw, 40px)));
}

.quick-lede {
  margin: 0 0 22px;
  max-width: 60ch;
  font-size: 0.86rem;
  line-height: 1.7;
  color: var(--bone-soft);
}
.quick-field + .quick-field { margin-top: 16px; }
.quick-field > span { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.quick-field input { padding: 10px 14px; font-size: 0.9rem; }
.quick-hint {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.quick-form .notice { margin-top: 20px; }
.quick-actions {
  display: flex;
  gap: 14px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.quick-fine {
  margin: 16px 0 0;
  font-size: 0.72rem;
  color: var(--bone-faint);
  line-height: 1.6;
}
/* The line the window reports a saved player on. It carries the way into the
   record just added, so the rest of their details can be filled in there. */
.quick-done { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }

/* ---------- Writing one answer to every ticked player ------------------- */

/* The bulk-edit window. Each line is a box to tick and the answer that box
   writes, side by side, because the two belong to one another: the answer on
   its own changes nothing, and the tick on its own says what to change without
   saying what to. Untick everything and the window writes nothing at all,
   which is what makes it safe to leave open while a selection is worked on. */
.bulk-edit-form h4 { margin-top: 26px; }
.bulk-edit-form h4:first-child { margin-top: 0; }
.bulk-edit-row {
  display: flex;
  gap: 14px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 12px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
/* The tick takes the left half and the field's name reads off it, so a line is
   turned on by clicking the words rather than by finding the box. */
.bulk-edit-tick { flex: 1 1 190px; margin: 0; }
.bulk-edit-value { flex: 1 1 220px; }
.bulk-edit-value input,
.bulk-edit-value select {
  width: 100%;
  padding: 9px 12px;
  font-size: 0.86rem;
}
.bulk-edit-value select { padding-right: 34px; }
/* A line nobody has ticked is quieter than one somebody has, so the window can
   be read for what it is about to do rather than for what it could do. */
.bulk-edit-row:has(input[data-bulk-apply]:not(:checked)) .bulk-edit-value { opacity: 0.55; }
.bulk-edit-row:has(input[data-bulk-apply]:checked) {
  border-color: var(--ice-deep);
  background: rgba(3, 224, 255, 0.05);
}
/* What the button is about to write, spelled out under the form and kept in
   step with it. Empty until something is ticked, so it never takes up a line
   saying nothing. */
.bulk-edit-summary {
  margin: 20px 0 0;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  line-height: 1.7;
  color: var(--bone-soft);
}
.bulk-edit-summary:empty { display: none; }

/* Renaming a team where it sits: a pencil beside the name for an account that
   can change the list, shown when the row is pointed at, and the box the new
   name is typed into in place of the name. */
.team-rename-btn {
  margin-left: 6px;
  padding: 0 4px;
  font-size: 0.8rem;
  line-height: 1;
  color: var(--bone-faint);
  background: none;
  border: 0;
  cursor: pointer;
  opacity: 0;
  vertical-align: middle;
  transition: opacity 0.15s var(--ease), color 0.15s var(--ease);
}
tr.team-row:hover .team-rename-btn,
.team-rename-btn:focus-visible { opacity: 1; }
.team-rename-btn:hover { color: var(--ice); }
@media (hover: none) { .team-rename-btn { opacity: 1; } }
.team-rename-input {
  width: 26ch;
  max-width: 100%;
  padding: 4px 6px;
  font: inherit;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--ice);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--ice);
  border-radius: var(--r-sm);
  vertical-align: middle;
}
.team-rename-input:focus { outline: none; }
.team-rename-input:disabled { opacity: 0.55; }
#teamCopyForm { display: grid; gap: 10px; }
#teamCopyForm[hidden] { display: none; }

/* ---------- CBBA registration, one tick per team ------------------------ */

/* Every team a player is placed on needs a CBBA registration of its own, so
   the registrations table carries a tick per team and each roster line one
   for the team it sits under. */
.cbba-ticks { display: inline-flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.cbba-tick {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  white-space: nowrap;
  cursor: pointer;
  vertical-align: middle;
}
.cbba-tick input { margin: 0; cursor: pointer; }
.tag--cbba-due { border-color: rgba(224, 163, 60, 0.5); color: #f2c477; }
