/* ============================================================================
   Sportspickr components.

   One class per UI thing, defined in exactly one place. That single-ownership
   rule is the point: the 841 !important declarations in the legacy sheets are
   what happens when three stylesheets each think they own .btn.

   Everything here reads sp-tokens.css. No hex, no magic numbers.

   Deliberately avoids Bootstrap's .modal-header/.modal-body/.modal-footer and
   the .btn family: Material Kit uppercases every button, forces 12px/30px
   padding and stacks three box-shadows on .btn-danger, and Bootstrap's modal
   partials carry hairline rules that chop a short dialog into three bands.

   This file loads AFTER custom.css/custom2.css, so it wins ties on specificity
   without needing !important. Components are added when a page needs one -
   nothing speculative. See docs/ui-components.md.
   ========================================================================= */


/* ============================================================================
   Buttons
   ========================================================================= */
.sp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--sp-control-h);
  padding: 0 var(--sp-7);
  border: 1px solid transparent;
  border-radius: var(--sp-radius-md);
  background: none;
  font-family: var(--sp-font);
  font-size: var(--sp-text-md);
  font-weight: 500;
  letter-spacing: 0.01em;
  text-transform: none;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--sp-dur) var(--sp-ease),
              border-color var(--sp-dur) var(--sp-ease),
              box-shadow var(--sp-dur) var(--sp-ease),
              transform 0.12s var(--sp-ease);
}
.sp-btn--primary {
  background: var(--sp-brand);
  color: var(--sp-on-brand);
  box-shadow: 0 1px 2px var(--sp-brand-ring);
}
.sp-btn--primary:hover,
.sp-btn--primary:focus {
  background: var(--sp-brand-hover);
  color: var(--sp-on-brand);
  box-shadow: 0 4px 12px var(--sp-brand-ring);
  transform: translateY(-1px);
  text-decoration: none;
}
.sp-btn--primary:active {
  background: var(--sp-brand-active);
  transform: translateY(0);
  box-shadow: 0 1px 2px var(--sp-brand-ring);
}
.sp-btn--secondary {
  background: var(--sp-surface);
  border-color: var(--sp-border);
  color: var(--sp-ink-2);
}
.sp-btn--secondary:hover,
.sp-btn--secondary:focus {
  background: var(--sp-surface-3);
  border-color: var(--sp-border-strong);
  color: var(--sp-ink-2);
  text-decoration: none;
}
/* Full width. A layout modifier, not a third size - used where the submit is
   the only action in its container (auth panel, auth modals). */
.sp-btn--block {
  width: 100%;
}
/* Small. Earns its place: btn-sm/btn-xs are used 64 times across picks,
   account, the cookie banner and the marketing nav. Two sizes is the whole
   system - if a third is ever needed, it needs a reason. */
.sp-btn--lg {
  height: 48px;
  padding: 0 28px;
  font-size: var(--sp-text-lg);
}
.sp-btn--sm {
  height: 36px;
  padding: 0 var(--sp-6);
  border-radius: var(--sp-radius);
  font-size: 13.5px;
}
/* An icon inside a button. .sp-btn is already inline-flex, so this is only the
   gap and a size that matches the label rather than Material's 24px default. */
.sp-btn .material-icons {
  font-size: 18px;
  line-height: 1;
  margin-right: var(--sp-3);
}
.sp-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--sp-brand-ring);
}
/* An <a> styled as a button needs its colour re-asserted at (0,1,1). Vendor
   sheets set `.fresh-table a { color: #fff }` and `a { color: ... }` at that
   weight, which beats the plain `.sp-btn--secondary` (0,1,0) - the Export
   button on the scoreboard rendered as white text on a white button. */
a.sp-btn--primary,
a.sp-btn--primary:hover,
a.sp-btn--primary:focus { color: var(--sp-on-brand); }
/* and the plain button as a link - the front page's "Compare plans" rendered
   in fresh-bootstrap-table's #2CA8FF until it got the same treatment */
a.sp-btn:not(.sp-btn--primary),
a.sp-btn:not(.sp-btn--primary):hover,
a.sp-btn:not(.sp-btn--primary):focus { color: var(--sp-ink-2); }
a.sp-btn--secondary,
a.sp-btn--secondary:hover,
a.sp-btn--secondary:focus { color: var(--sp-ink-2); }

/* Guard for the migration: while a page still carries .btn alongside .sp-btn,
   Material Kit's .btn { opacity: .8 } would wash the component out. Keep .btn
   on an <input type=submit> for GTM, but never let it dim the button. */
.sp-btn { opacity: 1; }
.sp-btn[disabled],
.sp-btn.disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}


/* ============================================================================
   Fields

   .form-control is kept alongside .sp-input so Yii's CActiveForm helpers keep
   working untouched - the class is the styling hook, the widget output is not
   our call.
   ========================================================================= */
.sp-field {
  margin-bottom: var(--sp-7);
}
/* Yii's CActiveForm helpers wrap every input in Material Kit's .form-group,
   which brings its own margin and floating-label machinery - 20px of dead
   space between each label and its input. The .sp-field owns its spacing.
   Only margin/padding are touched: .form-group and .has-error are read by
   custom.js for validation styling and must keep working. */
/* Material Kit's JS WRAPS text inputs in a .form-group at runtime, and that
   wrapper carries a 7px padding-bottom while the input carries a 7px
   margin-bottom. Nothing in the markup shows it, so a field that reads as a
   plain <input> is really a 7px-taller box - which knocks it off the line in
   any flex row it shares with a button. Both are neutralised here rather than
   per-row: .sp-field owns its own spacing.
   !important is needed because Material Kit sets these at (0,2,0) itself. */
.sp-field .form-group {
  /* block, not flex: a stacked field does not need it, and if Yii ever renders
     an error message inside this wrapper, flex would put it beside the input
     instead of under it. The stepper below does need flex - it is a row. */
  margin: 0 !important;
  padding: 0 !important;
}
.sp-field .form-control,
.sp-field select,
.sp-field textarea {
  margin: 0 !important;
}
.sp-field label,
.sp-label {
  display: block;
  margin: 0 0 7px;
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  font-weight: 500;
  line-height: var(--sp-leading-tight);
  letter-spacing: 0;
  color: var(--sp-ink);
  text-transform: none;
}
.sp-field label .required {
  color: var(--sp-danger);
  font-weight: 500;
}
.sp-field .form-control,
.sp-input {
  display: block;
  width: 100%;
  height: var(--sp-field-h);
  padding: 0 var(--sp-6);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background-color: var(--sp-surface);
  background-image: none;          /* Material Kit paints an underline gradient */
  box-shadow: none;
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  line-height: calc(var(--sp-field-h) - 2px);
  color: var(--sp-ink);
  transition: border-color var(--sp-dur) var(--sp-ease),
              box-shadow var(--sp-dur) var(--sp-ease);
}
.sp-field textarea.form-control,
textarea.sp-input {
  height: auto;
  min-height: 148px;
  padding: var(--sp-5) var(--sp-6);
  line-height: var(--sp-leading);
  resize: vertical;
}
.sp-field .form-control::placeholder,
.sp-input::placeholder {
  color: var(--sp-faint);
}
.sp-field .form-control:focus,
.sp-input:focus {
  outline: none;
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
.sp-field select.form-control,
select.sp-input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 40px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236B625C' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  cursor: pointer;
}
/* Material Kit's floating-label machinery repaints an underline bar via
   background-image on .form-control and reddens the label whenever the group is
   .is-focused / .is-filled. A <select> always has a value, so it is permanently
   .is-filled - the bar showed as a stray red line across the value, and the
   label went red. At (0,3,0) those rules beat sp-field's flat box; these win at
   (0,4,0). sp-field draws a bordered box with its own focus ring instead. */
/* Two wrapper shapes carry sp-field: a nested `.sp-field .form-group` and a
   combined `.form-group.sp-field` (the Futures forms use the latter). Cover both
   — the combined element IS the .form-group, so a descendant selector can't
   reach it and the red focus fill would leak through. */
.sp-field .form-group.is-focused .form-control,
.sp-field .form-group.is-filled .form-control,
.sp-field.form-group.is-focused .form-control,
.sp-field.form-group.is-filled .form-control {
  background-image: none;
  background-size: auto;
}
.sp-field .form-group.is-focused select.form-control,
.sp-field .form-group.is-filled select.form-control,
.sp-field.form-group.is-focused select.form-control,
.sp-field.form-group.is-filled select.form-control {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236B625C' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  background-size: auto;
}
.sp-field .form-group.is-focused .control-label,
.sp-field .form-group.is-filled .control-label,
.sp-field.form-group.is-focused .control-label,
.sp-field.form-group.is-filled .control-label {
  color: var(--sp-ink);
}
/* Stripe Elements card fields (Stripe.js v3): the mount divs carry .form-control
   for the box; Stripe injects an iframe and toggles StripeElement--* on the mount
   node. Field text/placeholder are styled via the JS `style` option (spMountCard
   in custom.js). */
.sp-field .sp-stripe-field {
  display: flex;
  align-items: center;
  background-image: none;
}
.sp-stripe-field.StripeElement--focus {
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
.sp-stripe-field.StripeElement--invalid {
  border-color: #E2A0A9;
}
.sp-stripe-field .__PrivateStripeElement { width: 100%; }
.sp-field .errorMessage,
.sp-field .help-block {
  margin: var(--sp-2) 0 0;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--sp-danger);
}
.sp-field .form-control.error,
.sp-field .error .form-control {
  border-color: #E2A0A9;
}


/* ============================================================================
   Modal

   Only .modal / .modal-dialog / .modal-content are kept, because Bootstrap's
   modal JS needs them. State is a quiet tinted disc beside the title, never a
   coloured header band - a band means inventing a colour for every future
   state, and green fights the red site chrome.
   ========================================================================= */

/* One scrim for every modal on the site, owned here.
   It used to be set from custom-bootstrap-wizard.css with !important - a
   vendor wizard file, whose only consumer is one homepage modal, dictating the
   backdrop for all 49 modals at 80% black. */
.modal-backdrop.in {
  background: var(--sp-scrim);
  opacity: 1;
}

/* Vertical centring WITHOUT changing `display` between states.
   This was `.sp-modal.in { display:flex }`, which centred the dialog only while
   the .in class was present. Bootstrap sets `display:block` inline on show and
   does not remove it until the fade-out finishes, so the instant .in came off -
   on Cancel, Esc or the X - the modal dropped out of flex layout and the dialog
   jumped from centred to the top of the viewport, then faded from there. That
   was the flash: a layout change, not an animation.
   The inline-block strut centres under plain `display:block`, so showing and
   hiding lay out identically and only opacity changes. */
.sp-modal {
  padding: var(--sp-9);
  text-align: center;
  white-space: nowrap;        /* keeps the strut and the dialog on one line */
}
.sp-modal::before {
  content: "";
  display: inline-block;
  height: 100%;
  vertical-align: middle;
}
.sp-modal .modal-dialog {
  display: inline-block;
  vertical-align: middle;
  margin: 0;
  width: 100%;
  max-width: 440px;
  text-align: left;
  white-space: normal;
}
/* Bootstrap slides .modal-dialog in from translate(0,-25%) and back out to it.
   Against a centred dialog the exit reads as the box leaping upwards, so the
   component fades only. */
.sp-modal.fade .modal-dialog,
.sp-modal.in .modal-dialog {
  transform: none;
}
/* custom2.css:167 sets `.modal .modal-content { border: 4px solid #ddd }` at
   the same specificity. This file loads later, so this is what wins - the
   dialog was rendering with a 4px grey border it was written not to have. */
.sp-modal .modal-content {
  position: relative;
  border: none;
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface);
  box-shadow: var(--sp-shadow-lg);
  padding: var(--sp-9) var(--sp-9) var(--sp-7);
  font-family: var(--sp-font);
  text-align: left;
}

/* custom2.css:208 gives .validate-pick-save-modal .modal-content a 2px red
   border and a 15px radius at (0,2,0) - the same specificity as the component's
   own rule, so it would win on load order. The dialog chrome is owned here. */
.sp-modal.validate-pick-save-modal .modal-content,
.sp-modal.notification-modal .modal-content {
  border: none;
  border-radius: var(--sp-radius-lg);
  padding: var(--sp-9) var(--sp-9) var(--sp-7);
}

/* Random Pick sits alone above the parlay/teaser forms and needs the vertical
   room the old inline margin gave it. */
.sp-random-pick {
  margin: var(--sp-8) 0;
}

/* Inline radio row - the teaser "Points" selector. Was five inline styles
   spread across the loop in _teaser.php. */
.sp-radio-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-6);
  min-height: 40px;
  margin-left: var(--sp-6);
}
.sp-radio-row .radio.inline {
  display: flex;
  align-items: center;
  margin: 0;
  gap: var(--sp-2);
}
.sp-radio-row .radio.inline label {
  margin: 0;
  font-size: var(--sp-text-base);
  color: var(--sp-ink-2);
  cursor: pointer;
}
/* stacked variant - one choice per line, for consent-style questions */
.sp-radio-row--stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-4);
  margin-left: 0;
  margin-bottom: var(--sp-6);
}
.sp-radio-row__label {
  text-align: right;
  margin-bottom: 0;
}
.points-radio-col { padding-left: 0; }

/* The save/cancel row on the pick, parlay and teaser forms. Was an inline
   style="display:flex; gap:16px" repeated in each view.
   .pick-form-save-cancel-wrap also carries data-allow-pick-for-this-game, read
   by custom.js after a save - the class is a hook as well as a layout box. */
.pick-form-save-cancel-wrap {
  display: flex;
  gap: var(--sp-6);
  align-items: center;
}

/* --- unit stepper ----------------------------------------------------------
   The wager field on the parlay and teaser forms: a narrow number input with
   Max / + / - / Min beside it. Deliberately NOT a third button size - these are
   .sp-btn--sm; what they needed was a grouping, not a smaller control.

   .btn-change-unit / .btn-max-unit / .btn-add-unit / .btn-sub-unit /
   .btn-min-unit are read by custom.js:934 and are styling-free hooks - kept.
   The buttons have no type attribute, so they are submit buttons that the
   handler preventDefault()s; that is left exactly as it was. */
.sp-stepper {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
}
/* the same runtime wrapper, for every stepper - see the note on .sp-field */
.sp-stepper .form-group,
.sp-stepper__controls .form-group {
  display: flex;
  align-items: center;
  margin: 0 !important;
  padding: 0 !important;
}
.sp-stepper .form-control,
.sp-stepper__controls .form-control {
  margin: 0 !important;
}

.sp-stepper .form-control {
  float: none;
  width: 88px;
  flex: 0 0 88px;
  height: var(--sp-field-h);
  padding: 0 var(--sp-5);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background-image: none;
  box-shadow: none;
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--sp-ink);
}
.sp-stepper .form-control:focus {
  outline: none;
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
/* +/- are single glyphs; Max/Min are words. Equal min-width keeps the row even
   without forcing the word buttons to wrap. */
.sp-stepper .sp-btn {
  min-width: 40px;
  padding: 0 var(--sp-5);
}

/* A right-aligned action row for buttons that live INSIDE a modal body rather
   than in the component's footer - forms that own their own submit. Same
   geometry as .sp-modal__foot so the two are indistinguishable. */
.sp-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
}

/* --- upgrade dialog --------------------------------------------------------
   Two steps in one dialog: what you get, then the card. Only one is in flow at
   a time, so the dialog is the height of the step you are on rather than the
   height of everything. */
.sp-upgrade__step { display: none; }
.sp-upgrade__step--offer { display: block; }
.sp-upgrade.is-paying .sp-upgrade__step--offer { display: none; }
.sp-upgrade.is-paying .sp-upgrade__step--pay { display: block; }

/* The price, said once and said properly. It used to be a clause in a sentence
   above a sixteen-row table. */
.sp-upgrade__price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-2);
}
.sp-upgrade__amount {
  font-size: 34px;
  font-weight: 600;
  letter-spacing: var(--sp-track-tight);
  line-height: 1;
  color: var(--sp-ink);
  font-variant-numeric: tabular-nums;
}
.sp-upgrade__per { font-size: var(--sp-text-md); color: var(--sp-muted); }
.sp-upgrade__offer { align-self: center; }
.sp-upgrade__for {
  margin: 0 0 var(--sp-8);
  font-size: var(--sp-text-md);
  color: var(--sp-muted);
}

.sp-upgrade__benefits {
  margin: 0 0 var(--sp-7);
  padding: 0;
  list-style: none;
}
.sp-upgrade__benefits > li {
  display: flex;
  gap: var(--sp-5);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--sp-hairline);
  font-size: var(--sp-text-md);
  line-height: var(--sp-leading);
  color: var(--sp-ink);
}
.sp-upgrade__benefits > li:last-child { border-bottom: none; }
.sp-upgrade__benefits .material-icons {
  flex: 0 0 auto;
  margin-top: 1px;
  font-size: 18px;
  color: var(--sp-success);
}
.sp-upgrade__benefits strong { font-weight: 500; }
.sp-upgrade__note {
  display: block;
  margin-top: 2px;
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading);
  color: var(--sp-muted);
}

.sp-upgrade__back {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-6);
}
.sp-upgrade__back .material-icons { font-size: 18px; }

/* What they are about to buy, kept in front of them while they type a card. */
.sp-upgrade__summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-6);
  padding: var(--sp-5) var(--sp-6);
  margin-bottom: var(--sp-7);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface-2);
}
.sp-upgrade__summary-name { font-size: var(--sp-text-md); font-weight: 500; color: var(--sp-ink); }
.sp-upgrade__summary-cost {
  flex: 0 0 auto;
  font-size: var(--sp-text-md);
  font-weight: 600;
  color: var(--sp-ink);
  font-variant-numeric: tabular-nums;
}
.sp-upgrade__notice {
  margin: var(--sp-6) 0 0;
  font-size: var(--sp-text-sm);
}
.sp-upgrade__terms {
  margin: var(--sp-6) 0 0;
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading-loose);
  color: var(--sp-muted);
}

/* --- disclosure ------------------------------------------------------------
   Detail that should be available without being the first thing anyone reads. */
.sp-disclosure {
  margin: 0 0 var(--sp-7);
  border-top: 1px solid var(--sp-hairline);
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-disclosure > summary {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-5) 0;
  font-size: var(--sp-text-sm);
  font-weight: 500;
  color: var(--sp-ink-2);
  cursor: pointer;
  list-style: none;
}
.sp-disclosure > summary::-webkit-details-marker { display: none; }
.sp-disclosure > summary::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 5px solid var(--sp-muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform var(--sp-dur) var(--sp-ease);
}
.sp-disclosure[open] > summary::before { transform: rotate(90deg); }
.sp-disclosure > summary:hover { color: var(--sp-brand); }
.sp-disclosure__body { padding-bottom: var(--sp-6); }

/* --- card fields -----------------------------------------------------------
   Card number across the top, then expiry, security code and postal code.
   Every one of these was a floated .col-md-6 with no .row, which took them out
   of flow and left the dialog around them with no height. */
.sp-cardgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-6);
}
.sp-cardgrid__wide { grid-column: 1 / -1; }
/* the grid owns the spacing; .form-group brings a bottom padding of its own */
.sp-cardgrid > .form-group {
  margin: 0;
  padding: 0;
  float: none;
  width: auto;
}
@media (max-width: 460px) {
  .sp-cardgrid { grid-template-columns: 1fr; }
}
/* custom.js marks a bad field by putting .has-error on its .form-group, which
   until now had nothing to show for it */
.sp-field.has-error .form-control,
.has-error .sp-field .form-control {
  border-color: var(--sp-danger);
}
.sp-field.has-error label { color: var(--sp-danger); }

/* Card-form trust line: "encrypted & handled by Stripe" + Powered by Stripe.
   A quiet footnote, not a banner - it reassures without shouting. */
.sp-securepay {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  margin: var(--sp-7) auto 0;
  text-align: center;
}
.sp-securepay__line {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--sp-text-sm);
  color: var(--sp-muted);
}
.sp-securepay__line .material-icons { font-size: 15px; color: var(--sp-success); }
.sp-securepay__brand {
  font-size: var(--sp-text-xs);
  letter-spacing: .02em;
  color: var(--sp-faint);
}
.sp-securepay__brand strong { font-weight: 700; color: var(--sp-muted); }

/* A scrollable list of checkboxes - the member picker in Copy League. Replaces
   a 174-line inline <style> block in league/_form.php that styled a bespoke
   .member-label / .member-info / .select-all-section structure. */
.sp-checklist {
  max-height: 240px;
  overflow-y: auto;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface-2);
}
.sp-checklist .sp-check:last-child { margin-bottom: 0; }
.sp-checklist--tall { max-height: 380px; }
/* The conference list is built by JS, so its rows are not .sp-check. Material
   Kit hides native checkboxes (opacity:0, absolutely positioned) to swap in its
   own rig, which leaves this list with invisible controls - the reason the old
   markup carried a block of !important overrides inline. */
#conference-checkboxes-list input[type="checkbox"] {
  opacity: 1 !important;
  position: relative !important;
  width: 17px !important;
  height: 17px !important;
  margin: 0 var(--sp-4) 0 0 !important;
  accent-color: var(--sp-brand);
  cursor: pointer !important;
}
#conference-checkboxes-list label {
  display: flex;
  align-items: center;
  margin: 0 0 var(--sp-4);
  font-size: var(--sp-text-md);
  color: var(--sp-ink-2);
  cursor: pointer !important;
}
/* left-aligned action row - the Select all / Select none pair */
.sp-actions--start { justify-content: flex-start; }
/* one action pushed to each end - a destructive link opposite the primary.
   Not space-between: the destructive one is often hidden (Remove card only
   appears when editing an existing card), and space-between would leave the
   remaining button stranded on the left. An auto margin on a display:none
   element does nothing, so the primary stays where it belongs either way. */
.sp-actions--split { justify-content: flex-end; align-items: center; }
.sp-actions--split > :first-child { margin-right: auto; }
/* a destructive action that is a link, not a button (Remove card) */
.sp-link-danger {
  font-size: var(--sp-text-md);
  color: var(--sp-danger);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}
.sp-link-danger:hover,
.sp-link-danger:focus { color: var(--sp-danger); text-decoration: underline; }
.sp-conference-tools {
  margin: 0 0 var(--sp-5);
  padding-bottom: var(--sp-5);
  border-bottom: 1px solid var(--sp-hairline);
}
/* secondary detail on a checkbox label - an email beside a team name */
.sp-check__meta {
  color: var(--sp-muted);
  font-size: var(--sp-text-sm);
}
/* an explanatory line above a form, quieter than body copy */
.sp-note {
  margin: 0 0 var(--sp-7);
  font-size: var(--sp-text-md);
  line-height: var(--sp-leading-loose);
  color: var(--sp-body);
}
.sp-note strong { color: var(--sp-ink); font-weight: 600; }

/* Quiet supporting text - a caveat under a control, not body copy. */
.sp-hint {
  margin: var(--sp-5) 0 0;
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading-loose);
  color: var(--sp-muted);
}
.sp-hint a { color: var(--sp-brand); }

/* A receipt: label/value pairs after a payment. */
.sp-receipt {
  margin: var(--sp-7) 0 0;
  padding: var(--sp-6);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface-2);
  font-size: var(--sp-text-md);
}
.sp-receipt dt {
  float: left;
  clear: left;
  width: 42%;
  padding: var(--sp-2) 0;
  font-weight: 400;
  color: var(--sp-muted);
}
.sp-receipt dd {
  margin: 0;
  padding: var(--sp-2) 0;
  padding-left: 42%;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--sp-ink);
  word-break: break-all;
}

/* --- document modal --------------------------------------------------------
   The legal documents (terms, privacy, refund, disclaimer). Several screens of
   prose, so the body scrolls inside the dialog rather than the dialog growing
   past the viewport, and the headings get real hierarchy. */
.sp-modal--doc .sp-modal__body {
  max-height: min(60vh, 520px);
  overflow-y: auto;
  padding-right: var(--sp-6);   /* clear of the scrollbar */
}
.sp-modal--doc .sp-modal__body h2 {
  margin: var(--sp-8) 0 var(--sp-4);
  font-size: var(--sp-text-lg);
  font-weight: 600;
  color: var(--sp-ink);
}
.sp-modal--doc .sp-modal__body h2:first-child { margin-top: 0; }
.sp-modal--doc .sp-modal__body p {
  margin: 0 0 var(--sp-5);
  line-height: var(--sp-leading-loose);
}
.sp-modal--doc .sp-modal__body ul,
.sp-modal--doc .sp-modal__body ol {
  margin: 0 0 var(--sp-5);
  padding-left: var(--sp-8);
  line-height: var(--sp-leading-loose);
}
.sp-modal--doc .sp-modal__body li { margin-bottom: var(--sp-2); }
.sp-modal--doc .sp-modal__body a { color: var(--sp-brand); }

/* --- auth modals -----------------------------------------------------------
   Sign in / create account / join a league. Same fields and copy as the
   standalone auth pages, so the two do not drift. */
.sp-auth-modal .sp-modal__body {
  padding-top: var(--sp-2);
}
.sp-auth-modal__lede {
  margin: 0 0 var(--sp-7);
  color: var(--sp-body);
}
.sp-auth-modal__lede strong { color: var(--sp-ink); font-weight: 600; }
/* the success pane that replaces the form after a successful sign-up */
.sp-auth-modal__done {
  text-align: center;
  padding: var(--sp-6) 0 var(--sp-2);
}
.sp-auth-modal__done h3 {
  margin: 0 0 var(--sp-4);
  font-size: var(--sp-text-2xl);
  font-weight: 600;
  color: var(--sp-ink);
}
.sp-auth-modal__done p {
  margin: 0 0 var(--sp-8);
  color: var(--sp-body);
}
/* an .sp-auth__aside inside a modal already has the body padding around it */
.sp-auth-modal .sp-auth__aside {
  margin-top: var(--sp-7);
}

/* Invite banner + choice-first sign-up (register modal). */
.sp-invite {
  display: flex; gap: var(--sp-5); align-items: center;
  padding: var(--sp-5) var(--sp-6); margin: 0 0 var(--sp-6);
  background: var(--sp-brand-wash); border: 1px solid var(--sp-brand-ring);
  border-radius: var(--sp-radius-md);
}
.sp-invite__ic {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: var(--sp-radius-md);
  background: var(--sp-brand); color: #fff; display: grid; place-items: center;
}
.sp-invite__ic svg { width: 22px; height: 22px; fill: currentColor; }
.sp-invite__t { min-width: 0; display: flex; flex-direction: column; }
.sp-invite__k { font-size: var(--sp-text-xs); letter-spacing: .10em; text-transform: uppercase; color: var(--sp-muted); font-weight: 700; }
.sp-invite__n { font-size: var(--sp-text-lg); font-weight: 700; color: var(--sp-ink); line-height: var(--sp-leading-tight); }
.sp-invite__c { font-size: var(--sp-text-sm); color: var(--sp-body); }
.sp-invite__foot {
  margin: var(--sp-6) 0 0; padding: var(--sp-4) var(--sp-5);
  background: var(--sp-surface-2); border-radius: var(--sp-radius);
  font-size: var(--sp-text-sm); color: var(--sp-muted); line-height: var(--sp-leading);
}
.sp-invite__foot strong { color: var(--sp-ink-2); font-weight: 600; }
.sp-choicebtns { display: grid; gap: var(--sp-3); }
.sp-auth__back {
  display: inline-block; background: none; border: 0; padding: 0;
  margin: 0 0 var(--sp-5); color: var(--sp-muted); font: inherit; font-size: var(--sp-text-sm); cursor: pointer;
}
.sp-auth__back:hover, .sp-auth__back:focus { color: var(--sp-ink); }

/* --- checkbox --------------------------------------------------------------
   Material Kit replaces the native box with a span-and-pseudo-element rig that
   only works inside its own .checkbox markup. This uses the real input, which
   keeps the label click target, focus ring and screen-reader semantics. */
.sp-check {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}
.sp-check input[type="checkbox"] {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  margin: 2px 0 0;
  accent-color: var(--sp-brand);
  cursor: pointer;
}
.sp-check label {
  margin: 0;
  font-size: var(--sp-text-md);
  line-height: var(--sp-leading-tight);
  color: var(--sp-ink-2);
  cursor: pointer;
}
.sp-check a { color: var(--sp-brand); }

/* Switch: a checkbox drawn as an on/off toggle, for feature settings. The real
   <input> sits on top at opacity 0 so it stays keyboard- and label-driven; the
   track/thumb are painted siblings. Green when on. */
.sp-switch { position: relative; display: inline-block; width: 42px; height: 24px; flex: none; cursor: pointer; }
.sp-switch input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; z-index: 2; }
.sp-switch__track { position: absolute; inset: 0; border-radius: var(--sp-radius-pill);
  background: var(--sp-border-strong); transition: background var(--sp-dur) var(--sp-ease); }
.sp-switch__thumb { position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.35); transition: left var(--sp-dur) var(--sp-ease); }
.sp-switch input:checked ~ .sp-switch__track { background: var(--sp-success); }
.sp-switch input:checked ~ .sp-switch__thumb { left: 20px; }
.sp-switch input:focus-visible ~ .sp-switch__track { box-shadow: 0 0 0 3px var(--sp-brand-ring); }

/* Setting card: a bordered card for one on/off feature - name + description on
   the left, a .sp-switch on the right, and any fields the feature depends on
   nested below a divider (hidden when the feature is off via .is-off). */
.sp-setcards { display: flex; flex-direction: column; gap: 14px; margin: 0 0 var(--sp-10); }
.sp-setcard { border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); padding: 16px 18px; background: var(--sp-surface); }
.sp-setcard__top { display: flex; align-items: flex-start; gap: 16px; }
.sp-setcard__tt { flex: 1; min-width: 0; }
.sp-setcard__name { font-size: 15.5px; font-weight: 700; color: var(--sp-ink); }
.sp-setcard__desc { margin: 5px 0 0; font-size: var(--sp-text-sm); line-height: 1.55; color: var(--sp-muted); }
.sp-setcard__desc b { color: var(--sp-ink-2); font-weight: 700; }
.sp-setcard__desc b.pos { color: var(--sp-success-strong); }
.sp-setcard__desc b.neg { color: var(--sp-brand); }
.sp-setcard__nested { margin: 16px 0 0; padding: 16px 0 0; border-top: 1px solid var(--sp-hairline);
  display: flex; gap: 22px; flex-wrap: wrap; }
.sp-setcard__nested .form-group { margin: 0; }
/* --stack: dependent fields that read top-to-bottom (with their own hints or a
   built list below), rather than the default side-by-side pair. */
.sp-setcard__nested--stack { flex-direction: column; gap: 18px; }
.sp-setcard.is-off .sp-setcard__nested { display: none; }

/* Settings forms read best at a comfortable measure, not stretched to a wide
   screen's edge. Cap the form (fields, cards, save bar all follow); it stays
   left-aligned. Reusable across settings forms. */
.sp-form-narrow { max-width: 680px; }

.sp-fieldset {
  margin: 0 0 var(--sp-7);
  padding: 0;
  border: none;
}
.sp-fieldset__legend {
  display: block;
  width: auto;
  margin: 0 0 var(--sp-5);
  padding: 0;
  border: none;
  font-size: var(--sp-text-sm);
  font-weight: 500;
  line-height: var(--sp-leading-tight);
  color: var(--sp-ink);
}

/* --section: a group of rows inside a longer page form, where the heading has
   to divide the page without competing with its title. Same quiet uppercase
   the settings captions and the pick-form section titles already use.
   --boxed: the same, drawn as a block, for a group that is different in kind
   from what surrounds it rather than just further down the page. */
.sp-fieldset--section,
.sp-fieldset--boxed { margin-bottom: var(--sp-10); }
/* .sp-section-title is the same heading outside a fieldset - a page divided
   into parts that are not all forms. One definition, both call sites. */
.sp-section-title,
.sp-fieldset--section > .sp-fieldset__legend,
.sp-fieldset--boxed > .sp-fieldset__legend {
  width: 100%;
  margin: 0 0 var(--sp-5);
  font-family: var(--sp-font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  line-height: var(--sp-leading-tight);
  text-transform: uppercase;
  color: var(--sp-muted);
}
.sp-section-title,
.sp-fieldset--section > .sp-fieldset__legend {
  margin-bottom: var(--sp-7);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--sp-hairline);
}
/* custom.css's `.wrapper h4` sets a 24px/500/#333 heading for the whole app at
   (0,1,1), where the component sits at (0,1,0). Only the three properties it
   sets are restated, element-qualified, so a real heading element carrying this
   class still gets the component's type. */
h1.sp-section-title, h2.sp-section-title, h3.sp-section-title,
h4.sp-section-title, h5.sp-section-title, h6.sp-section-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--sp-muted);
}
/* The box goes on an inner element, never on the <fieldset> itself: a bordered
   fieldset renders its <legend> into the border and notches it, and no
   combination of display/width/margin on the legend opts out of that reliably
   across engines. */
.sp-fieldset__body {
  padding: var(--sp-8) var(--sp-9) var(--sp-6);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface-2);
}
.sp-fieldset__note {
  max-width: 62ch;
  margin: 0 0 var(--sp-7);
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading-loose);
  color: var(--sp-ink-2);
}

/* A value the form shows but does not edit - an email address with an Edit
   next to it. The min-height is the field height, so the row it sits in lines
   up with the input rows above and below it. */
.sp-readonly {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  min-height: var(--sp-field-h);
}
.sp-readonly__value {
  font-size: var(--sp-text-base);
  color: var(--sp-ink);
  word-break: break-word;
}

/* One field and its actions on a single line. */
.sp-inlineform {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
}
.sp-inlineform__field { flex: 1 1 auto; min-width: 0; }
/* Material Kit gives every .form-control a 7px bottom margin, so the field box
   comes out 7px taller than the input inside it and anything centred beside it
   sits low. Same neutralisation .sp-field does, for the same reason. */
.sp-inlineform__field .form-control { margin: 0; }
@media (max-width: 600px) {
  .sp-inlineform { flex-wrap: wrap; }
  .sp-inlineform__field { flex: 1 0 100%; }
}

/* reCAPTCHA renders a fixed 304px iframe; centre it rather than let it sit
   left in a 460px dialog */
.sp-captcha {
  display: flex;
  justify-content: center;
  margin-bottom: var(--sp-7);
}

/* --- pricing ---------------------------------------------------------------
   Two Basic-vs-Premium comparison tables. Premium is the column being sold, so
   it is tinted and the rest of the table stays quiet. */
/* Narrower than a full .container: at 1170px the tables dwarfed a 62ch lede and
   the page read as two unrelated blocks. */
.sp-pricing {
  max-width: 960px;
}
.sp-pricing a {
  color: var(--sp-brand);
  text-decoration: none;
}
.sp-pricing a:hover,
.sp-pricing a:focus {
  color: var(--sp-brand-hover);
  text-decoration: underline;
}
.sp-pricing__section {
  margin-bottom: var(--sp-11);
}
.sp-pricing__heading {
  margin: 0 0 var(--sp-4);
  font-size: var(--sp-text-2xl);
  font-weight: 600;
  letter-spacing: var(--sp-track-tight);
  color: var(--sp-ink);
}
.sp-pricing__note {
  max-width: 68ch;
  margin: 0 0 var(--sp-7);
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading-loose);
  color: var(--sp-body);
}
.sp-pricing__note strong { color: var(--sp-ink); font-weight: 600; }

.sp-pricing__table td:first-child {
  color: var(--sp-ink-2);
}
/* The plan columns are centred by the column, not by a class on each cell. Ten
   of the sixteen "not included" dashes had no .text-center on their <td> and
   sat left, so the column read as two ragged columns. This also makes the
   per-cell .text-center in those tables redundant rather than load-bearing. */
.sp-pricing__table > thead > tr > th:not(:first-child),
.sp-pricing__table > tbody > tr > td:not(:first-child) {
  text-align: center;
}
.sp-pricing__col {
  width: 132px;
}
/* The column being sold - a wash, not the tint. --sp-brand-tint is sized for a
   badge; run down a whole column it reads as an error state. */
.sp-pricing__table > thead > tr > th.sp-pricing__col--premium,
.sp-pricing__table > tbody > tr > td:last-child {
  background: var(--sp-brand-wash);
}
.sp-pricing__table > thead > tr > th.sp-pricing__col--premium {
  color: var(--sp-brand);
}
.sp-pricing__table .material-icons {
  font-size: 20px;
  color: var(--sp-success);
}
/* an em dash reads as "not included" where a blank cell reads as an oversight */
.sp-pricing__no {
  color: var(--sp-faint);
  font-size: var(--sp-text-lg);
  line-height: 1;
}
.sp-pricing__cta {
  margin-top: var(--sp-10);
  padding-top: var(--sp-9);
  border-top: 1px solid var(--sp-hairline);
}
.sp-pricing__cta .sp-pricing__note {
  margin: var(--sp-5) 0 0;
}

/* --- banner ----------------------------------------------------------------
   A site-wide notice set by an admin, shown on league, picks, scoreboard and
   the public league view. It was #ffff00 with a grey border and four
   !importants - the single loudest thing on any page it appeared on. */
.sp-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-5);
  margin: 0 0 var(--sp-7);
  padding: var(--sp-5) var(--sp-6);
  border: 1px solid var(--sp-warning-border, rgba(184, 134, 11, 0.28));
  border-left: 3px solid var(--sp-warning);
  border-radius: var(--sp-radius-md);
  background: var(--sp-warning-tint);
  font-size: var(--sp-text-md);
  line-height: var(--sp-leading);
  color: var(--sp-ink-2);
}
.sp-banner__icon {
  flex: none;
  font-size: 20px;
  line-height: 1.2;
  color: var(--sp-warning);
}
.sp-banner__text > :last-child { margin-bottom: 0; }
.sp-banner a { color: var(--sp-brand); font-weight: 500; }

/* The action row at the foot of a long form: primary on the left with any
   secondary action beside it, and a related-but-separate action pushed right. */
.sp-formbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  margin-top: var(--sp-10);
  padding-top: var(--sp-8);
  border-top: 1px solid var(--sp-hairline);
}
.sp-formbar__main {
  display: flex;
  align-items: center;
  gap: var(--sp-7);
}
/* "Unsaved changes" hint next to the Save button, shown only once a field has
   been edited (JS adds .is-dirty). Switches auto-save and don't set it. */
.sp-formbar__hint {
  display: none;
  color: var(--sp-warning, #B8860B);
  font-size: var(--sp-text-sm);
  font-weight: 600;
}
.sp-formbar.is-dirty .sp-formbar__hint { display: inline; }
/* Unsaved text-field changes lift the Save bar into a card pinned to the bottom
   of the viewport (the picks submit bar, .fut-submit, does the same) so it's
   reachable without scrolling to the end of a long settings form. Clean = the
   plain inline bar above; only the dirty state floats. The switches auto-save
   and never set .is-dirty, so this appears solely for the fields that do need a
   Save. Sticky (not fixed) keeps it inside the form's centered column and lets
   it settle into place at the form's end. */
.sp-formbar.is-dirty {
  position: sticky;
  bottom: var(--sp-4);
  z-index: 3;
  padding: var(--sp-5) var(--sp-6);
  background: var(--sp-surface);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  box-shadow: var(--sp-shadow-lg);
}
@media (prefers-reduced-motion: no-preference) {
  .sp-formbar.is-dirty { animation: sp-formbar-rise .18s ease; }
}
@keyframes sp-formbar-rise {
  from { transform: translateY(8px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

/* Brief bottom-centre confirmation toast (app.toast). */
.sp-toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%) translateY(8px);
  background: var(--sp-ink);
  color: #fff;
  padding: 10px 18px;
  border-radius: var(--sp-radius-pill);
  font-size: var(--sp-text-md);
  font-weight: 600;
  box-shadow: var(--sp-shadow-lg);
  opacity: 0;
  transition: opacity .2s ease, transform .2s ease;
  z-index: 4000;
  pointer-events: none;
}
.sp-toast.is-show { opacity: 1; transform: translateX(-50%) translateY(0); }
@media (max-width: 600px) {
  .sp-formbar,
  .sp-formbar__main { flex-direction: column; align-items: stretch; gap: var(--sp-5); }
  .sp-formbar .sp-btn { width: 100%; }
  .sp-formbar .sp-linkbtn { text-align: center; }
}

/* Radio rows inside a settings form. Was an inline <style> block in
   league/user-setting.php setting `.form-group .radio label` globally with five
   !importants - unscoped, so it applied to every control on that page, not just
   the radio pairs it was written for.

   Material Kit hides the real <input> and draws the control as two absolutely
   positioned 15px spans - .circle is the ring, .check the filled dot, stacked
   on the same spot. That rig is pinned with a flat `top: 2px`, which only ever
   centred against the line-height the vendor sheet assumed. Here it sat ~4px
   high, and the row itself sat ~5px above the field label, because
   `.form-horizontal .radio` adds a padding-top of its own.

   So the row is given the same height as an input and centres its content, the
   label's box is reduced to exactly its line box, and the rig is centred on
   that box rather than offset from its top. Text, ring and dot then share one
   centre line by construction, and the row lines up with its field label the
   same way the text inputs above it do.

   --sp-radio-dot mirrors the vendor's 15px so the offsets stay derived rather
   than hand-computed; it is local to this block, and if the vendor size ever
   changes this is the one place to follow it. */
.sp-settings .form-group .radio.inline {
  display: inline-flex;
  align-items: center;
  min-height: var(--sp-field-h);
  /* both zeroed deliberately: .form-horizontal .radio brings a padding-top and
     .radio a margin-bottom, and either one pushes the row off its label */
  margin: 0;
  padding: 0;
  vertical-align: top;
}
.sp-settings .form-group .radio label,
.sp-settings .form-group .radio.inline label {
  display: inline-block;
  position: relative;
  margin: 0 var(--sp-9) 0 0;
  /* room for the rig, which is out of flow, plus the gap it needs */
  padding: 0 0 0 calc(var(--sp-radio-dot) + var(--sp-3));
  line-height: 20px;
  min-height: 20px;
  text-align: left;
}
/* The pick form has the same rig and had the same fault, so it is the same
   rule. custom-bootstrap-wizard.css:1395 pins every one of these spans with
   `.radio label span { left: 10px; top: 2px }` at (0,1,2) - a flat offset that
   only ever suited the vendor's own row. On the pick card it put the ring 10px
   right of Line history, Potential win, Unit risk and Save, all of which start
   at the card's edge, so the options looked indented from everything under
   them. left:0 puts the ring on that edge and top:50% centres it on the label
   rather than guessing from its top. */
.sp-settings .form-group .radio label .circle,
.sp-settings .form-group .radio label .check,
.pick-form .radio-group .radio label .circle,
.pick-form .radio-group .radio label .check {
  position: absolute;
  left: 0;
  top: 50%;
  margin: calc(var(--sp-radio-dot) / -2) 0 0;
}
/* spacing that was three inline margin-bottoms */
.sp-field-block { margin-bottom: var(--sp-9); }
.sp-field-block--wide { margin-bottom: var(--sp-10); }

/* --- tip ------------------------------------------------------------------
   The "what does this setting do" affordance on a field label. Built by
   app.setupLeagueHint from the hidden <p class="league-frm-hint"> the view
   already carries, so the copy stays in the view.

   It used to be a filled red info glyph that slide-toggled the paragraph open
   inside the control column - the form jumped on every read, the answer
   appeared where the control should be, and on this page alone five red discs
   competed with the Save button for the eye. The icon is now quiet until you
   want it: muted at rest, brand on hover, focus or open, with the answer in a
   bubble that costs the layout nothing.

   Hover and focus are pure CSS. .is-open is the touch route, where there is no
   hover - see setupLeagueHint.

   Named .sp-tip, not .sp-hint: .sp-hint is already taken above by the quiet
   supporting paragraph the cookie banner uses three times on every page, and
   this block loads after it. */
.sp-tip {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
  margin-left: var(--sp-2);
}
.sp-tip__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  background: none;
  border-radius: var(--sp-radius-pill);
  color: var(--sp-muted);
  cursor: pointer;
  transition: color var(--sp-dur) var(--sp-ease);
}
.sp-tip__btn .material-icons { font-size: 18px; line-height: 1; }
.sp-tip__btn:hover,
.sp-tip__btn:focus-visible,
.sp-tip.is-open .sp-tip__btn { color: var(--sp-brand); }
.sp-tip__btn:focus { outline: none; }
.sp-tip__btn:focus-visible {
  outline: 2px solid var(--sp-brand);
  outline-offset: 2px;
}

.sp-tip__bubble {
  position: absolute;
  /* 1070 is Bootstrap's tooltip level: above its modal (1050) so a tip inside a
     dialog still shows, and clear of page chrome that sets its own stacking -
     the Redactor toolbar on the league form is position:relative z-index:100
     with a white background, and was painting over the top of this bubble. */
  z-index: 1070;
  top: calc(100% + var(--sp-3));
  /* left-anchored, not centred: the icon sits at the end of a label that can be
     one word or eight, and a centred bubble would wander with it */
  left: -6px;
  width: max-content;
  max-width: 320px;
  padding: var(--sp-5) var(--sp-6);
  border-radius: var(--sp-radius);
  background: var(--sp-ink);
  color: #fff;
  font-size: var(--sp-text-sm);
  font-weight: 400;
  line-height: var(--sp-leading);
  letter-spacing: normal;
  text-align: left;
  text-transform: none;
  white-space: normal;
  box-shadow: var(--sp-shadow-lg);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--sp-dur) var(--sp-ease-out),
              transform var(--sp-dur) var(--sp-ease-out),
              visibility 0s linear var(--sp-dur);
  pointer-events: none;
}
/* The caret, lined up under the middle of the 18px icon: the bubble starts 6px
   left of the anchor and the caret's own box is 12px wide, so its centre sits
   at (left + 6) - 6 from the anchor - i.e. left is the icon's own half-width. */
.sp-tip__bubble::after {
  content: "";
  position: absolute;
  bottom: 100%;
  left: 9px;
  border: 6px solid transparent;
  border-bottom-color: var(--sp-ink);
}
.sp-tip__bubble strong { color: #fff; font-weight: 600; }
.sp-tip__bubble p:last-child { margin-bottom: 0; }

.sp-tip:hover .sp-tip__bubble,
.sp-tip__btn:focus-visible + .sp-tip__bubble,
.sp-tip.is-open .sp-tip__bubble {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}
/* the open bubble is selectable text, the resting one must never swallow a
   click meant for the control behind it */
.sp-tip.is-open .sp-tip__bubble { pointer-events: auto; }

/* Below Bootstrap's sm breakpoint the columns stack, the label runs the full
   width, and the icon can end up far enough right that a 320px bubble hangs off
   the screen - measured at 198px over on a 390px phone, and still 8px over at
   600px. From 768px up it always fits, because the label sits in a narrow left
   column.

   So on a phone the bubble stops hanging off the icon and spans the row
   instead: the anchor goes static, the row becomes the containing block, and
   the bubble insets from both edges. The caret goes with it - it would no
   longer be pointing at anything. The icon still turns brand while its bubble
   is open, which is what ties the two together at that size. */
@media (max-width: 767px) {
  .form-group:has(> label > .sp-tip) { position: relative; }
  /* the label is positioned too, and would otherwise claim the bubble and
     squeeze it to the width of the label's own text - "Status" gave a 60px
     column of wrapped words. Only labels that hold a tip go static, so the
     radio rig above, which is positioned against its own label, is untouched */
  .form-group:has(> label > .sp-tip) > label { position: static; }
  .sp-tip { position: static; }
  .sp-tip__bubble {
    left: var(--sp-6);
    right: var(--sp-6);
    width: auto;
    max-width: none;
  }
  .sp-tip__bubble::after { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .sp-tip__bubble { transition: opacity var(--sp-dur) linear, visibility 0s linear var(--sp-dur); transform: none; }
}

/* The generated schedule grid. Its three rules used to be a <style> block
   concatenated into the JS string that builds the table, re-injected on every
   render and unreachable by any stylesheet. */
.schedule-table td { vertical-align: middle; }
/* gap, not a margin on the .label: making this a flex row discards the literal
   space the markup has between the name and the chip, and a margin there has to
   win an argument with every .label rule in the app. The container owns it. */
.schedule-table .team-content {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
/* the count is a number, not a shouted status */
.schedule-table .label { text-transform: none; letter-spacing: 0; }

/* --- panel + callout -------------------------------------------------------
   A bordered block of related settings, and the explanatory notes inside it.
   The schedule page had eleven inline styles doing this by hand, on a
   Bootstrap 4 palette that is nowhere else on the site (#28a745 green, #007bff
   blue, #d4edda, #e3f2fd). */
.sp-panel {
  padding: var(--sp-9);
  margin-bottom: var(--sp-8);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface);
}
.sp-panel--sunken {
  margin-top: var(--sp-11);
  background: var(--sp-surface-2);
}
.sp-panel__title {
  margin: 0 0 var(--sp-6);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--sp-hairline);
  font-size: var(--sp-text-lg);
  font-weight: 600;
  color: var(--sp-ink);
}

/* An explanatory note. Neutral by default; --ok and --info carry meaning. */
.sp-callout {
  margin: 0 0 var(--sp-7);
  padding: var(--sp-5) var(--sp-6);
  border-left: 3px solid var(--sp-border-strong);
  border-radius: 0 var(--sp-radius) var(--sp-radius) 0;
  background: var(--sp-surface-2);
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading-loose);
  color: var(--sp-ink-2);
}
.sp-callout > :last-child { margin-bottom: 0; }
.sp-callout__line { margin: 0 0 var(--sp-3); }
.sp-callout__line--meta { color: var(--sp-muted); }
.sp-callout__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  margin-top: var(--sp-5);
}
.sp-callout__note { color: var(--sp-muted); }
.sp-callout p { margin: 0 0 var(--sp-4); font-size: inherit; color: inherit; }
.sp-callout--ok {
  border-left-color: var(--sp-success);
  background: var(--sp-success-tint);
}
.sp-callout--info {
  border-left-color: var(--sp-brand);
  background: var(--sp-brand-wash);
}
.sp-callout--warn {
  border-left-color: var(--sp-warning);
  background: var(--sp-warning-tint);
}
.sp-callout--danger {
  border-left-color: var(--sp-danger);
  background: var(--sp-danger-tint);
}

/* Flash notices (session messages) render as a callout with a leading icon and
   a dismiss control - the app-wide, readable replacement for the legacy
   Bootstrap alert bar (see //shared/_flashes). A notice is never a saturated
   bar of colour with white text (docs/ui-components.md). */
.sp-flash { display: flex; align-items: flex-start; gap: var(--sp-4); }
.sp-flash__icon { flex: none; font-size: 20px; line-height: 1.3; color: var(--sp-muted); }
.sp-flash__text { flex: 1 1 auto; min-width: 0; }
.sp-flash__text > :last-child { margin-bottom: 0; }
.sp-flash__close {
  flex: none; margin-left: var(--sp-3); padding: 0; background: none; border: 0;
  font-size: 22px; line-height: 1; color: var(--sp-muted); cursor: pointer; opacity: .7;
}
.sp-flash__close:hover { opacity: 1; }
.sp-callout--ok .sp-flash__icon { color: var(--sp-success); }
.sp-callout--info .sp-flash__icon { color: var(--sp-brand); }
.sp-callout--warn .sp-flash__icon { color: var(--sp-warning); }
.sp-callout--danger .sp-flash__icon { color: var(--sp-danger); }

/* A label, a control and its action on one line. Replaces layout tables. */
.sp-inline-field {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-5);
}
/* .sp-settings gives .control-label a padding-top to align it against a STACKED
   field; on one line that drops the label below the control it names.

   The rule to beat is `.sp-settings .control-label, .sp-settings .form-horizontal
   label` - and it is the second half that matters, at (0,2,1), because these
   rows sit inside a .form-horizontal. A tie loses on load order from here, so
   the second selector below carries three classes and a type and wins outright
   rather than by position. */
.sp-inline-field > label,
.form-section.sp-inline-field > label.control-label {
  margin: 0;
  padding-top: 0;
  padding-bottom: 0;
  font-size: var(--sp-text-sm);
  font-weight: 500;
  color: var(--sp-ink);
}
.sp-inline-field__control { flex: 0 1 320px; }
/* Material Kit wraps a control in its own .form-group at runtime and both carry
   margins, so this box came out 68px around a 38px select. Everything in the
   row centres on the box, which put the button 15px off the control it sits
   beside. The box is the control's height and nothing else. */
.sp-inline-field__control,
.sp-inline-field__control .form-group {
  margin: 0 !important;
  padding: 0 !important;
}
.sp-inline-field__control .form-control {
  width: 100%;
  height: var(--sp-field-h);
  margin: 0 !important;
}
@media (max-width: 600px) {
  .sp-inline-field { align-items: stretch; flex-direction: column; }
  .sp-inline-field__control { flex: 1 1 auto; }
}

/* Links inside a data grid. custom.css paints them Bootstrap link-blue
   (#337ab7) at `.fresh-table[class*="full-color-"] .table td a` - (0,3,2).
   Same selector here, so it ties and wins on load order. Blue appears nowhere
   else in the palette. */
.fresh-table[class*="full-color-"] .table td a {
  color: var(--sp-brand);
}
/* Underline only links that are actually text. The row expander and the delete
   control are icon-only anchors, and underlining a material-icons glyph draws a
   stray rule under the chevron. */
.fresh-table[class*="full-color-"] .table td a:hover,
.fresh-table[class*="full-color-"] .table td a:focus {
  color: var(--sp-brand-hover);
}
.fresh-table[class*="full-color-"] .table td a:not(:has(.material-icons)):hover,
.fresh-table[class*="full-color-"] .table td a:not(:has(.material-icons)):focus {
  text-decoration: underline;
}

/* --- settings row ----------------------------------------------------------
   A label on the left, its control at the right edge, a hairline under it.
   Use .sp-switchrow on a single row, or .sp-notify-list on a wrapper to give
   every row inside it the same treatment.

   The notification toggles on league user-settings were each a nested Bootstrap
   grid - a `col-sm-4 col-lg-2` label column inside an already offset col-sm-9 -
   so at desktop the label had about a sixth of the width and every one of them
   wrapped to two lines. A row is a label and a switch.

   Yii puts the control inside a wrapper <div> the layout does not want, so the
   wrapper is display:contents and its children join the row directly. That is
   what lets a toggle with dependent fields under it - parlays, teasers, units -
   keep its switch on the row and drop the fields onto their own line. */
.sp-switchrow,
.sp-notify-list > .form-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* no column gap: the control is pushed over by margin-left:auto, and a column
     gap only reserves space the control then stops short of. The separation
     from the label is padding on the control instead. */
  gap: 0;
  margin: 0 0 var(--sp-3);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-notify-list > .form-group:last-child { border-bottom: none; }
/* rows in a list run together - the hairline is the separator, and a margin
   under it just detaches the line from the row it belongs to */
.sp-notify-list > .form-group { margin-bottom: 0; }

.sp-switchrow > label,
.sp-switchrow > .control-label,
.sp-switchrow > .sp-switchrow__main,
.sp-notify-list > .form-group > label,
.sp-notify-list > .form-group > .control-label,
.sp-notify-list > .form-group > .sp-switchrow__main {
  /* basis 0, not auto: the row wraps (that is how a dependent block gets its
     own line), and a wrapping flex container breaks the line on the items'
     BASE sizes before it ever shrinks them. With basis auto, a label longer
     than the phone is wide took the whole row and pushed its control onto a
     line of its own - one row in nine did that at 390px. From zero it grows
     into whatever is left instead, and the 60ch cap still holds at desktop. */
  flex: 1 1 0;
  float: none;
  width: auto;
  max-width: 60ch;
  min-width: 0;
  margin: 0;
  padding: 0;
  font-weight: 400;
  color: var(--sp-ink-2);
}
/* :not(.togglebutton) matters: where Yii wraps the control in a div this
   flattens the wrapper, but where the control IS the div - the leagues list -
   flattening it would hand margin-left:auto to the <label> inside and the
   switch would stop halfway across the row. */
.sp-switchrow > div:not(.togglebutton),
.sp-notify-list > .form-group > div:not(.togglebutton) { display: contents; }

.sp-switchrow .togglebutton,
.sp-notify-list .togglebutton {
  margin: 0 0 0 auto;
  padding-left: var(--sp-6);   /* keeps a long label off the control */
}
/* the native checkbox carries the browser's own 3px margin, which is enough to
   knock it off the centre line of the label beside it */
.sp-switchrow .togglebutton input[type="checkbox"],
.sp-notify-list .togglebutton input[type="checkbox"] { margin: 0; }
.sp-switchrow .togglebutton label,
.sp-notify-list .togglebutton label { margin: 0; }

/* whatever else the wrapper held - a dependent block, a validation message -
   takes the full width of the next line rather than crowding the switch */
.sp-switchrow > div > .sp-subsection,
.sp-switchrow > div > .errorMessage,
.sp-notify-list > .form-group > div > .errorMessage { flex: 1 0 100%; }

/* --- date range picker -----------------------------------------------------
   The standings toolbar uses a different library from the rest of the site:
   the league form's start/end dates are bootstrap-datepicker (.datepicker,
   styled below), and this is daterangepicker, which shipped stock and had never
   been touched - a grey header band, Bootstrap 3 blue for the selection, and a
   green Apply next to a grey Cancel that belong to no palette here.

   Same treatment as .datepicker, so the two read as one control. The vendor
   sheet loads before this one and its selectors are single-class, so equal
   specificity and later order is enough; nothing here needs !important except
   where custom.css paints table headers unscoped, which is the same rule the
   .datepicker block below has to answer. */
.daterangepicker {
  padding: var(--sp-4);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface);
  box-shadow: var(--sp-shadow-lg);
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
}
/* the vendor draws the little pointer with two stacked triangles */
.daterangepicker:before { border-bottom-color: var(--sp-border); }
.daterangepicker:after { border-bottom-color: var(--sp-surface); }

/* the preset list down the side - Last Week, This Month, Custom Range */
.daterangepicker .ranges li {
  border-radius: var(--sp-radius-sm);
  background: none;
  border: none;
  color: var(--sp-ink-2);
  font-size: var(--sp-text-md);
  padding: var(--sp-4) var(--sp-5);
  transition: background-color var(--sp-dur) var(--sp-ease),
              color var(--sp-dur) var(--sp-ease);
}
.daterangepicker .ranges li:hover {
  background: var(--sp-surface-3);
  color: var(--sp-ink);
}
.daterangepicker .ranges li.active {
  background: var(--sp-brand);
  color: var(--sp-on-brand);
  font-weight: 600;
}

/* Apply / Cancel belong to a Custom Range only: every preset range applies the
   instant it is clicked, so these under the preset list read as "your pick
   hasn't taken yet". Hidden until the custom-range calendars open (custom.js
   toggles .show-custom), then sat at the bottom-right under the calendars
   rather than under the preset list. */
.daterangepicker .range_inputs { display: none; }
/* NB: do NOT set position here - .daterangepicker is already position:absolute
   (the dropdown overlay), so it is already the containing block for the absolute
   buttons below. Overriding it to relative drops the picker out of its overlay
   and it vanishes on open. */
.daterangepicker.show-custom { padding-bottom: 52px; }
.daterangepicker.show-custom .range_inputs {
  display: flex;
  gap: var(--sp-3);
  justify-content: flex-end;
  margin: 0;
  position: absolute;
  right: var(--sp-5);
  bottom: var(--sp-4);
}

/* Apply and Cancel arrive as .btn-success and .btn-default - a green and a grey
   that exist nowhere else in this interface. Given the same shape as .sp-btn. */
.daterangepicker .applyBtn,
.daterangepicker .cancelBtn {
  height: auto;
  padding: var(--sp-3) var(--sp-6);
  border-radius: var(--sp-radius);
  border: 1px solid transparent;
  font-family: var(--sp-font);
  font-size: var(--sp-text-md);
  font-weight: 600;
  text-transform: none;
  box-shadow: none;
}
.daterangepicker .applyBtn {
  background: var(--sp-brand);
  border-color: var(--sp-brand);
  color: var(--sp-on-brand);
}
.daterangepicker .applyBtn:hover,
.daterangepicker .applyBtn:focus {
  background: var(--sp-brand-hover);
  border-color: var(--sp-brand-hover);
  color: var(--sp-on-brand);
}
.daterangepicker .cancelBtn {
  background: var(--sp-surface);
  border-color: var(--sp-border);
  color: var(--sp-body);
}
.daterangepicker .cancelBtn:hover,
.daterangepicker .cancelBtn:focus {
  background: var(--sp-surface-3);
  border-color: var(--sp-border-strong);
  color: var(--sp-ink);
}

/* the two date fields above the calendars */
.daterangepicker .input-mini {
  height: var(--sp-field-h);
  padding-left: 32px;
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface);
  color: var(--sp-ink);
  font-family: var(--sp-font);
  font-size: var(--sp-text-md);
}
.daterangepicker .input-mini.active {
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-ring);
}
.daterangepicker .daterangepicker_input i { color: var(--sp-muted); }

/* the calendar grid, matching .datepicker below cell for cell */
.daterangepicker .calendar-table { border: none; background: none; }
.daterangepicker table { border-collapse: separate; border-spacing: 2px; }
.daterangepicker th,
.daterangepicker td { border: none; background: none; }
/* custom.css paints `table thead` AND `table thead th` #BDBDBD unscoped, with
   !important on both - the same pair the .datepicker block has to clear. */
.daterangepicker thead { background: none !important; }
.daterangepicker th {
  background: none !important;
  border: none !important;
  color: var(--sp-faint) !important;
  font-size: 10px !important;
  font-weight: 600 !important;
  letter-spacing: var(--sp-track-label) !important;
  text-transform: uppercase;
}
.daterangepicker th.month {
  color: var(--sp-ink) !important;
  font-size: var(--sp-text-sm) !important;
  letter-spacing: var(--sp-track-label) !important;
}
.daterangepicker th.prev,
.daterangepicker th.next {
  border-radius: var(--sp-radius-sm);
  color: var(--sp-muted) !important;
  cursor: pointer;
  transition: background-color var(--sp-dur) var(--sp-ease);
}
.daterangepicker th.prev:hover,
.daterangepicker th.next:hover {
  background: var(--sp-surface-3) !important;
  color: var(--sp-ink) !important;
}
.daterangepicker td {
  width: 34px;
  height: 32px;
  border-radius: var(--sp-radius-sm);
  color: var(--sp-ink-2);
  font-size: var(--sp-text-md);
  transition: background-color var(--sp-dur) var(--sp-ease),
              color var(--sp-dur) var(--sp-ease);
}
.daterangepicker td.available:hover {
  background: var(--sp-surface-3);
  color: var(--sp-ink);
}
/* days outside the month are context, not choices being offered */
.daterangepicker td.off,
.daterangepicker td.off.in-range,
.daterangepicker td.off.start-date,
.daterangepicker td.off.end-date {
  background: none;
  color: var(--sp-faint);
}
/* the span between the two ends - a wash, so a striped calendar reads evenly */
.daterangepicker td.in-range {
  background: var(--sp-brand-wash);
  color: var(--sp-ink);
  border-radius: 0;
}
.daterangepicker td.active,
.daterangepicker td.active:hover,
.daterangepicker td.start-date,
.daterangepicker td.end-date {
  background: var(--sp-brand);
  color: var(--sp-on-brand);
  font-weight: 600;
}
.daterangepicker td.start-date { border-radius: var(--sp-radius-sm) 0 0 var(--sp-radius-sm); }
.daterangepicker td.end-date { border-radius: 0 var(--sp-radius-sm) var(--sp-radius-sm) 0; }
.daterangepicker td.start-date.end-date { border-radius: var(--sp-radius-sm); }
.daterangepicker td.disabled { color: var(--sp-faint); text-decoration: none; }

/* --- date picker -----------------------------------------------------------
   The vendored bootstrap-datepicker is an 8KB build from before the options
   most people expect: no clearBtn, no autoclose, no todayHighlight, no
   orientation. So everything below is CSS on the markup it does emit, and the
   Clear control is ours - see .sp-datefield.

   It shipped as stock Bootstrap 3: a grey header band, 4px corners, 14px type
   in cells barely wider than a digit. */
.datepicker.dropdown-menu {
  padding: var(--sp-4);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface);
  box-shadow: var(--sp-shadow-lg);
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
}
.datepicker table { width: 100%; border-collapse: separate; border-spacing: 2px; }
.datepicker td,
.datepicker th { border: none; background: none; }
/* custom.css paints the grey twice: `table thead { background-color: #BDBDBD
   !important }` AND `table thead th { background: #BDBDBD !important }`, both
   unscoped. Clearing only the cells left the band showing through them from the
   thead behind - which is why this calendar still had two grey stripes after
   the first attempt. */
.datepicker thead { background: none !important; }
.datepicker thead th {
  background: none !important;
  border: none !important;
  border-bottom: none !important;
  padding: 0 !important;
  text-align: center !important;
}
/* the same legacy rule also forces #111 at 14px/600 on every header cell, which
   is why the month and the weekday key looked identical in weight */
.datepicker .switch-datepicker {
  color: var(--sp-ink) !important;
  font-size: var(--sp-text-sm) !important;
  font-weight: 600 !important;
}
.datepicker .dow {
  color: var(--sp-faint) !important;
  font-size: 10px !important;
  font-weight: 600 !important;
  letter-spacing: var(--sp-track-label) !important;
}
.datepicker .prev,
.datepicker .next { color: var(--sp-muted) !important; }

/* month and year, with the two arrows either side */
.datepicker .switch-datepicker {
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  padding: var(--sp-3) 0 !important;
}
.datepicker .prev,
.datepicker .next {
  width: 30px;
  border-radius: var(--sp-radius-sm);
  color: var(--sp-muted);
  cursor: pointer;
  transition: background-color var(--sp-dur) var(--sp-ease);
}
.datepicker .prev p,
.datepicker .next p { margin: 0; font-size: 18px; line-height: 1; }
.datepicker .prev:hover,
.datepicker .next:hover { background: var(--sp-surface-3); color: var(--sp-ink); }

/* the weekday initials are a key to the grid, not data in it */
.datepicker .dow {
  padding: var(--sp-2) 0 var(--sp-3) !important;
  text-transform: uppercase;
}

.datepicker td.day,
.datepicker .month,
.datepicker .year {
  width: 34px;
  height: 32px;
  font-size: var(--sp-text-md);
  border-radius: var(--sp-radius-sm);
  color: var(--sp-ink-2);
  cursor: pointer;
  transition: background-color var(--sp-dur) var(--sp-ease),
              color var(--sp-dur) var(--sp-ease);
}
.datepicker .month,
.datepicker .year { width: auto; padding: var(--sp-4) var(--sp-3); }
.datepicker td.day:hover,
.datepicker .month:hover,
.datepicker .year:hover { background: var(--sp-surface-3); color: var(--sp-ink); }

/* days either side of this month are context, not choices you are being offered */
.datepicker td.day.old,
.datepicker td.day.new,
.datepicker .old,
.datepicker .oldazure { color: var(--sp-faint); }

/* the selected day. .azure is the vendor's own accent class - overridden here
   so the picker agrees with the rest of the palette. */
.datepicker td.active,
.datepicker td.active:hover,
.datepicker td.day.active,
.datepicker .month.active,
.datepicker .year.active,
.datepicker .azure,
.datepicker .yearazure {
  background: var(--sp-brand) !important;
  color: #fff !important;
  font-weight: 600;
}
.datepicker td.active p,
.datepicker td.active:hover p { color: #fff; }

/* --- a date field you can empty ---------------------------------------------
   This build of the picker has no Clear button, so a date, once set, could only
   be changed - never removed. The x appears when there is something to clear
   and sits inside the field's right edge. */
.sp-datefield { position: relative; }
.sp-datefield .form-control { padding-right: 40px; }
.sp-datefield__clear {
  position: absolute;
  top: 50%;
  right: var(--sp-4);
  transform: translateY(-50%);
  display: none;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--sp-radius-pill);
  background: none;
  color: var(--sp-muted);
  cursor: pointer;
  transition: background-color var(--sp-dur) var(--sp-ease),
              color var(--sp-dur) var(--sp-ease);
}
.sp-datefield.has-value .sp-datefield__clear { display: inline-flex; }
.sp-datefield__clear:hover,
.sp-datefield__clear:focus-visible { background: var(--sp-surface-3); color: var(--sp-ink); }
.sp-datefield__clear .material-icons { font-size: 18px; }

/* --- leagues list ----------------------------------------------------------
   The switcher picker: one row per league, the same row as above. Its left side
   is a link rather than a label, which is what .sp-switchrow__main is for. */
.sp-leagues { margin-bottom: var(--sp-9); }
.sp-leagues__lede { margin-bottom: var(--sp-9); max-width: 62ch; }

/* A caption strip, not a table header: it names the column the switches are in
   and says how many rows there are, then gets out of the way. */
.sp-leagues__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-6);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--sp-border);
}
.sp-leagues__count,
.sp-leagues__caption {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  color: var(--sp-muted);
}

/* Thirty league names in brand red read as a wall of links. Ink at rest, brand
   on hover - the row is the thing you scan, the name is the thing you click. */
.sp-leagues__name,
.sp-leagues__name:link,
.sp-leagues__name:visited {
  color: var(--sp-ink-2);
  text-decoration: none;
}
/* matched through the row so it outranks the shared .sp-switchrow__main colour
   above, which is (0,3,1) */
.sp-notify-list > .form-group > .sp-leagues__name:hover,
.sp-notify-list > .form-group > .sp-leagues__name:focus-visible {
  color: var(--sp-brand);
  text-decoration: none;
}
.sp-leagues__list > .form-group { transition: background-color var(--sp-dur) var(--sp-ease); }
.sp-leagues__list > .form-group:hover { background: var(--sp-surface-2); }
.sp-leagues__row--current .sp-leagues__name { color: var(--sp-ink); font-weight: 500; }
/* a flex line rather than inline text, so the marker centres on the name
   instead of being hung off a vertical-align guess */
.sp-leagues__name {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

/* --- marker ----------------------------------------------------------------
   Says "this row is the one you are in" / "this one is you". Not a status
   badge: Bootstrap's .label is a solid grey capsule at 75% font size with its
   own padding and baseline, which next to a 14px name reads as a button that
   has lost its row. This is a tint and a word, sized to sit inside a line of
   text without displacing it. */
.sp-tag {
  /* inline-block, not inline: an inline box ignores vertical padding and takes
     its height from the font, so the pill collapsed to 13px anywhere its parent
     was not a flex container - the member list in Copy League */
  display: inline-block;
  flex: 0 0 auto;
  padding: 1px var(--sp-4) 0;
  border-radius: var(--sp-radius-pill);
  background: var(--sp-brand-tint);
  font-family: var(--sp-font);
  font-size: 10px;
  font-weight: 600;
  line-height: 17px;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  /* the darker brand, not --sp-brand: over the 10% tint composited on white,
     #E21838 measures 4.04:1, and at 10px this is not large text. #C4142F is
     5.12:1 and reads as the same red. */
  color: var(--sp-brand-hover);
  white-space: nowrap;
}
/* filled, for the one marker on a page that should be read before anything
   else - the standing offer beside a price */
.sp-tag--solid {
  background: var(--sp-success);
  color: #fff;
}
/* neutral variant, for a marker that is a fact rather than a state */
.sp-tag--quiet {
  background: var(--sp-surface-3);
  color: var(--sp-ink-2);
}

/* --- plan rows -------------------------------------------------------------
   The billing overview: a label, what you are on, and the one thing you can do
   about it. Was a nested Bootstrap grid - .row > .col-md-12 > four col-md-*
   cells - so the action landed in a different column depending on which state
   the plan was in, and no two buttons on the page shared an edge. */
.sp-planlist {
  margin: 0 0 var(--sp-10);
  border-top: 1px solid var(--sp-hairline);
}
.sp-planrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3) var(--sp-6);
  padding: var(--sp-6) 0;
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-planrow__label {
  flex: 0 0 168px;
  font-size: var(--sp-text-sm);
  font-weight: 500;
  color: var(--sp-ink);
}
.sp-planrow__value {
  flex: 1 1 0;
  min-width: 0;
  font-size: var(--sp-text-base);
  color: var(--sp-ink-2);
}
.sp-planrow__meta { color: var(--sp-muted); }
.sp-planrow__expired { color: var(--sp-danger); font-weight: 500; }
.sp-planrow__note {
  display: block;
  margin-top: var(--sp-2);
  font-size: var(--sp-text-sm);
  color: var(--sp-muted);
}
/* every action on the same right edge, whatever state each row is in */
.sp-planrow__action {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-5);
  margin-left: auto;
}
@media (max-width: 600px) {
  .sp-planrow__label { flex: 1 0 100%; }
  .sp-planrow__action { margin-left: 0; }
}

/* the card list opens under the Payment row, on its own line */
.sp-planrow__panel {
  flex: 1 0 100%;
  margin-top: var(--sp-6);
}

/* Money in a column reads as money only if the digits line up - header with it.
   !important because custom.css forces `text-align: left !important` on every
   grid header; nothing but !important can answer that, and at (0,4,3) against
   its (0,2,1) this rule wins the tie. */
/* A column that declares its own alignment keeps it. custom.css forces
   `text-align: left !important` on `table thead th` - every header in the app -
   which overrode the Bootstrap helper the view had already put on the header.
   So a column could ask for an alignment, get it on its cells, and be refused
   it on its heading. Not scoped to .fresh-table: the pricing tables are not in
   one, and their Basic/Premium headings had the same problem. !important is
   the only answer to !important; on specificity this wins at (0,1,1). */
th.text-right, th.text-right .th-inner   { text-align: right !important; }
th.text-center, th.text-center .th-inner { text-align: center !important; }
th.text-left, th.text-left .th-inner     { text-align: left !important; }
/* Grids need the same thing said again, louder: inside a .fresh-table the
   legacy rule is `.fresh-table .table thead th` at (0,2,2), which outranks the
   (0,1,1) above even though both are !important. */
.fresh-table .table > thead > tr > th.text-right,
.fresh-table .table > thead > tr > th.text-right .th-inner { text-align: right !important; }
.fresh-table .table > thead > tr > th.text-center,
.fresh-table .table > thead > tr > th.text-center .th-inner { text-align: center !important; }
.fresh-table .table > thead > tr > th.text-left,
.fresh-table .table > thead > tr > th.text-left .th-inner { text-align: left !important; }

.sp-amount { font-variant-numeric: tabular-nums; }
.sp-amount,
.fresh-table .table > tbody > tr > td.sp-amount,
.fresh-table .table > thead > tr > th.sp-amount,
.fresh-table .table > thead > tr > th.sp-amount .th-inner {
  text-align: right !important;
}
/* Seven columns of transaction history do not fit a phone. The table was 702px
   wide inside a 333px box with overflow visible, so Status and Amount were
   simply cut off and unreachable - the page did not scroll sideways either.
   The table scrolls inside its own box instead. */
.sp-history,
.sp-cardlist {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.sp-history { margin-bottom: var(--sp-10); }
.sp-cardlist { margin-bottom: var(--sp-6); }
/* a bar whose only content sits at the far end */
.sp-formbar--end { justify-content: flex-end; }

/* Fields that only exist because the switch above them is on. The rail says
   "these belong to that", which a flat list of fields cannot. */
.sp-subsection {
  margin: var(--sp-5) 0 var(--sp-3);
  padding: var(--sp-2) 0 0 var(--sp-7);
  border-left: 2px solid var(--sp-hairline);
}
.sp-subsection .sp-field:last-child { margin-bottom: 0; }

/* A group of checkboxes that reads as one answer - which sports, which line
   types. In one column, twenty odd types made a twenty-row list down the far
   left of a 1500px page. */
.sp-checkgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--sp-4) var(--sp-6);
}
.sp-checkgrid .sp-check { margin-bottom: 0; }

/* A question answered with checkboxes or radios needs more air under it than a
   single field does - the group reads as one block, and the next setting has to
   look like a different question rather than another option in this one. */
.sp-checkgroup { margin-bottom: var(--sp-10); }
.sp-checkgroup > label,
.sp-checkgroup > .control-label { margin-bottom: var(--sp-5); }

/* The all/none box that governs a grid. A rule under it sets it apart from the
   options, so it reads as a control over the list rather than the first item
   in it. */
.sp-checkall {
  margin-bottom: var(--sp-5);
  padding-bottom: var(--sp-5);
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-checkall label { font-weight: 500; color: var(--sp-ink); }

/* --- pick form -------------------------------------------------------------
   The market sections inside an expanded game (Money Line, Run Line, Total
   Runs) and the controls under each one.

   The section headings were brand red with a 2px red rule under each - on a
   game with six markets that is six red bars competing with the Save button.
   They are labels for a group, so they read as labels. */
.game-detail .pick-detail-title {
  margin: var(--sp-9) 0 var(--sp-5);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--sp-hairline);
  font-size: var(--sp-text-md);
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  line-height: var(--sp-leading-tight);
  color: var(--sp-muted);
}
.game-detail .pick-detail-title.no-border-bottom { border-bottom: 0; }

/* Potential win: a label and a figure, not a sentence. */
.potential-win-pick-form {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}
.potential-win-pick-form__label {
  font-size: var(--sp-text-sm);
  color: var(--sp-muted);
}
.potential-win-pick-form .potential-win {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--sp-ink);
}

/* The unit-risk row reuses .sp-stepper (built for the parlay and teaser
   forms); it just needs its label and control grouping. */
.units-to-risk-pick-form.sp-stepper {
  flex-wrap: wrap;
  gap: var(--sp-5);
}
.sp-stepper__label {
  font-size: var(--sp-text-sm);
  color: var(--sp-muted);
}
.sp-stepper__controls {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 !important;
  padding: 0;
}
.sp-stepper__controls .pick-form-wager {
  /* Material Kit gives .form-control a 7px margin-bottom, which against
     `align-items: center` lifted the input 7px above the buttons beside it.
     min-width is set as well as the basis because the intrinsic size of a
     number input was winning over flex-basis. */
  width: 72px;
  min-width: 72px;
  flex: 0 0 72px;
  margin: 0 !important;
  align-self: center;
  height: 36px;
  padding: 0 var(--sp-4);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius);
  background-image: none;
  box-shadow: none;
  text-align: center;
  font-variant-numeric: tabular-nums;
  color: var(--sp-ink);
}
.sp-stepper__controls .pick-form-wager:focus {
  outline: none;
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
.sp-stepper__controls .sp-btn { min-width: 38px; padding: 0 var(--sp-4); }

/* Line history and the alert toggle share a row under the options - see
   .pick-actions in picks.css. The margin-right and the two vertical-align
   declarations that used to sit here were propping up an inline row; the row
   is flex now and centres them itself. */

/* The save row needs air after the unit stepper above it - they were touching. */
.game-detail .pick-form-save-cancel-wrap {
  margin-top: var(--sp-7);
}

/* The key under the scoreboard explaining its markers. */
.sp-legend {
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading-loose);
  color: var(--sp-muted);
}
.sp-legend__mark { color: var(--sp-brand); font-weight: 600; }

/* --- score detail ------------------------------------------------------------
   The expanded row on the scoreboard: a stack of label/value pairs.

   These were Bootstrap grid rows - `.row > .col-xs-5.text-right + .col-xs-7` -
   so on a 1300px panel every label sat right-aligned against the middle and the
   whole left half was empty. They are a definition list and are now marked up
   as one, because restyling the grid rows in place meant fighting .col-* for
   float, width and text-align on every pair.

   Pairs flow into columns once there is room, rather than making one very long
   thin list of what is often twenty rows. */
.sctn {
  padding: var(--sp-8) var(--sp-9) var(--sp-9) !important;
}
.sctn .card-header-modern {
  margin: 0 0 var(--sp-6);
  padding: 0 0 var(--sp-4);
}
.sctn h6 {
  margin: var(--sp-8) 0 var(--sp-4);
  text-align: left;
  font-size: var(--sp-text-sm);
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  color: var(--sp-muted);
}

.sp-kv {
  display: grid;
  grid-template-columns: minmax(88px, 132px) 1fr;
  gap: 0 var(--sp-6);
  margin: 0;
}
.sp-kv > dt {
  padding: var(--sp-3) 0;
  font-weight: 400;
  color: var(--sp-muted);
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-kv > dd {
  margin: 0;
  padding: var(--sp-3) 0;
  color: var(--sp-ink);
  word-break: break-word;
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-kv > dt:last-of-type,
.sp-kv > dd:last-of-type { border-bottom: none; }
/* a pair with no label - the Line history link - keeps its row but not a
   dangling empty cell rule */
.sp-kv > dt:empty { border-bottom-color: transparent; }

@media (max-width: 999px) {
  .sp-kv { grid-template-columns: minmax(88px, 40%) 1fr; }
}

/* --- standings row expansion ------------------------------------------------
   The chevron on a standings row opens a .detail-row holding whatever columns
   are hidden at this width - Min/Max points risked, points available.
   custom.js builds it; this styles it.

   It used to be unstyled entirely, so it inherited white while the rows around
   it striped grey and read as a panel floating between rows rather than an
   expansion of the one above. The tint plus the removed top border attach it to
   its parent row, and the brand rule down the left says which direction the
   relationship runs. */
.at-responsive-wrapper .fresh-table .table > tbody > tr.detail-row > td,
.detail-row > td.detail-row__cell {
  /* !important answers an !important, which is the only thing that does:
     custom2.css:665 sets `.at-responsive-wrapper .fresh-table .table>tbody>tr>td
     { padding: 6px 4px !important }` in the portrait phone query, and repeats it
     at :759 for landscape and :844 for small phones - so every width this
     expansion is reachable at. Without this the panel is 6px from its own text
     on the only screens that can open it.

     Between two !important declarations specificity still decides, and that
     rule is (0,3,4). `.detail-row > td` at (0,1,2) would have lost silently -
     the full ancestor chain plus tr.detail-row reaches (0,4,4). The wrapper is
     real: _main.php puts .at-responsive-wrapper on the Picks tab. The shorter
     selector is kept for the class the JS also sets, so the rule still applies
     if the grid is ever moved out of that wrapper. */
  padding: var(--sp-5) var(--sp-6) !important;
  background: var(--sp-surface-2);
  border-top: 0;
  box-shadow: inset 3px 0 0 var(--sp-brand);
}
.sp-kv--detail {
  /* the pairs read as a block, not as two columns stretched across a phone */
  max-width: 320px;
  /* minmax(0, ...) so the label column may shrink below its longest word and
     wrap. Plain `1fr` floors at min-content, which on a narrow table pushed the
     right-anchored block past the left edge of its cell. */
  grid-template-columns: minmax(0, 1fr) auto;
  /* right-anchored: custom.js sets margin-right to the distance from the
     detail cell's inner edge to the aligned column, and this carries the block
     across to meet it. Without it the block stays left and the margin does
     nothing. */
  margin-left: auto;
}
.sp-kv--detail > dd {
  /* these line up under W / L / T / PTS above them, so the digits should too */
  text-align: right;
  font-variant-numeric: tabular-nums;
  /* a stat is one token ("140", "$1,150") - never let the base .sp-kv
     word-break split it a digit at a time, which also collapses the auto value
     column to one character and stacks the number vertically. */
  white-space: nowrap;
  word-break: normal;
}
.sp-kv a { color: var(--sp-brand); }
.sp-kv .text-muted { color: var(--sp-faint); }

/* two pairs per row once the panel is wide enough to read them */
@media (min-width: 1000px) {
  .sp-kv {
    grid-template-columns: minmax(88px, 132px) 1fr minmax(88px, 132px) 1fr;
    column-gap: var(--sp-11);
  }
  /* ...but NOT the standings row expansion: it's a right-anchored block capped
     at max-width:320px, so the four-column layout crushes it and both the label
     ("Min Points risked") and the value ("140") wrap a character at a time. Keep
     it one pair per row (label | value), the same as on a phone. */
  .sp-kv--detail {
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--sp-6);
  }
}

/* --- vote --------------------------------------------------------------------
   Like / dislike on a scoreboard pick. State lives in the .active class alone -
   the markup used to carry six inline style attributes and custom.js set the
   icon and count colours inline in 24 places, so the colours could not be
   themed and every code path had to remember to set them.

   .btn-like-pick / .btn-dislike-pick / .like-count / .dislike-count and the
   data-pick-id are JS hooks; .thumb-pending and .disabled are added while the
   POST is in flight. */
.sp-vote {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  height: 32px;
  padding: 0 var(--sp-5);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-pill);
  background: var(--sp-surface);
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--sp-muted);
  cursor: pointer;
  transition: border-color var(--sp-dur) var(--sp-ease),
              background-color var(--sp-dur) var(--sp-ease),
              color var(--sp-dur) var(--sp-ease);
}
.sp-vote .material-icons {
  font-size: 17px;
  line-height: 1;
  color: inherit;      /* the count and the icon always agree */
}
.sp-vote:hover { border-color: var(--sp-border-strong); color: var(--sp-ink-2); }

.sp-vote--up.active {
  border-color: var(--sp-success);
  background: var(--sp-success-tint);
  color: var(--sp-success);
}
.sp-vote--down.active {
  border-color: var(--sp-danger);
  background: var(--sp-danger-tint);
  color: var(--sp-danger);
}
.sp-vote.thumb-pending,
.sp-vote.disabled { opacity: 0.55; cursor: default; }

/* --- filter bar -------------------------------------------------------------
   The toolbar above a results table: a date-range control, some selects, a Run
   button and the Advanced/Reset/Hide links. Shared by the two league standings
   grids, the scoreboard and team history.

   Selector weight is deliberate. custom.css styles these at
   `.fresh-table form .form-group.medium-group .form-control` - (0,4,1), the 1
   being the `form` TYPE selector, which is easy to miss when counting. The
   rules here carry the same `form` so they tie at (0,4,1) and win on load
   order. Dropping it makes them (0,4,0) and they lose silently. The custom.css block goes when the last of the five views
   using .medium-group is converted.

   Hooks left alone: .date-range-picker, .date-range-label,
   .date-range-hidden-input, .league-filter-*-btn, .standard-advantage-filters,
   .advanced-filter-only-single. */
.sp-filterbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-7);
}
/* custom.css:447 insets any form inside a fresh-table wrapper by 20px, for the
   CGridView toolbars it was written for. The team-history modal wraps its whole
   body in one, and its two filter rows are forms, so the Season select sat 20px
   in from the headings and tables it belongs to while everything else was
   flush. That same rule also sets `margin: 0`, which was overriding the
   component's own bottom margin - zeroing only the padding left the select
   sitting directly on the table - so the margin is restated here too. The
   component owns its own box; (0,2,1) beats the (0,1,1) there without
   !important. */
.fresh-table form.sp-filterbar,
/* and the case where the form is INSIDE the bar rather than being it - the
   league standings toolbar. Same legacy rule, third place it has turned up
   today: it was putting 20px above the date-range dropdown and 13px below the
   row, which on a phone read as an empty band between the tabs and the first
   control. */
.sp-filterbar form { padding: 0; margin: 0 0 var(--sp-3); }

/* The "Advanced" toggle in a filter bar was WHITE TEXT ON WHITE - measured
   rgb(255,255,255) on a toolbar that custom.css:494 paints #fff. The vendor's
   toolbar was a saturated coloured bar once and its contents were coloured for
   that; the bar went white and the text never followed. It reads as a gap in
   the layout, which is exactly what it was reported as. */
.sp-filterbar a.sp-linkbtn { color: var(--sp-brand); }
.sp-filterbar a.sp-linkbtn:hover,
.sp-filterbar a.sp-linkbtn:focus { color: var(--sp-brand-hover); }
.sp-filterbar form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  width: 100%;
}
.sp-filterbar .form-group {
  /* flex so the wrapper hugs its control. With padding zeroed it was still
     49px around a 42px select - Material Kit's .form-control carries its own
     margin - and against `align-items: center` that put the select 4px above
     the date control beside it. */
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  position: relative;
}
.sp-filterbar .form-group > .form-control { margin: 0; }

/* the real selects */
/* select.form-control is needed for the caret: custom.css paints the old
   two-gradient triangle at `.fresh-table form .form-group.medium-group
   select.form-control` - (0,4,2), because `form` AND `select` are both types. */
/* Two shapes: .sp-filterbar wrapping a form (the grids), or the form itself
   carrying the class (team history). Both need the `form` type in the selector
   to reach (0,4,1) - see the note above. */
.sp-filterbar form .form-group.medium-group select.form-control,
.sp-filterbar form .form-group select.form-control,
.sp-filterbar form .form-group.medium-group .form-control,
.sp-filterbar form .form-group .form-control,
form.sp-filterbar .form-group.medium-group select.form-control,
form.sp-filterbar .form-group.medium-group .form-control,
form.sp-filterbar .form-group .form-control,
.sp-filterbar .form-group .form-control {
  min-width: 160px;
  height: var(--sp-control-h);
  padding: 0 34px 0 var(--sp-6);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background-color: var(--sp-surface);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236B625C' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 11px 7px;
  box-shadow: none;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  font-family: var(--sp-font);
  font-size: var(--sp-text-md);
  line-height: calc(var(--sp-control-h) - 2px);
  color: var(--sp-ink);
  /* custom.css uppercases these. The option text is real content
     ("All picks"), so it is shown as written. */
  text-transform: none;
}
.sp-filterbar form .form-control:focus,
.sp-filterbar .form-control:focus {
  outline: none;
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
/* the leading material icon inside each control - decorative, and it was
   crowding the text at 12px from the left */
.sp-filterbar i.select-icon,
.fresh-table form .sp-filterbar i.select-icon {
  display: none;
}

/* the date range control is a div pretending to be a select */
.sp-filterbar .date-range-picker,
.sp-filterbar .date-range-picker.form-group {
  display: inline-flex;
  align-items: center;
  min-width: 170px;
  height: var(--sp-control-h);
  margin: 0 !important;
  padding: 0 34px 0 var(--sp-6);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background-color: var(--sp-surface);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236B625C' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 11px 7px;
  /* Material Kit paints a triple red shadow on .form-group, which read as a
     pink halo on this control and on nothing beside it. */
  box-shadow: none;
  font-size: var(--sp-text-md);
  color: var(--sp-ink);
  cursor: pointer;
}
.sp-filterbar .date-range-picker:hover { border-color: var(--sp-border-strong); }
/* custom.css uppercases this control's label; the value is real text
   ("All Time"), so it is shown as written like every other control. */
.sp-filterbar .date-range-picker,
.sp-filterbar .date-range-label { text-transform: none; }
/* the fa caret is replaced by the background chevron above */
.sp-filterbar .date-range-picker .fa-sort-desc { display: none; }

/* Advanced / Reset / Hide - actions, but quiet ones */
.sp-linkbtn {
  padding: 0;
  border: none;
  background: none;
  font-family: var(--sp-font);
  font-size: var(--sp-text-md);
  font-weight: 500;
  color: var(--sp-brand);
  cursor: pointer;
}
.sp-linkbtn:hover,
.sp-linkbtn:focus { color: var(--sp-brand-hover); text-decoration: underline; }
/* (0,2,0) so it beats `.sp-filterbar .form-group { margin: 0 }` above -
   at (0,1,0) the auto margin lost and the control sat beside Run. */
.sp-filterbar .sp-filterbar__end {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-6);
}
.sp-filterbar__advanced {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  padding-top: var(--sp-5);
  margin-top: var(--sp-2);
  border-top: 1px solid var(--sp-hairline);
}

@media (max-width: 600px) {
  .sp-filterbar .form-group.medium-group .form-control,
  .sp-filterbar .date-range-picker { min-width: 0; width: 100%; }
  .sp-filterbar form > .form-group { flex: 1 1 100%; }
  /* ...except the two that belong side by side. `flex: 1 1 100%` above is right
     for the selects - a date range or a team picker wants the full width of a
     phone - but it also caught the button and the Advanced link, which are both
     small, so Run took a whole row and Advanced dropped underneath it. The Run
     wrapper carries the same three classes as every select wrapper, so it is
     marked in the markup rather than guessed at with :has(). Both shrink to
     their content and share one line, Advanced to the right of the button. */
  /* Both need the `form > .form-group` prefix to reach (0,4,1): the rule above
     is (0,3,1), so `.sp-filterbar .sp-filterbar__end` at (0,2,0) would lose the
     `flex` and stay full width - the link would still drop below the button.
     `width` and `margin-left` are uncontested and can stay on the short form. */
  .sp-filterbar form > .form-group.sp-filterbar__run { flex: 0 0 auto; }
  /* align-self, not align-items: the bar centres its items, which left the link
     sitting mid-button. flex-end drops it to the bottom of the line so its
     baseline sits with the button's lower edge. The `margin-left: auto` from the
     unprefixed rule above is deliberately NOT overridden here - it is what
     carries the link to the right edge, the same as on desktop. */
  .sp-filterbar form > .form-group.sp-filterbar__end {
    flex: 0 0 auto;
    align-self: flex-end;
  }
}

/* --- select (selectize) ---------------------------------------------------
   The parlay/teaser game pickers and the settings timezone picker are all
   selectize widgets. selectize.min.css ships a flat 2013 look - 3px radius, an
   inset shadow, a CSS-triangle caret - that no longer matches .sp-field. This
   restyles the widget to the same field it sits beside. sp-components.css loads
   after selectize.min.css, so equal specificity is enough and nothing here
   needs !important.

   Selectize owns this DOM entirely: it builds .selectize-control from the
   original <select>, and its JS reads .selectize-input / .selectize-dropdown /
   .option / .active by name. Appearance only below - no structure, no display
   changes that would break its measuring. */
.selectize-control.single .selectize-input,
.selectize-control.multi .selectize-input {
  min-height: var(--sp-field-h);
  padding: 0 40px 0 var(--sp-6);
  display: flex;
  align-items: center;
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface);
  background-image: none;
  box-shadow: none;
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  line-height: var(--sp-leading-tight);
  color: var(--sp-ink);
  cursor: pointer;
  transition: border-color var(--sp-dur) var(--sp-ease),
              box-shadow var(--sp-dur) var(--sp-ease);
}
.selectize-control.single .selectize-input.focus,
.selectize-control.single .selectize-input.dropdown-active,
.selectize-control.multi .selectize-input.focus {
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
/* selectize renders the placeholder as a child div, not a ::placeholder */
.selectize-control .selectize-input > input,
.selectize-control .selectize-input > input::placeholder {
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  color: var(--sp-ink);
}
.selectize-control .selectize-input > input::placeholder {
  color: var(--sp-faint);
}
.selectize-control .selectize-input.has-options:not(.has-items) > input::placeholder {
  color: var(--sp-faint);
}

/* Multi-select (the invite tag field): the box grows to fit chips wrapped onto
   rows, and each picked email is a pill on a single line with a remove (×).
   Without this the raw selectize items are grey boxes of uneven height - a long
   "Name (email)" label wraps to two lines and stretches the whole row. */
.selectize-control.multi .selectize-input {
  flex-wrap: wrap;
  gap: var(--sp-2);
  height: auto;
  padding-top: var(--sp-3);
  padding-bottom: var(--sp-3);
  padding-right: var(--sp-6);            /* no single-select caret on multi */
  cursor: text;
}
.selectize-control.multi .selectize-input > .item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 26px;
  max-width: 100%;
  margin: 0;
  padding: 0 var(--sp-3) 0 var(--sp-4);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius-pill);
  background: var(--sp-surface-3);
  font-size: var(--sp-text-sm);
  font-weight: 500;
  line-height: 1;
  color: var(--sp-ink-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.selectize-control.multi .selectize-input > .item.active {
  background: var(--sp-brand-tint);
  border-color: var(--sp-brand-ring);
  color: var(--sp-ink);
}
/* remove_button plugin renders <a class="remove">&times;</a> inside each item */
.selectize-control.multi .selectize-input > .item .remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--sp-muted);
  font-size: 15px;
  line-height: 1;
  text-decoration: none;
  transition: background var(--sp-dur) var(--sp-ease), color var(--sp-dur) var(--sp-ease);
}
.selectize-control.multi .selectize-input > .item .remove:hover {
  background: rgba(0, 0, 0, 0.08);
  color: var(--sp-ink);
}

/* the caret: same chevron the native .sp-field select uses, so a selectize
   widget and a plain <select> read as the same control */
.selectize-control.single .selectize-input:after {
  content: "";
  position: absolute;
  top: 50%;
  right: var(--sp-6);
  margin-top: -4px;
  width: 12px;
  height: 8px;
  border: none;
  background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236B625C' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E") no-repeat center;
  transition: transform var(--sp-dur) var(--sp-ease);
}
.selectize-control.single .selectize-input.dropdown-active:after {
  transform: rotate(180deg);
}

.selectize-dropdown {
  margin-top: var(--sp-2);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface);
  box-shadow: var(--sp-shadow-lg);
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  color: var(--sp-body);
  overflow: hidden;
}
.selectize-dropdown .selectize-dropdown-content {
  padding: var(--sp-2) 0;
  max-height: 280px;
}
.selectize-dropdown .option {
  padding: var(--sp-4) var(--sp-6);
  line-height: var(--sp-leading-tight);
  color: var(--sp-ink-2);
  cursor: pointer;
}
.selectize-dropdown .option.active {
  background: var(--sp-surface-3);
  color: var(--sp-ink);
}
.selectize-dropdown .option.selected {
  font-weight: 600;
  color: var(--sp-ink);
}
.selectize-dropdown .optgroup-header {
  padding: var(--sp-4) var(--sp-6) var(--sp-2);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  color: var(--sp-muted);
  background: none;
}
.selectize-dropdown .no-results,
.selectize-dropdown .selectize-dropdown-content .create {
  padding: var(--sp-5) var(--sp-6);
  color: var(--sp-muted);
}

/* Confirm-pick body: a sentence, then the two numbers that matter. The stats
   are a label/value pair rather than another line of prose, so the figure is
   what the eye lands on before hitting Save. .vp-* names are JS write targets -
   custom.js sets the inner span - so they are styled, never renamed. */
/* [hidden] must win - the note is display:block when shown */
.validate-pick-save-modal .vp-line-changed[hidden] { display: none; }
.validate-pick-save-modal .vp-line-changed {
  display: block;
  margin: 0 0 var(--sp-6);
  padding: var(--sp-4) var(--sp-5);
  border-left: 3px solid var(--sp-warning);
  border-radius: 0 var(--sp-radius) var(--sp-radius) 0;
  background: var(--sp-warning-tint);
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading-tight);
  color: var(--sp-ink-2);
}
.validate-pick-save-modal .vp-confirm-message {
  color: var(--sp-ink-2);
}
.validate-pick-save-modal .vp-confirm-message strong {
  color: var(--sp-ink);
  font-weight: 600;
}
.validate-pick-save-modal .vp-potential-win,
.validate-pick-save-modal .vp-points-risked {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-6);
  padding: var(--sp-3) 0;
  font-size: var(--sp-text-sm);
  color: var(--sp-muted);
}
/* No rule here any more - the dialog's one separator sits under the heading. */
.validate-pick-save-modal .vp-potential-win {
  margin-top: var(--sp-6);
}
.validate-pick-save-modal .vp-potential-win span,
.validate-pick-save-modal .vp-points-risked span {
  /* tabular figures, not the mono stack: the two values sit one above the other
     so their digits must align, but this is a sentence-level dialog, not data */
  font-variant-numeric: tabular-nums;
  font-size: var(--sp-text-base);
  font-weight: 600;
  color: var(--sp-ink);
}
/* an empty span means custom.js had nothing to write - hide the whole row
   rather than leave a dangling label */
.validate-pick-save-modal .vp-potential-win:has(span:empty),
.validate-pick-save-modal .vp-points-risked:has(span:empty) {
  display: none;
}

.sp-modal__head {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  padding-right: 34px;        /* clear of the close control */
  /* The separator belongs under the static heading, not below whatever
     variable copy happens to sit at the top of the body. Every modal gets it,
     so the dialogs read the same way regardless of what they contain. */
  padding-bottom: var(--sp-6);
  border-bottom: 1px solid var(--sp-hairline);
  margin-bottom: var(--sp-6);
}
.sp-modal__title {
  margin: 0;
  font-size: var(--sp-text-xl);
  font-weight: 600;
  line-height: var(--sp-leading-tight);
  letter-spacing: var(--sp-track-tight);
  color: var(--sp-brand);
}
.sp-modal__icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--sp-radius-pill);
}
.sp-modal__icon .material-icons {
  font-size: 19px;
  line-height: 1;
}
.sp-modal--success .sp-modal__icon {
  background: var(--sp-success-tint);
  color: var(--sp-success);
}
.sp-modal--error .sp-modal__icon {
  background: var(--sp-danger-tint);
  color: var(--sp-danger);
}

.sp-modal__close {
  position: absolute;
  top: var(--sp-6);
  right: var(--sp-6);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: var(--sp-radius-pill);
  background: transparent;
  color: var(--sp-muted);
  cursor: pointer;
  transition: background-color var(--sp-dur) var(--sp-ease),
              color var(--sp-dur) var(--sp-ease);
}
.sp-modal__close .material-icons {
  font-size: 19px;
  line-height: 1;
}
.sp-modal__close:hover,
.sp-modal__close:focus {
  background: var(--sp-surface-3);
  color: var(--sp-ink);
  outline: none;
}
.sp-modal__close:focus-visible {
  box-shadow: 0 0 0 3px var(--sp-brand-ring);
}

.sp-modal__body {
  font-size: var(--sp-text-base);
  line-height: var(--sp-leading);
  color: var(--sp-body);
}
.sp-modal__body p {
  margin: 0 0 var(--sp-4);
}
.sp-modal__body > :last-child {
  margin-bottom: 0;
}
.sp-modal__body b,
.sp-modal__body strong {
  font-weight: 500;
  color: var(--sp-ink);
}
/* Links in modal prose. The exclusions matter: a form that owns its own submit
   puts a .sp-btn on an <a> inside the body - the Add card dialog does - and
   this rule sits at the same specificity as a.sp-btn--primary but later in the
   file, so it was painting brand-red text onto a brand-red button. */
.sp-modal__body a:not(.sp-btn):not(.btn) {
  color: var(--sp-brand-hover);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.sp-modal__body a:not(.sp-btn):not(.btn):hover {
  color: var(--sp-brand-active);
}

.sp-modal__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
}
.sp-modal__foot .sp-btn {
  min-width: 92px;   /* the partial emits sp-btn--sm; this only sets the floor */
}

.sp-modal.fade .modal-dialog {
  transform: translateY(10px) scale(0.985);
  opacity: 0;
  transition: transform var(--sp-dur-slow) var(--sp-ease-out),
              opacity var(--sp-dur-slow) var(--sp-ease);
}
.sp-modal.fade.in .modal-dialog {
  transform: none;
  opacity: 1;
}


/* ============================================================================
   Page furniture
   ========================================================================= */
.sp-page {
  padding: var(--sp-11) 0 72px;
}
/* Bootstrap's .container runs to 1170px, which left a ~230px void between a
   form and its rail. Measure decides a reading column, not the grid. */
.sp-page.sp-contact {
  max-width: 1040px;
}
.sp-page__head {
  max-width: 62ch;
  margin-bottom: 38px;
}
.sp-page__title {
  margin: 0 0 var(--sp-5);
  font-family: var(--sp-font);
  font-size: var(--sp-text-4xl);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--sp-ink);
}
.sp-page__lede {
  margin: 0;
  font-size: var(--sp-text-lg);
  line-height: var(--sp-leading-loose);
  color: var(--sp-body);
}

.sp-form {
  max-width: 560px;
}
.sp-form__errors {
  margin-bottom: var(--sp-7);
  padding: var(--sp-5) var(--sp-6);
  border: 1px solid #F0CDD3;
  border-left: 3px solid var(--sp-brand);
  border-radius: var(--sp-radius);
  background: #FDF4F5;
  font-size: 13.5px;
  color: #8C1225;
}
.sp-form__errors p {
  margin: 0 0 var(--sp-1);
  font-weight: 500;
}
.sp-form__errors ul {
  margin: 0;
  padding-left: 18px;
}
.sp-field--captcha {
  margin-bottom: var(--sp-9);
}
.sp-field--captcha .g-recaptcha > div {
  border-radius: var(--sp-radius-md);
  overflow: hidden;
}


/* ============================================================================
   Rail - the secondary column
   ========================================================================= */
.sp-rail {
  padding: var(--sp-9) var(--sp-9) var(--sp-9);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface-2);
}
.sp-rail__block + .sp-rail__block {
  margin-top: var(--sp-8);
  padding-top: var(--sp-8);
  border-top: 1px solid var(--sp-hairline);
}
.sp-rail__label {
  margin: 0 0 var(--sp-4);
  font-family: var(--sp-font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  color: var(--sp-muted);
}
.sp-rail__address {
  margin: 0;
  font-size: var(--sp-text-base);
  font-style: normal;
  line-height: var(--sp-leading-loose);
  color: var(--sp-ink-2);
}
.sp-rail__pairs {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-2) var(--sp-6);
  margin: 0;
  font-size: var(--sp-text-base);
}
.sp-rail__pairs dt {
  font-weight: 400;
  color: var(--sp-muted);
}
.sp-rail__pairs dd {
  margin: 0;
  color: var(--sp-ink-2);
  font-variant-numeric: tabular-nums;
}
.sp-rail__pairs dd a { color: var(--sp-ink-2); }
.sp-rail__pairs dd a:hover { color: var(--sp-brand-hover); }

.sp-rail__links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.sp-rail__links a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  font-size: var(--sp-text-base);
  color: var(--sp-ink-2);
  text-decoration: none;
}
.sp-rail__links a:hover { color: var(--sp-brand-hover); }
/* Monochrome marks, so two third-party brand colours stop shouting from the
   quietest column on the page. */
.sp-rail__links svg {
  width: 17px;
  height: 17px;
  fill: #6B625C;
  transition: fill var(--sp-dur) var(--sp-ease);
}
.sp-rail__links a:hover svg { fill: var(--sp-brand-hover); }

.sp-rail__apps {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.sp-rail__apps img {
  width: 148px;
  height: auto;
  border-radius: 7px;
  display: block;
}


/* ============================================================================
   Responsive + motion preferences
   ========================================================================= */
@media (max-width: 991px) {
  .sp-rail { margin-top: 36px; }
}
@media (max-width: 600px) {
  .sp-page { padding: 28px 0 52px; }
  .sp-page__title { font-size: var(--sp-text-3xl); }
}
@media (max-width: 480px) {
  .sp-modal.in { padding: var(--sp-6); }
  .sp-modal .modal-content { padding: var(--sp-8) var(--sp-7) var(--sp-7); }
  .sp-modal__foot { flex-direction: column-reverse; }
  .sp-modal__foot .sp-btn { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .sp-btn,
  .sp-modal__close,
  .sp-modal.fade .modal-dialog {
    transition: none;
  }
  .sp-btn--primary:hover { transform: none; }
}


/* ============================================================================
   Global chrome - main nav and footer

   Scoped to #mainMenu / footer.footer rather than .navbar, so the homepage's
   own .top-nav and the guest layout are untouched.

   DO NOT RENAME: #mainMenu, .navbar-collapse, .navbar-toggle, .dropdown,
   .dropdown-toggle, .open. custom.js reads all of them - responsiveMenu()
   measures $('#mainMenu').innerHeight() against a hard-coded 75px and adds
   .collapsed above it, so the bar must stay under that.
   ========================================================================= */
#mainMenu.navbar.navbar-danger {
  background-color: var(--sp-brand);
  /* was three stacked Material Kit shadows; one hairline reads cleaner and
     stops the bar floating over the page like a card */
  box-shadow: 0 1px 0 rgba(26, 21, 18, 0.10), 0 1px 3px rgba(26, 21, 18, 0.08);
  border: none;
}

/* !important on colour only: custom.css has
   `.navbar .navbar-nav > li > a { color: #fff !important }`, which silently
   beat this rule when it was first written. White-on-red still read fine, so
   the screenshot did not show it - the computed value did. */
#mainMenu .navbar-nav > li > a {
  padding: 15px var(--sp-6);
  font-family: var(--sp-font);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82) !important;
  background: none;
  border-radius: 0;
  transition: color var(--sp-dur) var(--sp-ease),
              background-color var(--sp-dur) var(--sp-ease);
}
#mainMenu .navbar-nav > li > a:hover,
#mainMenu .navbar-nav > li > a:focus {
  color: #FFFFFF !important;
  background-color: rgba(255, 255, 255, 0.10);
}
#mainMenu .navbar-nav > li > a .material-icons {
  font-size: 18px;
  vertical-align: -4px;
  margin-right: var(--sp-2);
  opacity: 0.9;
}

/* Active: an underline rather than a pale full-height block. The block read as
   a stray button sitting in the bar. */
#mainMenu .navbar-nav > .active > a,
#mainMenu .navbar-nav > .active > a:hover,
#mainMenu .navbar-nav > .active > a:focus {
  position: relative;
  background-color: rgba(255, 255, 255, 0.08);
  color: #FFFFFF !important;
}
#mainMenu .navbar-nav > .active > a::after {
  content: "";
  position: absolute;
  left: var(--sp-6);
  right: var(--sp-6);
  bottom: 0;
  height: 3px;
  background: #FFFFFF;
  border-radius: 2px 2px 0 0;
}
#mainMenu .navbar-nav > .active > a .material-icons { opacity: 1; }

/* ---- fitting the top bar -------------------------------------------------
   app.setupSubnav measures scrollWidth against clientWidth to decide what
   fits, and neither means anything while the row is a right-floated ul of
   floated <li>s: a float shrink-wraps, so the row simply wraps to a second
   line and the numbers stay equal. That is why the old code measured the
   bar's HEIGHT against a hard-coded 75px and then collapsed the whole thing.

   A flex row instead, and only from 768px up - below that Bootstrap's own
   hamburger takes the bar over and stacks it, which is the right pattern on a
   phone and is left alone. */
@media (min-width: 768px) {
  #mainMenu .navbar-wrapper { display: flex; align-items: center; }
  #mainMenu .navbar-header { float: none; flex: 0 0 auto; }
  #mainMenu .navbar-collapse { flex: 1 1 auto; min-width: 0; padding: 0; }
  #mainMenu .navbar-nav {
    display: flex;
    flex-wrap: nowrap;
    align-items: stretch;
    /* !important because Bootstrap's `.navbar-right { float: right !important }`
       wins otherwise, and a FLOATED row shrink-wraps: it sized itself to its
       own contents, ignored the 580px its parent actually had, and hung out to
       the left over the logo while the fitter was measuring. That was the flash
       on resize - the row was over the logo before the fit caught up. */
    float: none !important;
    margin: 0;
  }
  /* Row is align-items:stretch (so the 50px "More" button sets the height); each
     li stretches to 50px, so center its <a> in that height or the labels ride
     high (the <a> is shorter than the row and block-flow top-aligns). */
  #mainMenu .navbar-nav > li { float: none; flex: 0 0 auto; display: flex; align-items: center; }
  #mainMenu .navbar-nav > li > a { display: flex; align-items: center; }
  /* Right-aligned by an auto margin on the first item, NOT by
     justify-content: flex-end. With flex-end, a row too wide for its box
     overflows at the START edge - the items ran back over the logo - and
     scrollWidth does not count overflow to the left, so the fitter never saw
     it. An auto margin collapses to nothing when the space runs out, and the
     overflow then goes right where it can be measured. */
  #mainMenu .navbar-nav > li:first-child { margin-left: auto; }
}

/* the control on the red bar */
.sp-subnav__more--onbrand .sp-subnav__more-bars { background: rgba(255, 255, 255, 0.82); }
.sp-subnav__more--onbrand .sp-subnav__more-btn:hover .sp-subnav__more-bars,
.sp-subnav__more--onbrand .sp-subnav__more-btn[aria-expanded="true"] .sp-subnav__more-bars { background: #FFFFFF; }
.sp-subnav__more--onbrand.is-active .sp-subnav__more-bars { background: #FFFFFF; }
.sp-subnav__more--onbrand.is-active::after { background: #FFFFFF; height: 3px; border-radius: 2px 2px 0 0; }
#mainMenu .sp-subnav__more-btn { height: 50px; }

#mainMenu .navbar-toggle {
  border: none;
  border-radius: var(--sp-radius);
  transition: background-color var(--sp-dur) var(--sp-ease);
}
#mainMenu .navbar-toggle:hover,
#mainMenu .navbar-toggle:focus {
  background-color: rgba(255, 255, 255, 0.12);
}

/* ---- footer -------------------------------------------------------------- */
/* Was #E74C3C - an eighth red, and different from the brand. A grounded dark
   band ends the page and keeps red for actions. */
footer.footer {
  background-color: var(--sp-ink);
  color: rgba(255, 255, 255, 0.62);
  padding: var(--sp-10) 0;
  font-size: var(--sp-text-md);
}
footer.footer .copyright {
  color: rgba(255, 255, 255, 0.52);
  font-size: var(--sp-text-sm);
}
footer.footer a {
  color: rgba(255, 255, 255, 0.72);
  font-size: var(--sp-text-md);
  text-decoration: none;
  transition: color var(--sp-dur) var(--sp-ease);
}
footer.footer a:hover,
footer.footer a:focus {
  color: #FFFFFF;
  text-decoration: none;
}
footer.footer .footer-nav ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-8);
}
footer.footer .footer-nav li { float: none; }

@media (max-width: 767px) {
  footer.footer { padding: var(--sp-8) 0; text-align: center; }
  footer.footer .footer-nav ul { justify-content: center; }
}


/* ============================================================================
   Auth card - login, forgot password, reset password

   The guest layout wrapped its content in .card.card-signup, which had no card
   styling at all: no border, no shadow, no padding. The form floated on a bare
   white page with Material Kit's underline inputs and huge vertical gaps.
   ========================================================================= */
.sp-auth {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--sp-surface-2);
}
.sp-auth__main {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-10) var(--sp-7);
}
.sp-auth__card {
  width: 100%;
  max-width: 400px;
}
.sp-auth__logo {
  display: block;
  width: 168px;
  height: auto;
  margin: 0 auto var(--sp-9);
}
.sp-auth__panel {
  padding: var(--sp-10) var(--sp-9) var(--sp-9);
  background: var(--sp-surface);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius-lg);
  box-shadow: var(--sp-shadow);
}
.sp-auth__title {
  margin: 0 0 var(--sp-2);
  font-family: var(--sp-font);
  font-size: var(--sp-text-3xl);
  font-weight: 600;
  letter-spacing: var(--sp-track-tight);
  line-height: var(--sp-leading-tight);
  color: var(--sp-ink);
  text-align: center;
}
.sp-auth__lede {
  margin: 0 0 var(--sp-9);
  font-size: var(--sp-text-base);
  line-height: var(--sp-leading);
  color: var(--sp-body);
  text-align: center;
}
/* The submit is the only action on the page, so it takes the full width. */
.sp-auth__panel .sp-btn {
  width: 100%;
}
.sp-auth__aside {
  margin: var(--sp-7) 0 0;
  padding-top: var(--sp-7);
  border-top: 1px solid var(--sp-hairline);
  font-size: var(--sp-text-md);
  color: var(--sp-body);
  text-align: center;
}
.sp-auth__aside p { margin: 0 0 var(--sp-2); }
.sp-auth__aside p:last-child { margin-bottom: 0; }
.sp-auth__aside a {
  color: var(--sp-brand-hover);
  font-weight: 500;
  text-decoration: none;
}
.sp-auth__aside a:hover,
.sp-auth__aside a:focus {
  color: var(--sp-brand-active);
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* Yii's flash alerts, above the card */
.sp-auth__card .alert {
  margin-bottom: var(--sp-7);
  padding: var(--sp-5) var(--sp-6);
  border: 1px solid #F0CDD3;
  border-left: 3px solid var(--sp-brand);
  border-radius: var(--sp-radius);
  background: #FDF4F5;
  color: #8C1225;
  font-size: 13.5px;
}

@media (max-width: 480px) {
  .sp-auth__main { padding: var(--sp-8) var(--sp-6); }
  .sp-auth__panel { padding: var(--sp-9) var(--sp-7) var(--sp-8); }
  .sp-auth__logo { width: 150px; margin-bottom: var(--sp-8); }
}


/* ============================================================================
   Settings scope - account, billing, league settings (the `preference` layout)

   These pages ran forms at the full 1400px container width with right-aligned
   labels, and headings at 2.6em with no relationship to the page. Scoped to
   .sp-settings so nothing outside the preference layout moves.

   Deliberately CSS-only for the account page: its inline email editor is driven
   by app.setupUserAccountPage() through .account-email-text /
   .account-email-editable / .btn-edit-email-account / .btn-resend-email /
   .btn-cancel-change-request and the .hide class. Rewriting that markup to
   .sp-field would be a functional risk for a cosmetic gain.
   ========================================================================= */
.sp-settings {
  padding: var(--sp-8) 0 var(--sp-10);
}
/* Futures admin pages (Settings, Invite, Contacts, Banner) match the pool
   pages' centered width - the same 1080px cap as .fut-standings. Members opts
   out (stays full width for its roster table); see league-admin.php. */
.sp-settings--narrow { max-width: 1080px; margin-left: auto; margin-right: auto; }

/* ---- headings ------------------------------------------------------------ */
.sp-settings h2,
.sp-settings .sp-settings__title {
  margin: 0 0 var(--sp-7);
  font-family: var(--sp-font);
  font-size: var(--sp-text-3xl);
  font-weight: 600;
  line-height: var(--sp-leading-tight);
  letter-spacing: var(--sp-track-tight);
  color: var(--sp-ink);
}
.sp-settings h2 + * { margin-top: 0; }
/* The settings title in the tabbed card header (above the admin tabs, or the
   league-preferences form) matches the page-title treatment used elsewhere -
   e.g. Standings: 20px, flush-left, one navbar gap. THE STANDARD PAGE-HEADING
   GAP is ~28px below the navbar (see the .text-title block); .sp-settings adds
   22px of its own top padding, so on the titled pages replace it with the same
   10px every other layout adds to reach 28. Desktop only - on phones every
   layout's base gap is already ~28px (the navbar is shorter), so no add. */
.sp-settings:has(.sp-settings__title) { padding-top: 0; }
@media (min-width: 481px) { .sp-settings:has(.sp-settings__title) { padding-top: 10px; } }
.sp-settings .card-nav-tabs > .sp-settings__title {
  margin: 0 0 10px;
  padding: 0;
  font-size: var(--sp-text-2xl);
  line-height: var(--sp-leading-tight);
}
/* League Preferences: the title sits directly above the form (no sub-nav in
   between, unlike the admin card), so the title margin AND the content's top
   pad stacked to ~20px of whitespace. Drop the content pad to halve it. This
   selector only matches when .content directly follows the title - i.e. the
   preferences page, not the admin card where a .sp-subnav sits between them. */
.sp-settings .card-nav-tabs > .sp-settings__title + .content { padding-top: 0; }
.sp-settings h3 {
  margin: var(--sp-10) 0 var(--sp-5);
  font-size: var(--sp-text-lg);
  font-weight: 600;
  color: var(--sp-ink);
}
/* The section title under the admin sub-nav is the .text-title component, one
   size on every admin tab (Settings, Members, Invite, Contacts, Banner). This
   beats `.sp-settings h3` above (0,1,1) at (0,2,0), so the title renders at the
   component's standard size and no per-page `font-size-20` band-aid is needed;
   generic sub-headings that are a plain <h3> still get the 16px above. */
.sp-settings .text-title {
  margin: var(--sp-10) 0 var(--sp-5);
  font-size: var(--sp-text-2xl);
  font-weight: 600;
  color: var(--sp-ink);
}

/* ---- forms --------------------------------------------------------------- */
/* A form is a reading column, not a page - the container is ~1400px wide.
   Scoped to the account form specifically: league settings holds a wide
   checkbox matrix whose label columns collapse to three-line wraps if the
   form is capped. Cap what is a form; leave what is a grid. */
.sp-settings #user-form {
  max-width: 820px;
}
/* League settings is a settings form too, but a much bigger one - 260 controls,
   including a checkbox matrix that must stay wide. So it gets the type and
   spacing but not the reading-column cap. Its labels were red
   (custom2.css `#league-form .form-group > label`), which made every field on
   the page compete with the save button; that rule is deleted. */
.sp-settings--league #league-form .form-group > label,
.sp-settings--league #league-form .control-label {
  color: var(--sp-ink);
  font-size: var(--sp-text-sm);
  font-weight: 500;
}
.sp-settings--league #league-form .form-group > label .required,
.sp-settings--league #league-form .control-label .required {
  color: var(--sp-danger);
}
/* Cap the FIELDS, not the form. A 1325px-wide text input is unusable, but the
   sport matrix and the checkbox rows underneath it need the full width - which
   is why the form itself is not capped the way #user-form is. */
.sp-settings--league #league-form input[type="text"],
.sp-settings--league #league-form input[type="number"],
.sp-settings--league #league-form input[type="email"],
.sp-settings--league #league-form select,
.sp-settings--league #league-form textarea,
.sp-settings--league #league-form .redactor-box,
/* the cap has to reach the wrapper too, or the Clear button anchors to the
   right edge of a 1590px row instead of the 820px field inside it */
.sp-settings--league #league-form .sp-datefield {
  max-width: 820px;
}
/* Row spacing on a settings page.

   custom.css's `.main .form-group { margin-bottom: 0 }` is (0,2,0) and silently
   flattens every component that sets its spacing at (0,1,0) - which is all of
   them. So the default is restated here at (0,2,0), where it wins on load
   order, and each component overrides it at (0,3,0). Deliberately NOT written
   as :not(.sp-field):not(.sp-switchrow) - those pseudo-classes count as classes
   and would put the default above the components that need to beat it.

   Material Kit's runtime .form-group wrapper matches the default too, but
   .sp-field neutralises that one with !important. */
.sp-settings .form-group               { margin-bottom: var(--sp-6); }
.sp-settings .form-group.sp-field      { margin-bottom: var(--sp-7); }
.sp-settings .form-group.sp-checkgroup { margin-bottom: var(--sp-10); }
.sp-settings .form-group.sp-switchrow  { margin-bottom: var(--sp-3); }
.sp-settings .sp-notify-list > .form-group { margin-bottom: 0; }
.sp-settings .sp-checkgroup > label,
.sp-settings .sp-checkgroup > .control-label { margin-bottom: var(--sp-5); }
.sp-settings .control-label,
.sp-settings .form-horizontal label {
  padding-top: var(--sp-4);
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  font-weight: 500;
  line-height: var(--sp-leading-tight);
  color: var(--sp-ink);
  text-align: left;
}
/* ...but a label that belongs to a component is the component's.

   Two rules reach these labels above the components' own: the page rule above,
   and custom.css's `.main .form-group label`, which adds a 5px top margin to
   every label in every form on the site. Both are (0,2,1). The component rules
   are (0,1,1) and lose, so a checkbox label picked up 10px of top padding, 5px
   of top margin and the page font size, and rode up off its box.

   Restated here, after both, at the specificity they use. The legacy global is
   deliberately left in place - every form that has not been migrated yet still
   leans on it. */
.sp-settings .sp-check label,
.form-group .sp-check label {
  margin-top: 0;
  padding-top: 0;
  font-size: var(--sp-text-md);
  font-weight: 400;
}
.sp-settings .sp-switchrow > label,
.form-group.sp-switchrow > label {
  margin: 0;
  padding: 0;
}
.sp-settings .togglebutton label,
.sp-switchrow .togglebutton label,
.sp-notify-list .togglebutton label {
  margin: 0;
  padding: 0;
}
.sp-settings .form-control {
  height: var(--sp-field-h);
  padding: 0 var(--sp-6);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background-color: var(--sp-surface);
  background-image: none;
  box-shadow: none;
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  line-height: calc(var(--sp-field-h) - 2px);
  color: var(--sp-ink);
  transition: border-color var(--sp-dur) var(--sp-ease),
              box-shadow var(--sp-dur) var(--sp-ease);
}
.sp-settings textarea.form-control { height: auto; line-height: var(--sp-leading); padding: var(--sp-5) var(--sp-6); }
.sp-settings .form-control::placeholder { color: var(--sp-faint); }
.sp-settings .form-control:focus {
  outline: none;
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
/* Material Kit only styles a checkbox when the markup carries its
   .checkbox-material span, which Yii's helpers do not emit - so these render as
   raw browser blue. accent-color fixes that with no markup change. */
.sp-settings input[type="checkbox"],
.sp-settings input[type="radio"] {
  accent-color: var(--sp-brand);
}
.sp-settings .errorMessage {
  margin-top: var(--sp-2);
  font-size: 12.5px;
  color: var(--sp-danger);
}

/* ---- links --------------------------------------------------------------- */
/* Bootstrap's #337ab7 blue has no business on a red-branded page.
   .sp-switchrow__main is excluded because it is a component's own surface, not
   a link in prose: it is the whole left half of a settings row, and thirty of
   them in brand red read as a wall of links rather than a list. */
.sp-settings a:not(.btn):not(.sp-btn):not(.sp-switchrow__main) {
  color: var(--sp-brand-hover);
}
.sp-settings a:not(.btn):not(.sp-btn):not(.sp-switchrow__main):hover {
  color: var(--sp-brand-active);
}
/* A pager link is a component surface too, and the rule above outranks the
   pager's own colours at (0,4,1). Restated through the wrapper, which is the
   same specificity and loads later. Rather than a fourth :not() - the exclusion
   list is already the part of this rule that ages worst. */
/* :any-link, not a bare `a`: without it this is (0,3,2) and the rule above is
   (0,4,1) - three :not()s count as three classes - so the class count decides
   and the pager link stays brand red. The pseudo-class buys the fourth. */
.sp-settings .sp-pager .pagination > li > a:any-link { color: var(--sp-ink-2); }
.sp-settings .sp-pager .pagination > li > a:hover,
.sp-settings .sp-pager .pagination > li > a:focus { color: var(--sp-ink); }
.sp-settings .sp-pager .pagination > .active > a:any-link,
.sp-settings .sp-pager .pagination > .active > a:hover { color: var(--sp-on-brand); }
.sp-settings .sp-pager .pagination > .disabled > a:any-link { color: var(--sp-faint); }

/* ---- tables -------------------------------------------------------------- */
/* !important here is deliberate and temporary. legacy-globals.css sets the grey
   #BDBDBD standings header with !important, which no amount of specificity or
   load order beats. Both go away together when the table component lands and
   the holding pen is emptied - see docs/ui-components.md. */
.sp-settings .table > thead > tr > th,
.sp-settings .fresh-table .table > thead > tr > th,
.sp-settings .fresh-table .table > thead > tr > th a,
.sp-settings .fresh-table .table > thead > tr > th .th-inner,
.sp-settings .fresh-table .table > thead > tr > th .th-inner a {
  background: var(--sp-surface-2) !important;
  color: var(--sp-muted) !important;
  border-bottom: 1px solid var(--sp-border) !important;
  font-family: var(--sp-font);
  font-size: 11px;
  font-weight: 600 !important;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
}
.sp-settings .table > tbody > tr > td {
  padding: var(--sp-5) var(--sp-6);
  border-top: 1px solid var(--sp-hairline);
  font-size: var(--sp-text-base);
  color: var(--sp-ink-2);
  vertical-align: middle;
}
.sp-settings .table > tbody > tr:hover > td {
  background: var(--sp-surface-2);
}


/* ============================================================================
   Table - .sp-table

   Built here because Trends needed it: eight near-identical partials, all
   plain <table class="table"> with inline widths and an inline-styled empty
   row. Applies to a Bootstrap .table, so CGridView output can adopt it too -
   Yii hardcodes .grid-view/.summary/.pager/.keys/.filters in
   jquery.yiigridview.js and those must never be renamed.
   ========================================================================= */
.sp-table-wrap {
  overflow-x: auto;              /* the page must never scroll sideways */
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}
/* A wide data table inside a scroll wrapper has to keep a minimum width, or it
   compresses to fit instead of scrolling and the columns become unreadable.
   These carried per-table inline min-widths (400/500/600px); one threshold
   covers all of them, and the wrapper does the scrolling. */
.sp-table-wrap > .sp-table {
  min-width: 520px;
}
.sp-table {
  width: 100%;
  margin: 0;
  border-collapse: collapse;
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  /* every figure in the table, not only the cells that opted in with
     .text-right - a column of numbers that does not line up is the main thing
     that separates a data table from a sharp one */
  font-variant-numeric: tabular-nums;
}
/* !important is required, not lazy: custom.css carries
   `.fresh-table .table thead th, .table thead th, table th { background: #BDBDBD !important }`
   - a selector broad enough to hit every table header on the site, with
   !important, which no specificity beats. .sp-table is opt-in, so this only
   affects tables that have been deliberately converted. DELETION TARGET: that
   custom.css rule goes when the last table is on the component. */
/* The fresh-table variant needs three classes to out-specify
   `.fresh-table .table thead th` (0,2,2), which is !important in custom.css.
   Same declarations; only the selector weight differs. */
/* The CELL paints the ground and draws the edges. Split from the type rule
   below on purpose: these three declarations were once shared with .th-inner
   and the sort link, and everything a header cell is supposed to own then got
   drawn three times over. The white produced a box around the label as soon as
   a sorted column had a tint; the borders produced a short line above and below
   each column NAME instead of one line across the row - which is what a rule on
   an inline link does. Ground and edges are the cell's job alone.

   The white covers custom.css's
   `.fresh-table .table thead th { background: #BDBDBD !important }`, which
   targets th and nothing inside it. */
.fresh-table .table.sp-table > thead > tr > th {
  background: var(--sp-surface) !important;
  border-top: 1px solid var(--sp-border) !important;
  border-bottom: 1px solid var(--sp-border) !important;
}
/* Type and colour DO belong to the cell and everything in it - the label is
   often wrapped in a link or a .th-inner, and it should look the same either
   way. Sized against the ESPN standings table measured off a screenshot holding
   both at the same scale: their header caps stand 21 device px, ours stood 17.
   Body type already matched - their digits 22, ours 21 - so the header was the
   only real size difference. At the old 0.13em label tracking "TEAM" read as
   T E A M, which is a label style rather than a column heading. Mobile keeps
   12px, pinned by custom2.css at (0,4,3). */
.fresh-table .table.sp-table > thead > tr > th,
.fresh-table .table.sp-table > thead > tr > th .th-inner,
.fresh-table .table.sp-table > thead > tr > th a {
  color: var(--sp-ink) !important;
  font-size: 14px;
  font-weight: 600 !important;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
/* and the things inside the cell paint nothing and draw nothing */
.fresh-table .table.sp-table > thead > tr > th .th-inner,
.fresh-table .table.sp-table > thead > tr > th a,
.sp-table > thead > tr > th .th-inner,
.sp-table > thead > tr > th a {
  background: transparent !important;
  border: 0 !important;
}
.sp-table > thead > tr > th {
  padding: var(--sp-4) var(--sp-6);
  border: none;
  border-top: 1px solid var(--sp-border) !important;
  border-bottom: 1px solid var(--sp-border) !important;
  background: var(--sp-surface) !important;
  color: var(--sp-ink) !important;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
}
.sp-table > tbody > tr > td {
  padding: var(--sp-5) var(--sp-6);
  border: none;
  border-bottom: 1px solid var(--sp-hairline);
  color: var(--sp-ink);
  /* material-kit.min.css opens with `body,h1..h4 { font-weight: 300 }`, so
     every cell inherited Roboto Light. Contrast was never the problem - it
     measured 10.1:1 - the strokes were just thin, which is what reads as
     washed out in a column of figures. 400 is the fix; the colour move from
     ink-2 to ink is the smaller half. */
  font-weight: 400;
  vertical-align: middle;
}
/* The label column carries the row's identity, so it takes a little more weight
   than the figures beside it - but only where column one IS a short label.
   Scoped to the two standings grids, not to .sp-table at large: on the picks
   page the first cell is .pick-summary-cell, which holds the whole row - sport,
   time, both teams and the line - so a blanket rule here set every word on that
   page in 500. That is what "too heavy" was. */
#standingGridView .table > tbody > tr > td:first-child,
#standingGridViewExtra .table > tbody > tr > td:first-child {
  font-weight: 500;
}
/* The row highlight is a scanning aid for rows you can click. An expanded
   detail is not one of those - on the picks page it is a tr.child-row holding
   a whole panel of cards, and highlighting it repainted the entire panel's
   ground the same --sp-surface-2 the cards inside it use, so every card border
   dissolved and the whole detail changed tone the moment the pointer entered
   it anywhere. Any table that expands a row in place has the same shape. */
.sp-table > tbody > tr:not(.child-row):not(.detail):hover > td {
  background: var(--sp-surface-2);
}
.sp-table > tbody > tr:last-child > td {
  border-bottom: none;
}

/* .sp-table--rule — one vertical rule after the label column.
   The pattern a sports standings table uses: the first column names the row,
   everything after it is figures, and a single divider between the two reads
   as far sharper than either a full grid of cell borders or none at all. Opt
   in, because it only makes sense where column one is a label. */
/* Drawn as an inset shadow, not a border, and that is the whole trick.
   `border-right` on the first cell is subject to border-collapse, and the
   header lost that contest to the next th's border-left while the body cell
   won it - screenshotted, a rule that ran down the tbody and stopped dead at
   the header. Two other rules are waiting for a border too: fresh-bootstrap
   -table's `.fresh-table .table thead th { border-left: 0 }` and custom2's
   `.at-responsive-wrapper .fresh-table .table>thead>tr>th { border-color:
   transparent }` in both phone queries. A shadow is outside all of it: no
   collapsing, no border-color reset, no specificity race. */
.fresh-table .table.sp-table--rule > thead > tr > th:first-child,
.fresh-table .table.sp-table--rule > tbody > tr > td:first-child,
.sp-table--rule > thead > tr > th:first-child,
.sp-table--rule > tbody > tr > td:first-child {
  box-shadow: inset -1px 0 0 var(--sp-border);
}
/* the expanded detail spans every column, so the rule would cut through it */
.fresh-table .table.sp-table--rule > tbody > tr.detail-row > td:first-child,
.sp-table--rule > tbody > tr.detail-row > td:first-child {
  box-shadow: none;
}
/* On a wide screen the label column should hug its names, the way the ESPN
   standings table does. Measured off a screenshot holding both at the same
   scale: their team column is 197px with the longest name ending 18px short of
   the rule - 9% dead space. Ours was 391px with the longest name ending at
   105px, so 286px of nothing, 73% of the column, and the divider ended up
   reading as "just before W" rather than "after the team".

   Cause: with no width on any column, auto table layout hands the slack to the
   column with the most text, which is the names. width:1% asks for content
   width, so the slack goes to the figure columns instead and they spread out
   evenly - which is what ESPN's are doing.

   Applied at every width. It was desktop-only at first, on the theory that a
   phone has no slack to give away - measured off a real screenshot, a phone had
   81px of it, 48% of the column. The mobile rule that gave the name column the
   remainder is gone with it; the figures share the slack at both sizes now. */
#standingGridView .table > thead > tr > th:first-child,
#standingGridView .table > tbody > tr:not(.detail-row) > td:first-child,
#standingGridViewExtra .table > thead > tr > th:first-child,
#standingGridViewExtra .table > tbody > tr:not(.detail-row) > td:first-child {
  width: 1%;
  white-space: nowrap;
}
/* The cap goes on the name itself, not the cell. max-width on a table cell does
   not bound a column in auto layout - the column cannot go below its content's
   min-content width, and a username is a single unbreakable token, so the cell
   version measured 267px in a 360px viewport and pushed the table to 468.
   Constraining the span sets the min-content instead, and the column follows.
   //score/_truncate renders the full name in .hidden-xs, which the phone rules
   above reveal in place of the abbreviated one. */
#standingGridView .table > tbody > tr:not(.detail-row) > td:first-child .hidden-xs,
#standingGridViewExtra .table > tbody > tr:not(.detail-row) > td:first-child .hidden-xs {
  display: inline-block;
  max-width: min(38vw, 320px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

/* --- sorting -------------------------------------------------------------
   Modelled on the ESPN standings table. Three parts, and only the first is
   obvious:

   1. Sortable headers are underlined, so which ones are clickable reads at a
      glance rather than from a hover.
   2. The double up/down chevron custom.css draws on EVERY sortable header is
      dropped for the columns that are not sorted. It was noise on eight
      headers to say something one of them was already saying, and it reserved
      20px of padding-right per header - real width on a phone.
   3. The sorted column is tinted, header and body both. This is the part that
      makes it read as a table that is ordered by something, rather than a
      table with an arrow in it.

   custom.css:2027 owns those pseudo-elements at (0,2,2) with !important on its
   padding, so the reset carries !important too - it is answering one. */
.sp-table > thead > tr > th a.sort-link {
  /* !important answers an !important: custom2.css has
     `.standing-grid-view-responsive th a { color: #fff !important;
     text-decoration: none !important }` - a leftover from when the toolbar was
     a saturated coloured bar. The colour half is already beaten on specificity
     by the header block above; text-decoration cannot be, because no
     specificity beats an !important. */
  text-decoration: underline !important;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.sp-table > thead > tr > th a.sort-link:not(.asc):not(.desc) {
  padding-right: 0 !important;
}
.sp-table > thead > tr > th a.sort-link:not(.asc):not(.desc)::before,
.sp-table > thead > tr > th a.sort-link:not(.asc):not(.desc)::after {
  display: none;
}
/* The tint is a wash rather than a solid, so it darkens the striped rows and
   the plain ones by the same amount instead of flattening the stripe. Set on
   the cell, which paints over the row's own background without needing to
   out-specify it. app.setupSortedColumn applies the class. */
.sp-table > tbody > tr > td.sp-col-sorted {
  background-color: var(--sp-brand-wash);
}
/* The header needs the wash LAYERED on its own white, not set as its
   background-color. A body cell is translucent over the row it sits in, which
   is the whole point - it darkens a striped row and a plain one by the same
   amount. A header cell has no such luxury: `thead tr` is painted
   rgb(239,83,80) by custom-homepage.css and `thead` itself #BDBDBD by
   custom.css, and the only reason headers are not red today is that every th
   sets an OPAQUE white over them. Handing the sorted th a translucent
   background-color removed that cover and let the red straight through - the
   sorted column read as a solid red block. So: white underneath, wash painted
   on top of it as a gradient layer, which composites to exactly what the body
   cells show over white. */
.sp-table > thead > tr > th.sp-col-sorted,
.fresh-table .table.sp-table > thead > tr > th.sp-col-sorted {
  background-color: var(--sp-surface) !important;
  background-image: linear-gradient(var(--sp-brand-wash), var(--sp-brand-wash)) !important;
}

/* Numbers line up. This is a betting site; columns of figures are the point. */
.sp-table td.text-center,
.sp-table th.text-center {
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.sp-table td.text-right,
.sp-table th.text-right {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ---- empty state --------------------------------------------------------- */
/* Was `<td colspan=7 style="color:#666;font-style:italic">No data available</td>`
   repeated across the trend partials. */
.sp-empty {
  padding: var(--sp-10) var(--sp-6) !important;
  text-align: center;
  color: var(--sp-muted);
  font-size: var(--sp-text-base);
  font-style: normal;
}

/* ---- pagination ---------------------------------------------------------- */
.sp-pager {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-5);
  margin-top: var(--sp-6);
}
/* The same gap for the third wrapper. Most trends tables render
   //shared/_pager and get .sp-pager's margin; Point Spread Moves and Line Moves
   emit a raw CLinkPager inside .pagination-container instead, and custom.css's
   rule for it sets display and gap but no top margin - so the pager sat flush
   against the table, and against the horizontal scrollbar that sits under a
   wide one. Spacing belongs to whichever element wraps the pager, so all three
   wrappers now agree on it. */
.pagination-container {
  margin-top: var(--sp-6);
}
/* The third pager wrapper: the picks event list is a CListView with
   pagerCssClass 'lvPager', which had no top margin, so its pager sat flush
   against the last table row. Same gap as the other two wrappers. */
.lvPager {
  margin-top: var(--sp-6);
}

/* "Last week summary" link under the FS standings table, aligned bottom-right.
   Lives below the table (not in the filter bar) so it stays clear of the date
   dropdown - the filter bar stacks on a phone and the link crowded it there. */
.sp-standings-summary-link {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--sp-4);
}
.sp-standings-summary-link .sp-linkbtn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--sp-brand);
}
.sp-standings-summary-link .sp-linkbtn:hover,
.sp-standings-summary-link .sp-linkbtn:focus { color: var(--sp-brand-hover); }
.sp-standings-summary-link .material-icons { font-size: 18px; }
/* The list styling is NOT scoped to .sp-pager. There are three pager wrappers
   in the app - .sp-pager (the shared partial), .pagination (what BsGridView
   emits) and .pagination-container (the trends tables) - and a page can show
   more than one of them. What they all have in common is a ul.pagination, so
   that is what carries the look; the wrapper class only decides where it sits.

   The grid one was a 36px-tall block with a 20px line-height, which left the
   label in the top 20px of the box with 16px of air under it. A flex box
   centres the label rather than relying on those two numbers agreeing. */
.pagination {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  margin: 0;
  padding: 0;
  list-style: none;
  border-radius: 0;
}
.pagination > li { display: block; }
.pagination > li > a,
.pagination > li > span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 var(--sp-5);
  border: 1px solid var(--sp-border);
  /* !important is the only answer to Material Kit's own
     `.pagination > li > a { border-radius: 30px !important }`. A pill pager
     next to 10px-radius buttons is a second visual language on the same page. */
  border-radius: var(--sp-radius-md) !important;
  background: var(--sp-surface);
  color: var(--sp-ink-2);
  font-size: var(--sp-text-sm);
  font-weight: 500;
  line-height: 1;
  text-align: center;
  text-transform: none;
  text-decoration: none;
  transition: background-color var(--sp-dur) var(--sp-ease),
              border-color var(--sp-dur) var(--sp-ease);
}
.pagination > li > a:hover,
.pagination > li > a:focus {
  background: var(--sp-surface-3);
  border-color: var(--sp-border-strong);
  color: var(--sp-ink);
  text-decoration: none;
}
.pagination > .active > a,
.pagination > .active > span,
.pagination > .active > a:hover,
.pagination > .active > a:focus {
  background: var(--sp-brand);
  border-color: var(--sp-brand);
  color: var(--sp-on-brand);
}
/* a page you cannot go to should not look like a button you can press */
.pagination > .disabled > a,
.pagination > .disabled > span,
.pagination > .disabled > a:hover {
  background: var(--sp-surface);
  border-color: var(--sp-hairline);
  color: var(--sp-faint);
  cursor: default;
  pointer-events: none;
}
.pagination > .hidden { display: none; }


/* ============================================================================
   .sp-subnav - the bar under the main navigation

   ONE treatment for every second-level nav in the app. There were three, and
   which one you got depended on the page rather than on what the nav did:

     .league-submenu-navbar-primary   14px sentence case, 4px underline drawn
       (14 views)                     on the text, hover #2CA8FF - a Material
                                      Kit blue on a red-branded site. It set
                                      `color: initial`, i.e. no colour of its
                                      own, so it came out brand red inside
                                      .sp-settings and black everywhere else.
                                      Two views shipped their own !important
                                      CSS to force the active underline back.

     .autoCollapseLeagueInformation   12px uppercase, muted -> ink, 2px brand
       (2 views)                      underline. Already on tokens.

     .fresh-table .nav-tabs           registered from trends/index.php against
       (0 views)                      markup that does not exist. Dead.

   WHICH LEVEL GOES WHERE. Two placements, and the test is whether the page
   title changes when you switch:

     above the <h1>   the item is a different page in the same section, and the
                      heading changes with it - Preferences, Insights, Admin,
                      System.
     below the <h1>   the item is a different view of the page you are on and
                      the heading still applies - the league information card,
                      and Standings' Picks / Head-to-Head (two routes, but one
                      heading, so it reads as an in-page tab and belongs here).

   Both use this component. Position carries the level, so the styling does not
   have to, and the two never appear adjacent.

   DO NOT RENAME .autoCollapseLeagueInformation or .league-virtual-menu-item:
   custom.js reads both in responsiveMenu() to collapse the bar on narrow
   screens. .sp-subnav is added alongside them, never instead of them.

   Gone with the old bars: a "More" button and an empty .sub-menu-second-menu
   list it was supposed to reveal. Neither ever worked - both were gated on
   .league-submenu-navbar-primary.collapsed, and nothing in the app has ever
   added that class, so the button was display:none on every page for its
   whole life and the second list shipped with `hide` on it. Narrow screens
   scroll the row instead, which is what the button was reaching for.
   ========================================================================= */
.sp-subnav {
  /* flow-root, so the bar is as tall as its own contents. Bootstrap floats
     .navbar-nav, which escaped the bar entirely: custom2.css held it open with
     `min-height: 50px`, a magic number that had to be re-guessed every time the
     row's padding changed. */
  display: flow-root;
  min-height: 0;
  margin: 0 0 var(--sp-7);
  padding: 0;
  background: none;
  background-image: none;
  border: none;
  border-bottom: 1px solid var(--sp-hairline);
  border-radius: 0;
  box-shadow: none;
}
.sp-subnav .navbar-wrapper { padding: 0; }
/* A flex row, not floats. Bootstrap and custom2.css both float these <li>s, and
   a floated row only holds the bar open through a clearfix or a min-height -
   custom2.css used `min-height: 50px`, a number nobody could change without
   breaking it. Worse, jQuery's outerHeight() on a bar full of escaped floats
   reads 0, and responsiveMenu() decides whether to collapse by comparing that
   height to 70. Flex contains the row by definition. */
.sp-subnav .nav {
  display: flex;
  align-items: stretch;
  float: none;
  margin: 0;
  padding: 0;
  /* One row, always. What does not fit goes into the overflow menu rather
     than wrapping - wrapping put the active item's underline in the middle of
     the bar instead of on its bottom rule, 83px adrift at 260px wide, and
     before that the floated row overflowed the bar's fixed 50px and ran into
     the page below it. overflow stays visible so the menu can hang out of the
     bar; app.setupSubnav does the fitting. */
  flex-wrap: nowrap;
  overflow: visible;
  /* Owned here, not inherited from Bootstrap's .nav: the component has to hold
     up wherever it is dropped, and on the style guide - which loads the design
     system and nothing else - it came out as a bulleted list of underlined
     links. */
  list-style: none;
}
.sp-subnav .nav > li { position: relative; float: none; flex: 0 0 auto; }

.sp-subnav .nav > li > a {
  display: block;
  padding: var(--sp-5) var(--sp-6);
  margin: 0;
  border: none;
  border-radius: 0;
  background: none;
  text-decoration: none;
  white-space: nowrap;
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  font-weight: 500;
  line-height: var(--sp-leading-tight);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--sp-muted);
  transition: color var(--sp-dur) var(--sp-ease);
}
.sp-subnav .nav > li > a:hover,
.sp-subnav .nav > li > a:focus {
  background: none;
  color: var(--sp-ink);
}
/* No icons in here. Standings carried a star and a bar-chart, the league
   information card five more, and nowhere else had any - so the icon was
   decoration on two bars out of sixteen rather than a way of telling items
   apart. Left as a rule so an icon dropped in from a copied block does not
   land at 24px and stretch the row. */
.sp-subnav .nav > li > a .material-icons,
.sp-subnav .nav > li > a .fa { display: none; }

/* Active: an underline sitting ON the bar's own bottom rule, not under the
   text. Drawn as ::after so it does not add height and cannot shift the row. */
.sp-subnav .nav > .active > a,
.sp-subnav .nav > .active > a:hover,
.sp-subnav .nav > .active > a:focus {
  position: relative;
  background: none;
  color: var(--sp-ink);
  box-shadow: none;
}
.sp-subnav .nav > .active > a::after {
  content: "";
  position: absolute;
  left: var(--sp-6);
  right: var(--sp-6);
  bottom: -1px;
  height: 2px;
  background: var(--sp-brand);
}

/* .sp-settings paints every link inside it brand red (see the links block
   above), at (0,4,1) - enough to reach in here and repaint the whole bar,
   which is how Preferences ended up with four red tabs and no visible active
   state. Restated at (0,4,2) via :any-link, the same device the pager uses. */
.sp-settings .sp-subnav .nav > li > a:any-link { color: var(--sp-muted); }
.sp-settings .sp-subnav .nav > li > a:any-link:hover,
.sp-settings .sp-subnav .nav > li > a:any-link:focus,
.sp-settings .sp-subnav .nav > .active > a:any-link { color: var(--sp-ink); }

/* The in-page variant also carries .navbar.navbar-danger, and two legacy rules
   reach it there: `.card .navbar.navbar-danger` (0,3,0) paints the gradient,
   and `.navbar .navbar-nav > li > a` in custom.css sets colour, size, weight
   and transform with !important. The container is answered with specificity;
   the link properties can only be answered with !important. Both go when
   custom.css does. */
.sp-subnav.navbar.navbar-danger {
  background: none;
  background-image: none;
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-subnav.navbar .nav > li > a {
  font-size: var(--sp-text-sm) !important;
  font-weight: 500 !important;
  text-transform: uppercase !important;
  color: var(--sp-muted) !important;
}
.sp-subnav.navbar .nav > li > a:hover,
.sp-subnav.navbar .nav > li > a:focus,
.sp-subnav.navbar .nav > .active > a { color: var(--sp-ink) !important; }

/* The hamburger on the in-page bar, which responsiveMenu() shows below 768px.
   The section bars had a "More" button beside it once; it is gone - see the
   note at the top of this block. */
/* The tab list lives inside a .collapse.navbar-collapse, which Bootstrap hides
   below 768px unless it is .in - that was the point of the hamburger. There is
   no hamburger now, so the row is always shown and scrolls if it has to. The id
   is kept: custom.js reaches #information-navbar-primary to open the matchup
   settings tab from elsewhere on the page. */
.sp-subnav .navbar-collapse {
  display: block !important;
  height: auto !important;
  max-height: none;
  padding: 0;
  border-top: none;
  box-shadow: none;
  overflow: visible;
}
.sp-subnav .navbar-header { float: none; }
/* nothing renders these any more; belt and braces if a copied block brings one
   back, since a stray hamburger would sit on the row with nothing to toggle */
.sp-subnav .navbar-toggle,
.sp-subnav .league-virtual-menu-item { display: none; }

/* ---- overflow control ---------------------------------------------------
   Built by app.setupSubnav and only when the row actually needs it. */
.sp-subnav .nav > li.sp-subnav__more { position: relative; margin-left: auto; }
.sp-subnav__more-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 100%;
  min-height: 41px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}
.sp-subnav__more-bars {
  position: relative;
  display: block;
  width: 18px;
  height: 2px;
  background: var(--sp-muted);
  transition: background var(--sp-dur) var(--sp-ease);
}
.sp-subnav__more-bars::before,
.sp-subnav__more-bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 18px;
  height: 2px;
  background: inherit;
}
.sp-subnav__more-bars::before { top: -6px; }
.sp-subnav__more-bars::after  { top: 6px; }
.sp-subnav__more-btn:hover .sp-subnav__more-bars,
.sp-subnav__more-btn[aria-expanded="true"] .sp-subnav__more-bars { background: var(--sp-ink); }
/* the active tab is in the menu, so the control says so */
.sp-subnav__more.is-active .sp-subnav__more-bars { background: var(--sp-brand); }
.sp-subnav__more.is-active::after {
  content: "";
  position: absolute;
  left: var(--sp-4);
  right: var(--sp-4);
  bottom: -1px;
  height: 2px;
  background: var(--sp-brand);
}

.sp-subnav__menu {
  display: none;
  position: absolute;
  z-index: 1000;
  top: 100%;
  /* left: auto matters. The menu carries .dropdown-menu so Bootstrap's tab
     plugin can find the nav above it, and that brings `left: 0` - which with
     `right: 0` and a 200px min-width stretches the box rightward off the
     screen, since the control it hangs from is only 44px wide. */
  left: auto;
  right: 0;
  min-width: 200px;
  margin: var(--sp-2) 0 0;
  padding: var(--sp-2) 0;
  list-style: none;
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius);
  background: var(--sp-surface);
  box-shadow: var(--sp-shadow);
}
.sp-subnav__menu.is-open { display: block; }
.sp-subnav__menu > li { float: none; position: relative; }
.sp-subnav__menu > li > a {
  display: block;
  padding: var(--sp-4) var(--sp-6);
  white-space: nowrap;
  text-decoration: none;
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--sp-muted);
}
.sp-subnav__menu > li > a:hover,
.sp-subnav__menu > li > a:focus { background: var(--sp-surface-3); color: var(--sp-ink); }
.sp-subnav__menu > li.active > a { color: var(--sp-ink); }
/* the bar's underline is meaningless in a dropdown */
.sp-subnav__menu > li.active > a::after { display: none; }
/* The overflow menu carries .dropdown-menu (the Bootstrap tab plugin needs it),
   so inside the .navbar-danger subnav the legacy Material Kit rule
   `.navbar.navbar-danger .dropdown-menu li>a:hover/:focus{background:#f44336}`
   (0,4,2) beat the intended subtle hover (0,2,2) and painted items brand-red —
   and on touch that :hover/:focus stuck, so every tapped item stayed red and
   piled up. Restore the intended surface hover with a higher-specificity match
   (the extra .sp-subnav__menu class -> 0,5,2), and on touch drop the highlight
   to the resting look so nothing sticks; the active item keeps its own style. */
.navbar.navbar-danger .sp-subnav__menu.dropdown-menu > li > a:hover,
.navbar.navbar-danger .sp-subnav__menu.dropdown-menu > li > a:focus {
  background: var(--sp-surface-3);
  color: var(--sp-ink);
}
/* The SELECTED item is the real culprit: Material Kit's
   `.navbar.navbar-danger .dropdown-menu .active > a { background:#f44336 }`
   paints the active overflow item brand-red, and because these items sit in
   the .dropdown-menu ul the tab plugin skips when clearing siblings, each
   selection keeps .active and they pile up red. Give the active item the
   subnav's own subtle active look (ink text, no fill) — a selected item reads
   as ink vs. muted, never a red block. */
.navbar.navbar-danger .sp-subnav__menu.dropdown-menu > li.active > a,
.navbar.navbar-danger .sp-subnav__menu.dropdown-menu > li.active > a:hover,
.navbar.navbar-danger .sp-subnav__menu.dropdown-menu > li.active > a:focus {
  background: none;
  color: var(--sp-ink);
}
@media (hover: none) {
  .navbar.navbar-danger .sp-subnav__menu.dropdown-menu > li > a:hover,
  .navbar.navbar-danger .sp-subnav__menu.dropdown-menu > li > a:focus {
    background: none;
    color: var(--sp-muted);
  }
}
.sp-settings .sp-subnav__menu > li > a:any-link { color: var(--sp-muted); }
.sp-settings .sp-subnav__menu > li > a:any-link:hover,
.sp-settings .sp-subnav__menu > li.active > a:any-link { color: var(--sp-ink); }



/* ============================================================================
   Picks page

   CSS-only. pick/index.php is 2,077 lines with 148 inline style attributes, a
   ~200-line inline <style> block and 98 inline jQuery selectors; the rows carry
   .summary / .detail / .child-row, which both custom.js and Yii read. The
   markup is not worth the risk here - the chrome around it is.
   ========================================================================= */

/* Section headings. .text-title is shared with league, score, message and
   trends, so this lands on all of them. */
.text-title {
  font-family: var(--sp-font);
  font-size: var(--sp-text-2xl);
  font-weight: 600;
  line-height: var(--sp-leading-tight);
  letter-spacing: var(--sp-track-tight);
  color: var(--sp-ink);
}

/* The first heading on a page sits under two stacked margins that never both
   show. .text-title is an h3, so it carries Bootstrap's 20px top margin, and
   .wrapper (custom.css:33) carries 30px of its own - 10px below 480px.
   Adjacent margins collapse, so the space above it is max(wrapper, heading):
   the heading's own value is invisible, and so is any change to it. Zeroing it
   on the first heading leaves the wrapper's margin alone to space the page,
   which is what that margin is for.

   Two corrections from getting this wrong twice. The margins ADD rather than
   collapse: _footer_homepage.php carries an inline
   `@media (max-width: 767px) { body.index-page { display: flex } }` for the
   sticky footer, which makes .wrapper a flex item and therefore its own
   formatting context. And the heading is not a child of .wrapper at all -
   LeagueController renders through //layouts/column2, which wraps content in
   `.span-19 > #content`, so the first selector below is the one that matches on
   the standings page and the .wrapper form covers the layouts without it.
   Neither intermediate div contributes spacing of its own. */
#content > .text-title:first-child,
.wrapper > .text-title:first-child {
  margin-top: 0;
}

/* THE STANDARD PAGE-HEADING GAP. Every page's first heading sits ~28px below
   the fixed navbar. The shared .wrapper margin lands a first heading at ~18px
   (its own margin minus the navbar's overlap into the body's top padding); add
   10px on the heading's container so every layout matches. Padding, not margin
   (a margin here just collapses with .wrapper's). One value, applied per layout:
   here for the column2 (#content) and .wrapper pages; .sp-settings and
   .fut-standings carry the same 10px in their own blocks. Scoped with :has so
   only heading-first pages move - pages with their own top element are left be.
   Desktop only: on phones the base gap is already ~28px (shorter navbar). */
@media (min-width: 481px) {
  #content:has(> .text-title:first-child),
  .wrapper:has(> .text-title:first-child) {
    padding-top: 10px;
  }
}

/* Date separators in the event list were h3.text-title.red-color at 28px -
   a loud brand-red headline for what is a divider between days. */
.sp-date-sep {
  margin: var(--sp-9) 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--sp-hairline);
  font-size: var(--sp-text-md);
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  color: var(--sp-muted);
}

/* Sport filter grid - 24 checkboxes crammed into a bordered box */
.picks-checkbox {
  padding: var(--sp-6) var(--sp-7);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface-2);
}
.picks-checkbox .checkbox label,
.picks-checkbox label {
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  font-weight: 500;
  letter-spacing: 0.03em;
  color: var(--sp-ink-2);
}
/* Material Kit only styles a checkbox when the markup carries its
   .checkbox-material span; these do not, so they render raw browser blue. */
.picks-checkbox input[type="checkbox"] {
  accent-color: var(--sp-brand);
}

/* Search

   The GROUP is the control, not the input. The input sits inside it next to a
   magnifier and a clear button, so the border, the radius, the background and
   the focus ring all belong to the wrapper - an earlier pass here put them on
   the input as well, and the field drew a second rounded box inside the first,
   2px short of it on the right.

   picks.css drew the wrapper as a 25px pill in 2px #e0e0e0 with a 0 2px 4px
   drop shadow, and lit it #2196F3 on focus - Material's blue, on a page whose
   every other field goes brand red. Those rules are gone; this owns it, and it
   is the same box as .sp-field so a search field and a text field on the same
   page are the same shape. */
.modern-search-input-group {
  height: var(--sp-field-h);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface);
  box-shadow: none;
  transition: border-color var(--sp-dur) var(--sp-ease),
              box-shadow var(--sp-dur) var(--sp-ease);
}
.modern-search-input-group:hover { border-color: var(--sp-border-strong); }
.modern-search-input-group:focus-within {
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
.modern-search-input {
  height: auto;
  border: none;
  border-radius: 0;
  background: none;
  box-shadow: none;
  padding: 0 var(--sp-5) 0 0;
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  color: var(--sp-ink);
}
.modern-search-input::placeholder { color: var(--sp-faint); }
.modern-search-input:focus {
  outline: none;
  border: none;
  box-shadow: none;   /* the ring is on the group */
}
.search-icon-wrapper,
.clear-icon-wrapper {
  padding: 0 var(--sp-5);
  color: var(--sp-faint);
  transition: color var(--sp-dur) var(--sp-ease);
}
.clear-icon-wrapper:hover { color: var(--sp-ink-2); }


/* ============================================================================
   Badges - result pills

   .label.label-* was defined six times across three files, including the
   .label.*-light variants hand-added to the vendor wizard stylesheet, where
   they rendered as a translucent fill with `color: #1a0505ad` - which is why a
   "PUSHING" pill came out muddy tan.

   EVERY DECLARATION HERE NEEDS !important, and the reason is worth knowing:
   custom2.css:1622 carries a block commented "Safari-specific fixes for
   badges" inside @media screen and (-webkit-min-device-pixel-ratio:0). That
   query matches all of Blink and WebKit - Chrome, Edge and Safari - so it is
   not a Safari hack and never was. It sets colour, font, padding and radius on
   every .label variant with !important, so no specificity beats it.
   DELETION TARGET: that block, and these !importants go with it.

   The markup is not renamed: score/_pick-result.php emits `label label-success
   small` and is rendered from ScoreForm.php, a model.

   The solid / -light split is meaningful and is kept:
     solid   settled result   - won, lost
     -light  live, unsettled  - winning, losing, pushing
   Distinguished by border and weight, NOT opacity: opacity multiplies the whole
   pill and took the first attempt to a contrast ratio of 1.0 - invisible.
   ========================================================================= */
.label {
  display: inline-flex !important;
  align-items: center;
  padding: 3px var(--sp-4) !important;
  border: 1px solid transparent;
  border-radius: var(--sp-radius-pill) !important;
  background: var(--sp-surface-3);
  font-family: var(--sp-font);
  font-size: 11px !important;
  font-weight: 600 !important;
  letter-spacing: 0.04em;
  line-height: 1.5 !important;
  text-transform: uppercase;
  color: var(--sp-muted) !important;
  white-space: nowrap;
}

/* Settled - a solid fill carrying white text. The first attempt was a pale
   tint with dark text, which reads well on its own but lost: custom2.css
   forced a saturated Material fill with !important while the component kept
   its dark text, leaving dark green on mid green. Those overrides are deleted
   and the fill is owned here, so background and text can never disagree. */
.label.label-success {
  background: var(--sp-success) !important;
  border-color: transparent;
  color: #fff !important;
}
.label.label-danger {
  background: var(--sp-danger) !important;
  border-color: transparent;
  color: #fff !important;
}
/* the quiet one: a fact, not a state. Same shape as the rest so a row of them
   still lines up with a coloured one. */
.label.label-default {
  background: var(--sp-surface-3) !important;
  border-color: var(--sp-hairline);
  color: var(--sp-ink-2) !important;
}
.label.label-warning {
  /* --sp-warning is too light to carry white text (3.4:1) */
  background: var(--sp-warning-strong) !important;
  border-color: transparent;
  color: #fff !important;
}

/* Live - the tint and dark text the settled chips used to have. The split is
   now fill-vs-tint rather than border-vs-no-border, which is a much stronger
   signal: a provisional result cannot be mistaken for a final one. */
.label.label-success-light {
  background: var(--sp-success-tint) !important;
  border-color: transparent;
  color: var(--sp-success-strong) !important;
  font-weight: 500 !important;
}
.label.label-danger-light {
  background: var(--sp-danger-tint) !important;
  border-color: transparent;
  color: var(--sp-danger) !important;
  font-weight: 500 !important;
}
.label.label-warning-light {
  background: var(--sp-warning-tint) !important;
  border-color: transparent;
  color: var(--sp-warning-strong) !important;
  font-weight: 500 !important;
}
.label.label-info {
  background: var(--sp-surface-3);
  border-color: var(--sp-hairline);
  color: var(--sp-body) !important;
}
/* In progress: a live game with no lean yet. The bare .label fell to the muted
   base (surface-3 fill, muted text) and was near-invisible on the scoreboard;
   give it a solid neutral fill with white text so it reads as clearly as the
   settled won/lost pills, without implying a win or loss. */
.label.label-in-progress {
  background: var(--sp-ink-2) !important;
  border-color: transparent;
  color: #fff !important;
}

/* ============================================================================
   Scoreboard V1

   The table itself is deliberately NOT put on .sp-table: its cells carry a
   bespoke dense layout (matchup cells, winner triangles, live clock) styled
   across custom2.css:477-740, and the component's padding would loosen it.
   Only the header is restyled - it was the grey #BDBDBD block.

   DO NOT RENAME .score-table: custom.js reads it six times for row expansion,
   delete and detail toggling. V2 (#scoreboard-v2) is a separate Tailwind
   namespace and is not touched.
   ========================================================================= */
.score-table .table > thead > tr > th,
.score-table .table > thead > tr > th .th-inner,
.fresh-table .score-table .table thead th {
  background: var(--sp-surface-2) !important;
  color: var(--sp-muted) !important;
  border-bottom: 1px solid var(--sp-border) !important;
  font-family: var(--sp-font);
  font-size: 11px;
  font-weight: 600 !important;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
}


/* ============================================================================
   League information card - the panes behind the tabs

   Note from Admin, Pick Summary, Matchup Settings and Contact Info.
   All four were built the same way and had drifted the same way: a card at
   `max-width: 700-1000px; margin: auto` centred inside a 1425px pane, so a
   third of the width was empty gutter; hardcoded #fafbfc / #e0e0e0 / #e21838
   rather than tokens; 1.5px borders; type in rem fractions (1.08rem, 1.13rem,
   1.18rem) instead of the scale; and a drop shadow on everything.

   sp-components.css loads after custom.css, custom2.css and legacy-globals,
   so these win their ties on order and the old blocks are deleted rather than
   fought.
   ========================================================================= */

/* ---- shared shell -------------------------------------------------------- */
.matchup-settings-card {
  max-width: none;
  width: auto;
  margin: 0 0 var(--sp-7);
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

/* ---- Note from Admin ---------------------------------------------
   Someone else's words, so it keeps a container - but a quiet panel rather
   than the floating shadowed card it was, and left where the eye already is
   instead of centred at 80vw with a gutter on each side. */
.commish-note-card {
  padding: var(--sp-6) var(--sp-7);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius);
  background: var(--sp-surface-2);
}
.commish-note-body {
  max-width: 78ch;
  font-family: var(--sp-font);
  font-size: var(--sp-text-base);
  line-height: var(--sp-leading-loose);
  color: var(--sp-body);
  word-break: break-word;
}
.commish-note-body p { margin: 0 0 var(--sp-5); }
.commish-note-body p:last-child { margin-bottom: 0; }
.commish-note-body a { color: var(--sp-brand-hover); }
.commish-note-body em.text-grey { color: var(--sp-muted); font-style: italic; }

/* ---- Pick Summary --------------------------------------------------------
   Also rendered on the picks page. It used to ship ~110 lines of CSS inline in
   the partial, so they went out with every page that used it and outranked the
   stylesheets by being last in the document - and they carried #2196f3,
   #1976d2, #388e3c, #ff7043, #43a047 and #d32f2f: six colours on no palette
   this site has. The progress bar's two states are semantic, so they take the
   warning and success tokens rather than an orange and a green picked by eye. */
.pick-summary-card {
  padding: var(--sp-6) var(--sp-7);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius);
  background: var(--sp-surface);
}
.pick-summary-progressbar-wrap { margin-top: var(--sp-6); }
.pick-summary-progressbar-labels,
.pick-summary-progressbar-values {
  display: flex;
  justify-content: space-between;
  font-size: var(--sp-text-sm);
  color: var(--sp-muted);
  font-variant-numeric: tabular-nums;
}
.pick-summary-progressbar-labels { margin-bottom: var(--sp-2); }
.pick-summary-progressbar-values { margin-top: var(--sp-2); }
.pick-summary-progressbar-bg {
  position: relative;
  height: 10px;
  width: 100%;
  border-radius: var(--sp-radius-pill);
  background: var(--sp-surface-3);
  overflow: hidden;
}
.pick-summary-progressbar-min,
.pick-summary-progressbar-max {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  height: 100%;
  border-radius: var(--sp-radius-pill);
  transition: width var(--sp-dur-slow) var(--sp-ease-out);
}
.pick-summary-progressbar-min { z-index: 1; background: var(--sp-warning); }
.pick-summary-progressbar-min.below-min { background: var(--sp-warning); }
.pick-summary-progressbar-min.met-min { background: var(--sp-success); }
.pick-summary-progressbar-max { z-index: 0; background: var(--sp-success-tint); }
.pick-summary-main {
  font-family: var(--sp-font);
  font-size: var(--sp-text-lg);
  font-weight: 500;
  line-height: var(--sp-leading);
  color: var(--sp-ink);
  margin-bottom: var(--sp-5);
}
.pick-summary-warning {
  margin: var(--sp-5) 0 0;
  font-size: var(--sp-text-md);
  font-weight: 500;
  color: var(--sp-body);
}
.pick-summary-points { color: var(--sp-brand); font-weight: 600; }
.pick-summary-date { color: var(--sp-ink-2); font-weight: 500; }
.pick-summary-pending {
  max-width: none;
  margin: var(--sp-7) 0 0;
  padding: var(--sp-5) var(--sp-6);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius);
  background: var(--sp-surface-2);
  box-shadow: none;
  font-size: var(--sp-text-md);
  color: var(--sp-body);
}
.pick-summary-team { font-weight: 600; color: var(--sp-ink); }
.pick-summary-wagered { font-weight: 600; color: var(--sp-success); }
.pick-summary-pick { font-weight: 400; font-style: normal; color: var(--sp-body); }

/* ---- Matchup Settings ---------------------------------------------------- */
.matchup-settings-info {
  margin-bottom: var(--sp-7);
  padding: var(--sp-5) var(--sp-6);
  border: 1px solid var(--sp-hairline);
  border-left: none;
  border-radius: var(--sp-radius);
  background: var(--sp-surface-2);
  font-size: var(--sp-text-md);
  line-height: var(--sp-leading);
  color: var(--sp-body);
}
.matchup-settings-info b { color: var(--sp-ink-2); }

/* The four groups were Bootstrap col-md-3 floats, so they were as tall as
   their own contents and their bottoms came out ragged - Points ended 145px
   above Units. A grid gives every card the height of the tallest. */
.matchup-settings-readonly-form { display: flow-root; }
.matchup-settings-readonly-form > .col-md-12 {
  float: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-5);
  padding: 0;
}
@media (min-width: 700px)  { .matchup-settings-readonly-form > .col-md-12 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .matchup-settings-readonly-form > .col-md-12 { grid-template-columns: repeat(4, 1fr); } }
.matchup-settings-readonly-form .matchup-settings-form-row {
  float: none;
  width: auto;
  min-height: 0;
  padding: 0;
  border-right: none;
}
.matchup-settings-section-card {
  height: 100%;
  margin: 0;
  padding: var(--sp-6);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius);
  background: var(--sp-surface);
  box-shadow: none;
  transition: border-color var(--sp-dur) var(--sp-ease);
}
.matchup-settings-section-card:hover { border-color: var(--sp-border); box-shadow: none; }
.matchup-settings-section-header {
  margin: 0 0 var(--sp-5);
  padding: 0 0 var(--sp-4);
  border-bottom: 1px solid var(--sp-hairline);
  font-size: var(--sp-text-xs);
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  color: var(--sp-muted);
}
/* label over value, the pair reading as one line item */
.matchup-settings-section-row {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-5);
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--sp-hairline);
  font-size: var(--sp-text-md);
}
.matchup-settings-section-row:last-child { border-bottom: none; padding-bottom: 0; }
.matchup-settings-form-label {
  margin: 0;
  font-size: var(--sp-text-md);
  font-weight: 400;
  color: var(--sp-muted);
  text-align: left;
}
.matchup-settings-form-value {
  display: block;
  margin: 0;
  font-size: var(--sp-text-md);
  font-weight: 600;
  color: var(--sp-ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.matchup-settings-form-value .bump-request-changed,
.matchup-settings-form-value.bump-request-changed { color: var(--sp-brand); }
/* the editable copy, in the bump-request dialog */
.matchup-settings-form-value input[type="text"],
.matchup-settings-form-value input[type="number"] {
  width: 84px;
  height: var(--sp-control-h);
  padding: 0 var(--sp-4);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-sm);
  background: var(--sp-surface);
  font-size: var(--sp-text-md);
  color: var(--sp-ink);
  text-align: center;
  transition: border-color var(--sp-dur) var(--sp-ease),
              box-shadow var(--sp-dur) var(--sp-ease);
}
.matchup-settings-form-value input[type="text"]:focus,
.matchup-settings-form-value input[type="number"]:focus {
  outline: none;
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
.matchup-settings-form-value input[readonly] {
  background: var(--sp-surface-3);
  color: var(--sp-muted);
}
.bump-request-btns-wrapper { margin-top: var(--sp-7); }

/* The bump-request dialog renders the same four groups, editable. It was
   broken before any of this: the groups are Bootstrap col-md-3 floats, nothing
   in the dialog cleared them, so the body measured 35px tall - the height of
   the button row alone - and the four cards spilled out of the dialog and down
   the page behind it, with "Send request" left floating over the title. The
   grid contains them, the same way it does on the tab. */
/* .col-md-12 is itself floated by Bootstrap, so the form - and with it the
   dialog body - still measured 35px until this unfloated it. */
#bump-requests-form { display: flow-root; }
#bump-requests-form > .col-md-12 {
  float: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-5);
  padding: 0;
  margin-bottom: var(--sp-6);
}
@media (min-width: 560px) { #bump-requests-form > .col-md-12 { grid-template-columns: repeat(2, 1fr); } }
#bump-requests-form .matchup-settings-form-row {
  float: none;
  width: auto;
  min-height: 0;
  padding: 0;
  border-right: none;
}
/* label and field on one line, the field hard right so the four groups line up */
#bump-requests-form .matchup-settings-section-card .row { margin: 0; }
#bump-requests-form .matchup-settings-section-card .form-group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin: 0;
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--sp-hairline);
}
#bump-requests-form .matchup-settings-section-card .row:last-child .form-group { border-bottom: none; }
#bump-requests-form .matchup-settings-section-card .form-group > div {
  float: none;
  width: auto;
  padding: 0;
  text-align: left;
}

/* ---- Contact Info -------------------------------------------------------
   A Bootstrap panel-group: grey headings, a card inside each body, labels
   right-aligned against a 140px gutter, and two contradictory
   `@media (max-width: 600px)` blocks in custom.css where the second turned
   the rows back into columns the first had just made rows.

   One list of members instead, in the same language as the rest of the app:
   hairline rows that open, and a label/value grid inside. */
.contact-info-league-page {
  margin: 0;
  border-top: 1px solid var(--sp-hairline);
}
.contact-info-league-page .panel {
  margin: 0;
  border: none;
  border-bottom: 1px solid var(--sp-hairline);
  border-radius: 0;
  background: none;
  box-shadow: none;
}
.contact-info-league-page .panel-heading {
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
  cursor: pointer;
}
.contact-info-league-page .panel-title { margin: 0; font-size: inherit; }
.contact-info-league-page .panel-title > a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-5) var(--sp-2);
  font-family: var(--sp-font);
  font-size: var(--sp-text-md);
  font-weight: 500;
  color: var(--sp-ink);
  text-decoration: none;
  transition: color var(--sp-dur) var(--sp-ease);
}
.contact-info-league-page .panel-heading:hover .panel-title > a { color: var(--sp-brand); }
.contact-info-league-page .panel-title .material-icons {
  float: none;
  font-size: 20px;
  color: var(--sp-muted);
  transition: transform var(--sp-dur) var(--sp-ease);
}
/* Bootstrap 3 only puts .collapsed on the trigger once it has been toggled, so
   the open state is read off the panel body instead. */
.contact-info-league-page .panel:has(> .panel-collapse.in) .panel-title .material-icons {
  transform: rotate(180deg);
}
.contact-info-league-page .panel-body { padding: 0 var(--sp-2) var(--sp-6); }
.contact-info-league-page .contact-info-card {
  max-width: none;
  margin: 0;
  padding: var(--sp-5) var(--sp-6);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius);
  background: var(--sp-surface-2);
  box-shadow: none;
}
.contact-info-league-page .contact-info-row {
  display: grid;
  grid-template-columns: 140px 1fr;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-5);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--sp-hairline);
}
.contact-info-league-page .contact-info-row:last-child { border-bottom: none; }
.contact-info-league-page .contact-info-label {
  font-size: var(--sp-text-sm);
  color: var(--sp-muted);
  text-align: left;
}
.contact-info-league-page .contact-info-value {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-width: 0;
  font-size: var(--sp-text-md);
  color: var(--sp-ink);
  word-break: break-word;
}
/* the value is duplicated into a hidden input for the clipboard copy */
.contact-info-league-page .copy-to-clipboard-btn input { display: none; }
.contact-info-league-page .copy-to-clipboard-btn {
  display: inline-flex;
  align-items: center;
  color: var(--sp-faint);
  cursor: pointer;
  transition: color var(--sp-dur) var(--sp-ease);
}
.contact-info-league-page .copy-to-clipboard-btn:hover { color: var(--sp-brand); }

@media (max-width: 600px) {
  .contact-info-league-page .contact-info-row {
    grid-template-columns: 1fr;
    gap: 0;
  }
}


/* ============================================================================
   .sp-wizard - the create-league dialog

   The vendored bootstrap-wizard drives it (.wizard-card, .wizard-navigation,
   .btn-next / .btn-previous / .btn-finish, .tab-pane ids), so the structure is
   its; only the look is here. custom-bootstrap-wizard.css - 49KB registered on
   every page for this one dialog - draws the rest, and this out-ranks the parts
   that matter.

   It was three steps. The first offered Head-to-Head or Standard and told you
   to pick the first unless you knew better, so sign-up now assumes it and the
   step is gone; two steps do not need a numbered progress bar to be legible,
   so they are a quiet pair of tabs.
   ========================================================================= */
.sp-wizard { margin: 0; box-shadow: none; background: none; }

.sp-wizard__steps {
  margin: 0 0 var(--sp-7);
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-wizard__steps ul {
  display: flex;
  gap: var(--sp-7);
  margin: 0;
  padding: 0;
  list-style: none;
  background: none;
}
/* !important on width: the plugin writes an inline `width: 50%` on each step
   so its sliding indicator can be positioned, and that stretched the active
   underline halfway across the dialog instead of sitting under its label. */
.sp-wizard__steps li,
.sp-wizard__steps li.active {
  float: none;
  margin: 0;
  background: none;
  width: auto !important;
  flex: 0 0 auto;
}
.sp-wizard__steps li > a {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 0 0 var(--sp-4);
  margin: 0;
  border: none;
  border-radius: 0;
  background: none !important;   /* .nav-pills paints the active pill */
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--sp-muted);
  text-decoration: none !important;
  cursor: default;               /* the buttons move you, not these */
  /* Material Kit gives an active pill a
     `0 16px 26px -10px rgba(156,39,176,.56)` glow - its purple, on a red site -
     which read as a soft grey box behind the step. */
  box-shadow: none !important;
}
.sp-wizard__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--sp-radius-pill);
  background: var(--sp-surface-3);
  color: var(--sp-muted);
  font-size: var(--sp-text-xs);
  font-weight: 600;
  transition: background var(--sp-dur) var(--sp-ease),
              color var(--sp-dur) var(--sp-ease);
}
.sp-wizard__steps li.active > a { color: var(--sp-ink); position: relative; }
.sp-wizard__steps li.active > a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: var(--sp-brand);
}
.sp-wizard__steps li.active .sp-wizard__num {
  background: var(--sp-brand);
  color: var(--sp-on-brand);
}

.sp-wizard__lede {
  margin: 0 0 var(--sp-7);
  font-size: var(--sp-text-md);
  line-height: var(--sp-leading);
  color: var(--sp-muted);
}
.sp-wizard__body .tab-pane { padding: 0; }
.sp-wizard__body .form-group:last-child { margin-bottom: 0; }

/* a hint under a field, so the label can be two words instead of a sentence */
.sp-field__hint {
  margin: var(--sp-2) 0 0;
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading);
  color: var(--sp-muted);
}
.sp-checkgrid--stack { grid-template-columns: 1fr; }

.sp-wizard__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--sp-hairline);
}
/* the plugin shows and hides these; it must win over the flex display */
.sp-wizard__foot .btn-previous[style*="none"],
.sp-wizard__foot .btn-next[style*="none"],
.sp-wizard__foot .btn-finish[style*="none"] { display: none !important; }

/* --- info icon + its tooltip ------------------------------------------------
   The small muted glyph that explains the control beside it: the Team Insights
   bulbs on the pick form, the pitcher bulbs on the info tab, the line-alert
   question mark. There were two of these written separately - .info.info-red
   and .line-alert-info, both in picks.css - and they had drifted: at rest both
   sat at --sp-muted, but one went brand red on hover and the other went dark
   grey. Two rules, one job, so this is the one rule.

   The .material-icons pairing is load-bearing, not decoration: .material-icons
   sets font-size 24px at (0,1,0), which ties .sp-infoicon and wins on load
   order, so the size has to be stated at (0,2,0) to take.

   ALWAYS PUT THIS ON A <button type="button">. Every one of these opens
   something - a modal, a tooltip - so it is a control, and as <span
   aria-hidden="true"> it was reachable by neither keyboard nor screen reader,
   and on touch there is no hover for the tooltip to answer. The appearance
   reset below is what lets a real button look like a bare glyph; iOS needs the
   -webkit- one. Give each a real aria-label: the tooltip's own text lives in
   title, which the tooltip plugin moves to data-original-title and removes. */
.sp-infoicon {
  /* inline-flex, not inline: a <button> flex item takes its baseline from its
     margin box rather than its glyph, which dropped each bulb 4.5px below the
     option label it sits beside once these stopped being <span>s. As a flex
     container it hands up its content's baseline instead and the two line up. */
  display: inline-flex;
  align-items: center;
  -webkit-appearance: none;
  appearance: none;
  padding: 0;
  border: 0;
  background: none;
  line-height: 1;
  color: var(--sp-muted);
  cursor: pointer;
  vertical-align: middle;
  transition: color 120ms ease;
}
.material-icons.sp-infoicon { font-size: 18px; }
.sp-infoicon:hover { color: var(--sp-brand); }
/* :focus-visible rather than :focus for both the colour and the ring - :focus
   fires on a mouse click too, which would leave an icon sitting brand red long
   after you clicked through to the modal it opened. */
.sp-infoicon:focus-visible {
  color: var(--sp-brand);
  outline: 2px solid var(--sp-brand);
  outline-offset: 2px;
  border-radius: var(--sp-radius-sm);
}

/* Bootstrap's tooltip is what every one of these icons opens, and it had never
   been themed - 13 elements across 10 files were still getting the vendor's
   black #000 box at 0.9 opacity in Helvetica. It is the app's only hover
   explainer, so it belongs here rather than nowhere. */
.tooltip { font-family: inherit; }
.tooltip.in { opacity: 1; }
.tooltip-inner {
  min-width: 0;   /* beat legacy custom-bootstrap-wizard.css .tooltip-inner{min-width:130px}, which padded short tips with dead space and forced the text to wrap */
  max-width: 280px;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--sp-radius-sm);
  background: var(--sp-ink);
  box-shadow: var(--sp-shadow-lg);
  color: var(--sp-surface);
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading);
  text-align: left;
}
.tooltip.top .tooltip-arrow    { border-top-color: var(--sp-ink); }
.tooltip.right .tooltip-arrow  { border-right-color: var(--sp-ink); }
.tooltip.bottom .tooltip-arrow { border-bottom-color: var(--sp-ink); }
.tooltip.left .tooltip-arrow   { border-left-color: var(--sp-ink); }

/* --- standings on a phone held upright -------------------------------------
   Measured at 420px: the table is 395px and W, L and T were taking 58.9, 53.6
   and 54.9 of it - 167px, 42% of the table - to show one digit each, while the
   team name got 108px and truncated to "Jace.zur..". The width goes to the
   headers, not the data: each numeric column needs 15.5px of digits, and its
   header spends 11px on the letter, 14px on the sort arrow's reserved space and
   16px on padding before the table's auto layout hands out what is left.

   So every column except the name is told to take only its content - which in
   table layout is what width: 1% means - and the name column gets the rest. It
   also shows the FULL name: //score/_truncate renders both a .hidden-xs full
   version and a .visible-xs-inline shortened one, and CSS can end the full one
   with an ellipsis at whatever width there actually is. A name that fits is
   shown whole, which a fixed 10-character limit could never do.

   Note .standing-grid-view-responsive is CGridView's WRAPPER DIV, not the
   table - these have to be descendant selectors. */
@media (max-width: 767px) and (orientation: portrait) {
  /* Show the full name rather than the pre-shortened one. //score/_truncate
     renders both a .hidden-xs full version and a .visible-xs-inline abbreviated
     one; the full one can end in an ellipsis at whatever width there actually
     is, which a fixed character limit could never do. */
  .standing-grid-view-responsive td:first-child .hidden-xs { display: inline !important; }
  .standing-grid-view-responsive td:first-child .visible-xs-inline { display: none !important; }
}

/* Breathing room between the figures, at both orientations.
   Two legacy rules squeeze them: custom2.css clamps every body cell to
   `padding: 6px 4px !important` (:665 portrait, :759 landscape, :844 for the
   320-480 band) and puts headers at `padding: 3px 2px` - all at (0,3,3). The
   line that used to sit in the portrait block above,
   `.standing-grid-view-responsive th { padding-left: var(--sp-1) }` at (0,1,1),
   was losing silently, so the headers were rendering at 2px. The full chain
   reaches (0,5,3) for cells and (0,4,3) for headers.

   Deliberately NOT inside the portrait query that the column rules use. The
   squeeze is there in landscape too, and measured at 414 and 430 wide the cells
   fell back to 4px - the fix would have covered only the orientation I happened
   to be looking at.

   It costs the name column nothing that matters: every other column is
   width:1% (content only), so the name takes the remainder and already
   ellipsises. Measured at 320px - the narrowest phone worth supporting - the
   table still fits with 99px left for the name. */
@media (max-width: 767px) {
  /* Keyed on the two grid ids rather than .standing-grid-view-responsive.
     That class is only on the Picks grid - Head-to-head's CGridView passes no
     htmlOptions at all - so scoping to it silently left half the feature
     behind: h2h cells stayed at custom2's 4px, which is why its sorted-column
     tint ran straight into the digit in the column before it.

     Adding the class to h2h would have been the tidier-looking fix and the
     wrong one: it carries 25 legacy rules, among them
     `.standing-grid-view-responsive th { background: #ef5350 !important }` and
     a blanket `text-align: right` on every cell. An id is (1,0,0), which beats
     custom2's (0,3,3) outright, and imports nothing. */
  #standingGridView .table > tbody > tr:not(.detail-row) > td,
  #standingGridViewExtra .table > tbody > tr:not(.detail-row) > td {
    padding-left: var(--sp-4) !important;
    padding-right: var(--sp-4) !important;
  }
  #standingGridView .table > thead > tr > th,
  #standingGridViewExtra .table > thead > tr > th {
    padding-left: var(--sp-4) !important;
    padding-right: var(--sp-4) !important;
  }
}

/* --- the tab strip at the head of a content card ---------------------------
   .sp-subnav in a .card-nav-tabs is the bar that switches what the card below
   it shows - Schedule / Note from Admin / Pick Summary on the league
   page, and the same shape on the league-admin and preference layouts.

   It was transparent text on white with a single hairline under it, so there
   was nothing to say where the section above ended and the tabbed one began -
   and directly below it the table toolbar drew a SECOND rule 20px lower in a
   different grey, which read as two separators with a strip of nothing between
   them rather than as one edge.

   So the bar is closed on both sides: a hairline above it as well as the one it
   already had below, which makes it a band rather than a line under some text.
   The active tab's underline still sits on the bottom edge, which is what makes
   it read as a tab rather than a link, and the toolbar's duplicate rule goes -
   the bar's own edge is the separator.

   No fill behind it. I tried one and this element refuses a background-color
   from any weight, including an inline style, which is not a cascade problem
   and not worth more of the page's time to chase; the two rules do the job. */
.card-nav-tabs > .sp-subnav {
  padding: 0 var(--sp-5);
  border-top: 1px solid var(--sp-hairline);
  /* and no bottom margin here. The component carries one for the pages where it
     is the only thing between a heading and its content, but in this card the
     .main-wrapper below already brings its own 20px, so the two stacked either
     side of a 6px gap and left 46px of nothing between the tabs and the first
     control. The wrapper's margin is the gap; the bar's border is the edge. */
  margin-bottom: 0;
}
.card-nav-tabs > .sp-subnav + .content .fixed-table-toolbar,
.card-nav-tabs .fixed-table-toolbar.sp-filterbar {
  border-top: 0;
}

/* ===========================================================================
   Front page — marketing family.

   The public homepage is a portal to the contests: a hero holding a grid of
   contest cards, a three-step "how it works" strip, a brand band for the
   apps, and a pricing teaser. Nothing else on the site is a landing page, so
   these are deliberately narrow: no generic "card" or "feature grid" that the
   app would then be tempted to reuse for data.

   All on tokens. The homepage cascade is material-kit → custom → custom2 →
   legacy-globals → custom-homepage → THIS FILE, so nothing here has to
   out-fight anything and nothing here carries !important.

   The contest cards are rendered from a PHP array in
   views/site/index/_contests.php whose field names match the planned
   `contests` table, so when contests become data the view does not change.
   ========================================================================= */

/* --- hero ------------------------------------------------------------------ */
.sp-hero {
  background: var(--sp-surface-2);
  border-bottom: 1px solid var(--sp-hairline);
  padding: clamp(40px, 7vw, 72px) 0 clamp(40px, 6vw, 64px);
}
.sp-hero__inner,
.sp-section__inner,
.sp-band__inner,
.sp-pricing-teaser__inner {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 clamp(16px, 4vw, 32px);
}
.sp-eyebrow {
  margin: 0 0 14px;
  font-family: var(--sp-font);
  font-size: var(--sp-text-xs);
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  color: var(--sp-brand);
}
.sp-hero h1 {
  margin: 0 0 14px;
  max-width: 18ch;
  font-family: var(--sp-font);
  /* 32px on a phone, 48px from ~960px up; the app scale tops out at 4xl */
  font-size: clamp(var(--sp-text-4xl), 5vw, var(--sp-text-6xl));
  line-height: 1.08;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--sp-ink);
  text-wrap: balance;
}
.sp-hero__sub {
  margin: 0 0 36px;
  max-width: 56ch;
  font-size: clamp(var(--sp-text-lg), 1.6vw, 19px);
  line-height: var(--sp-leading);
  color: var(--sp-body);
}

/* --- contest grid ---------------------------------------------------------- */
.sp-contests {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--sp-6);
  /* CGridView-style .row clearfix pseudo-elements would become grid items;
     this is a plain div, but say so in case it is ever wrapped in one */
  list-style: none;
  margin: 0;
  padding: 0;
}
.sp-contest {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  min-height: 250px;
  padding: 22px 22px 20px;
  background: var(--sp-surface);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-lg);
  box-shadow: var(--sp-shadow-sm);
}
/* the one you can play today: a brand rule along the top and a real shadow */
.sp-contest--live {
  border-color: var(--sp-brand-ring);
  box-shadow: var(--sp-shadow);
}
.sp-contest--live::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 4px;
  border-radius: var(--sp-radius-lg) var(--sp-radius-lg) 0 0;
  background: var(--sp-brand);
}
/* not yet: the tinted ground says "not a door" before the tag does */
.sp-contest--soon {
  background: var(--sp-surface-2);
}
.sp-contest__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}
.sp-contest__sport {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  text-transform: uppercase;
  color: var(--sp-muted);
}
.sp-contest h2 {
  margin: 0;
  font-family: var(--sp-font);
  font-size: 22px;
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--sp-ink);
}
.sp-contest p {
  flex: 1;
  margin: 0;
  font-size: 15px;
  line-height: var(--sp-leading);
  color: var(--sp-body);
}
.sp-contest--soon p { color: var(--sp-muted); }
.sp-contest__actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: var(--sp-2);
}
.sp-contest__actions .sp-linkbtn { padding: 0; }
.sp-contest--soon .sp-contest__actions {
  font-size: var(--sp-text-md);
  color: var(--sp-faint);
}
/* the status tags reuse .sp-tag; these two variants are only meaningful here */
.sp-tag--live {
  background: var(--sp-success-tint);
  color: var(--sp-success);
}
.sp-tag--soon {
  background: var(--sp-surface-3);
  border: 1px solid var(--sp-border);
  color: var(--sp-muted);
}

/* --- how it works ---------------------------------------------------------- */
.sp-section {
  padding: clamp(48px, 7vw, 80px) 0;
}
.sp-section-head {
  max-width: 60ch;
  margin: 0 0 32px;
}
.sp-section-head h2 {
  margin: 0 0 10px;
  font-family: var(--sp-font);
  font-size: clamp(26px, 3.2vw, 34px);
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.012em;
  color: var(--sp-ink);
}
.sp-section-head p {
  margin: 0;
  font-size: var(--sp-text-xl);
  color: var(--sp-body);
}
.sp-steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--sp-7);
}
.sp-step {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--sp-surface);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-lg);
}
/* The screenshots are portrait phone captures, shown as a phone: a fixed
   window, the image centred at phone width and anchored to the top so the
   nav, the title and the first rows are what shows, the rest cropped. A
   fixed height keeps the three cards level regardless of each capture's own
   length. The lightbox opens the full capture. */
.sp-step__img {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  height: 300px;
  overflow: hidden;
  padding: 20px 20px 0;
  background: var(--sp-surface-2);
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-step__img img {
  width: min(100%, 280px);
  height: auto;
  border-radius: 18px 18px 0 0;
  box-shadow: 0 -2px 0 var(--sp-border), 0 10px 28px -10px rgba(26, 21, 18, 0.4);
}
.sp-step__body { padding: 18px 22px 22px; }
.sp-step__num {
  margin: 0 0 6px;
  font-size: var(--sp-text-xs);
  font-weight: 600;
  letter-spacing: var(--sp-track-label);
  color: var(--sp-brand);
}
.sp-step h3 {
  margin: 0 0 8px;
  font-family: var(--sp-font);
  font-size: 19px;
  font-weight: 700;
  color: var(--sp-ink);
}
.sp-step ul {
  margin: 0;
  padding-left: 18px;
  font-size: 15px;
  color: var(--sp-body);
}
.sp-step li { margin: 0 0 4px; }
/* the section's own call to action, under the steps. Each contest section on
   the front page ends with one; the steps explain, this is where you act. */
.sp-section__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  margin-top: clamp(32px, 5vw, 48px);
  text-align: center;
}
.sp-section__cta-note {
  margin: 0;
  font-size: var(--sp-text-md);
  color: var(--sp-muted);
}

/* --- brand band (the apps) ------------------------------------------------- */
.sp-band--brand {
  background: var(--sp-brand);
  color: var(--sp-on-brand);
}
/* copy on the left, the store badges on the right, centred on each other;
   stacked on a phone. The phone capture that used to fill the right column
   was of a previous design, and the page now shows the app on a phone in the
   hero and the steps. */
.sp-band--brand .sp-band__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 32px 48px;
  padding-top: 44px;
  padding-bottom: 44px;
}
.sp-band--brand h2 {
  margin: 0 0 10px;
  font-family: var(--sp-font);
  font-size: clamp(24px, 3vw, 30px);
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--sp-on-brand);
}
.sp-band--brand p {
  margin: 0;
  max-width: 48ch;
  font-size: var(--sp-text-lg);
  color: rgba(255, 255, 255, 0.88);
}
.sp-band__badges {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  gap: var(--sp-4);
}
.sp-band__badges img { height: 40px; width: auto; }

/* --- contest pages (/contests, /contests/<slug>) ---------------------------
   Same family, three more pieces: a hero that is a page title rather than a
   grid, text-only steps (no screenshots for a pool that is not built yet),
   a rules list and a FAQ built on .sp-disclosure. */
.sp-contest h2 a {
  color: inherit;
  text-decoration: none;
}
.sp-contest h2 a:hover { color: var(--sp-brand); }
.sp-contest--soon .sp-contest__actions { flex-wrap: wrap; }
.sp-contest--soon .sp-contest__actions .sp-linkbtn { font-size: var(--sp-text-md); }
.sp-hero--page h1 { max-width: 22ch; }
.sp-hero--page .sp-hero__sub { margin-bottom: 28px; }
.sp-hero__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 22px;
  font-size: var(--sp-text-sm);
  font-weight: 500;
  color: var(--sp-muted);
  text-decoration: none;
}
.sp-hero__back::before {
  content: "";
  width: 0;
  height: 0;
  border-right: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
}
.sp-hero__back:hover { color: var(--sp-brand); }
.sp-hero__meta {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin: 0 0 14px;
}
.sp-hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-5);
}
.sp-hero__actions .sp-linkbtn { padding: 0; }
.sp-hero__note {
  font-size: var(--sp-text-md);
  color: var(--sp-muted);
}
.sp-section--tint {
  background: var(--sp-surface-2);
  border-top: 1px solid var(--sp-hairline);
  border-bottom: 1px solid var(--sp-hairline);
}
/* the steps of a contest page: no image window, a paragraph instead of bullets */
.sp-steps--text { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.sp-step--text .sp-step__body { padding: 22px; }
.sp-step--text .sp-step__body > p:last-child {
  margin: 0;
  font-size: 15px;
  line-height: var(--sp-leading);
  color: var(--sp-body);
}
/* Contest product page: "See it in action" screenshot strip (phone captures
   in the .sp-phone frame) + one landscape capstone shot. */
.sp-section-head--center { margin-left: auto; margin-right: auto; text-align: center; }
.sp-shots { display: flex; flex-wrap: wrap; gap: var(--sp-8); justify-content: center; align-items: flex-start; }
.sp-shot { margin: 0; }
.sp-shot .sp-phone { width: 264px; }
.sp-shot__cap { margin: 18px auto 0; max-width: 30ch; text-align: center; font-size: var(--sp-text-md); color: var(--sp-body); }
.sp-shot__cap b { display: block; color: var(--sp-ink); font-size: var(--sp-text-lg); margin-bottom: 4px; }
.sp-shotwide__frame { border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); overflow: hidden;
  box-shadow: var(--sp-shadow-lg); background: var(--sp-surface); }
.sp-shotwide__frame img { display: block; width: 100%; height: auto; }

/* Landscape-shot carousel: a CSS-only pager. The checked radio slides the track
   and lights its dot — no JS, so it works in the guest homepage layout. */
.sp-shotcar { position: relative; max-width: 920px; margin: clamp(36px, 5vw, 56px) auto 0; overflow: hidden; }
/* Pinned to the bottom by the dots, not the top: clicking a dot focuses its
   radio, and the browser scrolls that radio into view. At the top that jumped
   the page up; by the (already-visible) dots it's a no-op. */
.sp-shotcar__radio { position: absolute; bottom: 4px; left: 50%; width: 1px; height: 1px; opacity: 0; }
.sp-shotcar__track { display: flex; transition: transform .35s ease; }
@media (prefers-reduced-motion: reduce) { .sp-shotcar__track { transition: none; } }
.sp-shotcar__slide { flex: 0 0 100%; min-width: 0; padding: 0 2px; }
.sp-shotcar__radio:nth-of-type(1):checked ~ .sp-shotcar__track { transform: translateX(0); }
.sp-shotcar__radio:nth-of-type(2):checked ~ .sp-shotcar__track { transform: translateX(-100%); }
.sp-shotcar__radio:nth-of-type(3):checked ~ .sp-shotcar__track { transform: translateX(-200%); }
.sp-shotcar__radio:nth-of-type(4):checked ~ .sp-shotcar__track { transform: translateX(-300%); }
.sp-shotcar__dots { display: flex; gap: 10px; justify-content: center; margin-top: 20px; }
.sp-shotcar__dot { width: 10px; height: 10px; border-radius: 50%; background: var(--sp-border); cursor: pointer;
  transition: background .15s, transform .15s; }
.sp-shotcar__dot:hover { background: var(--sp-border-strong); }
.sp-shotcar__radio:nth-of-type(1):checked ~ .sp-shotcar__dots .sp-shotcar__dot:nth-of-type(1),
.sp-shotcar__radio:nth-of-type(2):checked ~ .sp-shotcar__dots .sp-shotcar__dot:nth-of-type(2),
.sp-shotcar__radio:nth-of-type(3):checked ~ .sp-shotcar__dots .sp-shotcar__dot:nth-of-type(3),
.sp-shotcar__radio:nth-of-type(4):checked ~ .sp-shotcar__dots .sp-shotcar__dot:nth-of-type(4) {
  background: var(--sp-brand); transform: scale(1.25);
}
.sp-shotcar__radio:focus-visible ~ .sp-shotcar__dots .sp-shotcar__dot { outline: 2px solid var(--sp-brand-ring); outline-offset: 2px; }

/* Contest product page: the features grid + the settings spec. Data-driven
   from ContestCatalog (features/settings), so every contest can reuse them. */
.sp-featgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); }
/* Fine print under the grid: a legal caveat, quieter than body copy, set off
   by a hairline and centered on a readable measure. */
.sp-featgrid__note {
  max-width: 62ch;
  margin: var(--sp-8) auto 0;
  padding-top: var(--sp-8);
  border-top: 1px solid var(--sp-border);
  text-align: center;
  font-size: var(--sp-text-sm);
  line-height: var(--sp-leading-loose);
  color: var(--sp-muted);
}
@media (max-width: 880px) { .sp-featgrid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .sp-featgrid { grid-template-columns: 1fr; } }
.sp-feat { background: var(--sp-surface); border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); padding: var(--sp-6); }
.sp-feat__ic { display: inline-grid; place-items: center; width: 40px; height: 40px; border-radius: var(--sp-radius-md);
  background: var(--sp-brand-tint); color: var(--sp-brand); margin-bottom: 14px; }
.sp-feat__ic .material-icons { font-size: 22px; }
.sp-feat h3 { margin: 0 0 6px; font-size: var(--sp-text-lg); font-weight: 700; color: var(--sp-ink); }
.sp-feat p { margin: 0; font-size: var(--sp-text-md); color: var(--sp-body); }

.sp-spec { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-4); }
@media (max-width: 700px) { .sp-spec { grid-template-columns: 1fr; } }
.sp-spec__row { display: flex; gap: 14px; background: var(--sp-surface); border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md); padding: 16px 18px; }
.sp-spec__ic { flex: none; display: inline-grid; place-items: center; width: 36px; height: 36px;
  border-radius: var(--sp-radius-sm); background: var(--sp-surface-3); color: var(--sp-brand); }
.sp-spec__ic .material-icons { font-size: 20px; }
.sp-spec__body h3 { margin: 0; font-size: var(--sp-text-lg); font-weight: 700; color: var(--sp-ink); }
.sp-spec__body p { margin: 5px 0 0; font-size: var(--sp-text-md); color: var(--sp-body); }

.sp-rules {
  margin: 0;
  padding: 0 0 0 28px;
  max-width: 68ch;
  font-size: var(--sp-text-lg);
  line-height: var(--sp-leading);
  color: var(--sp-body);
}
.sp-rules li {
  padding: 10px 0;
  border-top: 1px solid var(--sp-hairline);
}
.sp-rules li:first-child { border-top: 0; }
.sp-rules li::marker {
  font-weight: 600;
  color: var(--sp-brand);
}
/* FAQ: .sp-disclosure stacked. Each one carries its own top and bottom rule
   plus a margin, which doubles the lines between neighbours; here they share. */
.sp-faq {
  max-width: 72ch;
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-faq .sp-disclosure {
  margin: 0;
  border-bottom: 0;
}
.sp-faq .sp-disclosure > summary {
  padding: 18px 0;
  font-size: var(--sp-text-lg);
  font-weight: 600;
  color: var(--sp-ink);
}
.sp-faq .sp-disclosure__body {
  padding: 0 0 20px 15px;
}
.sp-faq .sp-disclosure__body p {
  margin: 0;
  font-size: 15px;
  line-height: var(--sp-leading);
  color: var(--sp-body);
}

/* Showcase: the app on a phone beside the hero copy. Two columns from 900px,
   the phone bottom-aligned and pushed below the hero's edge so the section
   crops it - the same move as the apps band. Stacked and smaller on a phone. */
.sp-hero--showcase { overflow: hidden; }
.sp-hero--showcase .sp-hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 32px;
}
.sp-hero__showcase {
  display: flex;
  justify-content: center;
  /* larger than the hero's bottom padding, so the phone runs off the edge */
  margin-bottom: calc(-1 * clamp(40px, 6vw, 64px) - 160px);
}
@media (min-width: 900px) {
  .sp-hero--showcase .sp-hero__inner {
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 64px;
  }
  /* The phone is taller than the copy. In flow it would set the row height
     and leave the copy floating in space above the fold, so it is taken out
     of flow: its top sits level with the copy's top, the copy alone sets the
     hero's height, and the hero's bottom edge crops the rest of the phone. */
  .sp-hero__showcase {
    position: relative;
    margin-bottom: 0;
  }
  .sp-hero__showcase .sp-phone {
    position: absolute;
    top: -8px;
    left: 50%;
    transform: translateX(-50%);
  }
}
/* The front page: headline beside the phone, the contest grid under both.
   The grid follows inside the hero, so there is no edge to crop the phone
   against; it sits in a fixed window that fades to the hero ground instead.
   Below 900px the window is dropped - on a phone the cards are the hero and
   the screenshots come with the steps further down. */
.sp-hero__lead {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin-bottom: 36px;
}
.sp-hero__lead .sp-hero__sub { margin-bottom: 0; }
.sp-hero__showcase--window { display: none; }
@media (min-width: 900px) {
  .sp-hero__lead {
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 64px;
    align-items: start;
  }
  /* Side padding matched by negative margins: the window has to be wider
     than the phone or overflow:hidden slices the phone's shadow into two
     hard grey bands down its sides. The column stays 320px. */
  .sp-hero__showcase--window {
    display: flex;
    justify-content: center;
    height: 400px;
    margin: -24px -48px 0;
    padding: 0 48px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
  }
  /* The window div carries both .sp-hero__showcase (which absolutely-positions
     the phone at top:-8px for the product pages) and --window; that leak pushed
     the phone's top above the window, where overflow:hidden sheared off its top
     frame. Keep the window phone in normal flow so its full top shows. */
  .sp-hero__showcase--window .sp-phone { position: static; transform: none; flex: none; align-self: start; }
  /* The homepage capture starts on its red app header (no status-bar strip in
     the image), so a little top padding keeps it off the screen's rounded top and
     clear of the notch (::before ends at 37px). 40px is enough breathing room --
     64px left too much whitespace between the notch and the header. */
  .sp-hero__showcase--window .sp-phone__screen { padding-top: 40px; }
}

/* An iPhone drawn in CSS: a rounded body on --sp-ink, a status strip with the
   island, and the capture as the screen. Width sets everything else. */
.sp-phone {
  width: 300px;
  padding: 12px;
  background: var(--sp-ink);
  border-radius: 46px;
  box-shadow: var(--sp-shadow-lg), 0 30px 60px -24px rgba(26, 21, 18, 0.5);
}
.sp-phone__screen {
  position: relative;
  overflow: hidden;
  padding-top: 40px;
  background: var(--sp-surface);
  border-radius: 34px;
}
.sp-phone__screen::before {
  content: "";
  position: absolute;
  top: 11px;
  left: 50%;
  width: 92px;
  height: 26px;
  transform: translateX(-50%);
  background: var(--sp-ink);
  border-radius: 13px;
}
.sp-phone__screen img {
  display: block;
  width: 100%;
  height: auto;
}
@media (max-width: 899px) {
  .sp-phone { width: 240px; padding: 10px; border-radius: 38px; }
  .sp-phone__screen { padding-top: 32px; border-radius: 28px; }
  .sp-phone__screen::before { top: 9px; width: 74px; height: 21px; }
}

/* --- pricing teaser -------------------------------------------------------- */
.sp-pricing-teaser {
  background: var(--sp-surface);
  border-top: 1px solid var(--sp-hairline);
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-pricing-teaser__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-8);
  padding-top: 28px;
  padding-bottom: 28px;
}
.sp-pricing-teaser h2 {
  margin: 0 0 4px;
  font-family: var(--sp-font);
  font-size: var(--sp-text-2xl);
  font-weight: 700;
  color: var(--sp-ink);
}
.sp-pricing-teaser p { margin: 0; color: var(--sp-body); }
.sp-pricing-teaser .sp-amount { color: var(--sp-ink); font-weight: 500; }



/* ===================================================================
   Account dropdown as contest switcher (_user.php / _menu.php)
   Leagues grouped under their contest; the nav button shows the
   current contest as an eyebrow above the league name.
   =================================================================== */
.sp-acct-label { display: inline-flex; flex-direction: column; align-items: flex-start; line-height: 1.15; vertical-align: middle; }
.sp-acct-label .sp-acct-eyebrow { font-size: 9px; font-weight: 700; letter-spacing: var(--sp-track-label); text-transform: uppercase; opacity: .72; }
.sp-acct-label .sp-acct-league { font-size: var(--sp-text-md); font-weight: 600; }
/* round avatar around the account icon in the navbar button */
.sp-acct-avatar { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: rgba(255, 255, 255, .18); flex: none; vertical-align: middle; margin-right: 6px; }
.sp-acct-avatar .material-icons { font-size: 19px; }

/* The switcher menu is a clean white panel (readable on the red navbar, in both
   the desktop float and the mobile navbar-collapse). !important beats the
   material-kit navbar-dropdown rules (purple hovers, color:inherit on mobile,
   3px radius) without editing that vendor sheet. */
.sp-league-menu.dropdown-menu {
  background: var(--sp-surface) !important;
  border: 1px solid var(--sp-border);
  border-radius: 14px !important;
  box-shadow: var(--sp-shadow-lg, 0 16px 44px -14px rgba(0,0,0,.35));
  padding: 6px 0 8px;
  min-width: 272px;
  overflow: hidden;
}
/* On desktop the account/league dropdown is an absolutely-positioned panel;
   an admin's long System list runs past the bottom of the screen and
   overflow:hidden clipped it with no way to reach the last items. Cap it to
   the viewport and scroll inside. (Mobile opens this list inside the
   already-scrollable navbar panel, so this only applies from 768px up.) */
@media (min-width: 768px) {
  .sp-league-menu.dropdown-menu {
    max-height: calc(100vh - 88px);
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
  }
}
/* section label ("Your leagues", "System") */
.sp-league-menu .sp-lm-sec {
  padding: 12px 16px 4px; font-family: "Barlow Condensed", sans-serif;
  font-size: 11px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase; color: var(--sp-faint);
}
/* contest group header */
.sp-league-menu .sp-lm-contest { display: flex; align-items: center; gap: 8px; padding: 12px 16px 4px; }
.sp-league-menu .sp-lm-dot {
  width: 22px; height: 22px; border-radius: 6px; background: var(--sp-surface-2);
  border: 1px solid var(--sp-hairline); display: flex; align-items: center; justify-content: center; flex: none; color: var(--sp-muted);
}
.sp-league-menu .sp-lm-dot .material-icons { font-size: 14px; }
.sp-league-menu .sp-lm-cname { font-size: 12px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--sp-muted); }

/* league rows */
.sp-league-menu .sp-lm-row > a {
  /* !important on padding + margin: on mobile material-kit's .navbar .navbar-nav
     .open .dropdown-menu > li > a out-specifies this (flattening the indent) and
     adds a 5px left margin (nudging the name past the contest text). */
  display: flex; align-items: center; gap: 10px; padding: 11px 16px 11px 46px !important; margin: 0 !important; position: relative;
  color: var(--sp-ink) !important; background: transparent !important; font-size: 15px; text-decoration: none;
}
.sp-league-menu .sp-lm-row > a:hover,
.sp-league-menu .sp-lm-row > a:focus { background: var(--sp-surface-2) !important; color: var(--sp-ink) !important; }
.sp-league-menu .sp-lm-name { font-weight: 500; }
.sp-league-menu .sp-lm-row.active > a { background: var(--sp-brand-tint) !important; }
.sp-league-menu .sp-lm-row.active > a::before {
  content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; border-radius: 0 3px 3px 0; background: var(--sp-brand);
}
.sp-league-menu .sp-lm-row.active .sp-lm-name { font-weight: 700; color: var(--sp-brand-hover); }
.sp-league-menu .sp-lm-check { margin-left: auto; color: var(--sp-success-strong); display: none; }
.sp-league-menu .sp-lm-check .material-icons { font-size: 18px; }
.sp-league-menu .sp-lm-row.active .sp-lm-check { display: inline-flex; }

/* league tiles (design "C", now live): sport badge + league name, with the
   contest on the line below. The grouped-row styles above are the retired "A"
   look, kept for a quick revert. */
.sp-league-menu .sp-lm-tile > a {
  /* padding + margin !important so material-kit's mobile .dropdown-menu > li > a
     rule can't flatten the tile (it out-specifies this otherwise). */
  display: flex; align-items: center; gap: 12px; padding: 10px 14px !important; margin: 8px 14px !important;
  border: 1px solid var(--sp-hairline); border-radius: 12px;
  color: var(--sp-ink) !important; background: transparent !important; text-decoration: none;
}
.sp-league-menu .sp-lm-tile > a:hover,
.sp-league-menu .sp-lm-tile > a:focus { background: var(--sp-surface-2) !important; }
.sp-league-menu .sp-lm-badge {
  width: 40px; height: 40px; border-radius: 11px; background: var(--sp-surface-2);
  border: 1px solid var(--sp-hairline); display: flex; align-items: center; justify-content: center; flex: none; color: var(--sp-muted);
}
.sp-league-menu .sp-lm-badge .material-icons { font-size: 20px; }
.sp-league-menu .sp-lm-tt { display: flex; flex-direction: column; min-width: 0; }
.sp-league-menu .sp-lm-tile .sp-lm-name { font-size: 15.5px; font-weight: 700; color: var(--sp-ink); line-height: 1.18; }
.sp-league-menu .sp-lm-ct { font-size: 11.5px; color: var(--sp-muted); margin-top: 1px; }
.sp-league-menu .sp-lm-tile .sp-lm-check { margin-left: auto; color: var(--sp-success-strong); display: none; }
.sp-league-menu .sp-lm-tile.active .sp-lm-check { display: inline-flex; }
.sp-league-menu .sp-lm-tile .sp-lm-check .material-icons { font-size: 20px; }
.sp-league-menu .sp-lm-tile.active > a { border-color: var(--sp-brand); background: var(--sp-brand-tint) !important; }
.sp-league-menu .sp-lm-tile.active .sp-lm-badge { background: #fff; color: var(--sp-brand); }
.sp-league-menu .sp-lm-tile.active .sp-lm-badge .material-icons { color: var(--sp-brand); }
.sp-league-menu .sp-lm-tile.active .sp-lm-name { color: var(--sp-brand-hover); }

/* divider + action rows (New league, Team activity, System, Account) */
.sp-league-menu .divider { height: 1px; background: var(--sp-hairline); margin: 8px 0; overflow: visible; }
.sp-league-menu .sp-lm-action > a {
  display: flex; align-items: center; gap: 12px; padding: 10px 16px !important; margin: 0 !important;
  color: var(--sp-body) !important; background: transparent !important; font-size: 15px; text-decoration: none;
}
.sp-league-menu .sp-lm-action > a:hover,
.sp-league-menu .sp-lm-action > a:focus { background: var(--sp-surface-2) !important; color: var(--sp-body) !important; }
.sp-league-menu .sp-lm-action > a .material-icons { font-size: 19px; color: var(--sp-muted); flex: none; }
.sp-league-menu .sp-lm-new > a { color: var(--sp-brand) !important; font-weight: 600; }
.sp-league-menu .sp-lm-new > a:hover { color: var(--sp-brand-hover) !important; }
.sp-league-menu .sp-lm-new > a .material-icons { color: var(--sp-brand); }
.sp-league-menu .sp-lm-danger > a { color: var(--sp-brand-hover) !important; }

/* BOLD mobile menu: the open hamburger is a white sheet with the current league
   as a RED BRAND HEADER at the top, the main nav dark below it. Tapping the
   header "switches league" — it swaps the nav out for the grouped league picker.
   (The red bar with the logo + hamburger stays; only the expanded menu is this
   sheet.) The fixed navbar otherwise caps the open menu at 340px, lifted here. */
@media (max-width: 767px) {
  #mainMenu .navbar-collapse.in,
  #mainMenu .navbar-collapse.collapsing {
    background: var(--sp-surface) !important;
    border-radius: 14px;
    margin: 6px 10px 12px;
    box-shadow: 0 12px 32px -12px rgba(0, 0, 0, .35);
    max-height: calc(100vh - 56px) !important;
    overflow: hidden auto !important;
    padding: 0 0 6px;
  }
  /* flex column so the current-league switcher can sit at the very top */
  #mainMenu .navbar-collapse.in .navbar-nav { margin: 0; display: flex; flex-direction: column; }
  #mainMenu .navbar-collapse.in .navbar-nav > li.dropdown { order: -1; }
  /* "switch league" open → swap the nav out for the picker (graceful w/o :has) */
  #mainMenu .navbar-collapse.in .navbar-nav:has(> li.dropdown.open) > li:not(.dropdown) { display: none; }

  /* the current-league button becomes a red brand header */
  #mainMenu .navbar-collapse.in .sp-acct-toggle {
    display: flex !important; align-items: center; gap: 12px;
    background: linear-gradient(140deg, var(--sp-brand), var(--sp-brand-hover)) !important;
    color: #fff !important; margin: 0 !important; padding: 15px 18px !important;
  }
  #mainMenu .navbar-collapse.in .sp-acct-toggle .sp-acct-avatar { background: rgba(255, 255, 255, .22); }
  #mainMenu .navbar-collapse.in .sp-acct-toggle .sp-acct-avatar .material-icons { color: #fff; }
  #mainMenu .navbar-collapse.in .sp-acct-toggle .sp-acct-eyebrow { color: rgba(255, 255, 255, .82) !important; opacity: 1; }
  #mainMenu .navbar-collapse.in .sp-acct-toggle .sp-acct-league { color: #fff !important; font-size: 18px; }
  #mainMenu .navbar-collapse.in .sp-acct-toggle .caret { margin-left: auto; color: #fff; border-top-color: #fff; }

  /* main nav rows (League/Picks/Reports/Admin/Activity) — dark on white */
  #mainMenu .navbar-collapse.in .navbar-nav > li:not(.dropdown) > a {
    display: flex !important; align-items: center; gap: 14px;
    padding: 13px 18px !important; margin: 0 !important;
    color: var(--sp-ink) !important; background: transparent !important;
  }
  #mainMenu .navbar-collapse.in .navbar-nav > li:not(.dropdown) > a:hover,
  #mainMenu .navbar-collapse.in .navbar-nav > li:not(.dropdown) > a:focus { background: var(--sp-surface-2) !important; color: var(--sp-ink) !important; }
  #mainMenu .navbar-collapse.in .navbar-nav > li:not(.dropdown) > a > .material-icons { color: var(--sp-muted); font-size: 20px; }
  #mainMenu .navbar-collapse.in .navbar-nav > li.active > a { background: var(--sp-brand-tint) !important; color: var(--sp-brand-hover) !important; position: relative; }
  #mainMenu .navbar-collapse.in .navbar-nav > li.active > a > .material-icons { color: var(--sp-brand); }
  #mainMenu .navbar-collapse.in .navbar-nav > li.active > a::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; border-radius: 0 3px 3px 0; background: var(--sp-brand); }

  /* the leagues picker fills the sheet under the red header */
  #mainMenu .navbar-collapse.in .open .sp-league-menu.dropdown-menu {
    position: static !important; float: none !important; width: auto !important;
    max-height: none !important; overflow: visible !important;
    margin: 0 !important; border: 0 !important; border-radius: 0 !important; box-shadow: none !important;
    background: transparent !important; padding: 6px 0 4px !important;
  }
}

/* =========================================================================
   .sp-invite-link - shareable league invite link + Copy button
   (views/league/_invite_link.php, copy handler js/invite-link.js). Reused in
   the create-wizard Invite step and on the members pages.
   ========================================================================= */
.sp-invite-link__row {
  display: flex;
  gap: var(--sp-3);
  align-items: stretch;
}
.sp-invite-link__url {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius);
  background: var(--sp-surface-2);
  padding: var(--sp-3) var(--sp-4);
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  color: var(--sp-body);
  text-overflow: ellipsis;
}
.sp-invite-link__url:focus {
  outline: none;
  border-color: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-glow);
}
.sp-invite-link__btn {
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}
.sp-invite-link__btn .material-icons { font-size: 18px; }
/* brief green confirm after copy; (0,3,1) beats .sp-btn--primary */
.sp-invite-link.is-copied .sp-invite-link__btn,
.sp-invite-link.is-copied .sp-invite-link__btn:hover {
  background: var(--sp-success);
  border-color: var(--sp-success);
}
.sp-invite-link__hint {
  margin: var(--sp-2) 0 0;
  font-size: var(--sp-text-xs);
  color: var(--sp-muted);
}
@media (max-width: 520px) {
  .sp-invite-link__row { flex-direction: column; }
  .sp-invite-link__btn { justify-content: center; }
}

/* =========================================================================
   .sp-invite-panel - the invite UI (views/league/_invite_panel.php), on its own
   admin tab. Two stacked cards - "Share a link" then "Invite by email" - so each
   way to invite reads as its own full-width section with room to breathe.
   ========================================================================= */
.sp-invite-panel {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-5);
  max-width: 720px;
}
.sp-invite-panel__card {
  padding: var(--sp-6);
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface);
}
/* .sp-invite-panel prefix beats legacy .wrapper h4 (0,1,1) and .wrapper p, which
   main.php's wrapper otherwise forces onto these (24px h4 / 15px grey p). */
.sp-invite-panel .sp-invite-panel__title {
  margin: 0 0 var(--sp-4);
  font-size: var(--sp-text-lg);
  font-weight: 700;
  color: var(--sp-ink);
}
.sp-invite-panel .selectize-control { margin-bottom: 0; }
/* The email field is a roomy box: several addresses fill it from the top-left
   like a compose window, so "invite many" reads at a glance. */
.sp-invite-panel .selectize-control.multi .selectize-input {
  min-height: 116px;
  align-items: flex-start;
  align-content: flex-start;
}
.sp-invite-panel .sp-invite-panel__hint {
  margin: var(--sp-2) 0 0;
  font-size: var(--sp-text-sm);
  color: var(--sp-muted);
  line-height: var(--sp-leading);
}
.sp-invite-panel__actions {
  display: flex;
  gap: var(--sp-3);
  align-items: center;
  margin-top: var(--sp-4);
}
@media (max-width: 520px) {
  .sp-invite-panel__actions { flex-direction: column-reverse; align-items: stretch; }
}

/* =========================================================================
   .sp-action-icon - an icon-only row action (Remove, Resend, Act as, ...).
   One hover for every such control so a table row's actions match: muted at
   rest, brand on a brand-tint chip on hover/focus. Works with a Material icon
   or a glyphicon child. Text row-actions (e.g. Active/Paused) keep their own
   look - this is for icon buttons only.
   ========================================================================= */
.sp-action-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: var(--sp-muted);
  border-radius: var(--sp-radius);
  cursor: pointer;
  text-decoration: none;
}
.sp-action-icon:hover,
.sp-action-icon:focus {
  color: var(--sp-brand);
  background: var(--sp-brand-tint);
  text-decoration: none;
}
.sp-action-icon .material-icons { font-size: 20px; }
.sp-action-icon .glyphicon { font-size: 14px; }

/* =========================================================================
   Role cell (members grid) + league change log (league/settings_log.php).
   ========================================================================= */
.sp-role-member { color: var(--sp-muted); font-size: var(--sp-text-sm); }
.sp-role-select {
  height: 34px;
  padding: 0 28px 0 10px;
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface);
  font-family: var(--sp-font);
  font-size: var(--sp-text-sm);
  color: var(--sp-ink);
  cursor: pointer;
  box-shadow: none;
}
.sp-role-select:focus { border-color: var(--sp-brand); box-shadow: 0 0 0 3px var(--sp-brand-glow); outline: none; }

/* =========================================================================
   Season Pass seat meter + invite banner (seat-priced/seasonal leagues).
   ========================================================================= */
.sp-seats { margin: 0 0 var(--sp-6); }
.sp-seats__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4); margin-bottom: var(--sp-2); }
.sp-seats__count { font-size: var(--sp-text-md); font-weight: 600; color: var(--sp-ink); font-variant-numeric: tabular-nums; }
.sp-seats__count .u { color: var(--sp-muted); font-weight: 400; }
.sp-seats__meta { font-size: var(--sp-text-sm); color: var(--sp-muted); white-space: nowrap; }
.sp-seats__track { height: 8px; border-radius: var(--sp-radius-pill); background: var(--sp-surface-3); overflow: hidden; }
.sp-seats__fill { height: 100%; border-radius: var(--sp-radius-pill); background: var(--sp-brand); }
.sp-seats__fill--full { background: var(--sp-warning); }
/* the seat banner shown on the Invite tab of a seat-priced league */
.sp-seatbar { display: flex; align-items: center; gap: var(--sp-5); max-width: 720px; margin: 0 0 var(--sp-7); padding: var(--sp-6); border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); background: var(--sp-surface); }
.sp-seatbar .sp-seats { flex: 1 1 auto; margin: 0; }
.sp-seatbar__action { flex: 0 0 auto; }
.sp-seatbar__note { max-width: 720px; margin: 0 0 var(--sp-7); font-size: var(--sp-text-sm); color: var(--sp-muted); }
@media (max-width: 520px) {
  .sp-seatbar { flex-direction: column; align-items: stretch; }
}

/* Season Pass price badge on a seat-priced contest's learn-more page. */
.sp-passcard { max-width: 420px; padding: var(--sp-9); border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); background: var(--sp-surface); box-shadow: var(--sp-shadow-sm); }
.sp-passcard__title { margin: 0 0 2px; font-size: var(--sp-text-2xl); font-weight: 700; letter-spacing: var(--sp-track-tight); color: var(--sp-ink); }
.sp-passcard__sub { margin: 0 0 var(--sp-6); font-size: var(--sp-text-md); color: var(--sp-muted); }
.sp-passcard__for { margin: 0 0 var(--sp-6); font-size: var(--sp-text-md); color: var(--sp-muted); }
.sp-passcard__list { margin: 0; padding: 0; list-style: none; }
.sp-passcard__list li { display: flex; gap: var(--sp-5); padding: var(--sp-4) 0; border-bottom: 1px solid var(--sp-hairline); font-size: var(--sp-text-md); color: var(--sp-ink-2); line-height: var(--sp-leading); }
.sp-passcard__list li:last-child { border-bottom: none; }
.sp-passcard__list .material-icons { font-size: 18px; color: var(--sp-success); flex: 0 0 auto; margin-top: 1px; }

/* =========================================================================
   Plan & billing (billing/_parts/_billing_overview.php) - account strip + the
   "leagues you manage" table. Redesigned 2026-09 for many leagues.
   ========================================================================= */
.sp-billlabel { margin: var(--sp-9) 0 var(--sp-4); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); }
.sp-billlabel:first-of-type { margin-top: var(--sp-6); }

.sp-acct { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-5); }
.sp-acct__cell { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-5); padding: var(--sp-5) var(--sp-6); border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); background: var(--sp-surface); }
.sp-acct__body { min-width: 0; }
.sp-acct__k { margin: 0 0 3px; font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); }
.sp-acct__v { font-size: var(--sp-text-base); font-weight: 500; color: var(--sp-ink); }
.sp-acct__v small { display: block; font-weight: 400; font-size: var(--sp-text-sm); color: var(--sp-muted); margin-top: 1px; }
.sp-acct__cards { margin: var(--sp-5) 0 0; }
@media (max-width: 640px) { .sp-acct { grid-template-columns: 1fr; } }

.sp-billtable-wrap { overflow-x: auto; border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); background: var(--sp-surface); padding: var(--sp-2) var(--sp-6); }
.sp-billtable { width: 100%; border-collapse: collapse; font-size: var(--sp-text-base); }
/* custom.css:2097-2111 paints every `table thead` AND `table thead th` #BDBDBD,
   plus header links #111, all unscoped and !important (the documented legacy
   header block). This table is not on .sp-table, so those hit it - beaten here
   on both the thead element and the th, the one case a legacy !important
   warrants answering in kind. */
.sp-billtable thead { background: transparent !important; }
.sp-billtable thead th { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--sp-muted) !important; background: transparent !important; padding: var(--sp-5) var(--sp-4) var(--sp-4); border-bottom: 1px solid var(--sp-border); }
/* Action column is left-aligned - header and buttons share the left edge. The
   header is already left via custom.css `table thead th { text-align:left!important }`. */
.sp-billtable .sp-billtable__act { text-align: left; }
.sp-billtable tbody td { padding: var(--sp-6) var(--sp-4); border-bottom: 1px solid var(--sp-hairline); vertical-align: middle; color: var(--sp-body); }
.sp-billtable tbody tr:last-child td { border-bottom: 0; }
.sp-billtable__name { font-weight: 600; color: var(--sp-ink); }
.sp-billtable__sport { font-size: var(--sp-text-sm); color: var(--sp-muted); }
.sp-billtable__status { color: var(--sp-ink-2); }
.sp-billtable__sub { display: block; font-size: var(--sp-text-sm); color: var(--sp-muted); margin-top: 1px; }
.sp-billtable__sub.warn { color: var(--sp-warning); }
.sp-billtable__sub.danger { color: var(--sp-danger); font-weight: 500; }
.sp-billtable__meter { height: 6px; width: 160px; max-width: 100%; margin-top: 6px; }
@media (max-width: 640px) {
  .sp-billtable-wrap { padding: 0; border: 0; overflow: visible; }
  .sp-billtable, .sp-billtable tbody, .sp-billtable tr, .sp-billtable td { display: block; width: 100%; }
  .sp-billtable thead { display: none; }
  .sp-billtable tbody tr { border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); background: var(--sp-surface); padding: var(--sp-4) var(--sp-6); margin-bottom: var(--sp-5); }
  .sp-billtable tbody td { border: 0; padding: var(--sp-2) 0; }
  .sp-billtable .sp-billtable__act { margin-top: var(--sp-4); }
  .sp-billtable .sp-billtable__act .sp-btn { width: 100%; }
}

.sp-changelog__desc { margin: 0 0 var(--sp-5); color: var(--sp-muted); font-size: var(--sp-text-sm); }
.sp-changelog { max-width: 760px; border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); background: var(--sp-surface); }
.sp-changelog__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-3) var(--sp-5); border-bottom: 1px solid var(--sp-hairline); }
.sp-changelog__row:last-child { border-bottom: 0; }
.sp-changelog__main { font-size: var(--sp-text-sm); color: var(--sp-body); line-height: 1.5; }
.sp-changelog__who, .sp-changelog__field, .sp-changelog__to { font-weight: 600; color: var(--sp-ink); }
.sp-changelog__who { font-weight: 700; }
.sp-changelog__from { color: var(--sp-muted); }
.sp-changelog__arrow { color: var(--sp-faint); margin: 0 2px; }
.sp-changelog__when { flex: 0 0 auto; font-size: var(--sp-text-xs); color: var(--sp-faint); white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (max-width: 560px) {
  .sp-changelog__row { flex-direction: column; gap: 2px; }
}

.sp-ta { max-width: 720px; margin: 0 auto; padding: var(--sp-7) var(--sp-4) var(--sp-9); }
.sp-ta__title { margin: 0 0 var(--sp-3); font-size: var(--sp-text-2xl); font-weight: 700; color: var(--sp-ink); }
.sp-ta__lede { margin: 0 0 var(--sp-7); font-size: var(--sp-text-md); color: var(--sp-body); }
.sp-ta__lede strong { color: var(--sp-ink-2); }

.sp-ta__empty {
  text-align: center;
  padding: var(--sp-9) var(--sp-6);
  border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius-lg);
  background: var(--sp-surface-2);
  color: var(--sp-muted);
}
.sp-ta__empty .material-icons { font-size: 40px; color: var(--sp-success); margin-bottom: var(--sp-3); }
.sp-ta__empty p { margin: 0 auto; max-width: 420px; font-size: var(--sp-text-md); }

.sp-ta__list { display: grid; gap: var(--sp-4); }
.sp-ta__session {
  border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-md);
  background: var(--sp-surface);
  overflow: hidden;
}
.sp-ta__head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-5);
  background: var(--sp-surface-2);
  border-bottom: 1px solid var(--sp-hairline);
}
.sp-ta__who { font-size: var(--sp-text-md); color: var(--sp-ink-2); }
.sp-ta__who strong { color: var(--sp-brand); font-weight: 700; }
.sp-ta__when { font-size: var(--sp-text-sm); color: var(--sp-muted); }
.sp-ta__actions { list-style: none; margin: 0; padding: var(--sp-3) var(--sp-5); display: grid; gap: var(--sp-2); }
.sp-ta__actions li { display: flex; align-items: center; gap: var(--sp-3); font-size: var(--sp-text-md); color: var(--sp-body); }
.sp-ta__ic { font-size: 18px; color: var(--sp-success); flex: 0 0 auto; }
.sp-ta__what { flex: 1; min-width: 0; }
.sp-ta__t { font-style: normal; font-size: var(--sp-text-xs); color: var(--sp-faint); white-space: nowrap; }
.sp-ta__none { margin: 0; padding: var(--sp-3) var(--sp-5); font-size: var(--sp-text-sm); color: var(--sp-muted); font-style: italic; }

/* NFL Futures roster: submitted status + manual "paid" toggle (members admin). */
.sp-table td.c, .sp-table th.c { text-align: center; }
.sp-sub-yes { color: var(--sp-success-strong); font-weight: 700; }
.sp-sub-no  { color: var(--sp-faint); font-weight: 600; }
.sp-paidbtn {
  appearance: none; border: 1px solid var(--sp-border); background: var(--sp-surface);
  border-radius: var(--sp-radius-pill); font-family: var(--sp-font); font-weight: 600;
  font-size: var(--sp-text-sm); color: var(--sp-muted); padding: 5px 13px; cursor: pointer;
  min-width: 88px; transition: background var(--sp-dur) var(--sp-ease), color var(--sp-dur) var(--sp-ease);
}
.sp-paidbtn:hover { border-color: var(--sp-border-strong); color: var(--sp-ink-2); }
.sp-paidbtn.is-paid { background: var(--sp-success-tint); color: var(--sp-success-strong); border-color: transparent; }
.sp-paidbtn.is-paid::before { content: "\2713 "; }
.sp-paidbtn[disabled] { opacity: .6; cursor: default; }

/* NFL Futures payout settings: entry fee ($ prefix) + percent-per-place inputs. */
.fut-narrow { max-width: 120px; }
/* Settings: the derived pick-lock note under the start date (amber, like the
   lock theme). Shows when picks close, in the admin's own timezone. */
.fut-locknote { display: flex; gap: 8px; align-items: flex-start; margin: 8px 0 0; padding: 10px 12px;
  background: var(--sp-warning-tint); border: 1px solid var(--sp-warning-ring); border-radius: var(--sp-radius-md);
  font-size: var(--sp-text-sm); line-height: 1.5; color: var(--sp-ink-2); }
.fut-locknote .material-icons { font-size: 16px; color: var(--sp-warning-strong); flex: none; position: relative; top: 1px; }
.fut-locknote b { color: var(--sp-ink); font-weight: 700; }
.fut-entry-sub { color: var(--sp-muted); font-family: var(--sp-font-num); }
.fut-standings-owner { margin-left: 6px; font-size: 12px; color: var(--sp-muted); font-weight: 400; }
/* Sortable + filterable table (app.setupSortableTable) */
.sp-table th.is-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.sp-table th.is-sortable::after { content: "\2195"; margin-left: 5px; font-size: 11px; color: var(--sp-border-strong); font-family: var(--sp-font); }
.sp-table th.is-sortable:hover::after { color: var(--sp-muted); }
.sp-table th.sorted-asc::after { content: "\2191"; color: var(--sp-brand); }
.sp-table th.sorted-desc::after { content: "\2193"; color: var(--sp-brand); }
/* Title + search on one row. The row owns the section-title spacing so the gap
   below the sub-nav matches the other admin tabs; the title itself sits flush
   (it would otherwise add its own top margin on top of the row's -> double gap). */
.fut-members-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin: var(--sp-10) 0 var(--sp-5); }
/* Title flush to the row top so its gap below the sub-nav matches the other
   admin tabs exactly (a taller search box beside it would otherwise centre the
   title ~9px lower). */
.fut-members-head .text-title { margin: 0; }
.fut-member-search { max-width: 300px; }
/* Hidden entry (admin hid it from standings): dim the row, keep the action column live */
.fut-row-hidden td:not(.member-action-wrapper) { opacity: 0.5; }
.fut-hidden-tag { vertical-align: middle; }
.js-hide-toggle.is-hidden-on { color: var(--sp-brand); }
.fut-dollar { position: relative; display: inline-block; }
.fut-dollar::before { content: "$"; position: absolute; left: 10px; top: 9px; color: var(--sp-muted); z-index: 1; }
.fut-dollar input { padding-left: 20px; }
/* The "how many…" hint + rows + total live in one block so the hint hugs the
   rows (was a tall empty band between them). */
.fut-payout-block .fut-payout-intro { margin: 0 0 8px; }
.fut-payout-block .fut-payout-total-hint { margin: 10px 0 0; }
.fut-payout-rows { margin: 0; }
.fut-payrow { display: grid; grid-template-columns: 56px 120px; align-items: center; gap: 10px; margin: 6px 0; }
.fut-payrow .place { font-weight: 700; color: var(--sp-ink); }
/* block .pct + block input drop the inline line-box leading that otherwise made
   the cell taller than the input (which then rode high while the place label
   centred low). Now the row is exactly the input's height, so the place label
   and input share one centre line. margin:0 kills the legacy bottom margin;
   width:100% fills the 120px column to match the entry-fee / paying-places fields. */
.fut-payrow .pct { position: relative; display: block; }
.fut-payrow .pct::after { content: "%"; position: absolute; right: 12px; top: 9px; color: var(--sp-muted); }
/* Material Kit's JS wraps the input in a .form-group with its own margin/padding
   (the floating-label spacing) — that wrapper, not the input, set the row height
   and threw off the centre line. Collapse it, same as .sp-field does. */
.fut-payrow .pct .form-group { margin: 0; padding: 0; }
.fut-payrow .pct input { padding-right: 24px; margin: 0; display: block; width: 100%; }

/* NFL Futures standings: the same standard page-heading gap (~28px below the
   navbar) as every other page - the shared +10px (desktop only), here on
   .fut-standings. */
.fut-standings > .text-title { margin-top: 0; }
@media (min-width: 481px) { .fut-standings { padding-top: 10px; } }
/* Standings shares the Futures data-page width (~1080px, centered). It renders
   on the app's default full-width .main-wrapper (no cap), so cap it here - and
   here, not futures.css, because the standings view doesn't load futures.css. */
.fut-standings { max-width: 1080px; margin-left: auto; margin-right: auto; }

/* NFL Futures standings: the scoring legend above the table. A scoreboard
   strip - three cells split by hairlines, a big scoreboard numeral over a quiet
   label - reads as sports scoring rather than a generic pill row. */
.fut-scoring__legend { display: flex; margin: 4px 0 16px; border: 1px solid var(--sp-hairline);
  border-radius: var(--sp-radius-md); overflow: hidden; background: var(--sp-surface); }
.fut-scoring__cell { flex: 1; text-align: center; padding: 11px 6px; }
.fut-scoring__cell + .fut-scoring__cell { border-left: 1px solid var(--sp-hairline); }
.fut-scoring__num { font-family: var(--sp-font-num); font-variant-numeric: tabular-nums;
  font-weight: 800; font-size: 23px; line-height: 1; letter-spacing: -0.02em; }
.fut-scoring__num.is-pos { color: var(--sp-success-strong); }
.fut-scoring__num.is-neg { color: var(--sp-brand); }
.fut-scoring__lbl { display: block; margin-top: 6px; font-size: 11px; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--sp-muted); font-weight: 600; }

/* NFL Futures standings: per-team winnings column + the prize-pool summary. */
.sp-table .fut-prize { font-weight: 700; color: var(--sp-success-strong); }

/* Pool identity header on the standings landing: pool type (season · sport
   Futures), the pool's name, and a one-line blurb. Mirrors the .fut-hero look
   on the other futures pages, self-contained so standings needn't pull futures.css. */
.fut-poolhead { padding: 4px 2px 2px; }
.fut-poolhead__type { font-size: var(--sp-text-xs); font-weight: 700; letter-spacing: var(--sp-track-label);
  text-transform: uppercase; color: var(--sp-brand); margin: 0 0 6px; }
.fut-poolhead__name { font-family: var(--sp-font); font-size: clamp(24px, 4.5vw, 32px); line-height: 1.12;
  letter-spacing: -0.02em; color: var(--sp-ink); margin: 0 0 6px; font-weight: 700; }
.fut-poolhead__blurb { margin: 0; color: var(--sp-body); font-size: var(--sp-text-base); }

/* ── Unpaid entry-fee notice ────────────────────────────────────────────────
   A slim, collapsible amber bar shown on the Picks and Standings pages to a
   member who still owes, until the admin marks the entry paid. Collapsed by
   default; "How to pay" reveals the admin's instructions + the delay footnote.
   Lives here (not futures.css) so it is available on Standings too, which
   loads only the shared sheets. Warning tokens, same family as .sp-banner.
   Two-class icon selectors beat the base .material-icons (0,1,0) font-size. */
.fut-paynote { background: var(--sp-warning-tint); border: 1px solid var(--sp-warning-ring);
  border-radius: var(--sp-radius); margin: 0 0 var(--sp-6); }
.fut-paynote__row { display: flex; align-items: center; gap: var(--sp-4); width: 100%;
  padding: var(--sp-4) var(--sp-6); background: none; border: 0; text-align: left; cursor: pointer;
  font: inherit; color: inherit; }
.fut-paynote__row:focus-visible { outline: 2px solid var(--sp-warning-strong); outline-offset: -3px;
  border-radius: var(--sp-radius); }
.fut-paynote__ic { flex: none; display: inline-flex; color: var(--sp-warning); }
.fut-paynote__ic .material-icons { font-size: 20px; }
.fut-paynote__msg { font-size: var(--sp-text-md); color: var(--sp-ink-2); }
.fut-paynote__due { font-family: "Barlow Condensed", sans-serif; font-weight: 700; color: var(--sp-warning-strong); }
.fut-paynote__toggle { margin-left: auto; display: inline-flex; align-items: center; gap: var(--sp-1);
  font-size: var(--sp-text-sm); font-weight: 600; color: var(--sp-warning-strong); white-space: nowrap; }
.fut-paynote__toggle .fut-paynote__chev { font-size: 18px; transition: transform var(--sp-dur) var(--sp-ease); }
.fut-paynote.open .fut-paynote__chev { transform: rotate(180deg); }
.fut-paynote__panel { display: none; padding: 0 var(--sp-6) var(--sp-5) 46px; }
.fut-paynote.open .fut-paynote__panel { display: block; }
.fut-paynote__pay { background: var(--sp-surface); border: 1px solid var(--sp-border);
  border-radius: var(--sp-radius-sm); padding: var(--sp-4) var(--sp-5); }
.fut-paynote__lbl { font-size: var(--sp-text-xs); font-weight: 700; letter-spacing: var(--sp-track-label);
  text-transform: uppercase; color: var(--sp-muted); margin-bottom: var(--sp-2); }
.fut-paynote__who { text-transform: none; letter-spacing: 0; font-weight: 600; color: var(--sp-warning-strong); }
.fut-paynote__txt { font-size: var(--sp-text-md); color: var(--sp-ink-2); line-height: var(--sp-leading); }
.fut-paynote__foot { display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-3);
  font-size: var(--sp-text-sm); color: var(--sp-muted); }
.fut-paynote__foot .material-icons { font-size: 15px; opacity: .85; }
@media (max-width: 600px) {
  .fut-paynote__panel { padding-left: var(--sp-6); }
}

/* ── Admin "submit your entry" reminder (Members page) ───────────────────────
   A banner above the roster when the pool hasn't locked and someone hasn't
   submitted, plus a per-row Remind and a confirm modal. Warning-tinted, same
   family as the other Futures notices. */
.fut-remindbar { display: flex; align-items: center; gap: var(--sp-5); margin: 0 0 var(--sp-6);
  padding: var(--sp-5) var(--sp-6); background: var(--sp-warning-tint);
  border: 1px solid var(--sp-warning-ring); border-left: 3px solid var(--sp-warning); border-radius: var(--sp-radius-md); }
.fut-remindbar__ic { flex: none; display: inline-flex; color: var(--sp-warning); }
.fut-remindbar__ic .material-icons { font-size: 24px; }
.fut-remindbar__txt { flex: 1; min-width: 0; font-size: var(--sp-text-md); color: var(--sp-ink-2); }
.fut-remindbar__txt b { color: var(--sp-ink); font-weight: 600; }
.fut-remindbar__sub { font-size: var(--sp-text-sm); color: var(--sp-muted); margin-top: 2px; }
.fut-remindbar .sp-btn { flex: none; }
.fut-remindbar .sp-btn .material-icons { font-size: 17px; vertical-align: -3px; margin-right: 4px; }

/* "Select users" dialog: a checkbox table of everyone who still needs a nudge
   (submit reminder or re-invite), with a Last Email column. */
.fut-nudge-status { margin: 0 0 var(--sp-4); padding: var(--sp-3) var(--sp-4); border-radius: var(--sp-radius-sm);
  background: var(--sp-success-tint); color: var(--sp-success-strong); font-size: var(--sp-text-sm); font-weight: 600; }
.fut-nudge-tablewrap { overflow-x: auto; }
.fut-nudge-table { width: 100%; border-collapse: collapse; font-size: var(--sp-text-md); }
.fut-nudge-table th { text-align: left; font-size: var(--sp-text-xs); font-weight: 700;
  letter-spacing: var(--sp-track-label); text-transform: uppercase; color: var(--sp-muted);
  padding: var(--sp-2) var(--sp-4); border-bottom: 1px solid var(--sp-border); white-space: nowrap; }
.fut-nudge-table td { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--sp-hairline);
  color: var(--sp-ink-2); vertical-align: middle; }
.fut-nudge-table tbody tr:last-child td { border-bottom: 0; }
.fut-nudge-check { width: 1%; text-align: center; }
.fut-nudge-check input { width: 16px; height: 16px; accent-color: var(--sp-brand); cursor: pointer; }
.js-lastemail { color: var(--sp-muted); white-space: nowrap; }
.fut-need { display: inline-block; font-size: var(--sp-text-xs); font-weight: 700; letter-spacing: .02em;
  padding: 2px var(--sp-3); border-radius: var(--sp-radius-pill); white-space: nowrap; }
.fut-need--submit { background: var(--sp-warning-tint); color: var(--sp-warning-strong); }
.fut-need--invite { background: var(--sp-brand-tint); color: var(--sp-brand); }
@media (max-width: 600px) {
  .fut-remindbar { flex-direction: column; align-items: flex-start; }
  .fut-remindbar .sp-btn { width: 100%; justify-content: center; }
}

.fut-pot { margin: 16px 0; padding: 16px 18px; background: var(--sp-surface);
  border: 1px solid var(--sp-border); border-radius: var(--sp-radius-lg); box-shadow: var(--sp-shadow-sm); }
.fut-pot__head { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 6px; flex-wrap: wrap; }
.fut-pot__amt { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.fut-pot__label { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); }
.fut-pot__num { font-family: "Barlow Condensed", sans-serif; font-variant-numeric: tabular-nums; font-weight: 700; letter-spacing: -.01em;
  font-size: 34px; line-height: 1; color: var(--sp-success-strong); }
.fut-pot__sub { font-size: 13px; color: var(--sp-muted); }
.fut-pot__sub b { color: var(--sp-ink); font-weight: 700; }

.fut-pot__places { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px;
  padding-top: 14px; border-top: 1px solid var(--sp-hairline); }
.fut-pot__place { flex: 1 1 0; min-width: 92px; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 2px;
  padding: 9px 10px; background: var(--sp-success-tint); border: 1px solid var(--sp-success-ring, var(--sp-hairline));
  border-radius: var(--sp-radius-md); }
.fut-pot__pl { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--sp-success-strong); }
.fut-pot__win { font-family: "Barlow Condensed", sans-serif; font-variant-numeric: tabular-nums; font-weight: 700; letter-spacing: -.01em; font-size: 22px; color: var(--sp-ink); }
.fut-pot__pct { font-size: 12px; font-weight: 600; color: var(--sp-muted); }

/* Dev/QA-only "seed test data" panel on the Futures Members admin page. */
.fut-testdata { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-bottom: 14px; }
.fut-testdata__msg { flex: 1 1 260px; font-size: 13px; }
.fut-testdata__form { display: inline-flex; align-items: center; gap: 8px; margin: 0; }
.fut-testdata__lbl { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--sp-muted); }
.fut-testdata__num { width: 72px; }
/* Dismiss the dev/QA seeder banner for a clean screenshot; non-persistent (it
   returns on reload). Sits at the top-right corner of the callout. */
.fut-testdata__x { flex: none; align-self: flex-start; border: 0; background: transparent; cursor: pointer;
  font-size: 20px; line-height: 1; color: var(--sp-warning-strong); padding: 2px 4px; margin-left: 2px; opacity: .7; }
.fut-testdata__x:hover { opacity: 1; }

/* NFL Futures standings: points column (only when locks are enabled). */
.sp-table .fut-pts { font-weight: 700; color: var(--sp-ink); }

/* Portrait phones: trim the standings table to the essentials — Team, Pts, Won,
   Lost. Rotate to landscape (or open on a wider screen) to see PCT / Open /
   Winnings. */
@media (orientation: portrait) and (max-width: 640px) {
  .fresh-table .table.sp-table th.fut-hide-portrait,
  .fresh-table .table.sp-table td.fut-hide-portrait { display: none; }
}
