:root {
  --away-color: #1b5fad;
  --away-dark: #0d3868;
  --away-light: #e7f0fb;
  --home-color: #b3181f;
  --home-dark: #711015;
  --home-light: #fbe9e9;
  --ink: #1c1c1c;
  --paper: #fbfaf6;
  --line: #cfc9ba;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Georgia", "Times New Roman", serif;
  background: #e9e5d8;
  color: var(--ink);
}

/* ---------- start screen ---------- */
.start-screen {
  /* 560 rather than 480 since the club-mark sheet went on this page
     (2026-09-25) -- at 480 the sixteen marks were too small to tell
     apart, which defeats the point of showing them. */
  max-width: 560px;
  margin: 8vh auto 6vh;
  text-align: center;
  background: var(--paper);
  padding: 40px;
  border-radius: 6px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.15);
}
.start-screen h1 { margin-top: 0; }
.start-screen button {
  font-size: 1.1em;
  padding: 10px 28px;
  margin-top: 18px;
  background: var(--home-color);
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.start-screen button:disabled { background: #999; cursor: default; }
.speed-row { margin-top: 20px; font-family: sans-serif; font-size: 0.9em; }
.start-hint { margin-top: 18px; font-family: sans-serif; font-size: 0.85em; color: #666; }
/* The league crest and the sixteen club marks. The art is drawn on white
   and the page is cream, so it gets its own white card with a hairline
   and a soft shadow -- a poster on the wall, not a failed cutout. */
.start-art { margin: 28px 0 0; padding: 0; }
.start-art img {
  display: block; width: 100%; height: auto; max-width: 480px;
  margin: 0 auto; border-radius: 6px; background: white;
  border: 1px solid var(--line); box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
}
.start-art figcaption {
  margin-top: 10px; font-family: sans-serif; font-size: 0.78em;
  color: #8a8474; letter-spacing: 0.02em;
}

/* ---------- game screen ---------- */
/* User request 2026-09-15: use the full screen width left to right instead
   of capping out at 1100px and floating in a sea of background -- the side
   gutters below are just breathing room, not a content cap. A generous
   ceiling (2400px) is kept only so the layout doesn't stretch into
   comically long line lengths on an ultrawide/multi-monitor setup; any
   normal desktop window is well under that and gets the full width. */
.game-screen {
  max-width: 2400px;
  margin: 0 auto;
  padding: 10px 20px 24px;
}

#scoreboardBar {
  display: flex;
  align-items: stretch;
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
  font-family: "Arial Narrow", Arial, sans-serif;
}
/* Item #38: the whole top banner (#scoreboardBar) is squeezed to roughly
   half its previous height -- tighter block padding throughout, the
   mid-block laid out as a single ROW instead of a stacked column (which
   also satisfies "move the inning arrow to the left of B/S/Outs" below,
   since row order = left-to-right position), and every piece of text in
   the banner sized down just enough to still read clearly at the new
   height. */
.team-block {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 16px;
  color: white;
}
.team-block.away { background: linear-gradient(135deg, var(--away-color), var(--away-dark)); justify-content: flex-start; }
.team-block.home { background: linear-gradient(135deg, var(--home-dark), var(--home-color)); justify-content: flex-end; }
.team-name { font-size: 0.85em; font-weight: bold; letter-spacing: 0.03em; }
.team-score { font-size: 1.3em; font-weight: bold; font-variant-numeric: tabular-nums; }

.mid-block {
  background: #262421;
  color: #f2ede0;
  padding: 4px 16px;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-width: 150px;
}
/* The inning indicator (up/down arrow + inning number) sits to the LEFT
   of the B/S/Outs count-row -- DOM order plus row layout above is what
   places it there; this rule just sizes it down to match the shrunk bar. */
.inning-indicator { font-size: 1em; font-weight: bold; display: flex; align-items: center; gap: 5px; }
.half-arrow { color: #d8c98a; font-size: 0.8em; }
.count-row { display: flex; gap: 10px; }
.count-item { text-align: center; font-size: 0.65em; }
.count-item.outs .count-val { color: #ff8a80; }
.count-label { display: block; letter-spacing: 0.08em; opacity: 0.75; }
.count-val { display: block; font-size: 1em; font-weight: bold; font-variant-numeric: tabular-nums; }

#lineScoreWrap { margin-top: 10px; overflow-x: auto; }
#lineScoreTable {
  border-collapse: collapse;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-size: 0.85em;
  background: var(--paper);
  width: 100%;
}
#lineScoreTable td, #lineScoreTable th {
  border: 1px solid var(--line);
  padding: 3px 8px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
#lineScoreTable td.team-label { text-align: left; font-weight: bold; }
#lineScoreTable td.runs, #lineScoreTable td.hits, #lineScoreTable td.errors { font-weight: bold; background: #f1ede0; }

#pitcherBar {
  margin-top: 10px;
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 8px 14px;
  font-family: "Arial Narrow", Arial, sans-serif;
}
.pitcher-card { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.pitcher-label { font-size: 0.7em; text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.6; }
.pitcher-name { font-weight: bold; font-size: 1.05em; }
.pitcher-grade { font-size: 0.85em; color: #555; }
.pitcher-stats { font-size: 0.85em; color: #333; font-variant-numeric: tabular-nums; }
.matchup-note { margin-left: auto; font-size: 0.85em; color: #555; font-style: italic; }

/* Baserunners + Defense Column Used share one row now -- the user's own
   "shorten the baserunners field and add a Defense Column Used field"
   ruling. Baserunners keeps most of the width (it can wrap to more
   content); Defense Column Used is a small fixed-width readout beside it. */
#statusBarsRow {
  margin-top: 8px;
  display: flex;
  align-items: stretch;
  gap: 8px;
  flex-wrap: wrap;
}
#baserunnerBar {
  flex: 1 1 auto;
  min-width: 220px;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 6px 14px;
  font-family: "Arial Narrow", Arial, sans-serif;
  flex-wrap: wrap;
}
#defenseColumnUsedBox {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 7px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 6px 14px;
  font-family: "Arial Narrow", Arial, sans-serif;
  white-space: nowrap;
}
#defenseColumnUsedLabel {
  font-size: 0.7em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.6;
}
#defenseColumnUsedVal {
  font-weight: bold;
  font-size: 0.9em;
  color: var(--ink);
}
#baserunnerLabel {
  font-size: 0.7em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.6;
}
.base-slot {
  display: flex;
  align-items: center;
  gap: 5px;
  min-height: 22px;
}
.base-slot-tag {
  font-size: 0.7em;
  font-weight: bold;
  color: #888;
}
.base-slot-body {
  font-size: 0.85em;
  color: #999;
}
.base-slot.occupied .base-slot-body {
  cursor: pointer;
  color: var(--ink);
  font-weight: bold;
  padding: 2px 8px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: #efeade;
}
.base-slot.occupied .base-slot-body:hover { background: #e3ddc9; }
.base-slot.occupied .base-slot-body .tier-tag {
  margin-left: 5px;
  font-size: 0.85em;
  font-weight: bold;
  letter-spacing: 0.03em;
}
.base-slot .tier-tag.tier-fast { color: var(--away-color); }
.base-slot .tier-tag.tier-slow { color: var(--home-color); }
.base-slot .tier-tag.tier-average { color: #777; }
/* 2-state speed toggle: "Playing Safe" is a manager instruction in
   force RIGHT NOW, not a speed tier -- given its own distinct look
   (a small pill, not just a colored letter) so it never reads as if the
   runner just happens to naturally be slow. */
.base-slot .tier-tag.tier-safe {
  color: #8a5a00;
  background: #f4ecc8;
  border: 1px solid #8a7a2a;
  border-radius: 8px;
  padding: 0 5px;
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.base-slot.occupied .base-slot-body.overridden {
  border-color: #8a7a2a;
  background: #f4ecc8;
}
.grid-tabs {
  margin-top: 14px;
  display: flex;
  align-items: center;
  gap: 4px;
}
.grid-tab {
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  padding: 7px 18px;
  border: 1px solid var(--line);
  border-bottom: none;
  border-radius: 6px 6px 0 0;
  background: #d9d4c4;
  color: #555;
  cursor: pointer;
}
.grid-tab.away-tab.active { background: var(--away-color); color: white; border-color: var(--away-dark); }
.grid-tab.home-tab.active { background: var(--home-color); color: white; border-color: var(--home-dark); }
.autofollow { margin-left: 16px; font-family: sans-serif; font-size: 0.8em; color: #555; white-space: nowrap; }

#boardResultPanel {
  flex: 1;
  min-width: 220px;
  margin-left: 14px;
  /* Was 4px top/bottom for a tier line stacked over the result. One
     larger line now, centred in the same band. */
  padding: 8px 14px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #f4f1e8;
  font-family: "Arial Narrow", Arial, sans-serif;
}
/* The .board-result-tier rule that used to live here is gone (2026-09-22):
   the small red "FIELDING ONE" line above the result duplicated the big
   DEFENSE COLUMN USED block to its right. Its space goes to the result
   text, which is now double size at the user's request. */
.board-result-row.board-result-text {
  font-size: 1.8em;          /* was 0.9em */
  font-weight: 600;
  color: #222;
  line-height: 1.2;
}
.board-result-row.board-result-ball {
  margin-top: 4px;
  font-size: 0.95em;
  font-weight: bold;
  color: var(--home-color);
}

#gridArea {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  position: relative;
}

/* Zero-result pop: a big "0" flashes in the center of the scorebook and
   quickly fades -- see app.js's triggerZeroPop(), fired off the dice_roll
   event's own zero_reroll flag (task #4's "reroll into column 2" beat).
   Hidden/inert by default; app.js toggles the "pop" class to (re)play the
   animation, including for repeated zeros in a row. */
#zeroPopOverlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 40;
}
#zeroPopDigit {
  font-family: "IBM Plex Mono", monospace;
  font-weight: 700;
  font-size: 440px;
  line-height: 1;
  color: var(--home-color, #b3181f);
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.35);
  opacity: 0;
  transform: scale(0.4);
}
/* User request: 4x the size (110px -> 440px above) and the fade 4x slower
   (700ms -> 2800ms) -- same keyframe shape, just stretched out. */
#zeroPopOverlay.pop #zeroPopDigit { animation: zeroPop 2800ms ease-out; }
@keyframes zeroPop {
  0%   { opacity: 0;   transform: scale(0.4); }
  22%  { opacity: 1;   transform: scale(1.18); }
  40%  { opacity: 1;   transform: scale(1); }
  100% { opacity: 0;   transform: scale(1.06); }
}
@media (prefers-reduced-motion: reduce) {
  #zeroPopOverlay.pop #zeroPopDigit { animation: none; opacity: 0.9; }
}
#gridWrap {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  border: 2px solid var(--line);
  background: var(--paper);
}

#diceSidebar {
  width: 150px;
  flex-shrink: 0;
  border: 2px solid var(--line);
  background: var(--paper);
  border-radius: 4px;
  padding: 12px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  font-family: "Arial Narrow", Arial, sans-serif;
}
#diceSidebarLabel {
  font-size: 0.75em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #777;
}
#dicePair {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 10px;
  min-height: 90px;
}
.die-face {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  border-radius: 12%;
  box-shadow: 0 2px 5px rgba(0,0,0,0.35), inset 0 0 0 1px rgba(0,0,0,0.15);
}
.die-large { width: 84px; height: 84px; padding: 9px; }
.die-small { width: 52px; height: 52px; padding: 6px; }
.die-red { background: linear-gradient(155deg, #d43a3a, var(--home-color)); }
.die-white { background: linear-gradient(155deg, #ffffff, #e7e4da); }
.die-pip {
  border-radius: 50%;
  width: 60%;
  height: 60%;
  align-self: center;
  justify-self: center;
  visibility: hidden;
}
.die-pip.on { visibility: visible; }
.die-red .die-pip.on { background: #fff; }
.die-white .die-pip.on { background: #1c1c1c; }
#diceCode {
  font-variant-numeric: tabular-nums;
  font-size: 0.85em;
  color: #888;
}
.waiting-indicator {
  margin-top: 4px;
  padding: 5px 10px;
  border-radius: 12px;
  background: var(--away-color);
  color: white;
  font-size: 0.72em;
  font-weight: bold;
  letter-spacing: 0.02em;
  text-align: center;
  animation: pulse-wait 1.4s ease-in-out infinite;
}
@keyframes pulse-wait {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* ---------- hitter card panel (left of the grid) ---------- */
/* Task #43: widened and stretched to the same height as the batting grid
   next to it (align-self: stretch, against #gridArea's own flex row --
   the grid is always at least tall enough for 9 batter rows, so this
   panel's bottom edge now lines up with the #9 hitter's row instead of
   stopping wherever the card's own content happened to end), with the
   print-card's own typography scaled up to actually use the extra room
   for legibility rather than just leaving blank space below it. */
#hitterCardPanel {
  width: 320px;
  flex-shrink: 0;
  align-self: stretch;
  border: 2px solid var(--line);
  background: var(--paper);
  border-radius: 4px;
  padding: 12px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  font-family: "Arial Narrow", Arial, sans-serif;
}
#hitterCardLabel {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 0.75em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #777;
}
.hitter-card-label-text { flex: 1; text-align: center; }
/* Opposing team's real Infield/Team Fielding-column tags, far left/right
   of the "At the Plate" label -- see index.html's own comment. Small,
   pill-shaped, and quiet (this is reference info while the OTHER team
   bats, not an active control) until a real column comes in, at which
   point the digit reads clearly against the tag's own background. */
.hitter-card-defense-tag {
  flex: 0 0 auto;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  font-size: 0.82em;
  letter-spacing: 0.03em;
  color: #6b5f47;
  background: #efe9d8;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 2px 5px;
  white-space: nowrap;
}
/* Name is shown inside .print-card itself once a real card loads (see
   below); this stays only as the fallback label when a hitter has no
   print_card (outside the 108-player ratings/bio pool). */
#hitterCardName {
  font-weight: bold;
  font-size: 0.95em;
  text-align: center;
  color: var(--ink);
  min-height: 1.3em;
}
#hitterCardName.has-print-card { display: none; }
#hitterCardGrids {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  flex: 1;
  min-height: 0;
}

/* Fallback plain dice grid -- used only for a hitter with no print_card
   (outside the 108-player ratings/bio pool this project's real cards are
   built from); see roster_builder.build_print_card. */
.hitter-card-col { width: 100%; }
.hitter-card-col-label {
  font-size: 0.68em;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #999;
  text-align: center;
  margin-bottom: 3px;
}
.hitter-card-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 2px;
  background: var(--line);
  border: 1px solid var(--line);
}
.hitter-card-cell {
  background: #f4f1e8;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.68em;
  font-variant-numeric: tabular-nums;
  color: #555;
  position: relative;
  transition: background 0.15s, color 0.15s;
}
.hitter-card-cell.starred::after {
  content: "*";
  position: absolute;
  top: 0px;
  right: 2px;
  font-size: 0.9em;
  color: var(--home-color);
}
.hitter-card-cell.active {
  background: var(--card-accent, #c98a2b);
  color: #fff;
  font-weight: bold;
  box-shadow: inset 0 0 0 2px #7a5316;
}
.hitter-card-cell.reroll-slot {
  color: #aaa;
  font-style: italic;
}

/* ---------- the REAL printed card (matches the physical hitter cards
   this project prints -- aml_prototype/print/cards_batch_final.html --
   not a re-invented stand-in). Sized to fit this side panel rather than
   the physical 2.5in x 3.5in card stock. ---------- */
.print-card {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: #F7F2E5;
  color: #3B2620;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 10px 9px 24px;
  font-family: "Public Sans", "Arial Narrow", Arial, sans-serif;
}
.print-card .pc-flag {
  position: absolute;
  top: 5px;
  right: 7px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 7px;
  font-weight: 700;
  color: #B8791A;
}
.print-card .pc-ratings {
  text-align: center;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #7A2331;
  margin: 0;
}
.print-card .pc-fullname {
  text-align: center;
  font-family: "Bitter", Georgia, serif;
  font-style: italic;
  font-size: 12px;
  color: #7A2331;
  opacity: 0.85;
  margin: 2px 0;
}
.print-card .pc-name {
  text-align: center;
  font-family: "Bitter", Georgia, serif;
  font-weight: 700;
  font-size: 21px;
  letter-spacing: 0.01em;
  color: #7A2331;
  margin: 0 0 2px;
}
.print-card .pc-position {
  text-align: center;
  font-family: "Bitter", Georgia, serif;
  font-weight: 600;
  font-size: 12px;
  color: #7A2331;
  margin: 0 0 8px;
}
.print-card table.pc-stats {
  width: 100%;
  border-collapse: collapse;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 10.5px;
  margin-bottom: 8px;
}
.print-card table.pc-stats th, .print-card table.pc-stats td {
  padding: 2px 3px;
  text-align: center;
}
.print-card table.pc-stats th { color: #17130F; font-weight: 600; }
.print-card table.pc-stats td { color: #1B5E3A; font-weight: 600; }
.print-card .pc-bio {
  text-align: center;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10.5px;
  line-height: 1.6;
  margin-bottom: 9px;
}
.print-card .pc-bio .pc-bio-label { color: #17130F; }
.print-card .pc-bio .pc-bio-value { color: #7A2331; }
/* The dice grid is the part actually cross-checked roll after roll, so it
   gets first claim on whatever extra vertical room the taller panel (task
   #43) provides -- flex: 1 plus even 1fr rows lets its 12 rows spread out
   to fill the panel instead of bunching at the top with dead space below. */
.print-card .pc-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(12, 1fr);
  gap: 0 8px;
  flex: 1;
  min-height: 0;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
}
.print-card .pc-cell {
  display: flex;
  align-items: center;
  padding: 2px 2px;
  white-space: nowrap;
  border-radius: 2px;
  transition: background 0.15s, color 0.15s;
}
.print-card .pc-cell .pc-code { color: #17130F; width: 2.3em; text-align: left; }
.print-card .pc-cell .pc-v1 { color: #7A2331; width: 1.7em; text-align: right; }
.print-card .pc-cell .pc-sep { color: #7A2331; width: 0.9em; text-align: center; }
.print-card .pc-cell .pc-v2 { color: #7A2331; text-align: left; }
.print-card .pc-cell.starred .pc-v1,
.print-card .pc-cell.starred .pc-v2 { position: relative; }
.print-card .pc-cell.starred .pc-v1::after,
.print-card .pc-cell.starred.pc-single .pc-v1::after {
  content: "*";
  color: var(--home-color);
}
.print-card .pc-cell.active {
  background: var(--card-accent, #c98a2b);
}
.print-card .pc-cell.active .pc-code,
.print-card .pc-cell.active .pc-v1,
.print-card .pc-cell.active .pc-sep,
.print-card .pc-cell.active .pc-v2 {
  color: #fff;
  font-weight: bold;
}
.print-card .pc-cell.reroll .pc-v1, .print-card .pc-cell.reroll .pc-v2 {
  font-style: italic;
  opacity: 0.7;
}
.print-card .pc-j-rating {
  position: absolute;
  bottom: 5px;
  left: 11px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 9.5px;
  font-weight: 600;
  color: #7A2331;
}

#pitchingChangeBtn {
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  padding: 8px 16px;
  border: 1px solid var(--home-dark);
  border-radius: 4px;
  background: var(--home-color);
  color: white;
  cursor: pointer;
  font-size: 0.9em;
}
#pitchingChangeBtn:hover { background: var(--home-dark); }
/* Item #38: now lives in #diceSidebar, right below the dice result
   (#diceCode), instead of down in the footer next to the play-by-play
   log -- sized to fit the sidebar's own 150px width. */
#diceSidebar #pitchingChangeBtn {
  width: 100%;
  padding: 6px 4px;
  font-size: 0.72em;
  letter-spacing: 0.01em;
}
/* Task #47: Defense Close/Deep infield-alignment toggle, right below the
   Pitching Change button. Dimmed/inert until a runner is actually on 3rd
   (see setDefensePositionUI in app.js, driven by the backend's own
   defense_selectable flag) -- ".active" is only added while that's true,
   so the control visibly "wakes up" the moment it matters instead of
   sitting there clickable-looking for a situation that can't happen. */
#defenseToggleWrap {
  width: 100%;
  margin-top: 6px;
  padding: 5px 6px;
  border: 1px solid var(--line, #c9c1ac);
  border-radius: 4px;
  background: #efe9d8;
  opacity: 0.5;
  transition: opacity 0.15s ease, background 0.15s ease;
}
#defenseToggleWrap.active {
  opacity: 1;
  background: #fff6df;
  border-color: var(--home-dark);
}
#defenseToggleLabel {
  display: block;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  font-size: 0.68em;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #6b5f47;
  margin-bottom: 2px;
}
#defensePositionSelect {
  width: 100%;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  font-size: 0.78em;
  padding: 3px 4px;
  border: 1px solid var(--home-dark);
  border-radius: 3px;
  background: white;
  color: #17130F;
}
#defensePositionSelect:disabled {
  cursor: not-allowed;
  color: #a89f8c;
  border-color: #c9c1ac;
}
/* Manual board-result validation override -- deliberately styled to read
   as a debugging tool (dashed amber border), not a normal game control,
   since the user's own plan is to hide it again once bugs are worked
   out. .active (a value is currently set) makes that state hard to miss,
   since it's STICKY -- forcing every roll until cleared. */
#manualOverrideWrap {
  width: 100%;
  margin-top: 6px;
  padding: 5px 6px;
  border: 1px dashed #a8823a;
  border-radius: 4px;
  background: #f7efdd;
}
#manualOverrideWrap.active {
  background: #fde9c8;
  border-style: solid;
}
#manualOverrideLabel {
  display: flex;
  align-items: center;
  gap: 5px;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  font-size: 0.68em;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #8a6a1a;
  margin-bottom: 3px;
  cursor: pointer;
}
#manualOverrideCheckbox {
  width: 13px;
  height: 13px;
  accent-color: #a8823a;
  cursor: pointer;
}
#manualOverrideSelect {
  width: 100%;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  font-size: 0.78em;
  padding: 3px 4px;
  border: 1px solid #a8823a;
  border-radius: 3px;
  background: white;
  box-sizing: border-box;
}
#manualOverrideSelect:disabled {
  cursor: not-allowed;
  color: #b3a172;
  background: #f2ead6;
}
/* While the checkbox is checked -- "editing" mode, the global keystroke
   listener stands down (see app.js' manualOverrideEditing) -- give the
   whole box a visibly different, "you're in here now" outline so it's
   obvious pressing keys won't roll dice right now. */
#manualOverrideWrap.editing {
  border-style: solid;
  border-color: #6b5100;
  box-shadow: 0 0 0 2px rgba(168, 130, 58, 0.35);
}
/* A forced roll has no real dice draw -- both faces sit empty rather than
   a misleading "1-1" (see updateDice's own comment). */
#dicePair.forced-roll .die-face { visibility: hidden; }
/* User request 2026-09-15: now that .game-screen uses the full screen
   width, table-layout switches from auto to fixed with explicit column
   widths (below) instead of the browser's auto-layout algorithm, which was
   dumping all the newly-freed width into the Batter name column (it grew
   past 650px) rather than into the inning columns where it actually helps
   readability. Fixed layout + explicit widths puts the reclaimed space
   exactly where it was asked for: bigger, easier-to-read result cells. */
#scoreGrid {
  border-collapse: collapse;
  table-layout: fixed;
  width: auto;
  font-family: "Arial Narrow", Arial, sans-serif;
}
#scoreGrid th, #scoreGrid td {
  border: 1px solid var(--line);
}
#scoreGrid th {
  background: #efe9d8;
  font-size: 0.86em;
  padding: 4px 2px;
  text-align: center;
  /* User request 2026-09-15: back up from the too-small 40px (which made
     results harder to read) to 64px, sized against the same full-width
     layout that now shows DEFAULT_INNINGS_SHOWN (13) columns by default --
     see app.js. Paired with the matching .pa-cell resize below. */
  width: 64px;
}
/* Item #34: a batting-around extension column (2nd+ trip through the
   order in the same inning) gets a slightly different header shade plus
   a heavier left border, so it visually reads as "continuation of the
   inning to its left" rather than a stray/mislabeled extra inning. */
#scoreGrid th.batting-around-col {
  background: #e3dcc4;
  border-left: 2px solid #b3a877;
}
#scoreGrid th.batting-around-col sup {
  font-size: 0.75em;
  color: #7a6f45;
}
#scoreGrid th.corner {
  text-align: left;
  padding-left: 7px;
  width: 200px;
  position: sticky;
  left: 0;
  background: #efe9d8;
  z-index: 2;
}
/* User request 2026-09-15: fixed-width (200px, sized against the longest
   realistic MLB name -- "CROW-ARMSTRONG, Pete" measures ~180px) rather
   than the old unbounded min-width, which is what let this column soak up
   the entire freed-up screen width instead of the inning columns (see
   #scoreGrid's own comment above). The rare name too long even for 200px
   (e.g. "ENCARNACION-STRAND, Christian") ellipsizes instead of blowing out
   the layout or clipping mid-letter -- same trade real paper scorebooks
   make with long names. */
.batter-cell {
  padding: 4px 7px;
  font-size: 0.8em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  position: sticky;
  left: 0;
  background: var(--paper);
  z-index: 1;
}
.batter-cell .slot-num { display: inline-block; width: 1.4em; color: #888; font-size: 0.85em; }
#scoreGrid.theme-away .batter-cell { border-left: 4px solid var(--away-color); }
#scoreGrid.theme-home .batter-cell { border-left: 4px solid var(--home-color); }

/* POS column -- each player's own assigned defensive position + rating,
   exactly as it appears on his printed card (see renderFullGrid). Sticky
   alongside the batter-name column (same pattern, offset by the corner
   header's own min-width) so it stays visible while scrolling through a
   long game's worth of inning columns. */
#scoreGrid th.pos-col, td.pos-col {
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.8em;
  color: #6a6454;
  text-align: center;
  white-space: nowrap;
  padding: 4px 6px;
  width: 56px;
}
td.pos-col {
  position: sticky;
  /* Must match th.corner's width (200px) above -- this is where the
     sticky POS column docks against the sticky Batter-name column. */
  left: 200px;
  background: var(--paper);
  z-index: 1;
}

/* Infield/Team Defense totals -- two rows appended right below batter #9
   (see appendDefenseTotalsRows), each labeling which real Fielding column
   (1/2/3) the total currently rates. */
.defense-totals-row td {
  font-family: "Arial Narrow", Arial, sans-serif;
  font-size: 0.78em;
  padding: 4px 7px;
  background: #efe9d8;
}
.defense-totals-label {
  position: sticky;
  left: 0;
  background: #efe9d8;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: #6b5f47;
  z-index: 1;
}
.defense-totals-value {
  font-variant-numeric: tabular-nums;
  font-weight: bold;
  text-align: left;
  color: var(--ink);
}
.defense-totals-row.team-defense td { border-top: none; }

/* User request 2026-09-15: back up to 64x58 (bigger than even the
   original 56x52) now that the full-width layout (see .game-screen) frees
   up enough room to make cells MORE readable, not less, while still
   showing DEFAULT_INNINGS_SHOWN (13) columns by default -- see app.js.
   The diamond glyph is an SVG scaled to 100% of .cellwrap, so it scales
   up cleanly with no redraw logic needed. */
.pa-cell {
  width: 64px;
  height: 58px;
  padding: 0;
  position: relative;
  vertical-align: top;
}
.pa-cell .cellwrap {
  position: relative;
  width: 100%;
  height: 100%;
}
.pa-cell svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.pa-cell .notation {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Sized back up alongside the larger 64x58 cell above -- bigger than
     the original 0.65em too, since there's now genuinely more room. */
  font-size: 0.72em;
  font-weight: bold;
  color: #222;
  font-family: Arial, sans-serif;
  pointer-events: none;
}
.pa-cell .out-badge {
  position: absolute;
  top: 2px;
  left: 3px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: #333;
  color: white;
  font-size: 9px;
  line-height: 13px;
  text-align: center;
  font-family: Arial, sans-serif;
}
.pa-cell .rbi-badge {
  position: absolute;
  bottom: 2px;
  right: 3px;
  font-size: 8px;
  font-weight: bold;
  color: #7a5b00;
  font-family: Arial, sans-serif;
}
.pa-cell.empty { background: repeating-linear-gradient(135deg, transparent, transparent 6px, #00000006 6px, #00000006 7px); }
/* Task #44/#57: marks whoever was up when the 3rd out was made -- instead
   of X-ing out every teammate below him who never got up this frame.
   Now drawn as a team-colored diagonal slash across the diamond glyph
   itself (see diamondSVG's own "lastBatterOfInning" handling in app.js)
   rather than a plain typographic "/" here, so red/blue tells the two
   teams' frame-ending cells apart at a glance. */
.pa-cell.updated { animation: flash 900ms ease-out; }
@keyframes flash { from { background: #fff6b0; } to { background: transparent; } }
/* Task #58: a pitching change right before/during this batter's PA --
   a thin colored bar along the very top edge of his cell (color matches
   his own team's accent, set inline per-cell in cellInnerHTML since it
   depends on which side's grid this cell belongs to), so both teams can
   see at a glance exactly which batter a new pitcher came in to face. */
.pa-cell .pitch-change-mark {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
}

#footerRow {
  margin-top: 14px;
  display: flex;
  gap: 14px;
}
#logWrap {
  flex: 2;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  display: flex;
  flex-direction: column;
  max-height: 220px;
}
#logHeader {
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  font-size: 0.85em;
  padding: 6px 10px;
  border-bottom: 1px solid var(--line);
  background: #efe9d8;
}
#logBox {
  overflow-y: auto;
  padding: 6px 10px;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-size: 0.85em;
  line-height: 1.5;
}
#logBox .log-line.big { font-weight: bold; color: var(--home-dark); }
#controlsWrap {
  flex: 1;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 10px;
  font-family: sans-serif;
  font-size: 0.85em;
}
/* Item #38: the Pitching Change button moved up into #diceSidebar, below
   the dice result -- .note is now #controlsWrap's only child, so it no
   longer needs the top margin that used to separate it from the button. */
#controlsWrap .note { margin-top: 0; font-size: 0.85em; color: #666; line-height: 1.4; }

/* ---------- modals ---------- */
.modal {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.55);
  display: flex; align-items: center; justify-content: center;
  z-index: 50;
}
.modal[hidden] { display: none; }
.modal-box {
  background: var(--paper);
  border-radius: 8px;
  padding: 24px 28px;
  min-width: 340px;
  max-width: 480px;
  font-family: sans-serif;
  box-shadow: 0 6px 24px rgba(0,0,0,0.4);
}
.modal-box h2 { margin-top: 0; }
#relieverList { display: flex; flex-direction: column; gap: 8px; margin: 14px 0; }
.reliever-btn {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #f4f1e8;
  cursor: pointer;
  font-size: 0.95em;
}
.reliever-btn:hover { background: #e6e1d0; }
/* A pitcher who has already been in the game cannot come back (MLB
   substitution rule, 2026-09-22). He stays on the list so the manager can
   see who he has spent, struck through and unclickable. */
.reliever-btn.used,
.reliever-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  background: #edeae1;
}
.reliever-btn.used:hover { background: #edeae1; }
.reliever-btn.used .role,
.reliever-btn.used > span:first-child { text-decoration: line-through; }
.reliever-btn.used > span:last-child {
  text-decoration: none; font-style: italic; white-space: nowrap;
}
.reliever-btn .role { font-weight: bold; color: var(--away-dark); }
.decline-btn {
  width: 100%;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #333;
  color: white;
  cursor: pointer;
}
/* Box score (2026-09-21: "box scores for each game that reflect the
   stats for each player") -- the game-end modal grows to fit a real
   per-player batting + pitching table for both teams instead of just the
   final score / W-L-SV line. */
.box-score-modal { max-width: 760px; width: 92vw; max-height: 86vh; overflow-y: auto; }
.box-score-team { margin-top: 18px; }
.box-score-team:first-of-type { margin-top: 22px; }
.box-score-team h3 {
  margin: 0 0 8px;
  padding: 4px 10px;
  border-radius: 4px;
  font-size: 0.95em;
  color: white;
}
.box-score-team.away h3 { background: linear-gradient(135deg, var(--away-color), var(--away-dark)); }
.box-score-team.home h3 { background: linear-gradient(135deg, var(--home-dark), var(--home-color)); }
.box-score-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
  margin-bottom: 6px;
}
.box-score-table caption {
  text-align: left;
  font-size: 0.78em;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #756f5e;
  padding: 4px 2px 3px;
}
.box-score-table th, .box-score-table td {
  padding: 3px 6px;
  border-bottom: 1px solid var(--line);
  text-align: right;
  white-space: nowrap;
}
.box-score-table th:first-child, .box-score-table td:first-child {
  text-align: left;
  white-space: normal;
}
.box-score-table thead th {
  border-bottom: 2px solid var(--line);
  color: #756f5e;
  font-weight: 600;
  font-size: 0.9em;
}
.box-score-table td.player-pos { color: #8a8371; font-size: 0.88em; padding-left: 2px; }
.box-score-table tfoot td { border-bottom: none; border-top: 2px solid var(--line); font-weight: 600; }
.box-score-table .pitcher-decision { color: #8a8371; font-size: 0.85em; font-weight: 500; }
.game-end-actions { display: flex; gap: 10px; align-items: center; }
#newGameBtn {
  padding: 8px 20px;
  border: none;
  border-radius: 4px;
  background: var(--home-color);
  color: white;
  cursor: pointer;
  font-size: 1em;
}
/* User request 2026-09-15: a second option next to "New Game" that goes
   back to the team-load screen (where standings, and eventually team
   stats, are reached) instead of straight into a rematch setup. */
#gameEndBackToStartBtn { margin-top: 0; padding: 8px 20px; font-size: 1em; }
#finalLine { font-family: "Arial Narrow", Arial, sans-serif; white-space: pre-line; margin: 14px 0; line-height: 1.6; }

/* ---------- team-select / roster-setup / lineup-builder screen ---------
   Pre-game flow inserted ahead of the existing game screen: pick a team
   from the league structure, tag bullpen roles if needed, build/save a
   lineup, then "Start Game" hands off to the unchanged live-game screen.
   Kept in the same paper-card / serif+mono / away-blue+home-red language
   as the rest of the app rather than inventing a new visual system. */
.setup-screen {
  max-width: 1180px;
  margin: 4vh auto;
  padding: 0 16px 32px;
}
.setup-title {
  text-align: center;
  font-family: "Bitter", Georgia, serif;
  margin-bottom: 18px;
}
#setupColumns {
  display: flex;
  gap: 18px;
  align-items: flex-start;
  flex-wrap: wrap;
}
.setup-col {
  flex: 1 1 460px;
  min-width: 320px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 16px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.setup-col.setup-away { border-top: 4px solid var(--away-color); }
.setup-col.setup-home { border-top: 4px solid var(--home-color); }
.setup-col-title {
  font-family: "Bitter", Georgia, serif;
  margin: 0 0 10px;
}
.setup-away .setup-col-title { color: var(--away-dark); }
.setup-home .setup-col-title { color: var(--home-dark); }
.setup-team-pickers { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.setup-team-pickers select {
  flex: 1 1 auto;
  min-width: 120px;
  padding: 6px 8px;
  font-family: "Public Sans", sans-serif;
  font-size: 0.9em;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: white;
}
.setup-body { font-family: "Public Sans", sans-serif; font-size: 0.92em; }
.setup-readiness {
  display: flex; flex-wrap: wrap; gap: 6px 14px;
  padding: 8px 10px; margin-bottom: 12px;
  background: #f4f1e8; border: 1px solid var(--line); border-radius: 4px;
  font-size: 0.85em;
}
.setup-readiness .ok { color: #1b6e3c; font-weight: 600; }
.setup-readiness .missing { color: var(--home-color); font-weight: 600; }
.setup-section {
  border: 1px solid var(--line);
  border-radius: 4px;
  margin-bottom: 12px;
  overflow: hidden;
}
.setup-section-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 12px;
  background: #f4f1e8;
  cursor: pointer;
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.82em;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.setup-section-body { padding: 10px 12px; }
.setup-section.collapsed .setup-section-body { display: none; }
.role-tag-table { width: 100%; border-collapse: collapse; font-size: 0.88em; }
.role-tag-table th, .role-tag-table td { padding: 3px 6px; border-bottom: 1px solid #e5e0d0; text-align: left; }
.role-tag-table select, .role-tag-table input[type=number] {
  font-family: "Public Sans", sans-serif; font-size: 0.95em;
  padding: 2px 4px; border: 1px solid var(--line); border-radius: 3px;
}
.role-tag-table input[type=number] { width: 42px; }
/* Pitcher grade/letters column -- "present the pitcher grades like they
   would be on their card" (see app.js's pitcherGradeCellHTML): one fixed
   grade string straight off the card (e.g. "B*"), then each K/walk-control
   letter group in its own parens (e.g. "(XY)(W)"), same as the printed
   card -- this doesn't change with the row's role-select choice. */
.pitcher-grade-cell {
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.9em;
  white-space: nowrap;
}
.pitcher-grade-value { color: var(--ink); font-weight: 700; }
.pitcher-grade-letters { color: #6a6454; margin-left: 2px; }
.pitcher-grade-missing { color: #a39a80; font-style: italic; font-size: 0.9em; }
/* "Not allowed to start a game" badge (0 recorded GS this season) next to
   a pitcher's name in the role-tagging table -- his SP option is removed
   from the role dropdown for the same reason (see renderRoleTagging). */
.pitcher-rp-only-badge {
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #a05a2c;
  background: #f3e3d3;
  border-radius: 3px;
  padding: 1px 5px;
  margin-left: 4px;
  white-space: nowrap;
}
/* Mirror image: only ever appeared as a starter (GP == GS) -- bullpen
   role options are removed from the dropdown for the same reason. */
.pitcher-sp-only-badge {
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #2c6ba0;
  background: #d3e3f3;
  border-radius: 3px;
  padding: 1px 5px;
  margin-left: 4px;
  white-space: nowrap;
}
.save-roles-btn, .save-lineup-btn, .setup-secondary-btn {
  padding: 6px 16px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #333;
  color: white;
  cursor: pointer;
  font-size: 0.88em;
  margin-top: 8px;
}
.setup-secondary-btn { background: #eee; color: var(--ink); }
/* Saved-lineup list -- Use/Edit/Rename per row (see renderLineupSection).
   Replaces the old single dropdown+Use control now that a saved lineup
   can also be loaded back into the builder to change it or just
   renamed in place. */
.saved-lineup-table { width: 100%; border-collapse: collapse; font-size: 0.87em; margin-bottom: 12px; }
.saved-lineup-table td { padding: 4px 5px; border-bottom: 1px solid #e5e0d0; }
.lineup-name-cell { font-weight: 600; }
.lineup-actions-cell { text-align: right; white-space: nowrap; }
.lineup-actions-cell button {
  padding: 3px 10px; border-radius: 4px; border: 1px solid var(--line);
  background: white; cursor: pointer; font-size: 0.92em; margin-left: 4px;
}
/* The chosen lineup is shown as a banner WITH a Change button, and the
   row it came from is highlighted -- a mis-click on Use must never be a
   one-way door (2026-09-24). */
.saved-lineup-table tr.selected { background: #eaf3ec; }
.saved-lineup-table tr.selected .lineup-name-cell { box-shadow: inset 3px 0 0 #1b6e3c; }
.lineup-actions-cell button.is-selected {
  background: #1b6e3c; border-color: #1b6e3c; color: white; font-weight: 600;
}
/* A one-game adjustment: the banner says so, and the panel that makes it
   says louder that nothing is being saved (2026-09-25). */
.lineup-chosen-banner.lineup-adjusted { background: #fdf4e3; border-color: #d9c8a0; }
.game-adjust-banner {
  background: #fdf4e3; border: 1px solid #d9c8a0; border-radius: 4px;
  padding: 8px 10px; margin-bottom: 10px; font-size: 0.85em; line-height: 1.45;
}
.game-adjust-banner a { color: #8a5a1c; }
.game-adjust-actions { display: flex; gap: 8px; align-items: center; margin-top: 10px; }
.lineup-chosen-banner {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 10px; margin-bottom: 12px;
  background: #eaf3ec; border: 1px solid #b9d6c2; border-radius: 4px;
  font-size: 0.85em;
}
.lineup-chosen-banner .ok { color: #1b6e3c; font-weight: 600; }
.lineup-chosen-actions button {
  padding: 3px 12px; border-radius: 4px; border: 1px solid var(--line);
  background: white; cursor: pointer; font-size: 0.95em; white-space: nowrap;
}
.lineup-chosen-actions button:hover { background: #f4f1e8; }
.lineup-editing-banner {
  display: flex; align-items: center; justify-content: space-between;
  background: #f3e9d3; border: 1px solid #d9c8a0; border-radius: 4px;
  padding: 5px 10px; margin-bottom: 8px; font-size: 0.88em;
}
.lineup-editing-banner button {
  padding: 3px 10px; border-radius: 4px; border: 1px solid var(--line);
  background: white; cursor: pointer; font-size: 0.92em;
}
/* Visual lineup builder (2026-09-15 redesign) -- three panes (Lineup /
   Player / Pos), per the user's own sample screenshot: select a slot,
   select a player, double-click a position to place him. Replaces the
   old one-row-per-hitter table+dropdown+order-number design, which the
   user found hard to visually reconcile for "is every position covered"
   and "does every hitter have a unique order number." */
.lb-hint { font-size: 0.85em; color: #666; margin-bottom: 8px; }
.lb-error {
  font-size: 0.87em; color: #8a2c1f; background: #fbe6e0; border: 1px solid #e3b3a3;
  border-radius: 4px; padding: 5px 9px; margin-bottom: 8px;
}
.lb-visual-builder { display: flex; gap: 10px; flex-wrap: wrap; }
.lb-pane {
  flex: 1 1 200px; min-width: 180px; border: 1px solid var(--line); border-radius: 5px;
  background: #fffdf8; overflow: hidden;
}
/* The Lineup pane carries three extra columns now (BA/SLG/OPS), so it
   takes the room the other two were not using -- "both the player and pos
   sections have plenty of room to give up" (2026-09-25). Player keeps
   enough for a long name; Pos is a nine-row list of short labels and
   needs least of all. */
.lb-lineup-pane { flex: 2.4 1 420px; min-width: 360px; }
.lb-player-pane { flex: 1.1 1 210px; }
.lb-pos-pane { flex: 0.7 1 130px; min-width: 120px; }
.lb-pane-head {
  font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700;
  color: #6a6454; background: #efe9d8; padding: 5px 8px; border-bottom: 1px solid var(--line);
}
.lb-pos-sub { text-transform: none; letter-spacing: normal; font-weight: 400; font-style: italic; }
.lb-lineup-table { width: 100%; border-collapse: collapse; font-size: 0.86em; }
.lb-lineup-table th { text-align: left; padding: 3px 6px; font-size: 0.85em; color: #6a6454; }
.lb-lineup-table td { padding: 4px 6px; border-bottom: 1px solid #ece6d4; }
.lb-slot-row { cursor: pointer; }
.lb-slot-row:hover { background: #f3efdf; }
.lb-slot-row.active { background: #fdeec2; }
.lb-ord {
  font-weight: 700; color: #a05a2c; width: 20px; text-align: center;
  font-family: "IBM Plex Mono", monospace;
}
/* nowrap + a floor wide enough for the longest reading ("1B-10"): the
   Pos column carries the player's rating AT that spot now, and without
   this it wrapped onto two lines in the narrow builder pane. */
.lb-slot-pos {
  font-family: "IBM Plex Mono", monospace; font-size: 0.9em; color: #6a6454;
  white-space: nowrap; width: 1%; text-align: right;
}
.lb-slot-player { min-width: 0; }
/* The real 2025 line, three columns on the right of the batting order.
   Monospace and right-aligned so a manager can read straight DOWN the BA
   column and compare, which is the whole point of putting them here. */
.lb-lineup-table th.lb-bat { text-align: right; }
.lb-bat {
  font-family: "IBM Plex Mono", monospace; font-size: 0.88em;
  text-align: right; white-space: nowrap; width: 1%; color: #55503f;
}
/* The pooled lineup line, in the table's own footer directly under the
   column it totals. */
.lb-bat-foot td {
  border-top: 2px solid var(--line); border-bottom: none;
  padding-top: 6px; font-weight: 700; background: #f6f3ea;
}
.lb-bat-foot .lb-bat { color: #1b4b7a; }
.lb-bat-foot-label {
  font-size: 0.85em; text-transform: uppercase; letter-spacing: 0.05em; color: #6a6454;
}
.lb-bat-warn {
  font-weight: 400; text-transform: none; letter-spacing: normal;
  color: #a05a2c; font-style: italic;
}
.lb-clear-btn {
  border: none; background: none; color: #a05a2c; cursor: pointer; font-size: 1.05em;
  line-height: 1; padding: 0 3px;
}
/* 400px = 16 rows at 25px. Thirteen position players is the roster
   minimum, and at 320px (12.8 rows) the thirteenth was always below the
   fold -- "13 position players doesn't quite fit without some scrolling"
   (2026-09-25). Three spare rows above that leaves room for a 30-man
   September roster's extra bats without the list jumping. */
.lb-player-list, .lb-pos-list { max-height: 400px; overflow-y: auto; }
.lb-player-row {
  display: flex; align-items: center; gap: 6px; padding: 4px 8px; cursor: pointer;
  border-bottom: 1px solid #f0ead9; font-size: 0.87em;
}
.lb-player-row:hover:not([data-disabled]) { background: #f3efdf; }
.lb-player-row.selected { background: #d9ecd6; }
.lb-player-row.assigned { color: #8f8873; }
.lb-player-row.card-not-ready { opacity: 0.45; cursor: not-allowed; }
.lb-player-mark {
  min-width: 22px; font-family: "IBM Plex Mono", monospace; font-size: 0.85em;
  font-weight: 700; color: #2c7a4b; text-align: center;
}
.lb-player-name { flex: 1; }
.lb-player-row .pos-tag { font-family: "IBM Plex Mono", monospace; font-size: 0.85em; color: #a39a80; }
.lb-pos-row {
  /* User request 2026-09-15: rows were tall enough that all 9 positions
     (C/1B/2B/3B/SS/LF/CF/RF/DH) didn't fit without scrolling, slowing down
     lineup entry -- padding trimmed from 6px to 3px top/bottom (each row
     ~6px shorter) and the list's own max-height bumped alongside (see
     .lb-player-list, .lb-pos-list above) so all 9 comfortably fit at once
     on typical window sizes. Also switched from justify-content:
     space-between (which pushed the rating all the way to the pane's
     right edge, far from the position label it describes -- "way over to
     the right" per the user's own words, making it harder to read at a
     glance) to flex-start with a small fixed gap, so the rating sits
     right next to its label instead. */
  display: flex; align-items: center; justify-content: flex-start; gap: 8px;
  padding: 3px 10px; cursor: pointer; border-bottom: 1px solid #f0ead9; font-size: 0.9em;
  font-family: "IBM Plex Mono", monospace; user-select: none;
}
.lb-pos-row:hover:not(.ineligible) { background: #f3efdf; }
.lb-pos-row.ineligible { opacity: 0.35; cursor: not-allowed; }
/* Where the man being MOVED currently stands, so "double-click his new
   position" has an obvious starting point (2026-09-25). */
.lb-pos-row.lb-pos-current {
  background: #fdeec2; font-weight: 700;
  box-shadow: inset 3px 0 0 #a05a2c;
}
.lb-pos-label { font-weight: 700; min-width: 30px; }
.lb-pos-rating { color: #6a6454; }
.lb-defense-totals { margin: 10px 0; display: flex; gap: 14px; align-items: center; font-size: 0.9em; flex-wrap: wrap; }
.lb-defense-tag { background: #efe9d8; border-radius: 4px; padding: 3px 9px; }
.lb-defense-note { color: #8f8873; font-size: 0.85em; font-style: italic; }
.lineup-sp-row { margin: 10px 0; display: flex; align-items: center; gap: 8px; }
.lineup-name-row { display: flex; gap: 8px; margin-top: 10px; }
.lineup-name-row input[type=text] {
  flex: 1; padding: 6px 8px; border: 1px solid var(--line); border-radius: 4px; font-family: inherit;
}
#setupFooter {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  margin-top: 22px;
}
#setupFooter button#setupStartGameBtn {
  font-size: 1.1em;
  padding: 10px 28px;
  background: var(--home-color);
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
#setupFooter button#setupStartGameBtn:disabled { background: #999; cursor: default; }
#setupStatusMsg { font-family: "Public Sans", sans-serif; font-size: 0.88em; color: #666; max-width: 320px; }
.setup-error { color: var(--home-color); font-size: 0.85em; margin-top: 6px; }

/* ---------- in-app league editing (rename/move a team, add a new one) --- */
#leagueEditBody { display: flex; flex-wrap: wrap; gap: 18px; margin-bottom: 18px; }
.league-edit-conf {
  flex: 1 1 460px; min-width: 320px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 6px; padding: 14px 16px;
}
.league-edit-conf h2 { font-family: "Bitter", Georgia, serif; margin: 0 0 10px; font-size: 1.1em; }
.league-edit-div h3 {
  font-family: "IBM Plex Mono", monospace; text-transform: uppercase; letter-spacing: 0.02em;
  font-size: 0.78em; color: #6a6454; margin: 12px 0 6px;
}
.league-edit-team-row {
  display: flex; align-items: center; gap: 6px; padding: 4px 0;
  font-family: "Public Sans", sans-serif; font-size: 0.9em;
}
.league-edit-team-row .team-name-display { flex: 1; }
.league-edit-team-row input[type=text] { width: 100%; padding: 4px 6px; border: 1px solid var(--line); border-radius: 3px; }
.league-edit-team-row select { padding: 4px 6px; border: 1px solid var(--line); border-radius: 3px; font-size: 0.95em; }
.league-edit-team-row button { padding: 3px 10px; border: 1px solid var(--line); border-radius: 3px; background: white; cursor: pointer; font-size: 0.85em; }
#addTeamSection { margin-top: 6px; }
#newTeamName { flex: 1; }

/* ---------- standings screen ---------- */
/* Two conference columns -- American left, National right -- with each
   conference's divisions stacked inside it in league order (Aaron above
   Boudreau; Musial above Ruth). User request 2026-09-22. */
#standingsBody { display: flex; align-items: flex-start; gap: 26px; margin-bottom: 22px; }
.standings-conf-col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 14px; }
.standings-conf-title {
  font-family: "Bitter", Georgia, serif; font-size: 1.15em; margin: 0 0 2px;
  padding-bottom: 5px; border-bottom: 2px solid var(--line, #c9c0a8);
}
@media (max-width: 900px) { #standingsBody { flex-direction: column; } }
.standings-div-card {
  flex: 1 1 320px; min-width: 280px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 6px; padding: 12px 14px;
}
.standings-div-card h3 { font-family: "Bitter", Georgia, serif; margin: 0 0 8px; font-size: 1em; }
.standings-table { width: 100%; border-collapse: collapse; font-family: "IBM Plex Mono", monospace; font-size: 0.82em; }
.standings-table th, .standings-table td { padding: 3px 5px; text-align: right; border-bottom: 1px solid #e5e0d0; }
.standings-table th:first-child, .standings-table td:first-child { text-align: left; font-family: "Public Sans", sans-serif; }
.standings-table td.rd-pos { color: #1b6e3c; }
.standings-table td.rd-neg { color: var(--home-color); }

/* ------------------------------------------------------------------
   PLAYER SUBSTITUTION (2026-09-21)
   ------------------------------------------------------------------ */

/* The vertical twin of .pitch-change-mark. A pitching change marks the
   TOP of the cell; a position-player substitution marks its LEFT EDGE,
   so the two read as related but never get confused for one another. */
.pa-cell .sub-change-mark {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 3px;
}

/* The pinch runner's own name on the cell of the man he ran for -- the
   cell now represents two players, and the diamond alone can't say so. */
.pa-cell .pinch-runner-name {
  position: absolute;
  left: 5px;
  bottom: 1px;
  font-size: 8px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: -0.2px;
  max-width: calc(100% - 8px);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  opacity: 0.85;
}

/* Right-click menu */
.ctx-menu {
  position: fixed;
  z-index: 400;
  min-width: 230px;
  max-height: 60vh;
  overflow-y: auto;
  background: #fff;
  border: 1px solid #9aa4b0;
  border-radius: 5px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
  padding: 4px 0;
  font-size: 12px;
}
.ctx-menu .ctx-head {
  padding: 5px 10px 4px;
  font-weight: 700;
  border-bottom: 1px solid #e2e6ea;
  margin-bottom: 3px;
}
.ctx-menu .ctx-sub { font-weight: 400; opacity: 0.7; display: block; font-size: 11px; }
.ctx-menu button {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  border: 0;
  background: none;
  text-align: left;
  padding: 5px 10px;
  cursor: pointer;
  font-size: 12px;
}
.ctx-menu button:hover { background: #eef3f9; }
.ctx-menu button .ctx-detail { opacity: 0.62; font-size: 11px; white-space: nowrap; }
.ctx-menu .ctx-empty { padding: 6px 10px; opacity: 0.6; }

.sub-toast {
  position: fixed;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
  z-index: 500;
  background: #33383d;
  color: #fff;
  padding: 9px 15px;
  border-radius: 5px;
  font-size: 12.5px;
  max-width: 460px;
  box-shadow: 0 5px 18px rgba(0, 0, 0, 0.3);
}

/* A forced unrated fill-in, wherever his position is printed. */
.pos-col.unrated-fill-in,
.ctx-menu button.unrated-opt { color: #b3181f; }
.pos-col.unrated-fill-in { font-weight: 700; }

/* ------------------------------------------------------------------ */
/* "Runner Score From 3rd on Fly Ball Success Chart"                    */
/* The send/hold modal's math block, and the 6x6 chart that replaces    */
/* the batter-card panel while the attempt resolves.                    */
/* ------------------------------------------------------------------ */
.sft-line { margin: 3px 0; }
.sft-rule { opacity: 0.75; }
.sft-depth-picked {
  font-weight: 700;
  background: #fff2c4;
  padding: 0 4px;
  border-radius: 3px;
}
.sft-depth-other { opacity: 0.55; }
.sft-die {
  display: inline-block;
  min-width: 17px;
  text-align: center;
  font-weight: 700;
  border: 1px solid #99a2ab;
  border-radius: 3px;
  padding: 0 3px;
  background: #fff;
}
.sft-math {
  margin-top: 7px;
  padding: 6px 8px;
  background: #f2f5f8;
  border: 1px solid #dbe1e8;
  border-radius: 4px;
  font-size: 13px;
  line-height: 1.7;
}
.sft-op { opacity: 0.6; font-size: 11px; }
.sft-chance { font-size: 16px; }

.sft-chart-wrap { padding: 4px 2px; font-size: 12px; }
.sft-chart-head { margin-bottom: 8px; }
.sft-chart {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 2px;
}
.sft-cell {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 3px;
  padding: 3px 4px;
  border: 1px solid #dde2e8;
  border-radius: 3px;
  background: #fff;
}
.sft-cell .sft-code { font-size: 10px; opacity: 0.5; }
.sft-cell .sft-val { font-weight: 700; }
/* Every value at or under the chance to score: the runner is safe on any */
/* of these, so the shaded block IS the odds, visible at a glance. */
.sft-cell.sft-safe { background: #e4f3e2; border-color: #b6d9b0; }
.sft-cell.sft-rolled {
  outline: 2px solid #b3181f;
  outline-offset: -1px;
  box-shadow: 0 0 0 2px rgba(179, 24, 31, 0.15);
}
.sft-verdict {
  margin-top: 9px;
  padding: 6px 8px;
  border-radius: 4px;
  font-weight: 700;
  text-align: center;
}
.sft-verdict-safe { background: #e4f3e2; color: #1d6b17; }
.sft-verdict-out { background: #fbe6e7; color: #b3181f; }

/* ------------------------------------------------------------------ */
/* Box score: substitutes (2026-09-21 change request)                   */
/* "Indent position players so it is intuitive to look at and see who   */
/* was a starter and who came in as a substitute."                      */
/* ------------------------------------------------------------------ */
.box-score-table td.box-player { white-space: nowrap; }
.box-score-table tr.box-sub td.box-player { padding-left: 22px; }
.box-sub-tag { font-weight: 700; }

/* The tab of whichever team is currently in the field -- right-click a
   lineup row there for a defensive change (2026-09-21 request). */
.grid-tab.fielding-tab { font-style: italic; }
.grid-tab.fielding-tab::after {
  content: '\2699';
  margin-left: 6px;
  opacity: 0.65;
  font-style: normal;
}

/* ------------------------------------------------------------------ */
/* Physical dice (2026-09-21)                                          */
/* ------------------------------------------------------------------ */
.grid-tabs .tab-group { display: inline-flex; align-items: center; gap: 8px; }
.throw-dice-box {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11.5px;
  opacity: 0.75;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.throw-dice-box:hover { opacity: 1; }
.throw-dice-box input { margin: 0; cursor: pointer; }
.dice-cal-btn {
  margin-left: auto;
  font-size: 11.5px;
  padding: 3px 9px;
  border: 1px solid #c3cbd4;
  border-radius: 4px;
  background: #fff;
  cursor: pointer;
}
.dice-cal-btn:hover { background: #eef3f9; }

.dice-modal-box { max-width: 520px; }
.dice-cal-box { max-width: 760px; }
.dice-sub { font-size: 12.5px; opacity: 0.78; margin-bottom: 10px; }
.dice-live { display: flex; gap: 12px; align-items: flex-start; }
.dice-live > img { max-width: 300px; border-radius: 5px; border: 1px solid #dde2e8; }
.dice-crops { display: flex; flex-direction: column; gap: 8px; }
.dice-crops figure { margin: 0; text-align: center; }
.dice-crops img {
  width: 96px; height: 96px; object-fit: contain;
  border: 1px solid #dde2e8; border-radius: 5px; background: #f6f8fa;
}
.dice-crops figcaption { font-size: 10.5px; opacity: 0.6; margin-top: 2px; }

.dice-readout {
  margin: 11px 0 4px;
  padding: 7px 10px;
  border-radius: 4px;
  background: #f2f5f8;
  border: 1px solid #dbe1e8;
  font-size: 14px;
  font-weight: 700;
  text-align: center;
  min-height: 19px;
}
.dice-readout.dice-good { background: #e4f3e2; border-color: #b6d9b0; color: #1d6b17; }
.dice-readout.dice-bad {
  background: #fbe6e7; border-color: #f0c2c4; color: #b3181f;
  font-weight: 400; font-size: 12.5px;
}

.dice-actions { display: flex; gap: 8px; margin-top: 12px; }
.dice-actions button {
  flex: 1;
  padding: 8px 10px;
  border: 1px solid #c3cbd4;
  border-radius: 5px;
  background: #fff;
  cursor: pointer;
  font-size: 12.5px;
}
.dice-actions button:hover:not(:disabled) { background: #eef3f9; }
.dice-actions .dice-accept {
  background: #1d6b17; border-color: #1d6b17; color: #fff; font-weight: 700;
}
.dice-actions .dice-accept:hover:not(:disabled) { background: #185a13; }
.dice-actions button:disabled { opacity: 0.45; cursor: default; }

.dice-cal-body { display: flex; gap: 14px; align-items: flex-start; }
.dice-cal-body > img {
  max-width: 340px; border-radius: 5px; border: 1px solid #dde2e8;
}
#diceCalSliders { flex: 1; display: flex; flex-direction: column; gap: 5px; }
.dice-cal-row {
  display: grid;
  grid-template-columns: 1fr 110px 42px;
  align-items: center;
  gap: 7px;
  font-size: 11.5px;
}
.dice-cal-row input { width: 100%; }
.dice-cal-row output { text-align: right; font-variant-numeric: tabular-nums; opacity: 0.7; }

/* ------------------------------------------------------------------ */
/* Lineup builder: defensive rating in the Pos column, and drag to      */
/* reorder the batting order (2026-09-21 request).                      */
/* ------------------------------------------------------------------ */
.lb-slot-row[draggable="true"] { cursor: grab; }
.lb-slot-row[draggable="true"]:active { cursor: grabbing; }
.lb-slot-row.lb-dragging { opacity: 0.4; }
/* The drop target, marked on its top edge so it reads as "he lands
   here" rather than "he swaps with this row". */
.lb-slot-row.lb-drag-over td {
  box-shadow: inset 0 3px 0 -1px #1d6b17;
  background: #eef7ec;
}
/* A saved lineup carrying a position its player has no rating for --
   only reachable if the roster changed under it. */
.lb-slot-pos.lb-unrated { color: #b3181f; font-weight: 700; }
.lb-slot-pos.lb-unrated::after { content: ' !'; }

/* Hit batsmen this game, both teams, in the B / S / OUTS row. Red at 3 --
   every board's HBP line reads "3rd + HBP in game, use the HBP Chart", so
   the colour change is the cue to reach for the chart. */
.count-item.hbp .count-val { color: #ffd479; }
.count-item.hbp.hbp-alert .count-val { color: #ff5252; }
.count-item.hbp.hbp-alert .count-label { opacity: 1; color: #ff8a80; }
.count-item.hbp.hbp-alert {
  background: rgba(255, 82, 82, 0.16);
  border-radius: 4px;
  padding: 0 6px;
  margin: 0 -2px;
}

/* Ball/strike count (2026-09-21). "Hot" = one pitch from ending the PA:
   3 balls (the next ball walks him) or 2 strikes (a called strike retires
   him -- a foul does not, which is exactly why the highlight is useful:
   at 2 strikes every Foul line is a free re-roll and every Strike line is
   the strikeout). Amber rather than the HBP counter's red, so the two
   signals never read as the same alarm. */
.count-item.count-hot .count-val { color: #ffd479; font-weight: 700; }
.count-item.count-hot .count-label { opacity: 1; color: #ffd479; }

/* Encoded-board shadow mode (2026-09-21). The encoded reading sits
   directly under the live one, inside the same light board-result panel,
   so the two flavor texts line up and can be read against each other at a
   glance. Green when they agree, amber when only the wording differs (a
   deliberate rewording to review), red when the OUTCOME differs -- which
   is the only kind that matters. Colours are chosen for the panel's own
   cream background, not the dark chrome around it. */
.board-shadow {
  margin-top: 5px; padding-top: 5px;
  border-top: 1px dashed rgba(0, 0, 0, 0.22);
}
.board-shadow-head {
  display: flex; align-items: baseline; gap: 8px; margin-bottom: 2px;
}
.shadow-label {
  font-size: 0.62em; letter-spacing: 0.12em; color: #6b6355; font-weight: 700;
}
.shadow-verdict { font-size: 0.66em; font-weight: 700; letter-spacing: 0.02em; }
.shadow-verdict.shadow-ok   { color: #1d7a3e; }
.shadow-verdict.shadow-warn { color: #9a6b00; }
.shadow-verdict.shadow-bad  { color: #c0271b; }
.board-shadow-text { font-size: 0.86em; color: #222; line-height: 1.35; }
.board-shadow-cell {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.66em; color: #6b6355; margin-top: 3px;
  word-break: break-word; line-height: 1.3;
}
.board-shadow-diff {
  margin-top: 3px; font-size: 0.72em; font-weight: 700; color: #c0271b;
}

/* Dice camera dependency notice (2026-09-21). Shown when OpenCV isn't
   installed in the Python running the server -- an install note, not an
   error, so it carries the exact command rather than a stack-trace
   fragment. Colours are for the modal's own light background. */
.dice-deps-box { max-width: 560px; }
.dice-deps-cmd {
  display: flex; align-items: center; gap: 10px;
  margin: 12px 0; padding: 9px 12px;
  background: #1f2430;
  border-radius: 4px;
}
.dice-deps-cmd code {
  flex: 1; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; color: #ffd479; user-select: all; letter-spacing: 0.01em;
}
.dice-deps-cmd button { flex: 0 0 auto; }

/* Camera picker on the dice calibration screen (2026-09-21). A machine
   with Logitech/OBS/Teams software enumerates several devices and only
   one is the real lens -- the thumbnail is the only reliable way to tell
   them apart, so each candidate shows what it actually delivers. Colours
   are for the modal's own light background. */
.dice-cams { margin: 8px 0 12px; }
.dice-cams-head {
  display: flex; align-items: center; gap: 10px;
  font-size: 12px; font-weight: 700; margin-bottom: 6px; color: #222;
}
.dice-scan-note { font-weight: 400; font-size: 11px; color: #555; }
.dice-cam-list { display: flex; flex-wrap: wrap; gap: 8px; }
.dice-cam {
  border: 2px solid #d8d2c4; border-radius: 5px;
  padding: 5px; cursor: pointer; text-align: center;
  background: #ffffff; min-width: 108px;
}
.dice-cam:hover { border-color: #9a9484; }
.dice-cam img {
  display: block; width: 104px; height: 72px; object-fit: cover;
  border-radius: 3px; background: #11141b;
}
.dice-cam .dice-cam-label { font-size: 11px; margin-top: 4px; color: #222; font-weight: 600; }
.dice-cam .dice-cam-why { font-size: 10px; color: #6b6355; line-height: 1.25; max-width: 110px; }
.dice-cam.selected { border-color: #1d7a3e; background: #eef8f0; }
.dice-cam.selected .dice-cam-label { color: #1d7a3e; }
.dice-cam.blank .dice-cam-label { color: #8a5a00; }
.dice-cam.blank .dice-cam-why { color: #8a5a00; }
.dice-cam.dead { opacity: 0.55; cursor: not-allowed; }
.dice-cam.dead:hover { border-color: #d8d2c4; }

/* Capture-resolution picker (2026-09-21). A 1080p webcam is worth using
   at 1080p -- the die face is ~2.25x the pixel area it is at 720p -- but
   the driver decides what it actually hands over, so the note beside the
   dropdown reports what is really arriving, not what was asked for. */
.dice-res-row {
  display: flex; align-items: center; gap: 10px;
  margin-top: 8px; font-size: 12px; font-weight: 700; color: #222;
}
.dice-res-row select { font-size: 12px; padding: 2px 4px; }
.dice-mjpg {
  font-weight: 400; font-size: 11px; color: #555; cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
}
.dice-mjpg input { margin: 0; cursor: pointer; }

/* Camera diagnosis output (2026-09-21). Plain words about what every
   index on the machine did, for when a scan comes back empty and the
   only other evidence is OpenCV's VIDEOIO warnings on the console.
   Selectable so it can be pasted into a bug report. */
.dice-diag {
  margin: 8px 0 0; padding: 8px 10px; max-height: 190px; overflow: auto;
  background: #1f2430; color: #d8dee9; border-radius: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; line-height: 1.45; white-space: pre-wrap;
  word-break: break-word; user-select: text;
}

/* Auto-calibrate (2026-09-22). The primary action on this screen: the
   shipped thresholds are absolute brightness values that fit exactly one
   lighting setup, so on a real rig the sliders are a last resort, not a
   first step. */
.dice-autocal-row {
  display: flex; align-items: center; gap: 10px;
  margin: 4px 0 10px; flex-wrap: wrap;
}
.dice-autocal-btn {
  font-weight: 700; padding: 6px 14px;
  background: #1d7a3e; color: #fff; border: 0; border-radius: 4px; cursor: pointer;
}
.dice-autocal-btn:hover { background: #17662f; }
.dice-autocal-btn:disabled { opacity: 0.6; cursor: default; }
.dice-autocal-hint { font-size: 11px; color: #555; }

/* =====================================================================
   READABILITY PASS  (user request, 2026-09-22)
   =====================================================================
   The scorebook is read across a table, not leaned into, so the live
   state -- the count, the inning, who's on base, who's pitching -- was
   simply too small. Everything below is a size change; no layout is
   restructured and nothing moves. Multipliers are against the measured
   "before" sizes, noted per rule.
   ------------------------------------------------------------------ */

/* ---- top banner: inning, B / S / OUTS / HBP ------------------------
   Measured before: count label and value both 10.4px, inning number
   16px. The banner is a full-width flex row whose mid-block was only
   150px min-width, so there was never a space problem here -- just
   timid type. The banner gets taller; that is the point. */
.mid-block { gap: 26px; padding: 8px 20px; }
.inning-indicator { font-size: 3.4em; gap: 10px; }      /* 16 -> ~54px */
.half-arrow { font-size: 0.5em; }                        /* stays proportional */
.count-row { gap: 22px; }
.count-item { font-size: 2.2em; }                        /* 10.4 -> 35px base */
.count-label { font-size: 0.6em; letter-spacing: 0.1em; opacity: 0.8; }
.count-val { font-size: 1.15em; line-height: 1.05; }     /* 10.4 -> ~40px, ~3.8x */

/* Team name and score bumped 1.5x so the banner reads as one unit --
   at 4x the count would otherwise tower over 13.6px team names. */
.team-name { font-size: 1.28em; }                        /* 13.6 -> ~20px */
.team-score { font-size: 1.95em; }                       /* 20.8 -> ~31px */

/* ---- baserunners: label and 1st / 2nd / 3rd, 2x -------------------- */
#baserunnerBar { gap: 18px; padding: 9px 16px; }
#baserunnerLabel { font-size: 1.4em; opacity: 0.75; }    /* 11.2 -> ~22px */
.base-slot { gap: 8px; min-height: 30px; }
.base-slot-tag { font-size: 1.4em; color: #6b6355; }     /* 11.2 -> ~22px */
.base-slot-body { font-size: 1.7em; }                    /* 13.6 -> ~27px */
.base-slot.occupied .base-slot-body .tier-tag { font-size: 0.62em; }

/* Defense Column Used sits in the same row; bring it along so the row
   doesn't read as two different sizes side by side. */
#defenseColumnUsedLabel { font-size: 1.4em; opacity: 0.75; }
#defenseColumnUsedVal { font-size: 1.7em; }

/* ---- pitching: label, grade and the game line, 2x ------------------ */
#pitcherBar { padding: 10px 16px; }
.pitcher-card { gap: 16px; }
.pitcher-label { font-size: 1.4em; opacity: 0.7; }       /* 11.2 -> ~22px */
.pitcher-name { font-size: 1.6em; }                      /* 16.8 -> ~26px */
.pitcher-grade { font-size: 1.7em; color: #4a4437; }     /* 13.6 -> ~27px */
/* Each stat is its own chip (see renderPitcherBar) rather than one
   run-on string, so this gap is real spacing that survives the larger
   type instead of double spaces that collapse. */
.pitcher-stats { display: inline-flex; flex-wrap: wrap; gap: 18px; font-size: 1.7em; }
.pstat { display: inline-flex; align-items: baseline; gap: 5px; }
.pstat-k { color: #857c66; font-size: 0.78em; letter-spacing: 0.04em; }
.pstat-v { font-weight: 700; font-variant-numeric: tabular-nums; }
/* GRADE REDUCED / ADVANCED brought along to this bar's scale. At the
   base 0.72em it would sit beside 1.7em stats and read as a footnote --
   which is the opposite of the point. */
.pitcher-alert { font-size: 1.25em; padding: 4px 11px; }
.matchup-note { font-size: 1.1em; }

/* ---- INFIELD / TEAM pills on the hitter card, +50% ------------------
   Measured before: 9.84px, the smallest text on the screen, for a number
   that governs every fielding roll while the other side bats. The digit
   now carries its own colour so it separates from the label rather than
   reading as one grey word. */
.hitter-card-defense-tag {
  font-size: 1.48em;                                     /* 9.84 -> ~14.6px */
  padding: 3px 7px;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
}
.hitter-card-defense-tag .hcd-label { color: #857c66; font-weight: 600; }
.hitter-card-defense-tag .hcd-value {
  color: #1b5fad;
  font-weight: 800;
  font-size: 1.25em;
  font-variant-numeric: tabular-nums;
}

/* ---- hitter card panel --------------------------------------------
   Width and the card's height are set from JS (see fitHitterCard): the
   card is a true 2.5 x 3.5 whose bottom lands on the bottom of the #9
   hitter's row. The panel no longer stretches to the grid's full height,
   which is what leaves the deliberate free space below it. */
#hitterCardPanel { align-self: flex-start; }

/* The scorebook table was silently COMPRESSING its columns when the
   window got narrow -- at 1366 the 64px result cells were squeezing to
   ~48px rather than the wrapper scrolling. Since the cells are meant to
   stay 64px (user request 2026-09-22: "i don't want to actually narrow
   the hitter cells"), pin the table to its natural width and let
   #gridWrap's own overflow-x do the scrolling instead. The sticky
   Batter/POS columns keep working -- they're sticky against the
   scrolling wrapper, not the table. */
#scoreGrid { min-width: max-content; }
/* fitHitterCard pins this box's height, so the FALLBACK plain dice grid
   (used for a hitter outside the 108-player print-card pool, where the
   two 6x6 grids are taller than a print card) scrolls inside it rather
   than pushing the panel past row 9 and breaking the alignment. */
#hitterCardGrids { overflow: auto; }

/* =====================================================================
   UNDO  (2026-09-22)
   Take back the last roll and everything it did. Three pieces of UI: a
   button in the space below the hitter card, a confirmation modal, and a
   running tally beside HBP in the header (also reported in the box).
   ===================================================================== */

/* The hitter card's own geometry is measured by fitHitterCard (card height
   pinned to the bottom of the #9 hitter's row, width derived from it at
   2.5:3.5). This column exists only so the button can sit UNDER that card
   without joining #gridArea's flex row and pushing the scorebook over. It
   takes its width from the panel, which fitHitterCard sizes explicitly. */
#cardColumn {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  align-self: flex-start;
  flex-shrink: 0;
  gap: 8px;
}

#undoBtn {
  width: 100%;
  padding: 9px 10px;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-size: 1.05em;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--ink, #eee);
  background: #4a3b2a;
  border: 2px solid #7a6247;
  border-radius: 4px;
  cursor: pointer;
}
#undoBtn:hover:not(:disabled) { background: #5d4a34; border-color: #a98a63; }
#undoBtn:active:not(:disabled) { transform: translateY(1px); }
/* Disabled until a roll exists to take back -- the server would refuse it
   anyway, and a button that cannot do anything should look like it. */
#undoBtn:disabled { opacity: 0.35; cursor: default; }

/* Retry mode, directly under the Undo button it modifies. Deliberately
   quieter than the button: it is a testing convenience, not a game
   control, and it should not read as something you reach for mid-game. */
#undoRetryLabel {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-size: 0.86em;
  letter-spacing: 0.02em;
  color: #b9a88f;
  cursor: pointer;
  user-select: none;
}
#undoRetryLabel:hover { color: #d8c8ae; }
#undoRetryLabel input { cursor: pointer; margin: 0; }

/* One line of feedback under the button after an undo (or a refusal), so
   the manager gets an answer in the place he clicked rather than only in
   the play-by-play log. */
#undoNote {
  min-height: 1.1em;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-size: 0.85em;
  line-height: 1.25;
  text-align: center;
  color: #d7b98a;
}
#undoNote.refused { color: #ff8a80; }

.undo-modal-box .undo-detail {
  font-size: 0.9em;
  opacity: 0.8;
  margin: 6px 0 0;
}
.undo-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 18px;
}
.undo-yes-btn {
  padding: 9px 18px;
  font-weight: 700;
  border-radius: 4px;
  border: 2px solid #a98a63;
  background: #5d4a34;
  color: #fff;
  cursor: pointer;
}
.undo-yes-btn:hover { background: #6f5940; }

/* Header tally, beside HBP. Neutral while it reads 0 -- an undo is a
   correction, not a penalty -- and picked out in the same amber as the
   other live counters once any have been taken. */
.count-item.undos .count-val { color: #8f8f8f; }
.count-item.undos.has-undos .count-val { color: #d7b98a; }
.count-item.undos.has-undos .count-label { opacity: 1; color: #d7b98a; }

/* Box-score line. Sits with the other game-level footnotes. */
.box-undo-line {
  margin-top: 10px;
  font-size: 0.9em;
  opacity: 0.85;
}

/* .decline-btn is styled full-width for the pitching-change modal, where
   "Leave current pitcher in" is the only button on its row. Here it shares
   a row with Yes, so both are sized to their text and neither wraps. */
.undo-actions .decline-btn,
.undo-actions .undo-yes-btn {
  flex: 0 0 auto;
  width: auto;
  min-width: 120px;
  white-space: nowrap;
  margin: 0;
}

/* =====================================================================
   SEASON STATISTICS  (2026-09-22)
   Division / team drill-downs off the standings page, and the league
   leaderboards that fill its lower half.
   ===================================================================== */

.standings-div-card h3 a,
.season-table a,
.standings-table a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dotted rgba(0, 0, 0, 0.35);
  cursor: pointer;
}
.standings-div-card h3 a:hover,
.season-table a:hover,
.standings-table a:hover { color: #8a5a2b; border-bottom-style: solid; }
.standings-div-card h3 a::after { content: " ›"; opacity: 0.55; }

/* ---------- leaderboards ---------- */
#leadersSection { margin-top: 26px; border-top: 2px solid var(--line, #c9c0a8); padding-top: 16px; }
#leadersHeader { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.leaders-title { font-family: "Bitter", Georgia, serif; font-size: 1.3em; margin: 0; }
.leaders-scope { font-family: "Public Sans", sans-serif; font-size: 0.9em; opacity: 0.85; }
.leaders-scope select { margin-left: 6px; font: inherit; padding: 2px 4px; }
.leaders-note { font-family: "Public Sans", sans-serif; font-size: 0.82em; opacity: 0.7; margin: 6px 0 4px; max-width: 70ch; }
.leaders-group { font-family: "Bitter", Georgia, serif; font-size: 1.02em; margin: 18px 0 8px; }

/* Three across, matching the printed AML stat sheet's own layout. */
.leader-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px 18px;
}
@media (max-width: 1100px) { .leader-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px)  { .leader-grid { grid-template-columns: 1fr; } }

.leader-card { border: 1px solid var(--line, #c9c0a8); border-radius: 3px; overflow: hidden; background: var(--paper, #fbf8f0); }
.leader-table { width: 100%; border-collapse: collapse; font-family: "IBM Plex Mono", monospace; font-size: 0.76em; }
.leader-table thead th {
  background: #2f6fa8; color: #fff; font-family: "Public Sans", sans-serif;
  font-size: 0.95em; text-align: left; padding: 3px 6px; font-weight: 700;
}
.leader-table thead th.lb-cat { text-align: right; }
.leader-table td { padding: 2px 6px; border-bottom: 1px solid #e9e4d4; }
.leader-table tbody tr:last-child td { border-bottom: none; }
.lb-rank { width: 2ch; text-align: right; opacity: 0.45; }
.lb-name { font-family: "Public Sans", sans-serif; white-space: nowrap; }
.lb-team { font-family: "Public Sans", sans-serif; font-size: 0.88em; opacity: 0.6; white-space: nowrap;
           overflow: hidden; text-overflow: ellipsis; max-width: 13ch; }
.lb-val  { text-align: right; font-weight: 700; white-space: nowrap; }
.lb-tied td { font-family: "Public Sans", sans-serif; font-size: 0.88em; opacity: 0.55; font-style: italic; }
.lb-empty td { text-align: center; opacity: 0.4; }

/* ---------- division / team season tables ---------- */
.season-group { font-family: "Bitter", Georgia, serif; font-size: 1.05em; margin: 18px 0 7px; }
.season-table {
  width: 100%; border-collapse: collapse;
  font-family: "IBM Plex Mono", monospace; font-size: 0.78em;
}
.season-table th, .season-table td { padding: 3px 6px; text-align: right; border-bottom: 1px solid #e9e4d4; white-space: nowrap; }
.season-table thead th { background: #efe9d8; font-family: "Public Sans", sans-serif; font-weight: 700; position: sticky; top: 0; }
.season-table th:first-child, .season-table td:first-child { text-align: left; font-family: "Public Sans", sans-serif; }
.season-table td.div-rec { font-weight: 700; }
.season-table tr.season-total td { border-top: 2px solid var(--line, #c9c0a8); font-weight: 700; background: #f6f2e6; }
.season-table .player-pos { opacity: 0.5; font-size: 0.9em; }

.season-splits {
  display: flex; gap: 22px; flex-wrap: wrap;
  font-family: "Public Sans", sans-serif; font-size: 0.88em;
  padding: 8px 0; border-bottom: 1px solid var(--line, #c9c0a8);
}
.season-splits b { font-family: "IBM Plex Mono", monospace; }

#setupFooter .setup-secondary-btn.danger { color: #8a2b2b; border-color: #c79a9a; }
#setupFooter .setup-secondary-btn.danger:hover { background: #f7ecec; }

/* .standings-div-card carries `flex: 1 1 320px` from when #standingsBody
   was a single wrapping ROW, where 320px is a WIDTH basis. Inside the
   per-conference column it became a HEIGHT basis instead, padding each
   division card out to 320px tall with dead space under the table.
   Reset it here; the card sizes to its four teams. */
.standings-conf-col .standings-div-card { flex: 0 0 auto; min-width: 0; }

/* ---- pitching-change list, now grouped by role (2026-09-22) ----
   The bullpen can hold several men per role, so the list needs headings
   and room to scroll; it used to be exactly four buttons. */
.reliever-role-header {
  font-family: "Public Sans", sans-serif;
  font-size: 0.72em; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; opacity: 0.55;
  margin: 8px 0 -2px; padding-bottom: 2px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
.reliever-role-header:first-child { margin-top: 0; }
#relieverList { max-height: 46vh; overflow-y: auto; }
.reliever-btn.suggested { border-color: #2f6fa8; box-shadow: inset 0 0 0 1px #2f6fa8; }
/* The current pitcher is in used_pids too (you can't bring in the man
   already pitching) but he isn't spent -- don't strike him through. */
.reliever-btn.current > span:first-child,
.reliever-btn.current .role { text-decoration: none; }
.reliever-btn.current { opacity: 0.7; }

/* ---- pitcher home-run allowance rating (2026-09-22) ----
   H rarely allows one, G above average at preventing, blank average,
   L more than average, M well above average. Its own column on the roles
   screen; appended as "HR: x" after the grade and K/WZ letters elsewhere.
   Colour runs green (prevents) to red (surrenders) with the two middle
   tiers deliberately muted -- this is a tag, not an alarm. */
.pitcher-hr-cell { text-align: center; }
.pitcher-hr-value {
  display: inline-block; min-width: 1.6em; padding: 0 4px;
  font-weight: 700; border-radius: 3px; cursor: help;
}
.pitcher-hr-value.hr-H { color: #14532d; background: #d8efdd; }
.pitcher-hr-value.hr-G { color: #2f6b3f; }
.pitcher-hr-value.hr-L { color: #8a5a2b; }
.pitcher-hr-value.hr-M { color: #7f1d1d; background: #f6dcdc; }
.pitcher-hr-none { opacity: 0.35; cursor: help; }

/* ---- bottom-right setup controls (2026-09-22) ----
   The dice-camera button and the shadow-boards checkbox moved out of the
   tab row into #controlsWrap, the box that was already reserved beside the
   play-by-play log. They are set once before or between games, not touched
   between pitches, and in the tab row they were squeezing the board result
   into a wrap. */
#controlsWrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
#controlsWrap .controls-title {
  font-family: "Public Sans", sans-serif;
  font-size: 0.82em; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; opacity: 0.45;
}
#controlsWrap .dice-cal-btn { margin: 0; }
/* With those two gone, the result panel owns the rest of the tab row --
   which is the point: at double size the text now has room to run without
   wrapping. */
.grid-tabs #boardResultPanel { flex: 1 1 auto; min-width: 320px; }

/* ------------------------------------------------------------------
   SEASON SCHEDULE (2026-09-24)
   The grid deliberately echoes the league's own workbook -- teams down,
   series across, colour by matchup category -- so a manager recognises
   it rather than having to learn a new layout. Category colours are
   quiet washes, not signals: nothing here is good or bad news.
   ------------------------------------------------------------------ */
#scheduleControls {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 10px; margin: 10px 0 14px;
}
#scheduleLegend { font-size: 0.8em; color: #6b6355; }
.sched-key {
  display: inline-block; width: 11px; height: 11px; border-radius: 2px;
  border: 1px solid var(--line); margin: 0 4px 0 12px; vertical-align: -1px;
}
#scheduleLegend .sched-key:first-child { margin-left: 0; }
.sched-division   { background: #c3dcb8; }
.sched-conference { background: #c6d6ee; }
.sched-interleague{ background: #e4dfcd; }

.sched-grid {
  width: 100%; border-collapse: collapse;
  font-size: 0.72em; font-family: "Public Sans", sans-serif;
}
.sched-grid th, .sched-grid td {
  border: 1px solid var(--line); padding: 3px 5px; text-align: left;
  white-space: nowrap;
}
.sched-grid thead th {
  background: var(--paper); text-align: center;
  font-family: "IBM Plex Mono", monospace;
}
.sched-month { font-size: 0.8em; color: #8a8272; font-weight: 400; }
.sched-grid td.sched-team, .sched-grid th.sched-team {
  font-weight: 600; background: var(--paper); position: sticky; left: 0;
}
.sched-empty { background: repeating-linear-gradient(45deg, #f6f3e9, #f6f3e9 4px, #efece2 4px, #efece2 8px); }

.sched-team-table {
  width: 100%; border-collapse: collapse; font-size: 0.85em;
  font-family: "Public Sans", sans-serif;
}
.sched-team-table th, .sched-team-table td {
  border-bottom: 1px solid #e5e0d0; padding: 5px 8px; text-align: left;
}
.sched-team-table thead th {
  font-family: "IBM Plex Mono", monospace; font-size: 0.85em;
  text-transform: uppercase; letter-spacing: 0.04em; color: #6b6355;
}
.sched-month-cell { font-family: "IBM Plex Mono", monospace; color: #6b6355; }
.sched-dim { color: #8a8272; }
.sched-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: 4px; }
.sched-scroll .sched-grid { border: 0; }
#scheduleLegend .sched-key { width: 14px; height: 14px; }

/* ------------------------------------------------------------------
   SERIES INSTRUCTIONS (2026-09-24)
   Seven steps down the left, the sheet's own blocks in the middle, the
   five requirements always visible on the right. The rail never moves
   and never hides: a manager should be able to see what is still
   missing without navigating anywhere.
   ------------------------------------------------------------------ */
.setup-screen.wide { max-width: 1560px; }

#instrScope {
  display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: end;
  background: var(--paper); border: 1px solid var(--line); border-radius: 6px;
  padding: 10px 14px; margin-bottom: 14px;
}
#instrScope label { display: flex; flex-direction: column; font-size: 0.72em;
  text-transform: uppercase; letter-spacing: 0.05em; color: #6b6355; gap: 3px; }
#instrScope select, #instrScope input {
  font-family: "Public Sans", sans-serif; font-size: 1.15em; padding: 3px 5px;
  border: 1px solid var(--line); border-radius: 3px; background: #fff;
}
#instrScope .instr-name { flex: 1 1 220px; }
#instrScope .instr-name input { width: 100%; }

#instrLayout { display: grid; grid-template-columns: 150px 1fr 290px; gap: 16px; align-items: start; }
#instrSteps { display: flex; flex-direction: column; gap: 4px; position: sticky; top: 12px; }
.instr-step {
  text-align: left; padding: 7px 10px; border: 1px solid var(--line);
  background: var(--paper); border-radius: 4px; cursor: pointer;
  font-family: "Public Sans", sans-serif; font-size: 0.88em;
}
.instr-step.active { background: #2f2a22; color: #f6f3e9; border-color: #2f2a22; }

#instrPanel {
  background: var(--paper); border: 1px solid var(--line); border-radius: 6px;
  padding: 14px 16px; min-height: 420px;
}
#instrPanel h2 { font-family: "Bitter", Georgia, serif; font-size: 1.05em; margin: 0; }
#instrPanel h3 { font-family: "Bitter", Georgia, serif; font-size: 0.92em; margin: 14px 0 6px; }
.instr-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.instr-note { font-size: 0.8em; color: #6b6355; margin: 6px 0 10px; }
.instr-count { font-family: "IBM Plex Mono", monospace; font-size: 1.1em; }
.instr-inline { font-size: 0.8em; color: #6b6355; display: inline-flex; gap: 5px; align-items: center; }
.instr-inline.instr-grow { flex: 1 1 260px; }
.instr-inline input { flex: 1; }
.instr-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

.instr-coverage { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0 14px; }
.instr-cover {
  font-family: "IBM Plex Mono", monospace; font-size: 0.76em;
  border: 1px solid var(--line); border-radius: 3px; padding: 2px 6px;
}
.instr-cover b { margin-left: 5px; }
.instr-cover.instr-ok   { background: #dbecd4; }
.instr-cover.instr-warn { background: #f6e9c8; }
.instr-cover.instr-fail { background: #f3d4d4; }

.instr-pick { display: flex; align-items: baseline; gap: 7px; padding: 2px 0; font-size: 0.85em; }
.instr-pick-name { flex: 1; }
.instr-pick-meta { font-family: "IBM Plex Mono", monospace; font-size: 0.82em; color: #6b6355; }

/* Lineup TABS (2026-09-25). A set holds up to five lineups and the shared
   three-pane builder draws one, so the lineups became tabs above it. The
   defense-totals table below still lists them all, which is what keeps
   comparing them from needing a click each. */
.instr-lu-tabs {
  display: flex; flex-wrap: wrap; gap: 4px;
  border-bottom: 2px solid var(--line); margin-bottom: 0;
}
.instr-lu-tab {
  border: 1px solid var(--line); border-bottom: none;
  border-radius: 5px 5px 0 0; background: #efece2; color: #55503f;
  padding: 6px 14px; cursor: pointer; font: inherit; font-size: 0.9em;
  position: relative; top: 2px;
}
.instr-lu-tab:hover { background: #f6f3ea; }
.instr-lu-tab.active {
  background: white; color: inherit; font-weight: 600;
  border-bottom: 2px solid white;
}
.instr-lu-bar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 9px 10px; border: 1px solid var(--line); border-top: none;
  background: #fcfbf7;
}
.instr-lu-bar label { font-size: 0.88em; color: #6a6454; }
.instr-lu-bar .instr-lu-del { margin-left: auto; }
.instr-lu-builder {
  border: 1px solid var(--line); border-top: none;
  border-radius: 0 0 5px 5px; padding: 10px; margin-bottom: 14px;
  background: white;
}
.instr-lineup { border: 1px solid var(--line); border-radius: 5px; padding: 8px 10px; margin-bottom: 12px; }
/* Arriving here from the Edit button on Choose Teams: say WHICH of the
   set's lineups was clicked, since a set can hold five of them. */
.instr-lineup-flash {
  border-color: #1b6e3c;
  box-shadow: 0 0 0 3px rgba(27, 110, 60, 0.18);
  animation: instrFlash 2.2s ease-out;
}
@keyframes instrFlash {
  0%, 30% { background: #eaf3ec; }
  100%    { background: transparent; }
}
.instr-lineup-table, .instr-rot-table, .instr-hooks,
.instr-tactics, .instr-sft-table, .instr-inj-table {
  width: 100%; border-collapse: collapse; font-size: 0.85em;
}
.instr-lineup-table td, .instr-rot-table td, .instr-hooks td,
.instr-tactics td, .instr-sft-table td, .instr-sft-table th, .instr-inj-table td {
  padding: 2px 4px; border-bottom: 1px solid #e9e4d5; vertical-align: middle;
}
.instr-slot { font-family: "IBM Plex Mono", monospace; color: #6b6355; width: 42px; }
#instrPanel select, #instrPanel input[type="text"] {
  font-family: "Public Sans", sans-serif; font-size: 0.95em; padding: 2px 4px;
  border: 1px solid var(--line); border-radius: 3px; background: #fff; max-width: 100%;
}
.instr-hooks input, .instr-tactics input { width: 100%; }
.instr-sentence { font-size: 0.95em; line-height: 2; }
.instr-sentence input { width: 60%; }

.instr-bp-grid { width: 100%; border-collapse: collapse; font-size: 0.78em; }
.instr-bp-grid th, .instr-bp-grid td { border: 1px solid var(--line); padding: 3px; text-align: center; }
.instr-bp-grid thead th { background: #efece2; font-family: "IBM Plex Mono", monospace; }
.instr-bp-grid tbody th { background: #efece2; text-align: left; white-space: nowrap; font-weight: 600; }
.instr-bp-grid select { display: block; width: 100%; margin: 1px 0; font-size: 0.95em; }
.instr-bp-grid td.instr-blank { background: #faf6ea; }
.instr-rules { display: flex; gap: 14px; flex-wrap: wrap; margin: 8px 0; }
.instr-inj-table select { margin-right: 5px; }

#instrRail {
  background: var(--paper); border: 1px solid var(--line); border-radius: 6px;
  padding: 12px 14px; position: sticky; top: 12px;
}
#instrRail h3 { font-family: "Bitter", Georgia, serif; margin: 0 0 8px; font-size: 0.95em; }
.instr-check { display: flex; gap: 8px; align-items: start; padding: 5px 0; border-bottom: 1px solid #e9e4d5; }
.instr-check:last-of-type { border-bottom: 0; }
.instr-mark { font-family: "IBM Plex Mono", monospace; font-weight: 700; width: 14px; }
.instr-check-label { font-size: 0.84em; }
.instr-check-detail { font-size: 0.76em; color: #6b6355; }
.instr-check.instr-ok   .instr-mark { color: #1b6e3c; }
.instr-check.instr-warn .instr-mark { color: #b07c1a; }
.instr-check.instr-fail .instr-mark { color: #b3181f; }
#instrActivateBtn { width: 100%; margin-top: 10px; }
#instrActivateBtn:disabled { opacity: 0.5; cursor: not-allowed; }

@media (max-width: 1200px) {
  #instrLayout { grid-template-columns: 1fr; }
  #instrSteps { flex-direction: row; flex-wrap: wrap; position: static; }
  #instrRail { position: static; }
  .instr-two-col { grid-template-columns: 1fr; }
}
.instr-bp-grid td.instr-fill { border: 0; background: none; padding-left: 6px; white-space: nowrap; }
.instr-fill button {
  border: 1px solid var(--line); background: var(--paper); border-radius: 3px;
  cursor: pointer; font-size: 0.95em; line-height: 1; padding: 3px 6px; margin: 0 1px;
}
.instr-totals-table { width: 100%; border-collapse: collapse; font-size: 0.85em; }
.instr-totals-table th, .instr-totals-table td {
  border-bottom: 1px solid #e9e4d5; padding: 4px 8px; text-align: left;
}
.instr-totals-table thead th {
  font-family: "IBM Plex Mono", monospace; font-size: 0.85em;
  text-transform: uppercase; letter-spacing: 0.04em; color: #6b6355;
}
.instr-total { font-family: "IBM Plex Mono", monospace; font-size: 1.05em; }
.instr-tier {
  font-size: 0.82em; border-radius: 3px; padding: 1px 6px;
  border: 1px solid var(--line);
}
.instr-tier-ONE   { background: #c3dcb8; }
.instr-tier-TWO   { background: #e4dfcd; }
.instr-tier-THREE { background: #f3d4d4; }
.instr-lu-status { font-size: 0.82em; }
.instr-lu-status.instr-ok   { color: #1b6e3c; }
.instr-lu-status.instr-fail { color: #b3181f; font-weight: 600; }
#instrPanel select.instr-dupe { border-color: #b3181f; background: #fdf2f2; }
.lineup-src {
  margin-left: 8px; font-size: 0.72em; letter-spacing: 0.04em;
  text-transform: uppercase; color: #6b6355;
  border: 1px solid var(--line); border-radius: 3px; padding: 1px 5px;
  background: #efece2;
}


/* ------------------------------------------------------------------
   SEASON ROTATION  (2026-09-25)
   The manager's workbook on screen: an allowance table over a grid of
   fifteen series, each with its two 81-game lanes.
   ------------------------------------------------------------------ */
.instr-sr-arms { border-collapse: collapse; font-size: 0.86em; margin-bottom: 12px; }
.instr-sr-arms th {
  text-align: right; padding: 3px 10px; font-size: 0.85em; color: #6a6454;
  border-bottom: 1px solid var(--line);
}
.instr-sr-arms th:first-child, .instr-sr-arms th:nth-child(2) { text-align: left; }
.instr-sr-arms td { padding: 3px 10px; border-bottom: 1px solid #ece6d4; }
.instr-sr-arm { cursor: pointer; }
.instr-sr-arm:hover { background: #f3efdf; }
/* The armed pitcher — the next slot clicked gets him. */
.instr-sr-arm.armed { background: #d9ecd6; font-weight: 600; }
.instr-sr-arm.armed .instr-sr-name::before { content: "▶ "; color: #1b6e3c; }
.instr-sr-arm.noneleft { opacity: 0.45; }
.instr-sr-name { font-weight: 600; }
.instr-sr-grade { font-family: "IBM Plex Mono", monospace; font-size: 0.9em; color: #6a6454; }

.instr-sr-checks { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-bottom: 14px; }
.instr-sr-checks .instr-check { flex: 0 1 auto; }

.instr-sr-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.instr-sr-series {
  border: 1px solid var(--line); border-radius: 5px; background: #fffdf8;
  padding: 6px 8px; min-width: 190px;
}
.instr-sr-head {
  font-size: 0.78em; text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; color: #6a6454; margin-bottom: 5px;
}
/* The opponent (2026-09-25). Darker and NOT uppercased, so the club name
   reads as a name and carries the eye -- it is the thing being scanned
   for when deciding where the better starters go. The series number is
   still the label; this is the answer. */
.instr-sr-opp {
  text-transform: none; letter-spacing: 0.01em; color: #33383f;
}
.instr-sr-lanes { display: flex; gap: 8px; }
.instr-sr-lane-h {
  font-size: 0.72em; text-transform: uppercase; letter-spacing: 0.05em;
  color: #8a8474; margin-bottom: 3px;
}
.instr-sr-slot {
  display: flex; gap: 5px; align-items: baseline;
  font-size: 0.8em; padding: 2px 5px; border-radius: 3px;
  border: 1px solid transparent; cursor: pointer; white-space: nowrap;
  min-width: 84px;
}
.instr-sr-slot:hover { background: #f3efdf; border-color: var(--line); }
.instr-sr-slot.empty { color: #b3ad9b; }
.instr-sr-slot.empty::after { content: "—"; }
.instr-sr-g {
  font-family: "IBM Plex Mono", monospace; font-size: 0.85em; color: #a05a2c;
  min-width: 10px;
}
/* Both ends of a rest violation, so the pair is visible at once rather
   than only the start that broke the rule. */
.instr-sr-slot.rest-bad {
  background: #fdf2f2; border-color: #d99; color: #b3181f; font-weight: 600;
}


/* ------------------------------------------------------------------
   PLAY A SERIES  (2026-09-25)
   ------------------------------------------------------------------ */
.sched-playable { cursor: pointer; }
.sched-grid .sched-playable:hover { outline: 2px solid #a05a2c; outline-offset: -2px; }
.sched-team-table tr.sched-playable:hover { background: #f3efdf; }

.series-lanes { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
.series-lane {
  flex: 1 1 320px; border: 1px solid var(--line); border-radius: 5px;
  background: #fffdf8; padding: 8px 10px;
}
.series-lane-head {
  font-size: 0.85em; font-weight: 700; color: #55503f; margin-bottom: 6px;
}
.series-lane-table { width: 100%; border-collapse: collapse; font-size: 0.86em; }
.series-lane-table td { padding: 4px 6px; border-bottom: 1px solid #ece6d4; }
.series-game { cursor: pointer; }
.series-game:hover:not(.played) { background: #f3efdf; }
/* The game that will be played. Its lane-mate rows stay plain so the one
   selection is unmistakable across both lanes. */
.series-game.picked { background: #d9ecd6; }
.series-game.picked .series-g { color: #1b6e3c; font-weight: 700; }
.series-game.played { opacity: 0.55; cursor: default; }
.series-g {
  font-family: "IBM Plex Mono", monospace; color: #a05a2c; width: 18px; text-align: center;
}
.series-state { text-align: right; white-space: nowrap; font-size: 0.92em; }
.series-lane-table th {
  text-align: left; font-size: 0.85em; font-weight: 600; color: #6a6454;
  padding: 2px 6px 5px; border-bottom: 1px solid var(--line);
}
.series-lane-note { margin-top: 7px; font-size: 0.8em; color: #8a5a1c; font-style: italic; }
.series-h3 { margin: 4px 0 2px; font-size: 1em; }
.series-sets { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 6px; }
.series-set { display: flex; align-items: center; gap: 8px; font-size: 0.88em; }

.schedule-launch-banner {
  max-width: 1100px; margin: 0 auto 12px; padding: 8px 12px;
  background: #eaf3ec; border: 1px solid #b9d6c2; border-radius: 4px;
  font-size: 0.88em; display: flex; align-items: center; gap: 12px;
  font-family: "Public Sans", sans-serif;
}
.schedule-launch-banner button { margin-left: auto; }

/* ---------- logins (Build 77) ----------
   The login panes live inside .start-screen, which is centre-aligned for
   the title and the club-mark sheet. Forms read badly centred, so the
   pane resets to left and stacks label-over-field. */
.auth-pane {
  text-align: left;
  margin: 18px auto 0;
  max-width: 340px;
}
.auth-pane h2 { margin: 0 0 10px; font-family: "Bitter", Georgia, serif; }
.auth-pane label {
  display: block;
  margin: 10px 0;
  font-size: 0.85em;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #6b6455;
}
.auth-pane input {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 8px 10px;
  font: inherit;
  font-size: 1em;
  text-transform: none;
  letter-spacing: normal;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 3px;
  background: white;
}
.auth-pane .auth-note {
  font-size: 0.9em;
  color: #6b6455;
  line-height: 1.45;
  margin: 0 0 4px;
}
.auth-pane button { margin-top: 16px; margin-right: 8px; }
.auth-pane .setup-error { margin-top: 12px; }

/* The header strip. The chip is the whole point of two separate logins:
   you should be able to tell from across the room which one is open. */
.session-strip {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  font-size: 0.9em;
}
.session-who { font-weight: 700; }
.session-teams { color: #6b6455; }
.session-strip button { margin-top: 0; font-size: 0.8em; padding: 4px 10px; }
.sys-chip {
  background: #33383f;
  color: #f4f1e8;
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.72em;
  font-weight: 700;
  letter-spacing: 0.09em;
  padding: 3px 8px;
  border-radius: 3px;
}

.instr-readonly-note {
  color: #8a5a12;
  font-size: 0.88em;
  font-style: italic;
  margin-left: 10px;
}

/* ---------- the Managers screen ---------- */
.mgr-table { width: 100%; border-collapse: collapse; }
.mgr-table th {
  text-align: left;
  font-size: 0.78em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #6b6455;
  border-bottom: 1px solid var(--line);
  padding: 6px 8px;
}
.mgr-table td { padding: 8px; border-bottom: 1px solid #e4dfd1; vertical-align: top; }
.mgr-team-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 2px 14px;
  margin-top: 10px;
}
.mgr-team {
  font-size: 0.88em;
  text-transform: none;
  letter-spacing: normal;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 6px;
}
.mgr-team input { width: auto; display: inline-block; margin: 0; }

/* Which build is actually running (Build 79). Quiet -- it is reference,
   not news -- but always there, so an upgrade that did not take is
   visible on the screen rather than only in a zip listing. */
.build-stamp {
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.72em;
  letter-spacing: 0.08em;
  color: #9a937f;
  margin: 18px 0 0;
}

/* ---- the grade just moved (Build 80) --------------------------------
   Light red when the pitcher drops a rung, light green when he gains
   one, on the bar itself -- that is the boxed field the manager reads.
   Raised and cleared by the server (PitcherState.grade_alert); this file
   only paints it.

   The words carry the meaning and the colour is the thing that catches
   the eye across the room. Colour alone would be no use to a manager who
   cannot easily tell red from green, and would not say which way the
   grade went in any case. */
#pitcherBar.grade-reduced {
  background: #fbe2e2;
  border-color: #c98b8b;
}
#pitcherBar.grade-advanced {
  background: #e2f3e4;
  border-color: #8fb894;
}
/* A colour change that simply appears reads as a glitch; half a second
   of fade reads as the app telling you something. */
#pitcherBar { transition: background-color .35s ease, border-color .35s ease; }

.pitcher-alert {
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.72em;
  font-weight: 700;
  letter-spacing: 0.1em;
  padding: 3px 9px;
  border-radius: 3px;
  white-space: nowrap;
}
.pitcher-alert.alert-reduced { background: #b3181f; color: #fff; }
.pitcher-alert.alert-advanced { background: #2f6b39; color: #fff; }

/* ---- Supplemental Fielding Chart panel (Build 82) -------------------
   Takes the hitter card's place, so it borrows its box: same width and
   border, or the whole column would jump when the chart appears and the
   scorebook beside it would reflow mid-play. */
#fieldChartPanel {
  width: 320px;
  flex-shrink: 0;
  align-self: flex-start;
  border: 2px solid var(--line);
  background: var(--paper);
  border-radius: 4px;
  padding: 12px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
#fieldChartLabel {
  width: 100%;
  text-align: center;
  font-size: 0.75em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #6b6455;
  border-bottom: 1px solid var(--line);
  padding-bottom: 6px;
  margin-bottom: 10px;
}
#fieldChartWho { text-align: center; margin-bottom: 12px; }
.fc-fielder {
  font-family: "Bitter", Georgia, serif;
  font-size: 1.15em;
  font-weight: 700;
  color: var(--home-dark);
}
.fc-row {
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.85em;
  color: #6b6455;
  letter-spacing: 0.04em;
}
#fieldChartBands { width: 100%; }
.fc-band {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 10px;
  align-items: baseline;
  padding: 7px 9px;
  margin-bottom: 4px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: #fff;
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.9em;
}
.fc-col { font-size: 0.78em; letter-spacing: 0.06em; color: #6b6455; }
.fc-range { font-weight: 700; font-variant-numeric: tabular-nums; }
.fc-odds { font-size: 0.78em; color: #9a937f; font-variant-numeric: tabular-nums; }
/* "---" on the printed chart: unreachable at this rating. Shown rather
   than hidden -- that a column CANNOT be reached is worth knowing. */
.fc-none { opacity: 0.45; background: #f3f0e6; }
/* Where the dice actually landed, and (for a silent line) the column his
   rating produces. Different words, same emphasis. */
.fc-hit, .fc-chosen {
  background: #fbe2e2;
  border-color: var(--home-color);
  border-width: 2px;
}
.fc-hit .fc-range, .fc-chosen .fc-range { color: var(--home-dark); }
#fieldChartNote { width: 100%; margin-top: 10px; font-size: 0.85em; line-height: 1.4; }
.fc-dim { color: #8a8474; }
.fc-landed { color: var(--home-dark); font-weight: 700; }
.fc-warn { color: #8a5a12; }

/* `hidden` is only a default, and `display: flex` beats it.
   Both of these panels set display, so without this the attribute did
   nothing at all and the two sat on screen together (caught 2026-09-25 --
   and caught LATE, because the harness was reading el.hidden, which was
   perfectly true, rather than what the browser was actually drawing). */
#hitterCardPanel[hidden], #fieldChartPanel[hidden] { display: none; }

/* ---- looking at players mid-game (Build 85) -------------------------
   The roster takes the card's place in the same box, so the column does
   not jump when it opens -- same reasoning as the fielding chart. */
#rosterPanel {
  width: 320px;
  flex-shrink: 0;
  align-self: flex-start;
  border: 2px solid var(--line);
  background: var(--paper);
  border-radius: 4px;
  padding: 12px 10px;
  display: flex;
  flex-direction: column;
  max-height: 78vh;
}
#hitterCardPanel[hidden], #fieldChartPanel[hidden], #rosterPanel[hidden] { display: none; }
#rosterPanelLabel {
  text-align: center;
  font-size: 0.75em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #6b6455;
  border-bottom: 1px solid var(--line);
  padding-bottom: 6px;
  margin-bottom: 8px;
}
#rosterTeamName {
  display: block;
  margin-top: 3px;
  font-family: "Bitter", Georgia, serif;
  font-size: 1.25em;
  text-transform: none;
  letter-spacing: normal;
  color: var(--home-dark);
}
#rosterList { overflow-y: auto; flex: 1; }
.roster-group {
  font-size: 0.68em;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: #9a937f;
  margin: 8px 0 3px;
  border-bottom: 1px solid #e4dfd1;
}
.roster-row {
  display: grid;
  grid-template-columns: 1fr auto 3.1em;
  align-items: baseline;
  gap: 0 7px;
  padding: 2px 6px;
  border-radius: 3px;
  cursor: pointer;
  font-size: 0.87em;
  white-space: nowrap;
}
.roster-row:hover { background: #f1ede0; }
.roster-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.roster-pos {
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.82em;
  color: #6b6455;
}
.roster-status {
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.74em;
  color: #9a937f;
  text-align: right;
}
/* In the order now / pitching now: the men actually in the game. */
.roster-order, .roster-pitching { background: #fbfaf6; box-shadow: inset 2px 0 0 var(--home-color); }
/* Out of the game for good -- 5.10(d). Struck through for the same
   reason the pitching-change list strikes a used arm: it is the one
   thing about a name here that changes what you may do. */
.roster-used { opacity: 0.55; }
.roster-used .roster-name { text-decoration: line-through; }
.roster-empty, #rosterHint {
  font-size: 0.8em;
  color: #9a937f;
  text-align: center;
  margin-top: 8px;
}
#rosterToggleBtn {
  margin-top: 10px;
  width: 320px;
  padding: 7px 10px;
  font: inherit;
  font-size: 0.9em;
  background: #f1ede0;
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
}
#rosterToggleBtn:hover { background: #e7e1d0; }
#rosterToggleBtn.roster-on { background: var(--home-color); color: #fff; border-color: var(--home-dark); }
#rosterPausedNote {
  width: 320px;
  margin-top: 6px;
  font-size: 0.78em;
  color: #8a5a12;
  text-align: center;
}
.batter-cell.card-openable { cursor: pointer; }

/* The card itself, centred over the scorebook. Click anywhere to close. */
#cardOverlay {
  position: absolute;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(28, 28, 28, 0.35);
  cursor: pointer;
}
#cardOverlay[hidden] { display: none; }
#cardOverlayInner {
  background: var(--paper);
  border: 2px solid var(--line);
  border-radius: 5px;
  padding: 14px;
  box-shadow: 0 8px 30px rgba(0,0,0,0.3);
  max-height: 88vh;
  overflow-y: auto;
}
.card-overlay-loading { padding: 30px 40px; color: #6b6455; font-size: 0.95em; }
.po-pitcher { text-align: center; padding: 18px 26px; min-width: 260px; }
.po-name { font-family: "Bitter", Georgia, serif; font-size: 1.5em; color: var(--home-dark); }
.po-sub { font-size: 0.85em; color: #6b6455; margin-bottom: 10px; }
.po-grade { font-family: "IBM Plex Mono", monospace; font-size: 1.6em; font-weight: 700; }
.po-line { font-family: "IBM Plex Mono", monospace; margin-top: 6px; color: #4a4437; }
.po-note { margin-top: 12px; font-size: 0.82em; color: #8a8474; max-width: 260px; }

/* #cardOverlay is absolutely positioned inside this, so it needs a
   positioned ancestor or it would size itself against the page and sit
   over the scoreboard too. */
.game-screen { position: relative; }

/* An unrated defensive option (Build 88). Offered by choice now, but it
   costs Fielding 3 on every play, so it is marked rather than left to
   look like any other name on the list. */
#ctxMenu button.ctx-unrated { background: #fdf6ec; }
#ctxMenu button.ctx-unrated:hover { background: #f8ead2; }
#ctxMenu button.ctx-unrated .ctx-detail { color: #8a5a12; }

/* ---------------------------------------------------------------------
   ROLLING DICE (Build 89)
   -------------------------------------------------------------------
   2026-09-26: "it would be ideal to have each die roll on the screen
   independently and bounce around within the scorebook area only where
   hitter cells are shown... while they are rolling they should be twice
   the size they are now. and after they stop rolling and the hitters card
   is highlighted they can move to where they are not and resize smaller."

   The layer sits over #gridArea and is inert to the mouse -- a die
   crossing the scorebook must never swallow a right-click meant for the
   lineup underneath it. Each die's position/rotation is written straight
   onto its own transform by app.js (rAF), so there are no keyframes to
   keep in step with the flight time; the only CSS animation here is the
   HAND-OFF at the end, which is a plain transition, so a flight can be
   cut short at any moment without leaving a half-played keyframe behind.
   --------------------------------------------------------------------- */
#diceFlight {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 45;            /* over the grid and the zero pop, under modals */
}
#diceFlight[hidden] { display: none; }
.die-fly {
  position: absolute;
  top: 0;
  left: 0;
  width: 84px;
  height: 84px;
  padding: 9px;
  /* The flight itself is written frame by frame, so only the hand-off is
     a transition -- and it is added as a class, never left on, so a new
     throw starts from a clean slate. */
  will-change: transform;
  box-shadow: 0 10px 22px rgba(0,0,0,0.38), inset 0 0 0 1px rgba(0,0,0,0.15);
}
.die-fly.landing {
  transition: transform 420ms cubic-bezier(.4,.1,.3,1),
              width 420ms cubic-bezier(.4,.1,.3,1),
              height 420ms cubic-bezier(.4,.1,.3,1),
              padding 420ms cubic-bezier(.4,.1,.3,1),
              opacity 300ms ease 120ms;
}
/* The sidebar pair steps aside while its dice are in the air, so there
   are never two readings of the same roll on screen at once. */
#dicePair.in-flight { opacity: 0.18; }

/* The settings, in the same bottom-right box as the camera button --
   set once and left alone. */
#diceAnimWrap {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 0.78em;
  color: #4a4437;
}
.dice-anim-row {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}
.dice-anim-row input[type="range"] { flex: 1; min-width: 70px; height: 14px; }
.dice-anim-val {
  font-family: "IBM Plex Mono", monospace;
  font-size: 0.95em;
  color: #6b6455;
  min-width: 34px;
  text-align: right;
}
.dice-anim-note { color: #8a8474; font-size: 0.95em; min-height: 1em; }
.dice-anim-note.missing { color: #8a5a12; }

/* "Reduce motion" is honoured by defaulting the throw OFF (see
   app.js's diceAnim.on) rather than by animating differently -- a die
   skidding across the scorebook is the whole effect, so there is no
   gentler version of it to fall back to. The hand-off transition is
   still switched off here for anyone who turns the throw back on. */
@media (prefers-reduced-motion: reduce) {
  .die-fly.landing { transition: none; }
}

/* The silent-fielding toggle sits in the same Setup box as the dice
   settings and reads as one list with them (Build 92). */
#silentFieldWrap { margin-top: 8px; font-size: 0.78em; color: #4a4437; }

/* ---------------------------------------------------------------------
   ROSTERS and TRADEWIRE (Build 93)
   Both copy the league's own paper as closely as the screen allows: the
   roster report is a plain alphabetical table, and a trade is two columns
   headed by what each team RECEIVED.
   --------------------------------------------------------------------- */
#rostersControls, #wireControls {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 6px 0 4px;
  flex-wrap: wrap;
}
table.roster-report {
  border-collapse: collapse;
  width: 100%;
  max-width: 900px;
  background: var(--paper);
  border: 2px solid var(--line);
  font-size: 0.92em;
}
table.roster-report th {
  text-align: left;
  background: #efeada;
  border-bottom: 2px solid var(--line);
  padding: 5px 9px;
  font-size: 0.85em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #5c5647;
}
table.roster-report td { padding: 4px 9px; border-bottom: 1px solid #e2ddcd; }
table.roster-report td.num, table.roster-report th.num { text-align: right; }
table.roster-report tr:hover td { background: #faf6e9; }
.rr-name { font-weight: 600; }
.rr-lim { font-family: "IBM Plex Mono", monospace; font-size: 0.9em; color: #4a4437; }
.rr-unc { color: #b3181f; font-weight: 700; }
tr.unc-row .rr-name { color: #8a8474; }

.picks-title {
  font-family: "Bitter", Georgia, serif;
  font-size: 1.15em;
  margin: 18px 0 6px;
  color: var(--home-dark);
}
.picks-wrap { display: flex; gap: 26px; flex-wrap: wrap; }
.pick-group {
  border: 2px solid var(--line);
  background: var(--paper);
  padding: 8px 12px;
  min-width: 210px;
}
.pick-group-title {
  font-size: 0.78em;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #5c5647;
  border-bottom: 1px solid #e2ddcd;
  padding-bottom: 3px;
  margin-bottom: 4px;
}
.pick-row { font-size: 0.92em; padding: 2px 0; }
.pick-trade { font-family: "IBM Plex Mono", monospace; font-size: 0.82em; color: #8a8474; }
.pick-to { color: #8a5a12; font-size: 0.88em; }
.picks-acquired .pick-row { color: #1d6a33; }
.picks-gone .pick-row { color: #8a8474; text-decoration: line-through; }
.picks-gone .pick-to { text-decoration: none; }

.wire-trade {
  border: 2px solid var(--line);
  background: var(--paper);
  margin-bottom: 12px;
  max-width: 900px;
}
.wire-trade.wire-review { border-color: #b3181f; }
.wire-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  background: #efeada;
  border-bottom: 2px solid var(--line);
  padding: 5px 10px;
}
.wire-id { font-family: "IBM Plex Mono", monospace; font-weight: 700; color: var(--home-dark); }
.wire-team { font-family: "Bitter", Georgia, serif; font-size: 1.02em; }
.wire-team + .wire-team::before { content: '↔ '; color: #8a8474; }
.wire-flag {
  margin-left: auto;
  color: #b3181f;
  font-size: 0.78em;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.wire-sides { display: flex; }
.wire-side { flex: 1; padding: 7px 10px; }
.wire-side + .wire-side { border-left: 1px solid #e2ddcd; }
.wire-side-head {
  font-size: 0.78em;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #5c5647;
  margin-bottom: 4px;
}
.wire-asset { font-size: 0.93em; padding: 1px 0; }
.wire-detail { font-family: "IBM Plex Mono", monospace; font-size: 0.85em; color: #6b6455; }
.wire-pick { color: #1d4f8a; }
.wire-none { color: #8a8474; }
.rr-nocard {
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #b3181f;
  border: 1px solid #e0b6b8;
  border-radius: 3px;
  padding: 0 4px;
  vertical-align: 1px;
}
.rr-legend { color: #8a8474; }

/* Manager email (Build 94): shown in the table, edited in place. */
.mgr-email-cell a { color: var(--home-dark); }
.mgr-email-cell .lineup-src { display: block; }
.link-btn {
  background: none;
  border: none;
  color: #1d4f8a;
  font-size: 0.8em;
  cursor: pointer;
  padding: 0 2px;
  text-decoration: underline;
}

/* Roster cuts (Build 95). A cut man stays on the report, struck through,
   with what the cut MEANT for him spelled out -- the draft pool for a
   carded player, out of the league for a no-card one. */
tr.cut-row td { background: #f4efe2; }
tr.cut-row .rr-name { text-decoration: line-through; color: #8a8474; }
.rr-cut {
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #8a5a12;
  border: 1px solid #ddc59a;
  border-radius: 3px;
  padding: 0 4px;
  vertical-align: 1px;
  text-decoration: none;
  display: inline-block;
}
.rr-cut-cell { text-align: right; white-space: nowrap; }
.draft-chip {
  background: #1d4f8a; color: #fff; font-size: 0.7em; letter-spacing: 0.06em;
  padding: 1px 5px; border-radius: 3px;
}
.mgr-draft-lbl { font-size: 0.82em; white-space: nowrap; }

/* The workbook import panel (Build 96). */
#importWrap { max-width: 900px; margin: 10px 0 14px; }
.import-report {
  margin-top: 8px;
  border: 2px solid var(--line);
  background: var(--paper);
  padding: 8px 12px;
  font-size: 0.88em;
}
.import-report h4 {
  margin: 0 0 4px;
  font-family: "Bitter", Georgia, serif;
  font-size: 1em;
  color: var(--home-dark);
}
.import-line { padding: 1px 0; }
.import-problem { color: #b3181f; }
.import-ok { color: #1d6a33; }
.import-dim { color: #8a8474; }

/* The draft pool (Build 97). The number is the point of the row -- it is
   what a manager types to make a pick -- so it is set in the board's own
   figures and given room. */
#poolControls { display: flex; gap: 14px; align-items: baseline;
                flex-wrap: wrap; margin: 6px 0 10px; }
.pool-notes { margin: 0 0 10px; }
table.pool-table { max-width: 980px; }
table.pool-table td.pool-num { font-weight: 700; font-variant-numeric: tabular-nums;
                               width: 3.5em; color: #4a4336; }
table.pool-table tr.pool-taken td { color: #a9a294; text-decoration: line-through; }
.pool-tag { font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
            background: #e8eef0; color: #2f5b66; border-radius: 3px;
            padding: 1px 5px; margin-right: 5px; }
td.pool-note { white-space: nowrap; }
td.pool-pos { white-space: nowrap; }
.pool-pos-more { color: #96907f; font-size: 12px; margin-left: 6px; }
td.pool-slash, td.pool-ratios { font-variant-numeric: tabular-nums;
                                white-space: nowrap; font-size: 13px; }
td.pool-ratios { color: #4a4336; }
th.pool-stat-head { white-space: nowrap; }

/* ------------------------------------------------------------------
   PRINT (Build 101). "Save as PDF" in the print dialogue is the PDF
   export, so what the printer sees has to be the report and nothing
   else: no start-page art, no controls, no buttons. The print surface
   is built on demand into #printArea and emptied again afterwards.
   ------------------------------------------------------------------ */
#printArea { display: none; }
.print-doc h1 { font-size: 20pt; margin: 0 0 2pt; }
.print-doc h2 { font-size: 14pt; margin: 0 0 2pt; }
.print-sub { color: #6b6455; font-size: 9pt; margin: 0 0 10pt; }
.print-picks { font-size: 9pt; margin: 4pt 0 0; }
.print-team { margin: 0 0 16pt; }

@media print {
  /* Everything that is not the print surface goes away, including the
     screen that was showing when Print was pressed. */
  body.printing > *:not(#printArea) { display: none !important; }
  body.printing #printArea { display: block !important; }
  body { background: #fff; color: #000; }
  .print-break { break-before: page; page-break-before: always; }
  table.roster-report, table.pool-table { width: 100%; font-size: 9pt;
                                          border-collapse: collapse; }
  table.roster-report th, table.pool-table th { background: #eee !important;
        -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  table.roster-report tr, .wire-trade, .print-team { break-inside: avoid;
                                                     page-break-inside: avoid; }
  thead { display: table-header-group; }
  a[href]:after { content: ""; }
  @page { margin: 12mm; }
}
/* The pool's two-row header: the name on top, what the numbers are under
   it. The sub-row is quieter than the name and sits tight against it. */
table.pool-table thead th { vertical-align: bottom; }
table.pool-table th.pool-sub { font-weight: 400; font-size: 11px;
                               letter-spacing: .02em; color: #7a7364;
                               padding-top: 0; white-space: nowrap; }

/* ------------------------------------------------------------------
   THE TRADE DESK (Build 103)
   ------------------------------------------------------------------ */
/* The landing page in blocks, grouped by what you are doing. The League
   Office block is absent, not greyed, for anyone without the access. */
.start-blocks { display: grid; gap: 18px 34px; margin: 14px 0 6px;
                grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
                text-align: left; max-width: 900px; }
.start-block h2 { font-size: 13px; text-transform: uppercase;
                  letter-spacing: .08em; color: #8a8474; margin: 0 0 6px;
                  border-bottom: 1px solid #e2ddcd; padding-bottom: 4px; }
.start-block .start-hint { margin: 4px 0; }
.start-block.league-office h2 { color: #7a5230; border-bottom-color: #e6d3bd; }
.start-badge { display: inline-block; min-width: 18px; padding: 0 5px;
               border-radius: 9px; background: #7a5230; color: #fff;
               font-size: 11px; line-height: 17px; text-align: center;
               margin-left: 5px; }

/* Building a trade: the club you started from on the left, the club you
   are trading with on the right. */
.trade-bar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
             background: #f3ecd9; border: 1px solid #d8d0bc; border-radius: 6px;
             padding: 8px 12px; margin: 8px 0 12px; }
.trade-split { display: block; }
.trade-split.trading { display: grid; grid-template-columns: 1fr 1fr; gap: 22px;
                       align-items: start; }
.trade-partner { border-left: 1px solid #e2ddcd; padding-left: 20px; }
.trade-partner-head { display: flex; justify-content: space-between;
                      align-items: baseline; font-weight: 700; margin-bottom: 8px; }
.trade-team-picker { max-height: 420px; overflow-y: auto; border: 1px solid #e2ddcd;
                     border-radius: 6px; padding: 4px; }
.trade-team { padding: 5px 9px; cursor: pointer; border-radius: 4px; }
.trade-team:hover { background: #f3ecd9; }
table.trade-table th.trade-col, table.trade-table td.trade-col {
  width: 3.6em; text-align: center; }
.trade-sub .trade-status { margin-left: auto; font-size: 12px;
                           text-transform: uppercase; letter-spacing: .04em;
                           color: #7a5230; }
.trade-tally { font-size: 12px; color: #6b6455; margin: 6px 0 0; }
.trade-actions { display: flex; gap: 8px; align-items: center; margin-top: 8px;
                 flex-wrap: wrap; }
.trade-agreed { font-size: 11px; color: #1d6a33; margin-top: 4px; }
.trade-approved { border-left: 3px solid #1d6a33; }
.trade-denied, .trade-rejected_by_team, .trade-cancelled { opacity: .68; }

/* Notices */
.notice { border: 1px solid #e2ddcd; border-radius: 6px; padding: 10px 12px;
          margin: 0 0 10px; }
.notice-head { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.notice-body { white-space: pre-wrap; font-size: 12px; color: #4a4336;
               background: #faf6e9; border-radius: 4px; padding: 8px;
               margin: 6px 0 0; max-height: 260px; overflow-y: auto; }
.notice-sent { border-color: #bcd8c2; }
.notice-failed { border-color: #d8bcbc; }

/* The league calendar and the trading gates (Build 104) */
.cal-window { background: #f3ecd9; border: 1px solid #d8d0bc; border-radius: 6px;
              padding: 10px 14px; margin: 0 0 14px; }
.cal-now { font-size: 15px; }
.cal-done { color: #1d6a33; }
.trade-blocked { background: #f6ecec; border: 1px solid #ddc6c6; border-radius: 6px;
                 padding: 8px 12px; margin: 0 0 12px; color: #7a3030; }
.trade-team-out { color: #a9a294; }
.trade-unavailable { font-size: 11px; text-transform: uppercase;
                     letter-spacing: .04em; }
button.btn-blocked { opacity: .5; cursor: not-allowed; }
.mgr-flag { display: block; font-size: 11px; color: #6b6455; }

/* ---------------------------------------------------------------------
   SOLID DICE (Build 105)
   A die is six faces on a box. The layer gets the perspective, so both
   dice are seen from one camera rather than each from its own -- two
   independent perspectives on the same table is the thing that makes
   cheap 3D look like cardboard.
   --------------------------------------------------------------------- */
#diceFlight { perspective: 900px; perspective-origin: 50% 40%; }
.die-fly.cube-mode {
  background: none;
  box-shadow: none;
  display: block;
  padding: 0;
  transform-style: preserve-3d;
}
.die-cube {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  will-change: transform;
}
.cube-face {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  padding: 10.7%;
  border-radius: 12%;
  backface-visibility: hidden;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.18);
}
/* The faces turned away from the light are darker: without it a cube
   reads as a flat cross of squares, because every face is lit the same. */
.cube-face[data-face="6"], .cube-face[data-face="4"] { filter: brightness(0.80); }
.cube-face[data-face="3"], .cube-face[data-face="2"] { filter: brightness(0.90); }
.cube-face[data-face="5"] { filter: brightness(1.06); }
.die-shadow {
  position: absolute;
  top: 0;
  left: 0;
  border-radius: 50%;
  background: radial-gradient(ellipse at center,
              rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.28) 55%, rgba(0,0,0,0) 72%);
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
}
.die-shadow[hidden] { display: none; }

/* =====================================================================
   THE DRAFT BOARD (Build 108)
   =====================================================================
   Three columns: the pool on the left because that is what a manager is
   reading while he waits, the board in the middle, the clubs and the
   tally on the right. It collapses to one column on a narrow screen --
   they draft on laptops, not phones, but a squashed three-column board
   is unreadable and a stacked one is merely long.
   ===================================================================== */
#draftMain {
  display: grid;
  grid-template-columns: minmax(300px, 1fr) minmax(420px, 1.4fr) minmax(280px, 1fr);
  gap: 14px;
  align-items: start;
}
@media (max-width: 1100px) { #draftMain { grid-template-columns: 1fr; } }

#draftPickPanel, #draftBoardPanel, #draftTeamPanel {
  background: #fffdf5;
  border: 1px solid #e2ddcd;
  border-radius: 6px;
  padding: 8px 10px 12px;
}
#draftPoolBody, #draftBoardBody { max-height: 62vh; overflow: auto; }
#draftPickControls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin-bottom: 6px; }

/* Who is on the clock, said once, large, at the top -- the one thing
   everybody in the room is looking for. */
.draft-onclock { display: flex; flex-wrap: wrap; gap: 12px; align-items: baseline; }
.draft-onclock-pick { font-weight: 700; font-size: 1.15rem; font-variant-numeric: tabular-nums; }
.draft-onclock-team { font-size: 1.25rem; font-weight: 700; }
.draft-onclock-from { color: #7a7362; font-style: italic; }
.draft-onclock.is-mine .draft-onclock-team { color: #1d6b2f; }
.draft-entry { display: flex; gap: 8px; align-items: center; }

/* The round strip. The lottery round is marked, because it is the one
   round whose order is not the order everywhere else. */
#draftRoundBar { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; }
.draft-round-btn {
  border: 1px solid #d8d2c0; background: #fffdf5; border-radius: 4px;
  padding: 3px 8px; cursor: pointer; font: inherit; line-height: 1.1;
}
.draft-round-btn.active { background: #2f5d8a; color: #fff; border-color: #2f5d8a; }
.draft-round-btn.lottery { border-color: #b8860b; border-width: 2px; }
.draft-round-made { display: block; font-size: 0.72rem; opacity: 0.7; }

table.draft-board tr.is-made td { background: #f6f3e6; }
table.draft-board tr.is-blocked td { color: #a9a294; }
table.draft-board tr.is-onclock td { background: #eef4fb; font-weight: 600; }
.draft-blockednote, .draft-onclocknote { font-style: italic; color: #7a7362; }
.draft-onclocknote { color: #2f5d8a; }
.draft-from { color: #7a7362; font-size: 0.85em; font-style: italic; }
td.draft-name { font-weight: 600; }

table.draft-teams tr.is-done td, table.draft-teams tr.is-full td { color: #a9a294; }
table.draft-teams tr.is-mine td:first-child { font-weight: 700; }
table.draft-pool-pick tr.is-taken td { color: #a9a294; text-decoration: line-through; }
table.draft-pool-pick tr.is-pickable { cursor: pointer; }
table.draft-pool-pick tr.is-pickable:hover td { background: #eef4fb; }

/* The order, with its working. The "why" column is the point of the
   table: an order nobody can check is an order somebody will argue with. */
table.draft-order td.draft-why { font-size: 0.85em; color: #5c5647; max-width: 420px; }
table.draft-order tr.draft-unsettled td { background: #fdf6e3; }
td.draft-move button { padding: 0 4px; }
.draft-flip { padding: 3px 0; }

.draft-menu {
  position: absolute; z-index: 60; background: #fffdf5;
  border: 1px solid #b9b2a0; border-radius: 5px; box-shadow: 0 6px 18px rgba(0,0,0,0.18);
  min-width: 190px; padding: 4px 0;
}
.draft-menu-head { padding: 4px 12px; font-weight: 700; border-bottom: 1px solid #e2ddcd; }
.draft-menu button {
  display: block; width: 100%; text-align: left; border: 0; background: none;
  font: inherit; padding: 6px 12px; cursor: pointer;
}
.draft-menu button:hover { background: #eef4fb; }

/* =====================================================================
   THE OFFENSIVE CALL (Build 113)
   =====================================================================
   Hit & Run and Sacrifice, under the Defense Close/Deep control, because
   they are the same kind of decision: what each manager settles before
   the pitch. Green when called, and only ever one of the two.
   ===================================================================== */
#offenseCallWrap {
  width: 100%;
  margin-top: 6px;
  padding: 5px 6px;
  border: 1px solid var(--line, #c9c1ac);
  border-radius: 4px;
  background: #efe9d8;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
#offenseCallLabel {
  display: block;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  font-size: 0.68em;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #6b5f47;
}
.offense-call-btn {
  width: 100%;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: bold;
  font-size: 0.78em;
  letter-spacing: 0.02em;
  padding: 4px 6px;
  border: 1px solid var(--line, #c9c1ac);
  border-radius: 3px;
  background: #fffdf5;
  color: #3b3427;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.offense-call-btn:hover { background: #f6f1e0; }
/* Called. The green is dark enough to read white type on, so the state
   does not depend on colour alone -- the weight and the border change
   too, and the button carries aria-pressed for anyone not seeing it. */
.offense-call-btn.is-on {
  background: #1d7a35;
  border-color: #145925;
  color: #fff;
}
.offense-call-btn.is-on:hover { background: #1a6c2f; }
/* No chart for these runners, so there is no call to make: a hit and run
   without a man on 1st, a sacrifice with the bases empty. Greyed rather
   than hidden so the control keeps its place and the manager can see the
   call exists and is simply not available right now -- the button's own
   title says which. */
.offense-call-btn:disabled,
.offense-call-btn:disabled:hover {
  background: #ece8dc;
  border-color: #dcd6c4;
  color: #8d8776;
  cursor: not-allowed;
  opacity: 0.55;
}

/* Restoring a league. Its own small modal rather than a reused one: this
   is the only control in the app that can replace a season, and it should
   not look like the others. */
.restore-modal-box { max-width: 560px; }
.restore-modal-box input[type="file"] {
  display: block;
  width: 100%;
  margin: 10px 0;
  font-size: 0.9em;
}
.restore-actions {
  display: flex;
  gap: 8px;
  margin-top: 14px;
  flex-wrap: wrap;
}
/* What the dry run found. Monospaced because it is a manifest -- file
   counts and season names line up and are meant to be compared. */
.restore-report {
  font-family: Consolas, "Courier New", monospace;
  font-size: 0.82em;
  line-height: 1.5;
  background: #fffdf5;
  border: 1px solid var(--line, #c9c1ac);
  border-radius: 3px;
  padding: 8px 10px;
  margin: 10px 0;
  white-space: pre-wrap;
}
.restore-report.warn { border-color: #9a3412; background: #fdf3ee; }

/* Head to head: whether the other manager is here. Its own strip across
   the top of the game screen rather than a corner badge -- a game that is
   waiting for somebody looks exactly like a game that is simply paused,
   and the difference is the one thing a manager cannot work out for
   himself. */
.h2h-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 14px;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-size: 0.85em;
  font-weight: bold;
  letter-spacing: 0.02em;
  border-bottom: 1px solid var(--line, #c9c1ac);
  background: #eef3e9;
  color: #2f4429;
}
/* Waiting is the state worth interrupting for, so it is the one that
   changes colour. */
.h2h-bar.is-waiting {
  background: #fdf3e3;
  color: #7a4a12;
  border-bottom-color: #d9b98a;
}
.h2h-btn {
  margin-left: auto;
  font-family: inherit;
  font-weight: bold;
  font-size: 0.95em;
  padding: 3px 12px;
  border: 1px solid #b58a4a;
  border-radius: 3px;
  background: #fffdf5;
  color: #6b3f0d;
  cursor: pointer;
}
.h2h-btn:hover { background: #f8efdc; }

/* Who is in a scheduled game. Sits in the series screen's state column,
   where the score goes once a game has been played -- the same slot
   answering the same question at three different times: nobody yet,
   somebody now, and this is how it finished. */
.live-note {
  font-family: "Arial Narrow", Arial, sans-serif;
  font-size: 0.82em;
  font-weight: bold;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
/* Waiting is the one worth acting on, so it is the one that stands out:
   somebody is sitting there wanting an opponent. */
.live-waiting { color: #7a4a12; }
.live-playing { color: #2f6b3a; }
/* Started but abandoned for now: real, but not a reason to stay away. */
.live-idle { color: #6f6a5c; font-weight: normal; font-style: italic; }
