/* Print styles */
@media print { 
    .hide-on-print {
        display: none !important;
    }
	textarea {
    /* remove the textarea box completely for print */
    border: none !important;   /* hide the border */
    outline: none !important;  /* hide the focus ring (some browsers) */
    resize: none;              /* prevent the little resize handle from printing */
  }
}

body {
    min-width: 350px;
    font-size: 1rem; /* using relative units */
    /* Past ~1400px the page gains nothing from more width -- the roster row is already at
       full density and the lineup tables have all the room they need -- while the eye has to
       travel further across to read a row. Cap it, and centre what is left over.

       The centring sits in its own media query rather than going straight onto this rule as
       `margin-inline: auto`. With `width: auto` an auto side margin resolves to ZERO, so that
       would quietly take the body's 8px gutters away at every width below the cap -- and
       every width budget measured on these pages is built on those gutters being there (the
       roster's 118px stepper floor, the 820px layout switch, the 52px of chrome the card
       budget assumes). 1417px is the first width at which the cap actually binds: 1400 plus
       the two 8px margins.

       No html background is needed to fill the rest of the screen: html is transparent on
       both pages, so the body's background still propagates to the canvas -- which also means
       RandomTeamGenerator's dark theme follows along for free. */
    max-width: 1400px;
}

/* Once the page stops filling the screen it has to look deliberate rather than merely
   narrow. Without this the gutters are the same colour as the page, so the capped column
   reads as two dead bands either side of the content with nothing explaining them.

   `screen and` because this is pure chrome: a backdrop and a drop shadow are wasted ink on
   paper, and the cap itself is in px so it never binds on a sheet anyway.

   Giving html a background is what makes the backdrop visible at all: html is transparent
   on both pages, so until now the BODY's background propagated up to the canvas and painted
   the whole viewport. Once html has one of its own that propagation stops and body paints
   only its own box -- which is the point, but it also means body now needs a min-height, or
   a short page would leave the backdrop showing under it. */
@media screen and (min-width: 1417px) {
    html {
        background: var(--backdrop, #E3E3E1);
    }

    body {
        margin-inline: auto;
        /* 16px is body's own 8px top and bottom margins, so this fills the viewport exactly
           rather than adding a scrollbar. */
        min-height: calc(100vh - 16px);
        box-shadow: var(--page-shadow, 0 0 0 1px rgba(0, 0, 0, .05), 0 1px 30px rgba(0, 0, 0, .09));
    }
}

table {
    border-collapse: collapse;
    width: 100%;
}

th,
td {
    border: 1px solid black;
    padding: 0.4em;
}

th {
    background-color: lightgray;
}

tr:nth-child(even) {
    background-color: #f2f2f2;
}

tr[draggable="true"] {
    cursor: grab;
}

tr[draggable="true"]:hover {
    background-color: lightblue;
}

ul {
    margin-block-start: 0.1em;
}

h3 {
    margin: 0.1em;
}

.fieldIcon {
    width: 1em;
    height: 1em;
    margin-left: 1em;
    cursor: pointer;
}

.info-icon {
    /* Match the font size */
    height: 1em;
    vertical-align: top;
    cursor: help;
}

.big-button {
    font-size: 1.5em;
    font-weight: bold;
}

@media (max-width: 700px) {
    body { min-width: 0; }
    #tableContainer, .table-container { overflow-x: auto; max-width: 100%; }
}

.table-container {
    position: relative;
}

.hidden {
    display: none;
}

.editButton {
    position: absolute;
    top: 0;
    left: 0;
    margin: 10px;
}

.editable-header {
    border: 2px solid #ddd;
    padding: 15px;
    margin: 20px 0;
    background-color: #f9f9f9;
    cursor: text;
    justify-content: center;
    align-items: center;
    text-align: center;
    position: relative;
    z-index: 1;
}

.commentsBox {
	display: none;
	width: 100%;
	min-height: 6rem;
	overflow-y: hidden;   /* so the scrollbar never shows */
	resize: vertical;     /* still lets the user drag‑resize if they want */
}

.custom-file-upload {
    cursor: pointer;
    padding: 0.2em 1.5em;
    margin: 0 0.5em;
    background-color: #007bff;
    color: #fff;
    border-radius: 5px;
    display: inline-block;
}

.custom-file-upload:hover {
    background-color: #0056b3;
}

/* Export/Import are <label>s; a <button> carrying the same class needs its default border
   and font reset to sit level with them. */
button.custom-file-upload {
    border: none;
    font: inherit;
}

#fieldingTable th.column-dragging {
    opacity: 0.5;
}

.gradient-text {
    background: linear-gradient(to bottom, blue, green, blue);
    -webkit-background-clip: text;
    color: transparent;
    font-weight: bold;
    display: inline-block;
    /* Ensures the blink animation affects the whole word */
}

@keyframes blink {
    0%,
    100% {
        opacity: 1;
    }
    50% {
        opacity: 0;
    }
}

.blinking {
    animation: blink 1s infinite;
}

/* ==========================================================================
   Redesign additions below this line (mobile-friendly roster cards, collapsible
   sections, tap-to-reorder). See agent/sports-mobile-redesign-plan.md.
   ========================================================================== */

#saveload {
    margin-bottom: 12px;
}

/* --- Collapsible accordion sections (Options / Notes) --- */

.collapsible {
    margin-bottom: 1em;
    border: 1px solid #E7E5E1;
    border-radius: 12px;
    overflow: hidden;
    background: #FFFFFF;
}

.collapsible-header {
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    min-height: 44px;
    box-sizing: border-box;
}

.collapsible-header h3 {
    margin: 0;
    font-size: 1em;
}

.collapsible-header .arrow {
    display: inline-flex;
    color: #75787E;
    transition: transform 0.15s ease;
}

.collapsible.open .collapsible-header .arrow {
    transform: rotate(90deg);
}

.collapsible-content {
    display: none;
    padding: 4px 16px 16px;
}

.collapsible.open .collapsible-content {
    display: block;
}

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

.roster-cards {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: 12px 0;
}

.roster-card {
    border: 1px solid #E7E5E1;
    border-radius: 12px;
    padding: 9px 8px 9px 14px;
    box-shadow: 0 1px 2px rgba(20, 20, 20, 0.05);
}

.roster-card.ineligible-flagged {
    border: 2px solid #D98C1F;
}

.roster-card-top {
    display: flex;
    align-items: center;
    gap: 8px;
}

.roster-name-input {
    flex: 1;
    min-width: 0;
    font-size: 16px;
    font-weight: 600;
    color: #24262B;
    border: none;
    background: transparent;
    padding: 6px 2px;
}

.roster-name-input:focus {
    outline: 2px solid #3B6FE0;
    outline-offset: 1px;
    border-radius: 4px;
}

toggle-element {
    flex-shrink: 0;
}

/* Generic circular "exception flag" toggle -- reused for Ineligible-to-pitch (LLLineup)
   and Ignore-buddy (RandomTeamGenerator): neutral outline off, filled amber active. */
.flag-toggle,
.card-expand-toggle {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 1.5px solid #DEDCD6;
    background: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    cursor: pointer;
    color: #9A9CA1;
    padding: 0;
}

.card-expand-toggle {
    border: none;
    background: transparent;
    color: #75787E;
    transition: transform 0.15s ease;
}

.roster-card.expanded .card-expand-toggle {
    transform: rotate(90deg);
}

.flag-toggle.active {
    border: none;
    background: #E8A33D;
    color: #FFFFFF;
}

.flag-toggle:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.roster-card-details {
    display: none;
    padding-top: 12px;
}

.roster-card.expanded .roster-card-details {
    display: block;
}

.roster-weights-label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: #5B5D63;
    margin-bottom: 8px;
    padding-left: 2px;
}

.roster-weights-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
}

.weight-cell {
    background: #FFFFFF;
    border: 1px solid #E2E0DB;
    border-radius: 10px;
    padding: 8px 10px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.weight-cell-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.03em;
    color: #75787E;
}

.weight-stepper {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.stepper-btn {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    border: 1px solid #E2E0DB;
    background: #FFFFFF;
    color: #3B6FE0;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
}

.weight-input {
    width: 32px;
    border: none;
    background: transparent;
    text-align: center;
    font-size: 16px;
    font-weight: 700;
    color: #24262B;
    -moz-appearance: textfield;
    padding: 0;
}

.weight-input::-webkit-outer-spin-button,
.weight-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.card-text-field {
    background: #FFFFFF;
    border: 1px solid #E2E0DB;
    border-radius: 10px;
    padding: 8px 10px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    /* positioning context for the shared name-autocomplete dropdown below */
    position: relative;
}

/* --- Shared name-reference autocomplete (js/name-autocomplete.js) ---
   Base/light appearance only; a themed page re-points these colors at its own tokens. */
.ac-list {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 40;
    margin: 3px 0 0;
    padding: 4px;
    list-style: none;
    background: #FFFFFF;
    border: 1px solid #E2E0DB;
    border-radius: 10px;
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
    max-height: 210px;
    overflow-y: auto;
}

.ac-option {
    padding: 9px 10px;
    border-radius: 6px;
    font-size: 0.9rem;
    color: #24262B;
    cursor: pointer;
}

.ac-option:hover,
.ac-option.active {
    background: #E2E0DB;
}

/* Section header inside the suggestion list, emitted only when a caller passes groupOf().
   Not selectable and not focusable -- it labels the options under it. RandomTeamGenerator
   passes no groupOf, so no page that does not ask for sections ever renders one. */
.ac-group {
    padding: 7px 10px 3px;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #75787E;
    cursor: default;
}

/* A name matching nobody does nothing at all, so flag it in the field itself. */
input.name-unknown {
    text-decoration: underline wavy #D64545;
    text-underline-offset: 3px;
}

.card-text-field label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.03em;
    color: #75787E;
}

.card-text-field input {
    border: none;
    background: transparent;
    font-size: 14px;
    font-weight: 600;
    color: #24262B;
    padding: 0;
    width: 100%;
}

.pitcher-ineligible-note {
    display: none;
}

.pitcher-cell.ineligible {
    background: #E8A33D;
    border-color: #E8A33D;
}

.pitcher-cell.ineligible .weight-cell-label {
    color: #FFFFFF;
}

.pitcher-cell.ineligible .weight-stepper {
    display: none;
}

.pitcher-cell.ineligible .pitcher-ineligible-note {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 32px;
    color: #FFFFFF;
}

.roster-card-divider {
    height: 1px;
    background: rgba(36, 38, 43, 0.12);
    margin: 14px 0 12px;
}

.roster-card-meta {
    display: flex;
    align-items: center;
    gap: 18px;
    padding-left: 2px;
}

.mvp-label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    color: #24262B;
    cursor: pointer;
}

.mvp-label input[type="checkbox"] {
    width: 18px;
    height: 18px;
    accent-color: #3B6FE0;
    cursor: pointer;
}

.group-select {
    width: 44px;
    height: 36px;
    border: 2px solid #24262B;
    border-radius: 6px;
    cursor: pointer;
    padding: 0;
}

/* --- Tap-to-reorder (batting order) --- */

#lineupTable tbody tr {
    cursor: pointer;
}

#lineupTable tbody tr.reorder-selected {
    outline: 2px solid #3B6FE0;
    outline-offset: -2px;
    background: #EAF0FE !important;
    box-shadow: 0 6px 16px rgba(59, 111, 224, 0.18);
}

.reorder-banner {
    display: none;
    align-items: center;
    justify-content: space-between;
    background: #EAF0FE;
    border: 1px solid #C7D8FB;
    border-radius: 10px;
    padding: 10px 10px 10px 14px;
    margin-bottom: 8px;
    font-size: 13px;
    font-weight: 700;
    color: #1F3E8C;
}

.reorder-banner.visible {
    display: flex;
}

.reorder-banner-cancel {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: none;
    background: #FFFFFF;
    color: #3B6FE0;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    flex-shrink: 0;
}

tr.reorder-drop-zone-row {
    background: transparent !important;
}

.reorder-drop-zone {
    border: none !important;
    /* Padded well past the visible 14px line so the tap target is a full ~44px, per touch-target guidance. */
    padding: 15px 0 !important;
    background: transparent !important;
    cursor: pointer;
}

.reorder-drop-zone-inner {
    height: 14px;
    margin: 2px 0;
    border-radius: 6px;
    border: 1.5px dashed transparent;
    transition: height 0.1s ease;
    pointer-events: none; /* the td handles the click; this is purely visual */
}

body.reorder-active .reorder-drop-zone-inner {
    border-color: #D8DCE5;
}

.reorder-drop-zone:hover .reorder-drop-zone-inner {
    height: 40px;
    border: 2px dashed #3B6FE0;
    background: #EAF0FE;
}

/* --- Wide-screen single-row layout for "compact" roster cards ---
   Opt-in via .roster-cards--compact on the cards container: only for card variants with few
   enough fields to fit one row at desktop widths (RandomTeamGenerator's Pool/Level/Buddy/
   Conflict/Team#). LLLineup's card (6 weight steppers + MVP + Group) is NOT compact and keeps
   its collapse/expand behavior at every width -- it would need a much larger breakpoint and its
   own tuned rule, not this one, since .roster-card/.weight-cell are shared base classes.

   Breakpoint math: Name (110px min) + RSVP (64px) + flag-toggle (44px) + Pool/Level/Team#
   (84px each, fixed) + Buddy/Conflict (130px min each) + ~7 gaps (10px) + card padding (~22px)
   = ~804px of required content width -- measured directly (a long name plus a filled Team#
   value at 850px viewport showed a card needing 818px, ~20px more than its box, since the
   card itself sits inside the page's own margins). 900px gives that a real margin of safety
   rather than fitting exactly at the edge. */
@media (min-width: 900px) {
    .roster-cards--compact .roster-card {
        display: flex;
        align-items: center;
        flex-wrap: nowrap;
        gap: 10px;
    }

    /* display:contents lets each wrapper's children become direct flex items of .roster-card,
       laying everything out in one row without changing the underlying HTML/JS. */
    .roster-cards--compact .roster-card-top,
    .roster-cards--compact .roster-card-details,
    .roster-cards--compact .roster-weights-grid {
        display: contents;
    }

    /* Nothing is collapsed anymore in this layout, so the expand affordances go away. */
    .roster-cards--compact .card-expand-toggle,
    .roster-cards--compact .roster-card-divider,
    .roster-cards--compact .roster-weights-label {
        display: none;
    }

    .roster-cards--compact .roster-name-input {
        flex: 1 1 140px;
        min-width: 110px;
        max-width: 320px;
    }

    /* Fields read as plain row cells now (no per-field box), like the old table's columns. */
    .roster-cards--compact .weight-cell,
    .roster-cards--compact .card-text-field {
        background: transparent;
        border: none;
        padding: 0;
        flex: 0 0 auto;
    }

    /* Pool/Level/Team# keep an obvious affordance even without a box (the +/- buttons), but a
       bare Buddy/Conflict text input would have no visible boundary at all -- give the input
       itself a light outline so it still reads as a fillable field. */
    .roster-cards--compact .card-text-field input {
        border: 1px solid #E2E0DB;
        border-radius: 6px;
        padding: 3px 6px;
    }

    .roster-cards--compact .weight-cell {
        width: 84px;
    }

    /* The 32px steppers were sized for the wider expanded-card layout; at 84px they'd overflow
       into the next field, so this variant gets its own smaller buttons instead. */
    .roster-cards--compact .stepper-btn {
        width: 24px;
        height: 24px;
    }

    .roster-cards--compact .weight-input {
        font-size: 14px;
        width: 22px;
    }

    .roster-cards--compact .card-text-field {
        flex: 1 1 130px;
        min-width: 130px;
        max-width: 260px;
    }
}


/* ============================================================================
   LLLineup roster surface  (R12 / decision D2)
   ============================================================================
   Structure only. Colours live in LLLineup.html's own <style>, which points these
   rules at the same token names RandomTeamGenerator uses -- light values only, no dark
   theme on this page. Everything is scoped under .roster-cards--lll so it cannot reach
   RTG, whose cards keep using .roster-cards--compact above.

   The weights are a players x positions matrix, and a matrix has two readings: down a
   column ("who are my catchers?") and across a row ("where can this kid play?"). The
   layout serves both at every width:

     <820px    -- name and status plus exactly ONE weight column, chosen by the position
                  tabs in the sticky roster action bar. The down-a-column reading, on a
                  phone. Expanding a player shows all six of theirs -- the across-a-row one.
     820px+    -- the full inline row, all six weights under a sticky column header. Both
                  readings at once. Tight at this step: every column is at its minimum.
     1040px+   -- the same row with the weight controls at RandomTeamGenerator's density
                  (84px cells, 24px steppers). This is the preferred display.

   TWO INVARIANTS. Both are load-bearing and both were asked for explicitly:

   1. THE WEIGHT COLUMNS ARE THE LAST DATA COLUMNS, at every width, with only the 40px row
      action after them. So the element order along a roster row never changes between the
      narrow and wide views, and the weight column sits at the same offset from the right
      edge in both. MVP and Group therefore come BEFORE the weights on wide (and live in
      the expanded card on narrow), and the delete button comes after them.
   2. On narrow, THE NAME INPUT IS THE ONLY FLEXIBLE ITEM in the row. Every control after
      it is fixed-width, which is the only reason the single weight cell lands at the same
      x on every card and the column is actually scannable. Don't give any trailing control
      a flexible width.
   -------------------------------------------------------------------------- */

/* --- Narrow (default): one weight column, chosen by the tabs in the action bar --- */

.roster-cards--lll .roster-card {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    /* 6px, not 8: at 360px the row is name + weight + RSVP + No-P + chevron, and the four
       gaps between them are competing directly with the name field. */
    gap: 6px;
    padding: 7px 6px 7px 10px;
}

/* display:contents flattens the card's wrappers so the one visible weight cell can sit in
   the top row next to the name, without moving any node in the DOM (every id survives). */
.roster-cards--lll .roster-card-top,
.roster-cards--lll .roster-card-details,
.roster-cards--lll .roster-weights-grid {
    display: contents;
}

/* Collapsed, only the picked weight is wanted; the section furniture and the per-player
   fields (MVP, Group, delete) belong to the expanded card. */
.roster-cards--lll .roster-weights-label,
.roster-cards--lll .roster-card-divider,
.roster-cards--lll .roster-card-meta {
    display: none;
}

.roster-cards--lll .weight-cell {
    display: none;
}

.roster-cards--lll[data-weightcol="P"] .weight-cell[data-pos="P"],
.roster-cards--lll[data-weightcol="C"] .weight-cell[data-pos="C"],
.roster-cards--lll[data-weightcol="1B"] .weight-cell[data-pos="1B"],
.roster-cards--lll[data-weightcol="2B"] .weight-cell[data-pos="2B"],
.roster-cards--lll[data-weightcol="3B"] .weight-cell[data-pos="3B"],
.roster-cards--lll[data-weightcol="SS"] .weight-cell[data-pos="SS"] {
    display: flex;
}

/* The inline cell is a row, not a labelled box -- the tabs in the action bar are its label,
   so repeating it on every card would be noise in the tightest space on the page. */
.roster-cards--lll .roster-card:not(.expanded) .weight-cell {
    flex: 0 0 auto;
    flex-direction: row;
    align-items: center;
    gap: 0;
    padding: 3px;
    border-radius: 8px;
}

.roster-cards--lll .roster-card:not(.expanded) .weight-cell-label {
    display: none;
}

.roster-cards--lll .roster-card:not(.expanded) .stepper-btn {
    width: 24px;
    height: 24px;
}

.roster-cards--lll .roster-card:not(.expanded) .weight-input {
    width: 20px;
    font-size: 15px;
}

.roster-cards--lll .roster-card:not(.expanded) .pitcher-cell.ineligible .pitcher-ineligible-note {
    height: 24px;
    width: 68px;
}

/* Invariant 1, narrow half: weights last, then the row action. */
.roster-cards--lll .roster-name-input { order: 1; }
.roster-cards--lll toggle-element { order: 2; }
.roster-cards--lll .flag-toggle { order: 3; }
.roster-cards--lll .weight-cell { order: 4; }
.roster-cards--lll .card-expand-toggle { order: 5; }

/* Ineligible-to-pitch is a property of the Pitcher column, so on narrow it travels with it:
   shown when the Pitcher weights are up, or when the card is open and every weight is
   visible. Measured, this is what buys the name field back -- 86px to 138px at 390px wide.
   The flag being *set* is never hidden by this: .ineligible-flagged outlines the whole card. */
.roster-cards--lll .flag-toggle {
    display: none;
}

.roster-cards--lll[data-weightcol="P"] .flag-toggle,
.roster-cards--lll .roster-card.expanded .flag-toggle {
    display: flex;
}

/* A 2px border on the flagged card would push its contents 1px in, and a column you scan
   down has to line up exactly. Same look, drawn inside the box instead. */
.roster-cards--lll .roster-card.ineligible-flagged {
    border: 1px solid;
    box-shadow: inset 0 0 0 1px currentColor;
}

/* --- Narrow, expanded: the existing card, restored exactly ---
   The details block un-flattens, so the same six cells that were feeding the top row now
   lay out as the card's own grid. One cell per position means no duplication to reconcile:
   the picked weight simply moves from the row into the card. */
.roster-cards--lll .roster-card.expanded .roster-card-details {
    display: block;
    flex: 1 0 100%;
    order: 6;
    padding-top: 10px;
}

.roster-cards--lll .roster-card.expanded .roster-weights-grid {
    display: grid;
}

.roster-cards--lll .roster-card.expanded .roster-weights-label,
.roster-cards--lll .roster-card.expanded .roster-card-divider {
    display: block;
}

.roster-cards--lll .roster-card.expanded .roster-card-meta {
    display: flex;
}

.roster-cards--lll .roster-card.expanded .weight-cell {
    display: flex;
}

/* --- The position tabs, and the column header ---
   The tabs live in the roster action bar, which is sticky on narrow, so the control that
   says which column you are looking at stays on screen while you scan the column. The
   header row replaces them at >=820px, where all six columns are labelled at once. */

/* The row is left-arrow / tabs / right-arrow, and it sits at the RIGHT of the action bar,
   over the weight column it labels -- the column is pinned to the right edge of every card
   (see the invariants above), so anything describing it belongs over there too.

   The extra right padding is what lines the group's right edge up with the column's rather
   than with the row action beside it: the column is inset from the card's edge by the action
   slot (44px), its gap (6px), and the card's own right padding and border (7px).

   Two measured thresholds, both arithmetic -- every width in this row is fixed and the
   position codes are 1-2 characters. The row gets `viewport - 18`px, the two arrows and six
   tabs need 300px, the label adds 48px, and the offset adds 57px:

     | contents                  | needs | fits from |
     | tabs only                 | 300px | 318px     |
     | tabs + offset             | 357px | 375px     |
     | tabs + offset + label     | 405px | 423px     |

   So the offset applies from 400px and the label from 460px, each with real slack rather
   than fitting at the edge. Below 400px the group is nearly the whole row anyway, so it
   still covers the column -- it just cannot also be inset without pushing off the edge, and
   shrinking the arrows to buy 57px would take them below a usable tap target. */
.roster-col-row {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 4px;
}

@media (min-width: 400px) {
    .roster-col-row {
        padding-right: calc(44px + 6px + 7px);
    }
}

/* A compact group, not a stretched one. `space-between` with `flex: 1` spread the six tabs
   across the whole row, which reads as six scattered buttons at 768px even though it looks
   right at 390px where the row is nearly full. They shrink but never grow, so the group is
   the same shape at every width and the two arrows stay next to the tabs they step. */
.roster-col-tabs {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
    flex: 0 1 auto;
    min-width: 0;
}

.roster-col-tabs-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    flex: 0 0 auto;
}

/* Every width in this row is fixed and the position codes are 1-2 characters, so the
   budget is arithmetic rather than a guess. Measured: the row gets `viewport - 18`px, the
   two arrows and six tabs need 292px, and the "Weight" label adds 48px on top.
     - without the label:  292px, so it fits from a 310px viewport
     - with the label:     340px, so it fits from a 358px viewport
   Hence the label drops below 380px (which keeps it on a 390px phone, the commonest size,
   with real slack) and the tabs alone carry the row on anything narrower. */
.roster-col-tab {
    min-width: 34px;
    height: 34px;
    padding: 0 4px;
    border: 1px solid;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    flex: 0 1 auto;
}

.roster-col-step {
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid;
    border-radius: 8px;
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    flex: 0 0 auto;
}

/* The label is the first thing to go when the row is short: the tabs are position codes,
   each in its own position colour with stepper arrows beside them, and expanding any card
   spells the six positions out in full. It yields to the right-hand offset above, because
   sitting over the column it labels does more for the row than the word does. Wrapping the
   tabs onto a second line instead would add 38px to a bar that is pinned to the top of the
   screen while you scroll. */
@media (max-width: 459px) {
    .roster-col-tabs-label {
        display: none;
    }
}

.roster-col-header {
    display: none;
}

/* --- 820px+: the full inline row (tight) --- */
@media (min-width: 820px) {
    :root {
        /* One template, shared by the header row and every card, so the columns cannot
           drift apart. It lives on :root because the header row is a *sibling* of the
           cards container, not a descendant -- a variable set on .roster-cards--lll would
           not reach it. --lll-w and --lll-gap are the only things the 1040px step changes.

           Width budget at 820px, all fixed: name 88 + RSVP 64 + No-P 44 + MVP 44 +
           Group 44 + six weights (66 each = 396) + action 36 = 716, plus eleven 4px gaps
           = 760. What is actually available is narrower than the viewport by more than it
           looks: 8px body margin each side, then the roster panel's own border and padding,
           then the card's border and padding, so a 820px viewport leaves ~768px. An earlier
           version of this budget forgot the panel and overflowed the card by 5px, which is
           why the numbers are spelled out here.

           A weight cell's floor is its stepper plus its box: 22 + 16 + 22 of controls, then
           4px of padding and 2px of border = 66px. Keeping the box (so the control looks
           the same either side of this breakpoint) costs 4px per column against the bare
           version, which is why the name minimum and the gaps came down to pay for it.
           A long name cannot eat into the slack -- the name column is the only flexible
           one, and an over-long name scrolls inside its own input. */
        --lll-w: 66px;
        --lll-gap: 4px;
        --lll-roster-cols: minmax(88px, 1fr) 64px 44px 44px 44px repeat(6, var(--lll-w)) 36px;
    }

    .roster-cards--lll .roster-card {
        display: grid;
        grid-template-columns: var(--lll-roster-cols);
        align-items: center;
        gap: var(--lll-gap);
        flex-wrap: nowrap;
    }

    /* Invariant 1, wide half: MVP and Group move ahead of the weights so the weights stay
       the last data columns, and the delete button takes the trailing action slot the
       chevron holds on narrow. `order` applies to grid items as well as flex items, so
       without these the narrow row order above would reshuffle the columns out of the
       header's order -- MVP and Group landed in columns 1 and 2, measured. */
    .roster-cards--lll .roster-name-input { order: 1; }
    .roster-cards--lll toggle-element { order: 2; }
    .roster-cards--lll .flag-toggle { order: 3; }
    .roster-cards--lll .mvp-label { order: 4; }
    .roster-cards--lll .group-select { order: 5; }
    .roster-cards--lll .weight-cell,
    .roster-cards--lll .roster-card:not(.expanded) .weight-cell { order: 6; }
    .roster-cards--lll .remove-player-btn { order: 7; }

    /* Nothing collapses at this width -- every field is on screen -- so the expand
       affordance and the in-card section furniture go away. */
    .roster-cards--lll .card-expand-toggle,
    .roster-cards--lll .roster-weights-label,
    .roster-cards--lll .roster-card-divider {
        display: none;
    }

    .roster-cards--lll .roster-card-meta {
        display: contents;
    }

    /* All six columns show regardless of the tabs, and regardless of expanded state --
       the tabs are a narrow-only control. */
    .roster-cards--lll .weight-cell,
    .roster-cards--lll .roster-card.expanded .weight-cell {
        display: flex;
    }

    .roster-cards--lll .roster-card.expanded .roster-card-details,
    .roster-cards--lll .roster-card-details,
    .roster-cards--lll .roster-card.expanded .roster-weights-grid {
        display: contents;
    }

    /* The narrow rule hides this outside the Pitcher column; at this width there is no
       "current column" and every control is on screen. */
    .roster-cards--lll .flag-toggle {
        display: flex;
    }

    /* Same boxed cell as the narrow layout -- background, border and radius all inherited
       rather than stripped, so a weight control looks the same either side of this
       breakpoint. Only the label goes: the header row above carries it, so keeping it would
       repeat the same six words on every player. */
    .roster-cards--lll .weight-cell,
    .roster-cards--lll .roster-card:not(.expanded) .weight-cell {
        flex-direction: row;
        align-items: center;
        justify-content: center;
        gap: 0;
        padding: 2px;
        border-radius: 8px;
    }

    .roster-cards--lll .weight-cell-label {
        display: none;
    }

    /* 22px rather than RTG's 24: the six cells are 62px at this step and a 24px pair would
       need 66px of content before the value had any room. The style is identical; only the
       size steps down, the same way RTG's own buttons are 32px in a card and 24px in a row. */
    .roster-cards--lll .stepper-btn,
    .roster-cards--lll .roster-card:not(.expanded) .stepper-btn {
        width: 22px;
        height: 22px;
    }

    .roster-cards--lll .weight-input,
    .roster-cards--lll .roster-card:not(.expanded) .weight-input {
        width: 16px;
        font-size: 14px;
    }

    .roster-cards--lll .pitcher-cell.ineligible .pitcher-ineligible-note,
    .roster-cards--lll .roster-card:not(.expanded) .pitcher-cell.ineligible .pitcher-ineligible-note {
        height: 20px;
        width: auto;
    }

    .roster-cards--lll .mvp-label {
        justify-content: center;
        gap: 0;
        font-size: 0;      /* the header row says MVP; the checkbox is the control */
    }

    .roster-cards--lll .group-select {
        width: 100%;
        margin: 0;
    }

    .roster-cards--lll .remove-player-btn {
        margin: 0 auto;
    }

    /* Sticky, because scanning a column down a 15-player roster is the whole point of this
       layout and the labels have to stay reachable while you do it. */
    .roster-col-header {
        display: grid;
        grid-template-columns: var(--lll-roster-cols);
        align-items: end;
        gap: var(--lll-gap);
        position: sticky;
        top: 0;
        z-index: 3;
        /* MUST match .roster-card's horizontal padding and border, or the shared grid
           template resolves to the same widths starting from a different x and every
           column label sits beside its column instead of over it. Vertical padding is
           free to differ. */
        padding: 8px 6px 6px 10px;
        border: 1px solid transparent;
        border-bottom: 1px solid;
    }

    .roster-col-header span {
        font-size: 10px;
        font-weight: 700;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        text-align: center;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .roster-col-header span:first-child {
        text-align: left;
    }
}

/* --- 1040px+: the same row, with the weight controls given real room ---
   This is the preferred display. Only the cell width, the stepper size and the column gap
   change: the template, the order and the header all read the same variables, so nothing
   can fall out of alignment here. Budget: 96 + 64 + 44 + 44 + 44 + 6x84 + 36 = 832, plus
   eleven 8px gaps = 920 against the 972px a 1040px viewport leaves, so the name column
   takes the remaining ~50px and grows from there. */
@media (min-width: 1040px) {
    :root {
        --lll-w: 84px;
        --lll-gap: 8px;
    }

    .roster-cards--lll .stepper-btn,
    .roster-cards--lll .roster-card:not(.expanded) .stepper-btn {
        width: 24px;
        height: 24px;
    }

    .roster-cards--lll .weight-input,
    .roster-cards--lll .roster-card:not(.expanded) .weight-input {
        width: 26px;
        font-size: 16px;
    }

    /* 24 + 26 + 24 of controls, 6px of padding and 2px of border = 82px in an 84px column. */
    .roster-cards--lll .weight-cell,
    .roster-cards--lll .roster-card:not(.expanded) .weight-cell {
        gap: 0;
        padding: 3px;
    }

    .roster-cards--lll .pitcher-cell.ineligible .pitcher-ineligible-note,
    .roster-cards--lll .roster-card:not(.expanded) .pitcher-cell.ineligible .pitcher-ineligible-note {
        height: 24px;
    }

    .roster-cards--lll .roster-card {
        padding: 9px 10px 9px 14px;
    }

    .roster-col-header {
        padding: 10px 10px 6px 14px;
    }
}
