/* base.css - structure for both directions. No colour literals below this
 * line; everything visual resolves through a --bd-* token from directions.css.
 * If you need a hex here, you need a token instead.
 *
 * Fonts are system stacks only (P1 Rule 6). Nothing is downloaded, so first
 * paint is identical offline and there is no reflow when a face lands.
 *
 * SHELL MODEL: the nav is a DRAWER at every width, closed on load. The report
 * is the product and it wants the whole viewport; a permanently parked 220px
 * sidebar costs two columns of a 17-column table on a laptop. The theme switch
 * lives at the top of that drawer.
 */

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* No `touch-action` on <html>. There used to be `pan-x pan-y` here, which is
   what actually blocked pinch-zoom and double-tap on iOS (Safari has ignored
   user-scalable=no since iOS 10). Zoom is wanted, so this is deliberately
   absent - see the viewport note in headerdeclarations.asp. If you need to
   constrain gestures on some scroller, scope it to that element; putting a
   touch-action back on <html> re-locks the whole page. */
body {
  font-family: var(--bd-font);
  font-size: 13px;
  line-height: 1.45;
  color: var(--bd-ink);
  background: var(--bd-void);
  -webkit-text-size-adjust: 100%;
}
h1, h2, h3, h4, h5, h6, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: var(--bd-phosphor); text-decoration: none; }
a:hover { text-decoration: underline; }
button, input, select, textarea { font: inherit; color: inherit; }
table { border-collapse: separate; border-spacing: 0; width: 100%; }
svg { display: block; }

:focus-visible {
  outline: 2px solid var(--bd-focus);
  outline-offset: 2px;
}

/* Every figure in the interface is tabular. Money that jitters column to
   column is the single most common failure in a reports UI. */
.fig, .rpt td, .rpt th, .stat-val, .tile-val {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ---------- money ------------------------------------------------
   Class names mirror what PM() already emits in basic.asp, so the ASP
   side needs no change: PM() keeps returning the same strings. */
.formatPositiveNumber, .up   { color: var(--bd-up); }
.formatNegativeNumber, .down { color: var(--bd-down); }
.formatZero, .flat           { color: var(--bd-flat); }

/* ---------- app shell ---------- */
.app { min-height: 100vh; display: flex; flex-direction: column; }
.main { min-width: 0; display: flex; flex-direction: column; flex: 1 1 auto; }

/* ---------- nav drawer -------------------------------------------
   Closed by default at every width. Opened by the burger, dismissed by
   the backdrop, Escape, or picking a destination. */
.side {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: 274px;
  max-width: 86vw;
  z-index: 80;
  background: var(--bd-chrome);
  color: var(--bd-chrome-ink);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateX(-100%);
  transition: transform .18s ease-out;
  box-shadow: none;
}
.side.is-open {
  transform: translateX(0);
  box-shadow: 0 0 46px rgba(0,0,0,.55);
}

.scrim {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.5);
  z-index: 70;
  opacity: 0;
  visibility: hidden;
  transition: opacity .18s ease-out, visibility .18s;
}
.scrim.is-open { opacity: 1; visibility: visible; }

.side-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px var(--bd-pad);
  border-bottom: 1px solid var(--bd-rule);
  flex: 0 0 auto;
}
/* The greeting takes the room the wordmark had, and the day/night pair and
   the close button sit at the end of the row. */
.side-brand .acct-who { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }


/* The drawer's wordmark rules are gone with the wordmark. index.asp and
   inactive.asp still print one, and it is styled by .login-card .brand-row
   .wordmark further down - not by anything that was here. */
.side-brand .x {
  margin-left: auto;
  background: none;
  border: 1px solid var(--bd-rule);
  border-radius: var(--bd-radius);
  color: inherit;
  padding: 5px;
  cursor: pointer;
  line-height: 0;
}
.side-brand .x:hover { background: var(--bd-chrome-hi); }

/* ---------- account figures: the drawer's first block -------------
   The six figures that used to open main.asp as tiles. They live here now
   because the drawer is on every page and the dashboard is not.

   MONEY COLOURS ARE NOT --bd-up / --bd-down HERE. Those two are tuned
   against the CONTENT ground - #008542 is 4.74:1 on white and about 2.6:1
   on Broadsheet's near-black chrome, which is the same trap the split bar
   in js/dash.js documents. The -chrome pair in theme.css is the lifted
   version, measured against #24211C: 7.48:1 and 5.37:1. Noir's chrome is
   the same black its content sits on, so there the two pairs are equal and
   the token still resolves.

   Values are mono and tabular so six figures of different widths read as a
   column rather than a ragged list. */
.acct-who {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 11px;
}
/* "Welcome" is the quiet half of this line - the name is what is being
   read, and the greeting is what makes it a greeting rather than a label. */
.acct-who .hi {
  font-size: 12px;
  color: var(--bd-chrome-dim);
  white-space: nowrap;
}
.acct-who .nm {
  font-size: 12px;
  font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* .acct-grp and the whole .acct-bal list are gone: those figures read out
   on the dashboard now, as .stat tiles, and nothing prints this markup. */
/* A figure that has not arrived. It was styled only inside the drawer's
   list, so on the dashboard's .stat-val the em dash rendered in full ink at
   40px and read as a real value rather than as a gap. */
.is-wait { color: var(--bd-dim); }
/* This sits on the dashboard now, on a content panel, not on the drawer's
   near-black chrome - so it takes the content tokens. The -chrome pair it
   used before is 2.6:1 on white. */
.acct-note {
  margin-top: 9px;
  font-size: 11px;
  color: var(--bd-muted);
}
.acct-note.bad { color: var(--bd-down); }
.acct-note:empty { display: none; }

.nav { padding: 8px 0; flex: 1 1 auto; }
/* CENTRED, IN THE CHROME ACCENT, AND A SIZE UP (2026-09-15). These were
   left-aligned 10px labels in the drawer's dim grey, which made a category
   look like one more item that happened to be quieter. A heading should read
   as a heading: centred over the links it names, in the one accent colour
   the chrome has (the same yellow as the wordmark), with a hairline above
   every group after the first so the groups are visibly separate blocks.
   --bd-accent-chrome is tuned against the chrome in both directions. */
.nav-grp {
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  font-weight: var(--bd-head-weight);
  color: var(--bd-accent-chrome);
  text-align: center;
  padding: 12px var(--bd-pad) 5px;
}
.nav-grp:not(:first-child) {
  margin-top: 6px;
  border-top: 1px solid rgba(255,255,255,.12);
}
.nav a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px var(--bd-pad);
  color: var(--bd-chrome-ink);
  border-radius: var(--bd-radius);
  margin: 1px 6px;
  font-size: 12.5px;
  white-space: nowrap;
}
.nav a:hover { background: var(--bd-chrome-hi); text-decoration: none; }
.nav a.is-on {
  background: var(--bd-phosphor);
  color: var(--bd-phosphor-ink);
  font-weight: 600;
}
.nav a.is-on .ico { opacity: 1; }
.nav .ico { width: 15px; height: 15px; opacity: .72; flex: 0 0 auto; }
.nav .tag {
  margin-left: auto;
  font-size: 10px;
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--bd-chrome-hi);
  color: var(--bd-chrome-ink);
  opacity: .85;
}
.nav a.is-on .tag { background: rgba(255,255,255,.22); color: inherit; opacity: 1; }

/* Log out lives here and nowhere else now, so this row has to read as a
   real destination rather than a footnote. It never had a layout rule: svg
   is display:block from the reset, so the icon was stacking ABOVE the words
   instead of sitting beside them. */
/* The two things in the drawer that are not destinations, on one row above
   the ones that are: the odds select and Log out. The link is styled here;
   the select has its own rules below. */
.side-act {
  display: flex;
  gap: 8px;
  padding: 9px var(--bd-pad) 8px;
  border-bottom: 1px solid var(--bd-rule);
  flex: 0 0 auto;
}
.side-act > a {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 7px 6px;
  background: none;
  border: 1px solid var(--bd-rule);
  border-radius: var(--bd-radius);
  color: var(--bd-chrome-ink);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.side-act > a:hover { background: var(--bd-chrome-hi); text-decoration: none; }
.side-act .ico { width: 15px; height: 15px; opacity: .72; flex: 0 0 auto; }

/* The odds select. A NATIVE control, so a phone opens it as a sheet and
   nothing here draws a list. Painted like the link beside it, 31px tall
   to match, and the popup is told its colours - Chrome on Windows draws
   the options in the select's ink on a white ground otherwise. compat.css's
   bare `select` rule (white field, 30px) loses here on specificity, not on
   load order, so it stays beaten if the stylesheets ever reorder. */
/* AS WIDE AS ITS WIDEST OPTION and no wider (asked for 2026-09-16): a native
   select sizes itself to "Fractional" plus its arrow, and Log out takes
   whatever the row has left. It shared the row half-and-half before. */
.side-act .odds {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 4px 0 8px;
  border: 1px solid var(--bd-rule);
  border-radius: var(--bd-radius);
  cursor: pointer;
}
.side-act .odds select {
  flex: 0 0 auto;
  width: auto;
  height: 29px;
  padding: 0 2px;
  background: transparent;
  border: 0;
  color: var(--bd-chrome-ink);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}
.side-act .odds select:focus { outline: 2px solid var(--bd-accent-chrome); outline-offset: -1px; border-color: transparent; }
.side-act .odds option { background: var(--bd-chrome); color: var(--bd-chrome-ink); }
/* .nav opens with 8px of its own; this row does not need to add to it. */
.side-act + .nav { padding-top: 2px; }

/* .side-foot is gone. Log out was the only thing in it and it sits beside
   Settings now; the drawer ends at the nav. */

/* ---------- header ---------- */
.head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 16px;
  background: var(--bd-surface);
  border-bottom: 1px solid var(--bd-rule);
  position: sticky;
  top: 0;
  z-index: 30;
}
/* HEADINGS TAKE --bd-title (2026-09-15): this, the panel and tile headings,
   the toolbar labels, the column heads and the band names. The token is the
   chrome yellow on the dark direction and the deepest gold that still reads
   on white and on the header band in the light one - see theme.css. */
.head h1 {
  font-family: var(--bd-display);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: var(--bd-display-track);
  line-height: 1.2;
  color: var(--bd-title);
}
.head .sub { color: var(--bd-muted); font-size: 11.5px; }
/* An icon on its own. .btn's 12px side padding is sized for a word beside
   the glyph and leaves it adrift without one. */
.btn.ico-btn { padding: 6px 9px; }
.btn.ico-btn .ico { width: 16px; height: 16px; }

/* The burger is now the ONLY way into the nav, so it is present at every
   width and reads as a control rather than a mobile afterthought. */
.burger {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--bd-surface);
  border: 1px solid var(--bd-edge);
  border-radius: var(--bd-radius);
  color: var(--bd-ink);
  padding: 7px 10px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
  flex: 0 0 auto;
}
.burger:hover { background: var(--bd-raised); }
.burger .btxt { display: inline; }


.content { padding: 16px; flex: 1 1 auto; }
.content.wide { padding: 14px; }

/* ---------- fixed shell: report screens ---------------------------
   On .app.fill the WINDOW does not scroll - the table pane does.

   This is not a style choice, it is the other half of the sticky-header
   fix. Bounding .rpt-wrap makes the table header stick within the pane,
   but if the window can still scroll, the whole panel rides up under the
   sticky .head and the table header pins BEHIND the chrome. Measured
   before this rule: page head bottom at y=60, table header at y=28.

   It also happens to be how a reports screen should behave: the summary
   tiles and the column labels both hold while the accounts move.

   Opt-in, because the dashboard genuinely wants a scrolling page. */
.app.fill { height: 100vh; overflow: hidden; }
.app.fill .main { min-height: 0; }
.app.fill .content { min-height: 0; display: flex; flex-direction: column; }
.app.fill .panel.grow { min-height: 0; display: flex; flex-direction: column; }
.app.fill .panel.grow .panel-bd { min-height: 0; flex: 1 1 auto; display: flex; }
.app.fill .panel.grow .rpt-wrap { max-height: none; flex: 1 1 auto; }

/* ---------- panels ---------- */
.panel {
  background: var(--bd-surface);
  border: var(--bd-panel-border);
  border-radius: var(--bd-radius-lg);
  box-shadow: var(--bd-shadow);
  overflow: hidden;
}
.panel-hd {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: var(--bd-pad) var(--bd-pad);
  border-bottom: 1px solid var(--bd-rule);
}
.panel-hd h2 {
  font-size: var(--bd-head-size);
  letter-spacing: var(--bd-head-track);
  text-transform: var(--bd-head-case);
  font-weight: var(--bd-head-weight);
  color: var(--bd-title);
}
.panel-hd .panel-act { margin-left: auto; display: flex; gap: 6px; align-items: center; }
.panel-bd { padding: var(--bd-pad); }
.panel-bd.flush { padding: 0; }

/* ---------- grid ---------- */
.grid { display: grid; gap: var(--bd-gap); }
.g-1 { grid-template-columns: 1fr; }
.g-2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.g-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.g-4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.g-dash { grid-template-columns: repeat(12, minmax(0,1fr)); }
.sp-3 { grid-column: span 3; }
.sp-4 { grid-column: span 4; }
.sp-5 { grid-column: span 5; }
.sp-6 { grid-column: span 6; }
.sp-7 { grid-column: span 7; }
.sp-8 { grid-column: span 8; }
.sp-12 { grid-column: span 12; }

/* ---------- stat tiles ---------- */
.stat { display: flex; flex-direction: column; gap: 3px; }
.stat-lbl {
  font-size: var(--bd-head-size);
  letter-spacing: var(--bd-head-track);
  text-transform: var(--bd-head-case);
  font-weight: var(--bd-head-weight);
  color: var(--bd-title);
}
.stat-val {
  font-family: var(--bd-display);
  font-size: var(--bd-display-size);
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: var(--bd-display-track);
}
.stat-val.sm { font-size: calc(var(--bd-display-size) * .62); }

/* A tile that opens something (the dashboard's Last transaction, 2026-09-17):
   the pointer and the row-hover tint say so. The global :focus-visible ring
   covers the keyboard. Only the tile's ground changes - the figure keeps its
   money colour. */
.stat-tile.is-link { cursor: pointer; }
.stat-tile.is-link:hover { background: var(--bd-hover); }

/* Label / value rows for one record in a small dialog - the transaction
   behind that tile. Label in the column-head voice, value in ink, a rule
   between rows and none above the first. */
.kv { display: grid; grid-template-columns: minmax(96px, 40%) minmax(0, 1fr); column-gap: 12px; }
.kv-row { display: contents; }
.kv-k, .kv-v { padding: 7px 0; border-top: 1px solid var(--bd-rule); }
.kv-row:first-child > .kv-k, .kv-row:first-child > .kv-v { border-top: 0; }
.kv-k { font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700; color: var(--bd-title); }
.kv-v { overflow-wrap: anywhere; }

.meter {
  height: 6px;
  background: var(--bd-sunken);
  border-radius: 999px;
  overflow: hidden;
  margin-top: 8px;
  border: 1px solid var(--bd-rule);
}
.meter > i { display: block; height: 100%; background: var(--bd-phosphor); }
.meter.warn > i { background: var(--bd-gold); }
.meter.over > i { background: var(--bd-down); }

:root {
  /* Height of the sticky .head. Structural, not a direction token, so it is
     defined here rather than in directions.css. Two things depend on it: the
     report pane subtracts it to size itself, and the sticky column header
     offsets itself by it so it parks under the head instead of behind it.

     THIS IS A FALLBACK, NOT THE SOURCE OF TRUTH. initHeadOffset() in js/ui.js
     measures the real .head and writes the answer to this property inline on
     <html>, which beats this value and the narrow-breakpoint one below.

     It has to. Both values here were hand-estimated, and the narrow one was
     wrong by 39px - see the note in the max-width:720px block. An offset that
     is too LARGE is not a cosmetic error: it pins the column header below the
     head, and the band in between fills with data rows that should have
     scrolled out of sight. */
  --bd-head-h: 60px;
}

/* ---------- tables ------------------------------------------------
   This is the product. Everything here exists because it is a reports
   app: 125 table-info uses in AgentV7 and a 17-column weekly report. */
.rpt-wrap {
  /* A REAL scrollport, both axes. It cannot be otherwise: the table must
     scroll horizontally, and per CSS Overflow, when one axis is `visible`
     and the other is not, `visible` computes to `auto`. So this element was
     already a scroll container - it just had no height to scroll within, so
     `thead th{position:sticky}` had nothing to stick against and the header
     left with the page. Bounding the height is what makes the sticky header
     and the sticky totals row actually work. See the print block, which must
     undo both properties or the printout is clipped to one pane. */
  overflow: auto;
  max-height: calc(100vh - var(--bd-head-h) - 28px);
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
/* ...but not in a modal, where the modal body is the scrollport. That
   max-height is `100vh - the page head - 28px`: a PAGE measurement, and
   inside a dialog it is as meaningless as the sticky offset it shares a
   variable with. Left alone it made two nested scrollers - measured on
   main.asp's day drill-down, .modal-body scrolling AND a 873px .rpt-wrap
   scrolling inside it, two scrollbars side by side and a sticky header
   pinned to the inner one. One scroller per dialog. */
.modal .rpt-wrap { overflow: visible; max-height: none; }
/* ...and not on the dashboard either, where a tile holds eight rows and the
   PAGE is the scrollport. The cap is the same page measurement, and the
   part that was felt is overscroll-behavior: Chrome takes a contained
   scroll container under the pointer as the wheel's target even when it has
   nothing to scroll, so the pointer over any of the four tables - the week
   table under its chart included - and the page would not move. No cap
   means no vertical overflow to be a target with; auto lets the wheel
   chain to the page regardless. overflow stays auto for the horizontal
   axis, so a table wider than its tile still scrolls sideways. */
.g-dash .rpt-wrap { max-height: none; overscroll-behavior: auto; }
/* THE GRID KEEPS A BOTTOM LINE under its last row. The pane is overflow:auto
   and, with max-height:none, exactly as tall as its table, so the last row's
   own cell borders sit at the pane's client edge and are clipped away - the
   whole bottom line vanished (a sticky first cell drops its border on top of
   that). A border on the PANE ITSELF is painted at its border edge, outside
   the clipped content box, so it draws one full-width line under the last row
   at every width and never clips. The dashboard grids have no tfoot, so this
   is their only bottom line. */
.g-dash .rpt-wrap { border-bottom: 1px solid var(--bd-rule); }

.rpt { font-size: var(--bd-fig-size); }
.rpt th, .rpt td {
  padding: 0 8px;
  height: var(--bd-row-h);
  white-space: nowrap;
  border-bottom: 1px solid var(--bd-rule);
  /* EVERY CELL IS CENTRED except the first column, asked for 2026-09-16 -
     figures, text columns and all. Only the identity column (first-child)
     stays left; a .txt cell that is not first centres like the rest. */
  text-align: center;
}
.rpt th:first-child, .rpt td:first-child { text-align: left; }

.rpt thead th {
  position: sticky;
  top: 0;
  z-index: 12;
  /* --bd-colhead, not --bd-raised: the head band has its own pair of tokens
     since 2026-09-16, when the light theme's band went dark blue under the
     chrome's yellow while the hover rows and the totals stayed light. */
  background: var(--bd-colhead);
  color: var(--bd-colhead-ink);
  font-size: var(--bd-head-size);
  /* TITLE CASE AND NO TRACKING since 2026-09-16 - "Prev Bal", "Mon 9/14" -
     set the way the figures are. Toolbar labels and panel headings keep
     --bd-head-case / --bd-head-track; only the column heads changed. */
  letter-spacing: 0;
  text-transform: none;
  font-weight: var(--bd-head-weight);
  border-bottom: 1px solid var(--bd-edge);
  user-select: none;
}
/* COLUMN NAMES ARE CENTRED, on every report - and so are the cells beneath
   them now (2026-09-16). The first column alone stays left: the first-child
   left rule above is (0,2,1) and beats this (0,1,2), so the identity head
   sits left over its left-aligned column. */
.rpt thead th { text-align: center; }
.rpt thead th.sortable { cursor: pointer; }
.rpt thead th.sortable:hover { color: var(--bd-colhead-hi); }
.rpt thead th .arrow { opacity: 0; margin-left: 3px; font-size: 9px; }
.rpt thead th.sort-asc .arrow,
.rpt thead th.sort-desc .arrow { opacity: 1; }
.rpt thead th.sort-desc .arrow { transform: rotate(180deg); display: inline-block; }

/* Row identity must never scroll away. */
.rpt th:first-child, .rpt td:first-child {
  position: sticky;
  left: 0;
  z-index: 10;
  background: var(--bd-surface);
  border-right: 1px solid var(--bd-rule);
}
.rpt thead th:first-child { z-index: 14; background: var(--bd-colhead); }
/* ONLY WHERE THERE IS A POINTER. On a touch screen :hover latches after a
   tap and stays until something else is tapped, so a row folded with a tap
   sat there highlighted as though it were still open or still selected.
   hover: hover is false on a touchscreen and true on a mouse. */
@media (hover: hover) {
  /* --bd-hover, not --bd-raised, since 2026-09-16: the row under the pointer
     is the Total column's blue. It has to beat the zebra (tr.alt > td at
     (0,2,3), declared later) and the tinted Total cell, so both are named. */
  .rpt tbody tr:hover td,
  .rpt tbody tr.alt:hover > td,
  .rpt tbody tr:hover td.tot,
  .rpt tbody tr.alt:hover > td.tot { background-color: var(--bd-hover); background-image: none; }
  .rpt tbody tr:hover td:first-child { background: var(--bd-hover); }
}
.rpt tbody tr.is-off td { color: var(--bd-dim); }

.rpt tfoot td {
  position: sticky;
  bottom: 0;
  /* ABOVE THE SUB-AGENT BAND. Both are sticky and both can want the same
     strip of the pane when a group ends near the bottom; the band pins to
     the top and the grand total to the bottom, and at equal z-index which
     one showed came down to document order. The total is the row you can
     least afford to have half-covered, so it wins outright. */
  z-index: 20;
  background: var(--bd-raised);
  font-weight: 700;
  border-top: 1px solid var(--bd-edge);
  border-bottom: none;
}
.rpt tfoot td:first-child {
  z-index: 21;
  /* AND THE BACKGROUND, not only the z-index. The first-column rule above is
     .rpt th:first-child/.rpt td:first-child at (0,2,1), which outranks
     .rpt tfoot td at (0,1,2) - so without this the grand total's first cell
     paints --bd-surface while the rest of its own row paints --bd-raised.
     The same guard is already on the thead cell and on tr.subtotal; the
     tfoot got the z-index half of it and not this half. */
  background: var(--bd-raised);
}

/* Hidden until the compact breakpoint turns it on. This declaration must
   stay ABOVE that media block - it previously sat after it at equal
   specificity and silently won, so the phone's only "rows are tappable" cue
   never rendered at any width. */
.compact-hint { display: none; }

.rpt .acct { font-family: var(--bd-mono); font-weight: 400; }   /* 400 since 2026-09-16: in Arial, 600 renders as bold */
.rpt .mono { font-family: var(--bd-mono); }

.chip {
  display: inline-block;
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--bd-phosphor-soft);
  color: var(--bd-phosphor);
  font-weight: 600;
}
.chip.mute { background: var(--bd-sunken); color: var(--bd-dim); }
.chip.warn { background: var(--bd-phosphor-soft); color: var(--bd-gold); }

/* ---------- buttons / forms ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--bd-edge);
  border-radius: var(--bd-radius);
  background: var(--bd-surface);
  color: var(--bd-ink);
  font-size: 12px; font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { background: var(--bd-raised); text-decoration: none; }
.btn-pri {
  background: var(--bd-phosphor);
  border-color: var(--bd-phosphor);
  color: var(--bd-phosphor-ink);
}
.btn-pri:hover { background: var(--bd-phosphor); filter: brightness(1.12); }
.btn-sm { padding: 3px 8px; font-size: 11px; }

/* A .seg segmented control lived here for the week panel's Daily/Weekly
   switch. The switch is gone and nothing else used it, so it went with it
   rather than sitting as dead vocabulary - git has it if a second one turns
   up. */

/* ---------- .pseg, and a second one turned up ---------------------
   allpending.asp's Regular / Futures switch. It had been shipping with
   NO CSS AT ALL - `.pseg` appeared exactly once in the tree, in that
   page's markup, and nowhere in any stylesheet - so both segments
   rendered as bare browser-default buttons and nothing distinguished
   the set being shown from the set that was not. The state was never
   the problem: SetSet() writes aria-pressed on both, correctly, and has
   from the day the control was added. This is the paint for it.

   PRESSED IS A FILL, NOT A TINT. On a two-item switch a tint is
   indistinguishable from hover, and the question this control answers -
   am I looking at straight action or at futures - is one the reader must
   not have to hunt for. Phosphor is the page accent in both directions
   and carries its own ink token, so the pressed segment stays legible
   without a second colour decision. */
.pseg {
  display: inline-flex;
  flex: 0 0 auto;
  border: 1px solid var(--bd-edge);
  border-radius: var(--bd-radius);
  overflow: hidden;
}
.pseg button {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  padding: 5px 14px;
  background: var(--bd-surface);
  border: 0;
  color: var(--bd-muted);
  font-family: inherit;
  font-size: var(--bd-head-size);
  letter-spacing: var(--bd-head-track);
  font-weight: var(--bd-head-weight);
  text-transform: uppercase;
  line-height: 1.6;
  cursor: pointer;
  white-space: nowrap;
}
.pseg button + button { border-left: 1px solid var(--bd-edge); }
/* Same reason .rpt tbody tr:hover is gated: on a touch screen :hover
   latches after a tap, and a latched hover on the segment you did NOT
   choose competes with the fill on the one you did. */
@media (hover: hover) {
  .pseg button:hover { background: var(--bd-raised); color: var(--bd-ink); }
}
.pseg button[aria-pressed="true"],
.pseg button[aria-pressed="true"]:hover {
  background: var(--bd-phosphor);
  color: var(--bd-phosphor-ink);
}

.btn .ico { width: 14px; height: 14px; }

/* A destructive action. Asked for by two migrations - "Remove Flag" and
   delete-wager had to render as neutral buttons for want of this. Uses the
   money-down token deliberately: red already means loss here, so a second
   red would be a second meaning. */
.btn-danger-x {
  background: transparent;
  border-color: var(--bd-down);
  color: var(--bd-down);
}
.btn-danger-x:hover { background: var(--bd-down); color: var(--bd-void); }

/* A compact field for use inside a table row, where 34px is too tall. */
.field.sm { height: 24px; padding: 0 6px; font-size: 11px; }
select.field.sm { height: 24px; background-position: calc(100% - 13px) 11px, calc(100% - 8px) 11px; }

/* Let a panel header wrap. Three migrations reached for an inline
   flex-wrap because this did not exist. */
.panel-hd.wrap, .panel-act.wrap { flex-wrap: wrap; }

/* .field is used on <select> as well as <input>. It sets padding but no
   height, and a bare select rule elsewhere setting height:30px then clips
   the text. Give the select an explicit box so the two cannot disagree. */
select.field {
  height: 34px;
  padding: 0 11px;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 15px) 15px, calc(100% - 10px) 15px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 28px;
}
select.field::-ms-expand { display: none; }

.field {
  width: 100%;
  padding: 9px 11px;
  background: var(--bd-field);
  border: 1px solid var(--bd-edge);
  border-radius: var(--bd-radius);
  color: var(--bd-ink);
}
.field::placeholder { color: var(--bd-dim); }
.lbl {
  display: block;
  font-size: var(--bd-head-size);
  letter-spacing: var(--bd-head-track);
  text-transform: var(--bd-head-case);
  font-weight: var(--bd-head-weight);
  color: var(--bd-title);
  margin-bottom: 5px;
}

/* ---------- switch ------------------------------------------------
   A checkbox that states its own position: the track carries the word
   OFF or ON, not just a colour.

   THE WORD IS THE POINT. What this replaces was the W3Schools slider -
   red track off, green track on, and nothing else to read. That fails
   three ways at once: it prints as two identical grey pills, it is the
   one distinction a red/green colour-blind reader cannot make, and on
   getpermissions.asp it painted a wall of 56 red switches that looked
   like 56 alarms rather than 56 permissions nobody had granted. Off is
   neutral now; red is kept for money that actually went the wrong way.

   Markup is unchanged from the old component, so all 79 instances pick
   this up with no edit:

       <label class="switch" for="X">
         <input type="checkbox" id="X" checked>
         <span class="slider round"></span>
       </label>

   .round is now redundant - the track is always a pill - but it is
   harmless and left in place rather than touched in five files.

   READ-ONLY IS A DISABLED CHECKBOX, not a radio. The pages that show a
   switch an agent may not change used <input type="radio"> for it,
   which is a control with different semantics wearing this one's
   costume. Every reader of these controls goes through .checked or
   :checked, which behaves identically on a disabled checkbox, so the
   swap changed nothing but the honesty of the markup. */
.switch {
  position: relative;
  display: inline-block;
  width: 52px;
  height: 24px;
  vertical-align: middle;
  flex: 0 0 auto;
}
/* Kept at zero size rather than stretched over the track: the label
   already forwards the click, and a full-size transparent input on top
   of the pill is one more thing to fight for the pointer. It stays in
   the tab order either way. */
.switch input {
  position: absolute;
  width: 0; height: 0;
  opacity: 0;
  margin: 0;
}
.slider {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: var(--bd-sunken);
  border: 1px solid var(--bd-edge);
  cursor: pointer;
  transition: background-color .16s ease, border-color .16s ease;
}
/* the knob */
.slider::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--bd-surface);
  border: 1px solid var(--bd-edge);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .28);
  transition: transform .16s ease;
}
/* the state word - opposite the knob, so the two never collide */
.slider::after {
  content: "OFF";
  position: absolute;
  top: 0;
  right: 7px;
  line-height: 22px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--bd-muted);
  transition: color .16s ease;
}
.switch input:checked + .slider {
  background: var(--bd-up);
  border-color: var(--bd-up);
}
.switch input:checked + .slider::before { transform: translateX(28px); }
.switch input:checked + .slider::after {
  content: "ON";
  right: auto;
  left: 8px;
  color: var(--bd-up-ink);
}
.switch input:focus-visible + .slider {
  outline: 2px solid var(--bd-focus);
  outline-offset: 2px;
}
.switch input:disabled + .slider {
  cursor: not-allowed;
  opacity: .5;
}

/* A switch and its label on one line. The label is a real <label for>,
   so the words are a second hit target for the same control - which is
   what getpermissions.asp lost when it put class="switch" on an <h5>
   and squeezed every permission name into a 52px box. */
.sw-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 2px;
  font-size: 12.5px;
  min-width: 0;
}
.sw-row .sw-lbl {
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  min-width: 0;
}

/* A column of switch rows, ruled between. Two columns once there is
   room - a 28-row permissions list is a scroll on one. */
.sw-list { display: grid; grid-template-columns: 1fr; column-gap: 26px; }
.sw-list .sw-row + .sw-row { border-top: 1px solid var(--bd-rule); }

/* The action under a list of switches. Its own block, because these
   fragments are injected into a host page and cannot borrow that page's
   .modal-footer. */
.sw-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding-top: 12px;
  margin-top: 4px;
  border-top: 1px solid var(--bd-rule);
}
/* ---------- locked by permission ----------------------------------
   A control an agent may see but not change. The server already refuses
   the edit - updateagent.asp and updatecustomer.asp re-check the same
   Session("AAllow...") flag before assigning anything - so this is not a
   security boundary, it is the missing explanation. What the agent had
   before was a switch that silently ignored the click.

   [data-locked] carries the human name of the permission and is what
   initLocked() in js/ui.js listens for. Put it on whatever the agent
   actually clicks: the <label class="switch"> for a toggle, a wrapping
   span for a value that would otherwise be a field. */
[data-locked] { cursor: not-allowed; }
.switch[data-locked] { -webkit-user-select: none; user-select: none; }

/* THE CONTROL INSIDE A LOCKED WRAPPER MUST NOT EAT THE CLICK.
   Two browser behaviours conspire here and both need this one rule:

   1. A disabled form control dispatches NO click event, and nothing
      bubbles from it either - so a click landing on a locked Delay or
      Temp Credit field reached no handler at all and the modal never
      opened. Only the label beside it worked.
   2. cursor is inherited, but the UA stylesheet gives inputs their own
      (text over a field, default over a disabled one), so not-allowed on
      the wrapper never showed over the control itself.

   pointer-events:none makes the control transparent to the pointer: the
   click lands on the [data-locked] wrapper, which has the handler, and
   the wrapper's cursor is what the reader sees across the whole row. */
[data-locked] input,
[data-locked] select,
[data-locked] textarea,
[data-locked] button { pointer-events: none; }

/* A control that IS the locked element rather than sitting inside one -
   editlimits.asp locks its text fields in place, readonly rather than
   disabled, because that page posts a real form. Readonly controls do
   take clicks, so they keep their pointer events and only need the
   cursor. */
input[data-locked],
select[data-locked],
textarea[data-locked] { cursor: not-allowed; }

/* Belt and braces for any disabled control that never got a wrapper. */
input:disabled, select:disabled, textarea:disabled, button:disabled { cursor: not-allowed; }

/* The words beside a locked switch. .sw-lbl is a <label for>, which the
   browser renders with a pointer even though activating it does nothing. */
.sw-row[data-locked] .sw-lbl { cursor: not-allowed; }
/* A value shown in place of a field the agent may not edit. Faded so it
   reads as inert, but still selectable - copying a password out is a
   legitimate thing to do with one you are not allowed to change. */
.locked { display: inline-block; opacity: .6; }
.perm-note { font-size: 11.5px; color: var(--bd-muted); margin-top: 10px; }

/* A one-line result under a control that saves on the spot. */
.sw-note { font-size: 11.5px; color: var(--bd-muted); margin-top: 6px; min-height: 16px; }
.sw-note.ok { color: var(--bd-up); }
.sw-note.bad { color: var(--bd-down); }
@media (min-width: 660px) {
  .sw-list.two { grid-template-columns: 1fr 1fr; }
  /* with two columns the first row of the SECOND column also needs its
     rule, or the columns look misaligned by one */
  .sw-list.two .sw-row { border-top: 1px solid var(--bd-rule); }
  .sw-list.two .sw-row:first-child, .sw-list.two .sw-row:nth-child(2) { border-top: 0; }
}

.banner {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 12px;
  border-radius: var(--bd-radius);
  background: var(--bd-phosphor-soft);
  border-left: 3px solid var(--bd-phosphor);
  font-size: 12.5px;
  margin-bottom: var(--bd-gap);
}
.banner .ico { width: 16px; height: 16px; color: var(--bd-phosphor); flex: 0 0 auto; }

/* ---------- charts ------------------------------------------------
   chart.js emits plain SVG with these classes. All colour comes from
   tokens, so a chart inherits its direction with no JS involvement. */
.chart { width: 100%; }
.chart .axis line, .chart .axis path { stroke: var(--bd-rule); }
.chart .grid line { stroke: var(--bd-rule); }
.chart .zero { stroke: var(--bd-edge); }
.chart text { fill: var(--bd-muted); font-size: 10px; font-family: var(--bd-font); }
.chart text.val {
  fill: var(--bd-ink);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
}
.chart .bar-up { fill: var(--bd-up); }
.chart .bar-down { fill: var(--bd-down); }
.chart .bar-flat { fill: var(--bd-flat); }
.chart .mark { fill: var(--bd-phosphor); }
.chart .line { fill: none; stroke: var(--bd-phosphor); stroke-width: 2; }
.chart .area { fill: var(--bd-phosphor-soft); }
.chart .dot { fill: var(--bd-phosphor); }

/* Category series draw from the --bd-cat-* ramp, NEVER from --bd-up or
   --bd-down. Those two mean money and nothing else may borrow them, or a
   chart ends up telling the reader that NFL won and MLB lost. */
.chart .seg-0 { fill: var(--bd-cat-0); }
.chart .seg-1 { fill: var(--bd-cat-1); }
.chart .seg-2 { fill: var(--bd-cat-2); }
.chart .seg-3 { fill: var(--bd-cat-3); }
.chart .seg-4 { fill: var(--bd-cat-4); }
.chart .seg-5 { fill: var(--bd-cat-5); }
.chart .seg-6 { fill: var(--bd-cat-6); }
.chart .seg-7 { fill: var(--bd-cat-7); }
/* 8..11 exist because Handle by wager type lists every type it finds. */
.chart .seg-8 { fill: var(--bd-cat-8); }
.chart .seg-9 { fill: var(--bd-cat-9); }
.chart .seg-10 { fill: var(--bd-cat-10); }
.chart .seg-11 { fill: var(--bd-cat-11); }
.chart .track { fill: var(--bd-sunken); }

/* ---------- the week chart's day cells -----------------------------
   signedBarsVs() draws one <g class="daycell"> per date. The cell rect is
   the date divider, the hover target and the tap target all at once; a
   transparent .hit rect over the whole column means the labels above and
   below the plot highlight it too. */
.chart .bar-prev { fill: var(--bd-prev); }
.daycell .hit { fill: transparent; pointer-events: all; }
.daycell .cell { fill: var(--bd-cell); transition: fill .12s ease; }
.daycell.can-pick { cursor: pointer; }
.daycell.can-pick:hover .cell { fill: var(--bd-cell-hov); }
.daycell.is-sel .cell { fill: var(--bd-cell-sel); }
.daycell .marker { fill: transparent; }
.daycell.is-sel .marker { fill: var(--bd-phosphor); }
.daycell.is-sel text.dow { fill: var(--bd-ink); font-weight: 700; }
.daycell:focus { outline: none; }
.daycell:focus-visible .cell { stroke: var(--bd-focus); stroke-width: 2; }

/* The period figure, in a fixed band BELOW the cell, on a navy band the
   width of the cell - the column heads' blue (asked for 2026-09-16). The
   money colours are the -chrome pair, tuned for a dark ground; the
   content pair is 2.6:1 on this navy. Zero takes the band's own ink. */
.chart rect.figbg { fill: var(--bd-colhead); }
.chart text.fig { font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; }
.chart text.fig.up { fill: var(--bd-up-chrome); }
.chart text.fig.down { fill: var(--bd-down-chrome); }
.chart text.fig.flat { fill: var(--bd-colhead-ink); }

/* The line under a chart. On the dashboard it says what a cell does; a
   <p> with nothing in it would still take its margin, so an empty one
   collapses. */
.chart-note { margin: 8px 0 0; text-align: center; }
.chart-note:empty { display: none; }

/* The week's Total and Pending, centred above the cells in the week panel
   (2026-09-16, in place of the seven-day table under the chart). Money
   colour on the total through the PM() classes; the label is the small
   grey key, the figure the bold one. */
.wk-sum { display: flex; justify-content: center; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; margin: 0 0 10px; }
.wk-sum .k { font-size: 11px; letter-spacing: var(--bd-head-track); text-transform: uppercase; font-weight: 700; color: var(--bd-dim); }
.wk-sum .v { font-family: var(--bd-mono); font-variant-numeric: tabular-nums; font-weight: 700; font-size: 16px; margin-right: 16px; }
.wk-sum .v:last-child { margin-right: 0; }

/* A cell waiting on its day's wagers. The request is made before anything
   opens - so that a day with nothing on it can flash a note instead of an
   empty modal - which means the cell itself has to say a request is in
   flight, or a click looks like it did nothing. */
.daycell.is-busy .cell { animation: cell-busy .9s ease-in-out infinite; }
@keyframes cell-busy { 50% { opacity: .4; } }

/* The note that replaces the modal for a day with no action. Inverted
   against the panel so it reads as a transient overlay rather than as
   content, and pointer-events:none so it can never eat a click on the cell
   underneath it. */
.chart-hold { position: relative; }
.chart-toast {
  position: absolute; left: 50%; top: 50%;
  background: var(--bd-ink); color: var(--bd-surface);
  padding: 6px 14px; border-radius: var(--bd-radius);
  font-size: 12px; white-space: nowrap; pointer-events: none;
  animation: toast-pop 1.9s ease-out forwards;
}
.chart-toast[hidden] { display: none; }
@keyframes toast-pop {
  0%   { opacity: 0; transform: translate(-50%, -46%) scale(.96); }
  12%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  76%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -54%) scale(.99); }
}
/* Still appears and still goes away - dash.js owns the timer - it just does
   not move or fade to get there. */
@media (prefers-reduced-motion: reduce) {
  .daycell.is-busy .cell { animation: none; opacity: .6; }
  .chart-toast { animation: none; opacity: 1; transform: translate(-50%, -50%); }
}

.legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 10px; }
.legend span { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: var(--bd-muted); }
.legend i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; flex: 0 0 auto; }


/* ---------- movers list ---------- */
.movers { display: flex; flex-direction: column; gap: 5px; }
.mover { display: grid; grid-template-columns: 92px 1fr 74px; gap: 8px; align-items: center; font-size: 12px; }
.mover .who-n { font-family: var(--bd-mono); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.mover .amt { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.mover .track { height: 8px; background: var(--bd-sunken); border-radius: 2px; overflow: hidden; }
.mover .track > i { display: block; height: 100%; }
.mover .track > i.up { background: var(--bd-up); }
.mover .track > i.down { background: var(--bd-down); }
/* Position hot spots: the two sides of one game. A colour and a tint of it,
   not two ramp entries - see the --bd-split-* note in theme.css. */
.mover .track > i.split-a { background: var(--bd-split-a); }
.mover .track > i.split-b { background: var(--bd-split-b); }
/* The 50% mark, so "lopsided" is a position on the bar and not a judgement
   the reader has to make by eye. */
.mover .track.split { position: relative; }
.mover .track.split::after {
  content: "";
  position: absolute; left: 50%; top: -2px; bottom: -2px;
  width: 1px; background: var(--bd-edge);
}

/* ---------- loading states ------------------------------------------
   Every dashboard tile ships as a skeleton and is replaced when its endpoint
   answers. The endpoints run in SERIES - Classic ASP holds a per-session lock
   while a page executes, so the browser firing eight XHRs at once still gets
   them one at a time - which is exactly why each tile has to say something
   while it waits rather than the page showing one spinner for all of them.

   The skeleton takes the SHAPE of what is coming: a chart tile reserves the
   chart's height, a table tile reserves its rows. Otherwise every tile lands
   by shoving the ones below it down the page, and an agent reading the week
   figure loses their place twice while the slow reports arrive. */
.skel { position: relative; overflow: hidden; background: var(--bd-sunken); border-radius: var(--bd-radius); }
.skel::after {
  content: "";
  position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, var(--bd-raised), transparent);
  animation: skel-sweep 1.25s ease-in-out infinite;
}
@keyframes skel-sweep { to { transform: translateX(100%); } }

.skel-line { height: 11px; margin-bottom: 8px; }
.skel-line:last-child { margin-bottom: 0; }
.skel-line.w-70 { width: 70%; }
.skel-line.w-45 { width: 45%; }
.skel-line.w-30 { width: 30%; }
.skel-fig { height: calc(var(--bd-display-size) * .92); width: 62%; margin: 2px 0 5px; }
.skel-chart { width: 100%; }
.skel-rows { display: flex; flex-direction: column; gap: 7px; padding: var(--bd-pad); }

/* Arrived. One pass, then the class is left on - re-running it on a resize
   redraw would flash a settled tile for no reason. */
.tile-in { animation: tile-in .34s ease-out both; }
@keyframes tile-in {
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: none; }
}

/* Shown only once a tile has been waiting long enough that the shimmer alone
   reads as broken. dash.js sets the text; this is just where it sits. */
.skel-note {
  font-size: 11px;
  color: var(--bd-muted);
  font-variant-numeric: tabular-nums;
  margin-top: 8px;
}

/* An endpoint that failed, or a report with genuinely nothing in it. Both are
   states a tile must be able to show - a permanently shimmering panel is the
   one outcome that tells the reader nothing at all. */
.tile-empty {
  display: flex; align-items: center; justify-content: center;
  min-height: 92px; padding: var(--bd-pad);
  font-size: 12px; color: var(--bd-muted); text-align: center;
}
.tile-empty.bad { color: var(--bd-down); }

/* ---------- busy: the veil and the inline ring ----------------------
   What was here before was not a loading state, it was a page teardown:
   weeks.asp hid the report AND the week picker and left one animated GIF
   alone on a white page. The reader lost the figures they were reading,
   lost the control they had just used, and got no word for what was
   happening. On a slow week that is four seconds of a blank screen.

   The veil leaves all of it on screen and lays a scrim over the top. The
   report stays legible underneath, so the wait reads as "the same page,
   fetching" rather than "the page is gone".

   It is deliberately NOT shown at once - ui.js waits ~120ms first. Most
   week changes come back faster than that, and a veil that flashes for one
   frame is worse than no veil. */
.veil {
  display: none;
  position: fixed; inset: 0;
  z-index: 200;                       /* over the modal (90) and the sheet (91) */
  align-items: center; justify-content: center;
  background: var(--bd-scrim);
  -webkit-backdrop-filter: saturate(.85) blur(1.5px);
          backdrop-filter: saturate(.85) blur(1.5px);
}
.veil.is-on { display: flex; animation: veil-in .14s ease-out; }
@keyframes veil-in { from { opacity: 0; } to { opacity: 1; } }

/* The card carries the word. A spinner on its own says "wait"; it does not
   say what for, and on a report page the reader wants to know whether they
   are waiting on the week they just picked or on something they mis-clicked. */
.veil-card {
  display: flex; align-items: center; gap: 12px;
  background: var(--bd-surface);
  color: var(--bd-ink);
  border: 1px solid var(--bd-edge);
  border-radius: var(--bd-radius);
  box-shadow: 0 6px 28px rgba(0,0,0,.22);
  padding: 14px 20px;
  max-width: min(88vw, 380px);
}
.veil-card b {
  font-family: var(--bd-font);
  font-size: 13px; font-weight: 600;
  letter-spacing: .01em;
}

/* One ring, two sizes. A conic sweep would be one declaration fewer but
   would not degrade: under prefers-reduced-motion the global rule at the
   foot of this file kills the rotation, and a stopped conic gradient is a
   smear. A bordered ring stops as a legible arc - still obviously a
   progress mark, just not turning. */
.spin {
  display: inline-block;
  width: 18px; height: 18px;
  flex: 0 0 auto;
  border: 2px solid var(--bd-rule);
  border-top-color: var(--bd-phosphor);
  border-radius: 50%;
  animation: spin-turn .7s linear infinite;
}
.spin.lg { width: 24px; height: 24px; border-width: 3px; }
@keyframes spin-turn { to { transform: rotate(360deg); } }

/* The in-place version, for a modal body or a cell that is fetching its own
   content. Replaces the pw.gif that used to be dropped into innerHTML. */
.load-inline {
  display: flex; align-items: center; gap: 10px;
  padding: 18px var(--bd-pad);
  font-size: 12px; color: var(--bd-muted);
}

/* ---------- wager list: the three drill-down reports ------------------
   agentdaily, playerdaily and pending listed seven or eight columns of
   ticket anatomy - rotation numbers, lines, game times, once per parlay
   leg. That is a reference table, and it was being read on a phone inside
   a dialog. Two columns now: what the ticket is, and what it came to.
   Everything else is one tap away in the detail overlay.

   NOT A TABLE, AND THAT IS THE WHOLE POINT OF THE SHAPE.

   It was `.rpt.wlist` on a real <table> and it cost three bugs, all the
   same one: rules written for a 21-column scrolling report leaking into a
   two-column list that never scrolls sideways. The frozen first column
   painted over the account divider; then it swallowed the row hover,
   because it was the only cell with a competing background; then
   `.rpt-wrap`'s max-height made a second scroller inside the modal.

   The fourth was not fixable at all. A sticky <td> in a <tbody> row does
   not travel - measured on a 35-wager account scrolled 2297px, the
   divider sat at -16 rather than 0 - because only <thead> and <tfoot>
   cells are special-cased. So the account name could never stay on screen
   while its wagers were being read, which is the one thing a grouped list
   of 281 tickets under 53 accounts actually needs.

   As divs it is native. .wgrp is the containing block, so its .wdiv
   sticks while any of that account's rows are in view, is pushed out by
   the next group, and comes back on the way up - no JS, no scroll
   listener, and no gate on aria-expanded either: a collapsed group is
   one row tall, so there is nothing for its divider to travel within and
   it stays put on its own.
   ------------------------------------------------------------------ */
.wlist { display: block; }
.wgrp { display: block; }

/* Two columns, and the same track definition on all three row kinds so a
   ticket's amount, its account's total and the report's total line up. */
.wlist .wdiv,
.wlist .wrow,
.wlist .wfoot {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: baseline;
  padding: 9px var(--bd-pad);
}

/* The account. Sticky against .wgrp, which is what makes it release and
   return by itself. */
.wlist .wdiv {
  position: sticky;
  top: 0;
  z-index: 3;
  cursor: pointer;
  background: var(--bd-raised);
  border-top: 1px solid var(--bd-edge);
  border-bottom: 1px solid var(--bd-edge);
  font-family: var(--bd-mono);
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: .02em;
}
.wlist .wgrp:first-child .wdiv { border-top: 0; }
.wlist .wdiv:hover { background: var(--bd-sunken); }
.wlist .wdiv:focus-visible { outline: 2px solid var(--bd-focus); outline-offset: -2px; }
.wlist .wdiv .n { color: var(--bd-muted); font-weight: 400; margin-left: 8px; }
.wlist .wdiv .tw {
  display: inline-block; width: 0; height: 0;
  margin-right: 9px; vertical-align: 1px;
  border-left: 5px solid var(--bd-muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform .12s ease-out;
}
.wlist .wdiv[aria-expanded="true"] .tw { transform: rotate(90deg); }

/* One ticket. Hover is on the ROW now, not on cells, so there is no
   first-child with its own background left to lose the highlight. */
.wlist .wrow {
  cursor: pointer;
  border-bottom: 1px solid var(--bd-rule);
  font-size: 12.5px;
  line-height: 1.35;
}
.wlist .wrow:hover { background: var(--bd-raised); }
.wlist .wrow .desc { min-width: 0; overflow-wrap: anywhere; }
/* Sport, league, rotation - enough to know the row without opening it,
   quiet enough not to compete with the wager. */
.wlist .wrow .sub {
  display: block; margin-top: 2px;
  font-size: 10.5px; color: var(--bd-muted); letter-spacing: .02em;
}
.wlist .res {
  text-align: right; white-space: nowrap;
  font-family: var(--bd-mono); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.wlist .wdiv .res { font-size: 13.5px; }

/* Collapsed by default - the ASP ships `hidden` on the container, and
   ui.js flips it. One element per account rather than one per ticket. */
.wlist .wrows[hidden] { display: none; }

/* The report total. Sticky against .wlist, so it holds the bottom of the
   dialog for the whole scroll where a group divider only holds its own
   account. z-index above the dividers so one cannot ride over it. */
.wlist .wfoot {
  position: sticky;
  bottom: 0;
  z-index: 4;
  background: var(--bd-raised);
  border-top: 1px solid var(--bd-edge);
  font-weight: 700;
}

/* The overlay's body is the slip now (see .tk below). What is left here
   is the action strip and the result form the two lists that act still
   emit; .tk .wd-acts and .tk .wd-edit further down give them the slip's
   own gutters. */
.wdetail .wd-acts { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 12px; }
/* ToggleEdit() flips this to display:inline, so the layout has to survive
   being an inline box - hence the wrapping row rather than a grid. */
.wdetail .wd-edit {
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--bd-rule);
}
.wdetail .wd-edit .field { width: 78px; margin: 0 6px 6px 0; }
.wdetail .wd-edit select.field { width: auto; }
.wdetail .wd-edit .sw-lbl { margin: 0 10px 6px 0; font-size: 12px; }

/* Rendered by the ASP next to the list and never shown - ui.js moves the
   matching one into the overlay. */
.wsrc { display: none; }

/* ---------- the slip: one ticket, drawn one way -----------------------
   Built by ticket.asp on every page that shows a ticket - All Pending's
   selection cell, the drill-down rows and their overlay, the Position
   card, the dashboard ticket modal, Deleted Wagers. Approved 2026-09-12
   from the Ticket Formats mockup, direction B: heavy top rule, ticket and
   time in mono, the type as WagerDesc names it, every leg with its LINE
   and its PRICE in two columns at the right, the figures boxed in the
   foot, the IP last and quietest.

   Everything is tk-prefixed - see the note at the top of ticket.asp. The
   sizes are px, as the rest of this file's are, and sit on the 13/12
   figure size the two directions set. */
.tk { border-top: 2px solid var(--bd-ink); }
.tk-hd {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 12px; padding: 9px var(--bd-pad) 8px;
  border-bottom: 1px dashed var(--bd-edge);
  align-items: baseline;
}
.tk-no { font-family: var(--bd-mono); font-weight: 700; letter-spacing: .04em; }
.tk-no span { color: var(--bd-muted); font-weight: 400; margin-right: 6px; }
.tk-placed {
  display: flex; justify-content: flex-end; align-items: center; gap: 10px;
  font-family: var(--bd-mono); color: var(--bd-muted); font-size: 12px; white-space: nowrap;
}
/* the Position card's Delete and Change result, at the trailing edge of
   the head - the corner a thumb reaches and the eye ignores */
.tk-acts { display: inline-flex; gap: 8px; }
.tk-acts a { cursor: pointer; color: var(--bd-muted); }
.tk-acts .ico { width: 15px; height: 15px; }
.tk-acct { color: var(--bd-muted); font-size: 12px; }
.tk-acct b { color: var(--bd-ink); font-family: var(--bd-mono); }
.tk-kind, .tk-k {
  font-size: var(--bd-head-size); letter-spacing: var(--bd-head-track);
  text-transform: uppercase; font-weight: var(--bd-head-weight); color: var(--bd-muted);
}
.tk-kind { text-align: right; }
.tk-legs { margin: 0; padding: 0 var(--bd-pad); list-style: none; }
/* THE LEG IS A GRID WITH THE LINE AND THE PRICE IN THEIR OWN COLUMNS, so a
   parlay's lines and prices stack down the ticket instead of ending each
   leg's sentence at a different place. Dashed rules between legs, the
   slip's own perforation. */
.tk-leg {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas: "tag line price" "pick line price" "when line price" "note line price";
  column-gap: 14px; padding: 8px 0;
  border-bottom: 1px dashed var(--bd-rule);
  align-items: center;
}
.tk-leg:last-child { border-bottom: 0; }
/* the graded day reports' per-leg ResultStr; absent everywhere else, and an
   absent grid item costs its row nothing */
.tk-note { grid-area: note; font-size: 10.5px; color: var(--bd-muted); margin-top: 2px; }
.tk-tag { grid-area: tag; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--bd-muted); }
.tk-tag b { font-weight: 400; margin-left: 6px; color: var(--bd-dim); letter-spacing: 0; }
.tk-pick { grid-area: pick; font-weight: 600; font-size: 13.5px; line-height: 1.3; overflow-wrap: anywhere; }
.tk-pick small { font-weight: 400; color: var(--bd-muted); margin-left: 6px; font-size: 12px; }
.tk-when { grid-area: when; font-size: 10.5px; color: var(--bd-dim); margin-top: 1px; }
.tk-line {
  grid-area: line; font-family: var(--bd-mono); font-size: 14px; font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap;
}
/* a moneyline has no line; ML holds the column so it never reads as
   missing data */
.tk-line.ml { color: var(--bd-muted); font-weight: 400; font-size: 11px; letter-spacing: .06em; }
.tk-price {
  grid-area: price; font-family: var(--bd-mono); font-size: 15px; font-weight: 700;
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap;
}
.tk-ft { display: grid; grid-template-columns: 1fr 1fr; border-top: 1px solid var(--bd-ink); }
.tk-ft.three { grid-template-columns: 1fr 1fr 1fr; }
.tk-cell { padding: 8px var(--bd-pad) 10px; }
.tk-cell + .tk-cell { border-left: 1px solid var(--bd-rule); }
.tk-cell .tk-k { display: block; margin-bottom: 2px; }
.tk-fig { font-family: var(--bd-mono); font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.tk-cell.risk .tk-fig { color: var(--bd-muted); font-weight: 400; }
.tk-ip {
  padding: 5px var(--bd-pad) 8px; border-top: 1px solid var(--bd-rule);
  font-size: 10.5px; color: var(--bd-dim); font-family: var(--bd-mono); overflow-wrap: anywhere;
}
.tk-del { display: flex; align-items: center; gap: 10px; padding: 7px var(--bd-pad); border-top: 1px solid var(--bd-rule); }
.tk-delchip {
  color: var(--bd-down); font-size: var(--bd-head-size); letter-spacing: var(--bd-head-track);
  text-transform: uppercase; font-weight: 700; border: 1px solid currentColor;
  border-radius: var(--bd-radius); padding: 1px 6px; white-space: nowrap;
}
.tk.is-del .tk-fig { color: var(--bd-dim); font-weight: 400; }
/* the slip carries its own gutters - same escape hatch .tk-list has */
.modal-body:has(> .tk) { padding: 0; }
/* Delete / Change result and the result form, inside the slip on the
   drill-downs that permit them: a ruled strip under the IP with the slip's
   gutters, not the overlay's margin they had when the body still padded */
.tk .wd-acts, .tk .wd-edit { margin: 0; padding: 10px var(--bd-pad); border-top: 1px solid var(--bd-rule); }

/* THE LIST ROW - the drill-downs' one line per ticket. .wlist owns the
   list, the sticky account divider and the foot (see its block above);
   this is the row's own grid: the pick with its sport, rotation and game
   time under it, then the LINE and the PRICE in their own columns, then
   the figure the report is about. Minimum widths on the two columns are
   what keep them in step from row to row, since each row is its own grid
   and a multi-leg ticket's two are empty. */
.wlist .wrow { grid-template-columns: minmax(0, 1fr) auto auto auto; align-items: center; }
/* grid-area: auto, because .tk-line and .tk-price carry grid-area names for
   the LEG's grid, and inside a row grid that has no such areas a named
   area places the item on implicit lines - measured: a four-item row with
   six tracks, three of them 0px, and the figure a column further out than
   its neighbours. Reset, they take the row's own columns in order. */
.wlist .wrow .tk-line { grid-area: auto; min-width: 3em; font-size: 13px; }
.wlist .wrow .tk-price { grid-area: auto; min-width: 3.5em; font-size: 14px; }
.wlist .wrow .desc > small { font-weight: 400; color: var(--bd-muted); margin-left: 6px; font-size: 11.5px; }
.wlist .wrow .sub b { font-weight: 400; color: var(--bd-dim); }

/* THE INLINE FORM - a report row's selection cell, with the report's own
   columns beside it. Smaller, tighter, no head or foot; the type leads as
   a chip when there is more than one leg. */
.tk-inl .tk-type { display: block; margin-bottom: 3px; }
.tk-inl .tk-legs { padding: 0; }
.tk-inl .tk-leg { padding: 3px 0; column-gap: 12px; }
.tk-inl .tk-pick { font-size: 13px; }
.tk-inl .tk-price { font-size: 13px; }
.tk-inl .tk-line { font-size: 12.5px; }
.tk-inl .tk-delchip { display: inline-block; margin-top: 5px; }
/* a report cell holding the inline form: .rpt td is nowrap and row-high,
   and a slip's legs need neither */
.rpt td:has(> .tk-inl) { white-space: normal; height: auto; padding-top: 6px; padding-bottom: 6px; vertical-align: top; }

/* ---------- detail sheet (mobile row drill-in) ---------- */
.sheet-back {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.55);
  z-index: 90;
  display: none;
}
.sheet-back.is-open { display: block; }
.sheet {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 91;
  background: var(--bd-surface);
  border-top-left-radius: var(--bd-radius-lg);
  border-top-right-radius: var(--bd-radius-lg);
  border-top: 1px solid var(--bd-edge);
  max-height: 82vh;
  overflow-y: auto;
  transform: translateY(100%);
  transition: transform .18s ease-out;
}
.sheet.is-open { transform: translateY(0); }
.sheet-hd {
  display: flex; align-items: center; gap: 10px;
  padding: 14px var(--bd-pad);
  border-bottom: 1px solid var(--bd-rule);
  position: sticky; top: 0; background: var(--bd-surface);
}
.sheet-hd h3 { font-family: var(--bd-mono); font-size: 15px; font-weight: 700; }
.sheet-bd { padding: 6px var(--bd-pad) 22px; }
.sheet-row {
  display: flex; justify-content: space-between; gap: 16px;
  padding: 9px 0;
  border-bottom: 1px solid var(--bd-rule);
  font-size: 13px;
}
.sheet-row:last-child { border-bottom: none; }
.sheet-row .k { color: var(--bd-muted); }
.sheet-row .v { font-variant-numeric: tabular-nums; font-weight: 600; }

/* ---------- login -------------------------------------------------
   One centred dialog and nothing else, the same shape index.asp has
   today. A login page's whole job is to get out of the way. */
.login-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px 20px 20px;
  gap: 18px;
  background: var(--bd-void);
}
.login-card {
  width: 100%;
  max-width: 372px;
  background: var(--bd-surface);
  border: 1px solid var(--bd-rule);
  border-radius: var(--bd-radius-lg);
  box-shadow: var(--bd-shadow);
  padding: 30px 30px 26px;
}
.login-card .brand-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 20px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--bd-rule);
}
.login-card .brand-row .mark {
  width: 30px; height: 30px;
  border-radius: var(--bd-radius);
  background: var(--bd-phosphor);
  color: var(--bd-phosphor-ink);
  display: grid; place-items: center;
  font-weight: 700; font-size: 13px;
  flex: 0 0 auto;
}
.login-card .brand-row .wordmark {
  font-family: var(--bd-display);
  font-weight: 700;
  font-size: 16px;
  letter-spacing: var(--bd-display-track);
}
.login-card .brand-row .wordmark i { font-style: normal; color: var(--bd-phosphor); }

.login-card h1 {
  font-family: var(--bd-display);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: var(--bd-display-track);
  margin-bottom: 3px;
}
.login-card .lead { color: var(--bd-muted); font-size: 12.5px; margin-bottom: 20px; }
.login-card .fgrp { margin-bottom: 14px; }
.login-card .btn { width: 100%; justify-content: center; padding: 10px; margin-top: 4px; }

.login-err {
  display: none;
  gap: 8px;
  align-items: flex-start;
  padding: 9px 11px;
  margin-bottom: 16px;
  border-radius: var(--bd-radius);
  border-left: 3px solid var(--bd-down);
  background: var(--bd-raised);
  color: var(--bd-ink);
  font-size: 12px;
}
.login-err.is-on { display: flex; }
.login-err .ico { width: 15px; height: 15px; color: var(--bd-down); flex: 0 0 auto; }

.login-alt {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--bd-rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 12px;
  color: var(--bd-muted);
}
.login-alt a { display: inline-flex; align-items: center; gap: 4px; font-weight: 600; }
.login-foot {
  font-size: 11.5px;
  color: var(--bd-dim);
  text-align: center;
  max-width: 372px;
}

/* ---------- helpers ---------- */
.row { display: flex; align-items: center; gap: 8px; }
.row.wrap { flex-wrap: wrap; }
.push { margin-left: auto; }
.muted { color: var(--bd-muted); }

/* Page footer. Deliberately almost nothing - it exists so the content has a
   bottom edge, not to carry a credit line. */
.foot { padding: 10px 14px; border-top: 1px solid var(--bd-rule); }

/* ---------- vocabulary the report pages asked for -------------------
   Each of these existed only inside a compound selector, so a page that
   used the class on its own got nothing. Reported by the migration of
   pweekly/aweekly rather than invented here. */

/* A panel that should absorb the leftover height. .app.fill scopes the
   flex plumbing; this is the standalone fallback so .grow is never a
   no-op on a page that is not a fixed shell. */
.grow { min-height: 0; }

/* Field group. Was defined only as .login-card .fgrp, so every form
   outside the login card had unspaced fields. */
.fgrp { margin-bottom: 14px; }
.fgrp:last-child { margin-bottom: 0; }

/* De-emphasis. Was defined only as .chip.mute. */
.mute { color: var(--bd-dim); }

/* An IN-BODY subtotal row - a per-agent group total inside <tbody>, which
   <tfoot> styling cannot reach because there is exactly one tfoot and many
   groups. The report needs it: pweekly subtotals every agent.
   NOTE the money colours are NOT set here. A subtotal cell carries PM() on
   an inner <span>; setting a colour on the row would beat it and silently
   kill the win/loss colour on every totals row, which is exactly the bug
   compat.css's .table-primary was causing. */
/* An alternating row. Several reports compute an alt flag server-side and
   emit a class for it - position.asp writes AltClass, CasinoPL and
   HandleReportBySportGroup and ipreports compute one - and every name they
   emitted (table-secondary, wagerentry, wagerentryalt) was defined NOWHERE.
   The VBScript was doing the work and throwing it away. This is the class
   those should point at. */
.rpt tbody tr.alt > td { background: var(--bd-zebra); }   /* --bd-zebra since 2026-09-16; --bd-sunken was a chip ground, too strong for a row */
/* The Total column carries a tint on every account row, over white or over
   the zebra - LiveAction's Week column, asked for 2026-09-16. pweekly marks
   the cell `tot`. A hover beats both; see the hover block. */
.rpt tbody td.tot { background: var(--bd-tint); }
.rpt tbody tr.alt > td.tot { background-color: var(--bd-zebra); background-image: linear-gradient(var(--bd-tint), var(--bd-tint)); }

.rpt tbody tr.subtotal > td {
  background: var(--bd-raised);
  font-weight: 700;
  border-top: 1px solid var(--bd-edge);
}
.rpt tbody tr.subtotal > td:first-child { background: var(--bd-raised); }

/* A page title in the head bar. header.asp emits no title element, so a
   report's name had nowhere to go but a .panel-hd label. Pages that want a
   real title put one here. */
.head h1, .page-h1 {
  font-family: var(--bd-display);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: var(--bd-display-track);
  margin: 0;
}
.dim { color: var(--bd-dim); }
.sm { font-size: 11.5px; }
.mono { font-family: var(--bd-mono); }
.nowrap { white-space: nowrap; }
.mb { margin-bottom: var(--bd-gap); }
.hidden { display: none !important; }
.sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.ico { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- column priority ---------------------------------------
   Every th/td carries data-p. Narrow viewports DROP low-priority
   columns rather than squeezing all 17 into unreadable 8px text.
     p=1  identity + the figures you cannot report without
     p=2  useful, sheddable
     p=3  reference detail - first to go
   The detail sheet still shows everything that was dropped.

   Thresholds are higher than they look because the drawer is an
   overlay: content gets the full viewport, so 1400px of viewport is
   1400px of table. */
@media (max-width: 1400px) {
  .rpt [data-p="3"] { display: none; }
}
@media (max-width: 1180px) {
  .rpt [data-p="2"] { display: none; }
}

/* ---------- responsive ---------- */
@media (max-width: 1100px) {
  .g-dash > .sp-3 { grid-column: span 6; }
  .g-dash > .sp-4 { grid-column: span 6; }
  .g-dash > .sp-5 { grid-column: span 12; }
  .g-dash > .sp-7 { grid-column: span 12; }
  .g-dash > .sp-8 { grid-column: span 12; }
}

@media (max-width: 720px) {
  .g-dash > .sp-3,
  .g-dash > .sp-4,
  .g-dash > .sp-6 { grid-column: span 12; }

  /* ...EXCEPT THE THREE STAT TILES, which stay side by side. Stacked they
     were three full-width panels and most of the first screen before the
     week chart began. Same specificity as the rule above and written after
     it, so the later one wins.

     A third of 390px is about 118px, which the 40px display size does not
     fit - "-$12,450" alone is wider than the tile. Everything in them comes
     down a size; they are a glance, and the dashboard below is the detail. */
  .g-dash > .stat-tile { grid-column: span 4; }
  .stat-tile .panel-bd { padding: 8px 7px 9px; }
  .stat-tile .stat-lbl { font-size: 9px; letter-spacing: .04em; }
  .stat-tile .stat-val { font-size: 19px; letter-spacing: -.01em; }
  .stat-tile .acct-note { font-size: 10px; margin-top: 6px; }
  .g-2, .g-3, .g-4 { grid-template-columns: 1fr; }

  /* 92px sat here, justified by "Broadsheet's Georgia h1 wraps to two lines".
     THERE IS NO h1. header.asp emits a burger and a Settings button, nothing
     else - .head is the only element with that class in the tree and it has
     carried no title since the redesign. The real head measures
     10 + 32 + 10 + 1 = 53px, so the column header was pinning itself ~39px
     too low: a band of data rows scrolled through the gap ABOVE it, and the
     header read as sitting on top of the table rather than above it.

     The rule that sized that phantom h1 went with the estimate. What is left
     is the pre-JS fallback only - initHeadOffset() measures the real head. */
  :root { --bd-head-h: 54px; }
  .head { padding: 10px 12px; }
  /* The burger's label is the ACCOUNT NAME now, and with .who gone it is
     the only identity in the chrome - so it stays visible at this width,
     where the old "Menu" text used to be dropped. */
  .content { padding: 12px; }

  /* Report goes to identity + the two figures that matter, and the row
     becomes a tap target for the rest. No horizontal scrolling war.
     The seven day columns get their own attribute rather than a priority
     tier, because they travel together: showing Mon-Wed and hiding Thu-Sun
     would be worse than showing none of them. */
  /* Only three columns survive here, so the pane needs no horizontal
     scroll - which means it need not be a scrollport at all. Handing the
     scrolling back to the window puts the table header into the viewport's
     sticky context, where it can offset itself below the page head. A
     fixed 100vh shell on a phone is also a trap: the address bar makes
     100vh lie. */
  .app.fill { height: auto; overflow: visible; }
  .app.fill .content { display: block; }
  .rpt-wrap, .app.fill .panel.grow .rpt-wrap {
    overflow: visible; max-height: none; height: auto;
  }
  /* .panel is overflow:hidden to clip its own radius. That also makes it the
     sticky containing block the moment .rpt-wrap stops being a scrollport,
     which parks the table header off-screen with the panel. Release it here;
     at 0-2px radius there is nothing left to clip anyway. */
  .app.fill .panel.grow { overflow: visible; }
  .rpt thead th { top: var(--bd-head-h); }
  /* ...but NOT inside a modal. --bd-head-h is the height of .head, and there
     is no .head above a table in a dialog - the offset just pushed the column
     header 54px down from the top of the modal's scrollport and let rows run
     through the band above it. Reported as "the rows overlap the header but
     the header stays visible", which is exactly what that looks like.
     (0,2,2) against the (0,1,2) above it. */
  .modal .rpt thead th { top: 0; }

  /* THE TOTALS ROW STAYS STICKY HERE NOW. It used to be released, because
     with the window scrolling instead of the pane it becomes a bar parked
     over the last rows for the whole length of the scroll rather than a
     footer pinned to a pane. That was the right call when the row it
     covered was one of seventeen columns you were trying to read past.
     It is the wrong call now: the phone shows Acct / Total / Net / Bal,
     the totals row is those same three figures for the whole book, and
     keeping it in view is the point of it.
     The overlap it used to cause is paid for below - the content gets
     enough bottom padding for the last account to clear the bar. */
  .rpt tfoot td, .rpt tfoot td:first-child {
    position: sticky;
    bottom: 0;
    z-index: 20;
    background: var(--bd-raised);
  }
  /* ROOM FOR THE LAST ROW TO CLEAR THE PINNED TOTALS, AND IT HAS TO BE
     INSIDE THE TABLE. This was padding on .content and it did neither job.
     A sticky element is held by its containing block, which for a tfoot cell
     is the TABLE - so padding outside the table does not extend anything the
     totals row is pinned within. It reached its natural place at the end of
     the scroll, un-pinned, and rode up with the last account, while
     mid-scroll it sat over rows that had nowhere to go.

     Padding on the table itself extends that containing block. The last
     account now scrolls clear of the totals, and the totals stay pinned
     until the padding itself scrolls in. It works because the reset sets
     border-collapse: separate; with collapse, padding on a table is
     ignored. */
  .rpt { padding-bottom: 76px; }

  /* THE GRAND TOTAL LABEL MAY WRAP, ON EVERY REPORT. This is the generic
     half of a rule allpending already carries: the label is a nowrap run
     inherited from `.rpt td`, and a nowrap run sets the TABLE's min-content
     whatever min-width says about the flex item holding it. Below 720
     nothing scrolls sideways - `body { overflow-x: clip }` - so a table
     that cannot shrink is not a scroll, it is a clip.

     Measured on pweekly at 402: "GRAND TOTAL 122" was 165px and pinned the
     table at 434px inside a 372px pane, 62px of the last column gone. With
     the label free to wrap the table is 357px and nothing is lost. The
     word GRAND goes too, so it stays one line rather than actually
     wrapping - the row is unmistakably the total without it. */
  .rpt tfoot td:first-child { white-space: normal; }
  .rpt tfoot .gt-wide { display: none; }

  .rpt [data-p="2"], .rpt [data-p="3"], .rpt [data-dow] { display: none; }
  .rpt tbody tr { cursor: pointer; }
  .rpt th, .rpt td { height: 42px; }
  .compact-hint { display: block; }

  .login-card { padding: 24px 20px 22px; }
}

/* ---------- the smallest phones ----------------------------------
   320-360px, where a report has one more column than it has room for.
   pweekly carries five at this width - Acct, Total, Net, Pend, Bal -
   and measured 358px inside a 320px viewport, which below 720 is a
   silent CLIP and not a scroll: `body { overflow-x: clip }` means the
   right-hand column simply stops existing, with no scrollbar to say so.

   Two levers, both cheap, and neither touches a wider screen: 2px off
   each side of every cell and one point off the figures. Measured on
   pweekly at 320 - 358px base, 338 on the padding alone, 320 on the
   size alone, 300px with both. 20px of headroom rather than 38px of
   lost column.

   `screen and` because paper has neither the width constraint nor a
   viewport to clip against, and the print block wants full-size
   figures. */
@media screen and (max-width: 360px) {
  .rpt { font-size: 11px; }
  .rpt th, .rpt td { padding: 0 4px; }
}


/* ================================================================
   FOLDING REPORTS - pweekly.asp and aweekly.asp
   ================================================================
   Three levels on one table: a sub-agent band, an account row, and the
   fields the width dropped. Everything below the band lands folded, so
   the report opens as the book rather than as a wall, and each level
   costs one tap.

   Nothing here is a mobile feature bolted on the side. It is the other
   half of the column-priority system above: the sub-row holds exactly
   what the priority tiers took away, and it answers to the same three
   breakpoints, inverted.
   ================================================================ */

/* ---------- the report toolbar -----------------------------------
   A full-bleed band between the page head and the panel: week picker on
   the left, Fields on the right. Sticky matters on a PHONE, where
   .app.fill is released and the window scrolls; above 720 it already
   sits outside the table's scrollport and never moved.

   The negative margin is -14px and not var(--bd-pad) on purpose:
   `.content.wide` sets a literal 14px and outranks the 12px the narrow
   breakpoint gives `.content`, so the padding it cancels is 14 at every
   width. */
.rptbar {
  position: sticky;
  top: var(--bd-head-h);
  z-index: 26;
  flex: 0 0 auto;
  background: var(--bd-void);
  border-bottom: 1px solid var(--bd-rule);
  padding: 9px 14px;
  margin: -14px -14px var(--bd-gap);
  display: flex;
  align-items: center;
  gap: 10px;
}
/* weeks.asp wraps its row in a <form> - the week is a real postback -
   so the form is the flex child that grows and Fields sits beside it,
   rather than the button having to be moved inside someone else's form. */
.rptbar > form { flex: 1 1 auto; min-width: 0; margin: 0; }
/* weeks.asp keeps its own `.row wrap mb` - Deleted.asp includes the same
   file with no toolbar around it - so the bar only cancels the margin
   that row carries for the pages that use it standalone. */
.rptbar .row { margin-bottom: 0; }
.rptbar .lbl { margin: 0; flex: 0 0 auto; }
/* Not scoped to .rptbar: the week select is `.field`, which is width:100%,
   and Deleted.asp would stretch it across the page. */
.wsel { width: auto; min-width: 220px; }
/* The native select is hidden wherever the picker replaces it - which is
   wherever there IS a picker. initWeekPicker() bails out when the page has
   no .rptbar (Deleted.asp includes weeks.asp with no toolbar around it),
   so scoping the hide to .rptbar is what keeps that page's plain select
   working rather than hiding a control with nothing standing in for it. */
.rptbar .wsel { display: none; }
.rptbar .wkpick { flex: 0 0 auto; width: 270px; }
.rptbar .fbtn { flex: 0 0 auto; }

/* The three week presets are HIDDEN, NOT REMOVED. weeks.asp still emits
   them and SF() still drives them; one rule takes them off the page until
   the call to delete them is made. (0,2,0) so it holds inside the narrow
   block below whatever that does with layout. */
.rptbar .wpre { display: none; }

/* ---------- the week picker on a phone -----------------------------
   Above 720 the native <select> is the right control and stays. Below
   it, the OS picker is the problem: iOS throws up a full-height wheel
   and Android a full-screen dialog, both of which cover the report, and
   on the wheel the "cancel" is a small word in a corner - an agent
   checking which weeks exist has to commit to one to get out.

   This is a plain list instead: five weeks visible, the rest a scroll,
   and three ways out that are all bigger than a word - the backdrop,
   the Cancel button, and Escape. It writes the chosen week back into
   the real <select>, which is still what posts.

   Built by initWeekPicker() in js/ui.js from the select's own options,
   so weeks.asp keeps printing exactly what it printed before. */
/* THE PICKER IS THE CONTROL AT EVERY WIDTH NOW. It was built for the
   phone and hidden above 720, where the native <select> took over - so
   the same list of weeks was a themed five-row list on a phone and an OS
   dropdown on a laptop, in the OS's own font and colours, ignoring the
   direction the rest of the page is painted in.

   Nothing about the reasoning was phone-specific except the OS picker
   being especially bad there. On a desktop the native menu is merely the
   wrong furniture: it cannot be themed, it renders the whole week list at
   once, and it is the only control on these pages that does not follow
   Broadsheet or Noir.

   initWeekPicker() already built this unconditionally - the comment there
   says CSS decides which of the two is on screen - so this is a display
   swap and not new machinery. The <select> stays in the DOM, keeps its
   name, and is still what posts. */
.wkpick { display: block; position: relative; }
/* The row height is the unit FIVE is counted in - see .wkpick-list. Bigger
   on a phone because it is a touch target, smaller where there is a
   pointer, and either way the list shows five and scrolls the rest. */
.wkpick { --wk-row: 34px; }
.wkpick-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  cursor: pointer;
}
.wkpick-btn .wkpick-val {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wkpick-btn .ico { flex: 0 0 auto; opacity: .7; transition: transform .14s ease; }
.wkpick.is-open .wkpick-btn .ico { transform: rotate(180deg); }

/* THE BACKDROP IS TWO DIFFERENT JOBS AT TWO WIDTHS. Everywhere it is the
   click-catcher that closes the popup on a press outside it. On a phone it
   is also a dimmer, because the popup there is a panel across the width of
   the report and the page behind it should recede. On a desktop the popup
   is a 270px dropdown, and dimming the entire report to open one is the
   sort of thing that makes a page feel like it is nagging - so it stays
   transparent and just catches the click. */
.wkpick-back {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: transparent;
  display: none;
}
.wkpick.is-open .wkpick-back { display: block; }

.wkpick-pop {
  position: absolute;
  z-index: 61;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  display: none;
  flex-direction: column;
  background: var(--bd-surface);
  border: 1px solid var(--bd-edge);
  box-shadow: 0 14px 40px rgba(0, 0, 0, .32);
}
.wkpick.is-open .wkpick-pop { display: flex; }
/* "09/01/2026 Ending 09/07/2026" is the longest thing the list holds. The
   button is 270px in the toolbar, and left/right:0 sizes the popup to it -
   this is the floor for anywhere the button ends up narrower. */
.wkpick-pop { min-width: 250px; }
.wkpick-hd {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border-bottom: 1px solid var(--bd-rule);
  font-size: var(--bd-head-size);
  letter-spacing: var(--bd-head-track);
  text-transform: uppercase;
  font-weight: var(--bd-head-weight);
  color: var(--bd-muted);
}
.wkpick-hd .btn { margin-left: auto; }

/* FIVE AT A TIME. 5 x 44px is the whole point of the control - enough of
   the list to see that it is a list and that it scrolls, without taking
   the report off the screen behind it. overscroll-behavior stops the
   page scrolling on when the list hits its end. */
.wkpick-list {
  max-height: calc(var(--wk-row) * 5);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  margin: 0;
  padding: 0;
  list-style: none;
}
.wkpick-list li {
  min-height: var(--wk-row);
  display: flex;
  align-items: center;
  padding: 6px 11px;
  border-top: 1px solid var(--bd-rule);
  font-size: 12.5px;
  cursor: pointer;
}
.wkpick-list li:first-child { border-top: 0; }
.wkpick-list li:hover { background: var(--bd-raised); }
.wkpick-list li[aria-selected="true"] {
  background: var(--bd-phosphor-soft);
  color: var(--bd-phosphor);
  font-weight: 700;
}

/* ---------- money keeps its colour when it is a link --------------
   A day figure with action and the pending balance are links, and the
   base `a { color: var(--bd-phosphor) }` beat the class PM() puts on the
   span around them - so every clickable figure rendered navy on
   Broadsheet and steel blue on Noir, throwing the sign away exactly
   where the reader is looking hardest.

   The dotted underline now does the job the colour was doing: it says
   this figure opens something. Solid on hover. */
.rpt td a {
  color: inherit;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.rpt td a:hover { text-decoration-style: solid; }

/* ---------- a shorter column head --------------------------------
   (0,1,2), so it beats both the base row height and the 42px the narrow
   breakpoint gives every row for touch. The head is not a hit target. */
.rpt thead th { height: var(--bd-head-th); }
/* Both are pre-JS fallbacks. initHeadOffset() in js/ui.js measures the
   real head and the real toolbar and writes the answers inline on <html>,
   which outranks these. --bd-bar-h must exist even on pages with no
   toolbar, or the calc() that offsets the column head is invalid and the
   header stops sticking at all. */
:root { --bd-head-th: 26px; --bd-bar-h: 0px; }
/* The MEASURED height of the column-head strip - see initHeadOffset(). This
   value is only the no-JS fallback and is deliberately not --bd-head-th:
   that one sizes the cell, this one records what the strip came out at. */
:root { --bd-th-h: 27px; }

/* ---------- the disclosure ----------------------------------------
   The whole row is the control - see initReport() in js/ui.js - and this
   is the affordance for it, not the only way in. Hidden at >=1400 where
   nothing is dropped and a caret would open an empty sub-row. */
.disc {
  display: none;
  vertical-align: middle;
  margin: 0 5px 0 0;
  padding: 2px;
  background: none;
  border: 0;
  border-radius: 2px;
  color: var(--bd-dim);
  line-height: 0;
  cursor: pointer;
}
.disc svg { width: 11px; height: 11px; transition: transform .14s ease; }
@media (hover: hover) {
  .disc:hover { color: var(--bd-ink); background: var(--bd-sunken); }
}
.disc[aria-expanded="false"] svg { transform: rotate(-90deg); }

/* The account password, restored inline. It used to print on a SECOND
   LINE under the login, which cost every row its height whether the
   field was on or not; in brackets it costs nothing vertical and the
   account number still leads. */
.rpt .acct .pw {
  color: var(--bd-dim);
  font-weight: 400;
  font-size: 11.5px;
  margin-left: 5px;
}

/* ---------- the sub-row -------------------------------------------
   Built by js/ui.js from the row's own cells the first time a row is
   opened - not printed by the ASP. On a book with 400 players that is
   400 rows and about double the response saved, and there is one source
   of truth for every figure. */
.rpt tr.sub { display: none; }
.rpt tr.sub > td {
  padding: 0;
  white-space: normal;
  background: var(--bd-sunken);
  border-bottom: 2px solid var(--bd-rule);
}
/* A colspan cell IS :first-child, so without this it inherits the sticky
   identity column and rides over the table on a sideways scroll. */
.rpt tr.sub > td:first-child { position: static; left: auto; border-right: 0; }
/* An account and its sub-row are one object; they light together. */
@media (hover: hover) {
  .rpt tbody tr:hover + tr.sub > td { background: var(--bd-hover); }
}
.rpt tr.sub.is-shut { display: none; }
.rpt tbody tr.is-hid { display: none; }

.sub-in { padding: 8px 10px 11px; display: flex; flex-direction: column; gap: 8px; }

/* the week, keeping its weekday order and its right-aligned figures -
   the same shape as the table it came out of, at a smaller scale */
.dstrip {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border: 1px solid var(--bd-rule);
  background: var(--bd-surface);
}
.dstrip > div {
  padding: 4px 2px 5px;
  text-align: center;
  border-left: 1px solid var(--bd-rule);
  min-width: 0;
}
.dstrip > div:first-child { border-left: 0; }
.dstrip b {
  display: block;
  font-size: 9px;
  letter-spacing: .09em;
  color: var(--bd-muted);
  font-weight: 700;
}
.dstrip .v { font-family: var(--bd-mono); font-size: 12px; }

/* the money, in as many columns as the width allows */
.fgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(102px, 1fr));
  background: var(--bd-surface);
  border: 1px solid var(--bd-rule);
  border-width: 1px 0 0 1px;
}
.fgrid > div {
  padding: 3px 7px 4px;
  min-width: 0;
  border-right: 1px solid var(--bd-rule);
  border-bottom: 1px solid var(--bd-rule);
}
.fgrid dt {
  font-size: 9px;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--bd-muted);
  font-weight: 700;
}
.fgrid dd {
  margin: 0;
  font-family: var(--bd-mono);
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* a memo in a 102px cell is a note nobody reads */
.fgrid .wide { grid-column: 1 / -1; }
.fgrid .wide dd { font-family: var(--bd-font); white-space: normal; }

/* A week with nothing in it, folded to one line. Only below 1180, where
   the sub-row is carrying enough for the collapse to be worth it. */
.rpt tr.sub .sub-lead { display: none; }
.sub-lead {
  font-size: 11px;
  letter-spacing: .09em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--bd-dim);
  padding: 8px 11px;
  gap: 10px;
  align-items: baseline;
}
.sub-lead .q {
  font-family: var(--bd-mono);
  letter-spacing: 0;
  text-transform: none;
  font-size: 12px;
  color: var(--bd-muted);
}

/* ---------- the sub-agent band ------------------------------------
   Two rows per group and exactly one on screen. OPEN it is a single
   full-width cell - the group's own rows are underneath saying the
   figures. FOLDED it is the full cell skeleton, so its Total, Net and
   Bal land under their own headings at every width. colspan cannot be
   changed from CSS, which is why it is two rows rather than one row
   pretending to be both.

   It STICKS while its own rows are in view. That is what the <tbody>
   per agent in pweekly.asp is for: a sticky table cell is bounded by its
   row group, so with one tbody the first band would pin itself to the
   top and stay there while the second agent's rows scrolled underneath.

   And the tint has to be OPAQUE. --bd-phosphor-soft is translucent -
   fine for a row that scrolls, fatal for one that does not. Painting the
   same tint as a flat gradient over an opaque ground keeps the exact
   colour and stops the rows showing through. */
.rpt tr.grp > td,
.rpt tr.grp > td:first-child {
  position: sticky;
  top: var(--bd-head-th);
  z-index: 11;
  background-color: var(--bd-surface);
  background-image: linear-gradient(var(--bd-phosphor-soft), var(--bd-phosphor-soft));
  border-top: 1px solid var(--bd-edge);
  border-bottom: 1px solid var(--bd-edge);
}
/* THE BAND IS ONE FLAT LAYER, and the 13 that used to be here is why it was
   not. Two bands overlap whenever the next one reaches the sticky offset
   before the previous group has ended - a short window, or a small group. In
   that overlap the outgoing band's name cell at 13 beat the incoming band's
   FIGURE cells at 11, so the name scrolled away and the rest of the outgoing
   row stayed pinned on top of the arriving one. An open band made it worse
   still: it is a single colspan cell, so its 13 covered the full width.

   11 keeps everything the 13 was actually for - it is still above the body
   rows' frozen identity column (10) and their ordinary cells (auto), and
   still below the header (12, and 14 on its own first cell), so a band still
   scrolls behind the headings. What it gives up is beating another BAND, and
   at equal z-index the incoming one wins on document order, which is what a
   pinned group header should do. Measured before: two bands at 140 and 142
   overlapping by 32px. */
.rpt tr.grp > td:first-child { left: 0; z-index: 11; }
/* the open band is one colspan cell: still sticky vertically, but out of
   the sticky identity column */
.rpt tr.grp-open > td:first-child { left: auto; border-right: 0; }
.rpt tr.grp .gname {
  color: var(--bd-title);
  font-weight: 700;
  font-size: 11.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  vertical-align: middle;
}
/* FLOATED, not flexed. Making the cell a flex container would take it out
   of the table's own box model, and on the folded band that cell is one of
   twenty-one that have to stay in their columns. A float is the one way to
   push it to the trailing edge without touching the cell's display. */
.rpt tr.grp .gcount {
  float: right;
  color: var(--bd-muted);
  font-size: 10.5px;
  margin-left: 12px;
  font-family: var(--bd-font);
  line-height: var(--bd-row-h);
}
/* The glyph and the number read as one mark: the icon is the noun, the
   number is the value, and they sit tight together so the pair does not
   read as two separate things floated into the same corner. Sized here
   rather than left at .ico's 1em - at the 10.5px this span sets, 1em
   would be a 10px icon, which is under the size a person-shape stays
   legible at. */
.rpt tr.grp .gcount .ico {
  width: 13px;
  height: 13px;
  vertical-align: -2px;
  margin-right: 3px;
  opacity: .8;
}
.rpt tr.grp .gcount b {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.rpt tr.grp-shut .gcount { opacity: .75; }
.rpt tr.grp .disc { color: var(--bd-phosphor); }
.rpt tr.grp .grpfig { font-weight: 700; font-family: var(--bd-mono); }
/* stacking the same translucent tint is the hover, so the band keeps its
   identity under the pointer instead of turning into a normal row */
@media (hover: hover) {
  .rpt tbody tr.grp:hover > td { box-shadow: inset 0 0 0 999px var(--bd-phosphor-soft); }
}
/* one of the two band rows is always off */
.rpt tr.is-off { display: none; }

/* ---------- what the filter took away -----------------------------
   !important because this has to beat every display the two reports set
   on a row - table-row, block, flex - at whatever width. is-hid next to
   it is the fold and is a different question: a row can be filtered out
   of the report and folded away by its band at the same time, and either
   one alone is reason enough not to draw it. */
.rpt tr.is-filtered { display: none !important; }

/* THE NATIVE SELECT IS HIDDEN WHEREVER A PICKER STANDS IN FOR IT, and
   scoped to .rptbar for the same reason .wsel is: a page with no toolbar
   gets no picker built, and must keep its working control. The select stays
   in the DOM and is still what the filter reads. */
.rptbar .rfilter { display: none; }
/* A wager type is shorter than a week. 200px holds "Action Reverse - Win
   Only" and still leaves the fold button its place on a 1180 laptop. */
.rptbar .wkpick.pick-filter { flex: 0 0 auto; width: 200px; }

.rpt tbody tr[data-row],
.rpt tbody tr[data-grp-band],
.rpt tfoot tr[data-row] { cursor: pointer; }

/* ---------- what the sub-row holds, per width ---------------------
   Hidden by default, revealed at exactly the width its parent column is
   dropped. The whole sub-row goes above 1400, where nothing is missing
   from the row. */
.rpt tr.sub [data-p="2"],
.rpt tr.sub [data-p="3"],
.rpt tr.sub [data-dow] { display: none; }

@media (max-width: 1400px) {
  .rpt tr.sub { display: table-row; }
  .rpt tr.sub [data-p="3"] { display: revert; }
  .disc { display: inline-flex; }
}
@media (max-width: 1180px) {
  .rpt tr.sub [data-p="2"] { display: revert; }
  html.qc .rpt tr.sub .sub-lead { display: flex; }
  html.qc .rpt tr.sub [data-full] { display: none; }
}
@media (max-width: 720px) {
  .rpt tr.sub [data-dow] { display: revert; }
  .rpt tr.sub .dstrip[data-dow] { display: grid; }
  /* NOTHING SCROLLS SIDEWAYS HERE, so the identity column has nothing to
     stay pinned against - and its opaque background was painting the first
     cell of every row in a colour the rest of the row did not share. Let it
     be an ordinary cell and let the row's own background show through. */
  .rpt th:first-child,
  .rpt td:first-child {
    position: static;
    left: auto;
    background: transparent;
    border-right: 0;
  }
  .rpt tbody tr:hover td:first-child { background: transparent; }

  /* the band offsets by the whole stack now, because below 720 the
     WINDOW is the scrollport rather than the table pane */
  .rpt tr.grp > td,
  .rpt tr.grp > td:first-child {
    top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th));
  }
  /* BOTH SELECTORS NAMED, the way the tfoot rule above does it. Releasing
     the sticky first COLUMN a few rules up uses .rpt th:first-child, which
     at (0,2,1) also outranks .rpt thead th at (0,1,2) - so the heading cell
     lost its vertical stickiness along with the horizontal freeze it was
     meant to lose, and "Acct" scrolled away while the rest of the header
     stayed pinned. Re-stating position here at (0,2,2) wins it back without
     re-freezing the column. */
  .rpt thead th,
  .rpt thead th:first-child {
    position: sticky;
    top: calc(var(--bd-head-h) + var(--bd-bar-h));
  }

  /* THE ACCOUNT COUNT DROPS UNDER THE NAME HERE, and that is the cheaper of
     the two ways it can go. The identity column is about 78px of usable
     width on a 390px phone, and the caret, the agent login and the count
     want 79 - so the float wraps, by one pixel of inter-element whitespace.
     Forcing it onto one line (float: none) does fit the line but widens the
     column past the budget: measured 4-6px of overflow, which below 720 is
     a clip and not a scroll.

     So it wraps, and what gets fixed is the COST of wrapping. The 32px
     line-height is --bd-row-h, sized for a count sitting BESIDE a name; on
     a second line it just pads the band out to 53px. At 1 the band is back
     to the 42px every other row gets.

     IT HAS TO BE IN THIS BLOCK, not the narrow block further up. The base
     `.rpt tr.grp .gcount` is declared after that one at equal specificity,
     so an override up there loses however correct it looks - the same
     shadowing that has caught this stylesheet before. */
  .rpt tr.grp .gcount { line-height: 1; }
  /* Two rows at 390, not one. The week picker takes the first on its own
     and the two buttons take the second - three controls on one line left
     the select about 190px, which truncates the week it is naming. The
     word "Week" goes either way; the select already reads
     "Current Week - 09/01 - 09/07". */
  .rptbar { padding: 8px 14px; flex-wrap: wrap; }
  .rptbar .lbl { display: none; }
  .rptbar > form { flex: 1 1 100%; }
  .wkpick-back { background: var(--bd-scrim); }
  .wkpick-pop { min-width: 0; }
  /* .rptbar .wsel is hidden at every width now - see the base rule. */
  .rptbar .wkpick { flex: 1 1 100%; width: auto; }
  /* ...but the filter shares the row with the fold button rather than
     taking a line of its own: it is two or six short words, not a date
     range, and a phone has no room for a third bar of chrome. */
  .rptbar .wkpick.pick-filter { flex: 1 1 auto; width: auto; min-width: 0; }
  /* 44px is the touch target, and five of them is still the list. */
  .wkpick { --wk-row: 44px; }
  .rptbar .foldall { flex: 1 1 auto; justify-content: center; }
  .rptbar .fbtn { flex: 1 1 auto; justify-content: center; }

  /* ONE LINE, NOT TWO, WHEN THE FORM IS A SWITCH. The 100% above is for
     weeks.asp: a <select> naming "Current Week - 09/01 - 09/07" needs the
     whole width, so its form takes a line and the buttons take the next.
     allpending's form is two short segments, and the same rule was pushing
     Collapse agents onto a row of its own for no reason - two bars of
     chrome above a report that has to fit on a phone.

     Measured at 390: REGULAR + FUTURES is 149px and the fold button 158px,
     which with the 10px gap and 28px of padding leaves 45px spare. */
  .rptbar-seg > form { flex: 0 1 auto; }
  .rptbar-seg .foldall { flex: 0 0 auto; margin-left: auto; justify-content: center; }
  .rptbar-seg .pseg button { padding: 5px 10px; }

  /* ---------- NOTHING MAY SCROLL SIDEWAYS ------------------------
     The viewport is locked to the device width, so a table wider than
     it is a page the reader cannot reach the right-hand edge of and
     cannot zoom out to see. Two things were pushing past 390:

       1. `.rpt th, .rpt td { white-space: nowrap }` plus an account cell
          holding a caret, a login and a bracketed password. It is the
          one cell with prose in it, so it is the one cell allowed to
          wrap; the figures must never wrap and do not.
       2. 8px of padding either side of four columns.

     overflow-x: clip on the body is the backstop. NOT overflow-x:
     hidden - hidden makes the element a scroll container, which changes
     what `position: sticky` sticks to, and this page has four sticky
     layers riding on it. clip does the same visual job and creates no
     scrollport. */
  body { overflow-x: clip; }
  .rpt th, .rpt td { padding: 0 6px; }
  .rpt td.acct { white-space: normal; }
  .rpt .acct .pw { margin-left: 4px; }

  /* seven day cells across 378px is 54px each, and a mono -1,250 does not
     fit at 12px */
  .dstrip .v { font-size: 11px; }
  .dstrip > div { padding: 4px 1px 5px; }
  .sub-in { padding: 8px 6px 11px; }
}

/* ================================================================
   ALL PENDING WAGERS - allpending.asp
   ================================================================
   The folding report above, one level deeper: agent, account, wager,
   ticket detail. Everything that is shared lives up there; only what
   this report needs that the weekly does not is here.

   THE DIFFERENCE IS THE IDENTITY COLUMN. On the weekly it is BOB100 -
   eight characters, and three money columns fit beside it on a phone.
   Here it is the bet: a sport, a league, a rotation number, a team, a
   price, a game time, and on a parlay all of that three or four times
   over. It cannot be a nowrap cell, so below 720 the row stops being a
   row - see the block layout at the end of this section.

   NO COLOUR IS SET ON A TICKET FIELD ANYWHERE IN HERE. .WagerNumber,
   .BT and .RW are defined in css/agent.css (the leg classes that sat
   beside them went with the slip - see ticket.asp);
   and have coloured this report since long before the redesign. This
   file loads BEFORE agent.css, so a colour here would lose to it
   anyway - but the rule is worth stating, because the redesign moves a
   ticket and does not restyle it.
   ================================================================ */

/* The one cell in the app allowed to be prose. */
/* LEFT, not centred like the rest (asked for 2026-09-17): a selection is a
   line of prose, and prose reads from a margin. The head stays centred. */
.rpt.pend td.sel { white-space: normal; padding-top: 5px; padding-bottom: 6px; text-align: left; }
.rpt.pend th, .rpt.pend td { vertical-align: top; }
.rpt.pend tbody tr td { height: auto; padding-top: 6px; padding-bottom: 6px; }

/* THE CARET SITS BESIDE THE TICKET, NOT ABOVE IT. .wsel holds block-level
   legs now, so the inline .disc in front of it got pushed onto a line of
   its own - a lone chevron with an empty row beside it, and every wager
   14px taller for it.

   FLOATED, for the reason .gcount is floated: making the cell a flex
   container would take it out of the table's box model, and at full width
   this is one of seven cells that have to stay in their columns. overflow
   on .wsel is what stops the legs wrapping underneath the float - it wants
   a block formatting context, not a clip. */
.rpt.pend td.sel > .disc { float: left; margin-top: 2px; }
.rpt.pend td.sel .wsel { display: block; overflow: hidden; }

/* ---------- risk and to-win --------------------------------------
   The two figures the report exists for. Mono and tabular comes from
   .RW in agent.css; what is set here is the COLUMN - a shared minimum
   width, so a 100.00 and a 128,550.25 occupy the same box and the
   decimal points stack from the first row to the grand total.

   NEITHER IS COLOURED, and that is a decision rather than an omission.
   Green and red already mean money up and money down on every other
   report, and a pending wager's risk and win are both simply positive -
   painting them would be inventing a third meaning for the two colours
   that carry the most weight in the app. The grand total used to be the
   exception: it carried PM(), which made it green for being positive,
   which is a thing a to-win is by definition. That came out of the ASP. */
.rpt.pend {
  /* One source for the figure columns. The phone reads them off the same
     two properties, so the column head, the rows, the folded bands and the
     grand total cannot drift apart at one width and not the other. */
  --pend-fig-w: 92px;
  --pend-act-w: 46px;
  /* The gap between the figure columns, shared for the same reason the
     widths are. A grand total is by definition the widest figure on the
     page - 3,357,399.68 fills a 92px box at 12.5px Consolas with nothing
     to spare - so without a gap the two totals touch while every row
     above them looks fine. */
  --pend-gap: 8px;
}
.rpt.pend th[data-col="risk"],
.rpt.pend th[data-col="win"],
.rpt.pend td.risk,
.rpt.pend td.wintotal { min-width: var(--pend-fig-w); }
.rpt.pend td.risk .RW { color: var(--bd-muted); }
.rpt.pend td.wintotal .RW { color: var(--bd-ink); }
/* The band's folded figures and the grand total are the same two columns
   and must read as the same two columns. */
.rpt.pend tfoot td.risk .RW,
.rpt.pend tr.grp > td.risk { color: var(--bd-muted); }
.rpt.pend tr.grp > td.grpfig,
.rpt.pend tfoot td.risk .RW,
.rpt.pend tfoot td.wintotal .RW {
  font-family: var(--bd-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* An account band is a band one level in: same machinery, quieter
   ground so it does not compete with the agent above it, and an offset
   that parks it under the agent band rather than on top of it. */
.rpt tr.grp.acc > td,
.rpt tr.grp.acc > td:first-child {
  background-image: none;
  background-color: var(--bd-raised);
  top: calc(var(--bd-head-th) + 30px);
}
.rpt tr.grp.acc > td { padding-left: 22px; }
.rpt tr.grp.acc .aname { font-family: var(--bd-mono); font-weight: 700; font-size: 12px; color: var(--bd-title); }
.rpt tr.grp.acc .disc { color: var(--bd-muted); }

/* The actions are a real column and never a dropped one. That is not a
   layout preference: the change-result form carries ids built from the
   wager number, and js/ui.js builds a sub-row by copying cell innerHTML
   - so a dropped actions column would put a second element with the
   same id on the page and ToggleEdit() would find the wrong one. */
/* MIDDLE, not top. The selection cell sets the row's height - a parlay is
   several legs tall - and the one control on the row should sit on its
   centre line, not hang at the first leg. (0,3,1) over the top rule's
   (0,2,1) above. */
.rpt.pend td.act { white-space: nowrap; text-align: right; vertical-align: middle; }
.rpt.pend td.act a { cursor: pointer; margin-left: 6px; }
.rpt.pend td.act .ico { width: 15px; height: 15px; }
.rpt.pend td.act .ew { text-align: left; padding-top: 6px; }
.rpt.pend td.act .ew .field { display: inline-block; width: auto; }

/* SCREEN ONLY, both of the blocks below. The print block at the end of
   this file puts every dropped column back and releases every sticky
   layer, but it does that through `.rpt ...` selectors - and the pend
   rules here are `.rpt.pend ...`, a class more specific, so on a narrow
   paper size they would win and the printout would come out as blocks
   with a sticky total bar on every page. The media type settles it
   outright rather than by a specificity race. */
@media screen and (max-width: 720px) {
  /* A WAGER STOPS BEING A ROW. The selection needs the full width and
     will not share a line with two figures and two icons, so the row
     becomes a block: selection across the top, then Risk, Win and the
     actions on one line beneath it.

     :not(.is-hid) on the row rule and :not(.is-off) on the band rules
     is load-bearing - turning a row into a block is a more specific
     selector than the rules that hide it, and without the guard the
     whole report unfolds at 720px and stays that way. */
  .rpt.pend, .rpt.pend tbody, .rpt.pend thead, .rpt.pend tfoot { display: block; }

  /* THE WIDTH BUDGET, AND IT IS A BUDGET. .panel-bd is a flex container
     and .rpt-wrap a flex item with min-width:auto, so the table cannot
     shrink below its own min-content - and below 720 nothing scrolls
     sideways, `body { overflow-x: clip }` sees to that. A table wider
     than the pane is therefore not a scroll, it is a CLIP: the right-hand
     edge of the actions column simply stops existing.

     Measured at 402 with the fixed columns first written at 92/46: the
     table's min-content came out at 403px inside a 357px pane, 31px of
     the row silently gone. So the columns are sized to fit rather than
     sized and hoped for. 10 + 88 + 8 + 88 + 8 + 34 + 10 = 246px of
     reserved width, which leaves the identity 126px on a 402px phone.

     88px is not arbitrary either: the widest figure the report can print
     is the grand total, and 3,357,399.68 is 12 characters at 12.5px
     Consolas - 82.5px. */
  .rpt.pend { --pend-fig-w: 88px; --pend-act-w: 34px; }

  /* ---------- THE COLUMN HEAD IS BACK ------------------------------
     It was display:none here, and the reasoning was sound as far as it
     went: a wager stops being a row at this width, so a seven-column
     header has nothing to head. What that missed is that the report
     still HAS columns on a phone - the risk and the win are a fixed
     pair at the right-hand edge of every row, band and total - and with
     no header the reader had nothing naming them but two ::before
     labels repeated on all 183 rows.

     So the head becomes the same flex strip the folded band and the
     grand total already are: the identity takes the slack, RISK and TO
     WIN sit in the two figure columns, and one narrow cell holds the
     place the actions occupy. Named once, at the top, pinned - instead
     of restated on every row.

     STICKY LIVES ON THE thead, NOT ON ITS CELLS. Below 720 the window
     scrolls and the table is display:block, so a sticky <th> would be
     bounded by its own <tr> - a containing block exactly its own height,
     which is a sticky element with nowhere to travel. The thead's
     containing block is the table, which is the whole report. */
  .rpt.pend thead {
    position: sticky;
    top: calc(var(--bd-head-h) + var(--bd-bar-h));
    z-index: 12;
    background: var(--bd-colhead);
    border-bottom: 1px solid var(--bd-edge);
  }
  .rpt.pend thead tr {
    display: flex;
    align-items: baseline;
    column-gap: var(--pend-gap);
    padding: 0 10px;
  }
  .rpt.pend thead th,
  .rpt.pend thead th:first-child {
    display: none;
    position: static;
    top: auto;
    left: auto;
    background: none;
    border: 0;
    padding: 0;
    height: var(--bd-head-th);
    line-height: var(--bd-head-th);
  }
  /* th.sel, not th:first-child: Ticket leads the table now and is one of
     the cells this width hides, so the strip's identity label has to be
     named by class or it would read TICKET over a column of selections. */
  .rpt.pend thead th.sel { display: block; flex: 1 1 auto; min-width: 0; }
  .rpt.pend thead th[data-col] {
    display: block;
    flex: 0 0 var(--pend-fig-w);
    min-width: 0;
    text-align: right;
  }
  .rpt.pend thead th.act { display: block; flex: 0 0 var(--pend-act-w); min-width: 0; }

  /* THE ROW IS A FLEX WRAP, not a block with inline-blocks in it. The
     selection takes a whole line and the figures take the next, which is
     what it did before - but as flex items on fixed bases they land in
     the SAME two columns as the header above them and the total below,
     rather than at 38%/38%/22% of whatever the row happened to be. That
     is what makes a column head mean anything here.

     margin-left:auto on the risk pushes the trio to the trailing edge,
     the same way the folded band and the tfoot do it a few rules down. */
  .rpt.pend tbody tr[data-row]:not(.is-hid) {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--pend-gap);
    padding: 7px 10px 8px;
    border-bottom: 1px solid var(--bd-rule);
  }
  .rpt.pend tbody tr[data-row] > td { display: none; border: 0; padding: 0; }
  .rpt.pend tbody tr[data-row] > td.sel { display: block; flex: 1 1 100%; min-width: 0; }
  .rpt.pend tbody tr[data-row] > td.risk,
  .rpt.pend tbody tr[data-row] > td.wintotal {
    display: block;
    flex: 0 0 var(--pend-fig-w);
    min-width: 0;
    text-align: right;
    padding-top: 5px;
  }
  .rpt.pend tbody tr[data-row] > td.risk { margin-left: auto; }
  .rpt.pend tbody tr[data-row] > td.act {
    display: block;
    flex: 0 0 var(--pend-act-w);
    min-width: 0;
    text-align: right;
    padding-top: 5px;
  }

  /* A cell that is no longer a table cell cannot carry the row's colour:
     it paints its own content box and leaves the gaps between cells bare,
     which is the blocky banding this fixes. The row is the box that spans
     the width once the table stops being a table, so the row is painted and
     the cells are cleared.

     The band gives up being sticky here as part of that trade. Sticky lives
     on the CELLS, and a sticky cell with no background of its own would
     scroll the rows straight through it. The weekly keeps its sticky band -
     it stays a real table at every width. */
  .rpt.pend tr.grp > td,
  .rpt.pend tr.grp > td:first-child {
    position: static;
    top: auto;
    left: auto;
    background-color: transparent;
    background-image: none;
    border-top: 0;
    border-bottom: 0;
  }
  .rpt.pend tr.grp {
    background-color: var(--bd-surface);
    background-image: linear-gradient(var(--bd-phosphor-soft), var(--bd-phosphor-soft));
    border-top: 1px solid var(--bd-edge);
    border-bottom: 1px solid var(--bd-edge);
  }
  .rpt.pend tr.grp.acc {
    background-image: none;
    background-color: var(--bd-raised);
  }

  /* ---------- THE AGENT BAND IS STICKY AGAIN, ON THE ROW -----------
     The rule above releases sticky from the CELLS, and that is still
     right: a cell that no longer paints its own background would let the
     wagers scroll straight through it.

     But the row does paint - that is the whole point of the rule above -
     and below 720 the row is no longer a table row. .rpt.pend and its
     tbody are display:block here, so tr.grp is an ordinary block box in
     an ordinary block container, and position:sticky on it is legal and
     paints. In the table layout it would not be: a sticky <tr> is
     ignored, which is why this could never have been done at full width
     and why the cells carry it there instead.

     ITS TBODY IS ITS RANGE, and that is exactly the behaviour wanted.
     allpending.asp opens one <tbody> per agent, so the band pins while
     its own agent is on screen and releases when the next agent's tbody
     arrives - it can never ride over a group it does not belong to.

     Only the agent band. The account band underneath it would need a
     second offset built from the agent band's own height, and stacking
     two pinned strips over a 402px screen costs more of the report than
     the second one is worth. */
  .rpt.pend tr.grp:not(.acc) {
    position: sticky;
    top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-th-h));
    z-index: 11;
  }
  .rpt.pend tr.sub > td { background: transparent; border-bottom: 0; }
  .rpt.pend tr.sub { display: block; background: var(--bd-sunken);
                     border-bottom: 2px solid var(--bd-rule); }
  /* AND IT MUST STILL BE ABLE TO CLOSE. `.rpt tr.sub.is-shut{display:none}`
     is (0,3,1) and the rule directly above is (0,3,1) too - equal, and this
     one is ~660 lines later, so it won and a sub-row opened on a phone could
     never be put away again. The JS was doing its half correctly the whole
     time: is-shut lands on the element and aria-expanded flips back to
     false, and the row just stayed on screen.

     (0,4,1) settles it. Same shadowing that compat.css has pulled on this
     stylesheet four times - a later rule at equal specificity is invisible
     until something measurable stops working. It also fixes folding a band
     with open rows under it, which hides its rows by shutting them and hit
     exactly the same wall. */
  .rpt.pend tr.sub.is-shut { display: none; }

  /* ---------- THE GRAND TOTAL IS PINNED --------------------------
     It was released here - `position: static` on the cells - and that
     was the band rule's reasoning applied to the wrong row. A band gives
     up sticky at this width because sticky lives on the CELLS and a
     cell that no longer paints its own background would let the rows
     scroll straight through it. The total has the same problem and the
     opposite answer: it is one row, not one per group, so the sticky can
     move up to the row group and take the background with it.

     THE thead TRICK, INVERTED. `.rpt.pend tfoot` is display:block, so
     its containing block is the table rather than a <tr> - and the
     table already carries `padding-bottom: 76px` from the generic
     narrow block, put there for exactly this so the last wager can
     scroll clear of the bar instead of ending underneath it. A sticky
     tfoot has that whole span to travel.

     Cells go back to inheriting: painting them transparent here and the
     row underneath is what a static tfoot needed, and a sticky one
     paints itself. */
  .rpt.pend tfoot {
    position: sticky;
    bottom: 0;
    z-index: 20;
    background: var(--bd-raised);
    border-top: 1px solid var(--bd-edge);
  }
  .rpt.pend tfoot td,
  .rpt.pend tfoot td:first-child { background: transparent; position: static; }
  .rpt.pend tfoot tr { background: transparent; border-top: 0; }
  /* NOT A TAP TARGET. The narrow block gives every cell height: 42px so a
     row can be tapped open, and the total inherited it - a pinned bar 58px
     tall, 7% of the viewport, permanently. Nothing about this row is
     tappable. Released, it is 34px. */
  .rpt.pend tfoot td { height: auto; }

  .rpt.pend tr.grp.grp-open:not(.is-off):not(.is-hid) { display: block; }
  .rpt.pend tr.grp.grp-open > td { display: block; width: auto; }
  /* Folded, a band keeps its figures - they are the answer the fold gave.
     Flex rather than two percentages: the name is variable width and the
     figures are not, so the name takes the slack. */
  .rpt.pend tr.grp.grp-shut:not(.is-off):not(.is-hid) {
    display: flex;
    align-items: baseline;
    column-gap: var(--pend-gap);
    /* The trailing pad is the actions column's width plus the gap in
       front of it, so the band's two
       figures land in the same boxes the rows' do. It has to be set HERE
       rather than as a padding-right further down: this selector is
       (0,6,1) for the :not() pair, and a later shorthand-free rule at
       (0,4,1) loses to it however far down the file it sits. */
    padding: 6px calc(10px + var(--pend-act-w) + var(--pend-gap)) 6px 10px;
  }
  /* EVERY cell is hidden and three are named back. The trailing empty cell
     that holds the actions at full width was still a flex item here - an
     unpainted block sitting after the figures, which is most of what "the
     background is blocky" was describing. margin-left:auto on the first
     figure pushes the pair to the edge rather than trusting the name cell
     to grow into whatever is left. */
  .rpt.pend tr.grp.grp-shut > td { display: none; padding: 0; border: 0; }
  .rpt.pend tr.grp.grp-shut > td:first-child { display: block; flex: 1 1 auto; min-width: 0; }
  /* THE SAME TWO COLUMNS THE HEAD NAMES. These were `flex: 0 0 auto` -
     shrink-wrapped to whatever the figure happened to be - so a band
     total and the rows it totals sat at two different offsets and the
     column head above them agreed with neither. On the fixed basis every
     figure on the page stacks in one of two boxes. */
  .rpt.pend tr.grp.grp-shut > td.grpfig {
    display: block;
    flex: 0 0 var(--pend-fig-w);
    min-width: 0;
    text-align: right;
  }
  .rpt.pend tr.grp.grp-shut > td.risk { margin-left: auto; }
  .rpt.pend tr.grp.grp-shut .gcount { float: none; margin-left: 8px; line-height: inherit; }

  /* Same shape as a folded band, and for the same reason - the empty
     trailing cell was wrapping the figures onto a line of their own. */
  .rpt.pend tfoot tr {
    display: flex;
    align-items: baseline;
    column-gap: var(--pend-gap);
    padding: 8px 10px;
  }
  .rpt.pend tfoot td { display: none !important; padding: 0; border: 0; }
  /* "GRAND" is dropped by the generic .rpt tfoot rule at this width - see
     the note there. It takes allpending's label from 112px to about 76px,
     which is what clears the budget above at 360 as well as at 402. */
  /* AND THE REST OF THE LABEL COMES DOWN A SIZE. Dropping GRAND was not
     enough on its own: the cell is 76px at 375 and "TOTAL" at the 13px
     figure size plus the count pill measured 78, so the pill wrapped to a
     line of its own and the pinned bar went to 54px. At 11px the pair
     needs 62px, which clears every width from 320 up.

     It reads as a label rather than as a figure at this size, which is
     what it is - the figures beside it keep the full size. */
  .rpt.pend tfoot td:first-child { font-size: 11px; letter-spacing: 0; }
  .rpt.pend tfoot .chip { font-size: 9px; padding: 1px 5px; }

  /* AND THE LABEL MAY WRAP. This is the backstop under the budget above,
     and it is the rule that actually found the clip: "GRAND TOTAL 183"
     is a nowrap run inherited from the base `.rpt td`, and a nowrap run
     sets the table's min-content whatever min-width:0 says about the
     flex item holding it. It was contributing 129px and the table could
     not shrink past it.

     Normal here means the worst case is a two-line total bar on a very
     narrow phone instead of a column sliced off the right of every row. */
  .rpt.pend tfoot td:first-child {
    display: block !important;
    flex: 1 1 auto;
    min-width: 0;
    white-space: normal;
  }
  .rpt.pend tfoot td.risk,
  .rpt.pend tfoot td.wintotal {
    display: block !important;
    flex: 0 0 var(--pend-fig-w);
    min-width: 0;
    text-align: right;
  }
  .rpt.pend tfoot td.risk { margin-left: auto; }

  /* The total ends with the actions column's width of empty space, or its
     figures sit one cell further right than the rows'. Padding on the row
     rather than a cell, because the cell that would hold it is
     display:none here. The folded band gets the same pad, set inside its
     own rule above for the specificity reason noted there. */
  .rpt.pend tfoot tr { padding-right: calc(10px + var(--pend-act-w) + var(--pend-gap)); }
}

/* A tighter budget for the small phones. Measured: the 88/34/8 columns
   above need 322px of layout viewport and clear 340 with room; at 320 -
   an SE, and the narrowest width still in the wild - they are 17px over,
   which the clip would take off the actions column. 82/30/6 needs 302.

   82px still holds the widest figure the report can print: 3,357,399.68
   is 82.5px at 12.5px Consolas, so it bleeds half a pixel into a 6px gap
   rather than into the column beside it. */
@media screen and (max-width: 360px) {
  .rpt.pend { --pend-fig-w: 82px; --pend-act-w: 30px; --pend-gap: 6px; }
}

/* ---------- print ------------------------------------------------
   Agents print these. Chrome goes, charts go, the table stays and is
   allowed to break across pages with its header repeating. */
/* ================================================================
   THE POSITION REPORT - position.asp
   ================================================================
   Drawn after DynamicOne's odds board (PLR_Interfaces\DynamicOne\app\lines.asp),
   which is the player-facing version of this same information: a dark league
   banner with a gold caption, a rotation cell, a crest and a name, then three
   bordered price boxes carrying the line over the juice. What this adds is the
   agent's own money under each price, and a percentage column saying how much
   of the game sits on that side.

   ONE ROW PER TEAM, which is what the player board is and what makes a per-row
   percentage possible at all: one row, one side, one share. The split bar that
   preceded it said the same thing but lived in a cell BETWEEN the two rows it
   compared, so it belonged to neither.

   IT IS STILL A <table>, and that is deliberate rather than incidental. The
   board on the player side is a Bootstrap grid of <div>s; here the fold engine
   and the row filter in js/ui.js both require real rows and real cells -
   membersOf() reads tr[data-row], sumCol() reads tr.cells[idx] - so divs would
   mean hand-rolling both. A table does proportional columns just as well.

   THE COLUMNS ARE PROPORTIONAL, NOT FIXED. DynamicOne's board is a 12-column
   grid (2 Game / 1 / 3 Team / 2 Spread / 2 Total / 2 MLine), so every column
   grows with the viewport and the board always fills the width it is given.
   Fixed pixel columns did the opposite here: the team column took every spare
   pixel and the three prices huddled together at the right of a wide screen.
   These percentages are the same 12ths, with the two columns this report has
   that a player board does not - the share and the game's exposure - taking
   the space the player board spends on its pick controls.
   ---------------------------------------------------------------- */
/* ---------- the board is the page, not a card in it -------------------
   position.asp used to wrap this in `.panel grow` > `.panel-bd flush`. That
   panel cost 45px of table width at 1280 - .content 1265 -> panel 1237 ->
   table 1220 - and drew a border that made the report read as an object on
   the page instead of as the page.

   These two rules are what the panel was carrying that still has to happen.
   The pane must stay BOUNDED - see .app.fill above: unbind it and the sticky
   thead pins behind the page head and the grand total stops being pinned -
   but the base .rpt-wrap max-height is a 100vh measurement that assumes the
   pane starts at the top of the page, so it overshoots by the height of the
   sport bar. Flexing it inside .content is the same fix .panel.grow had.

   The negative margin cancels .content's 14px gutter so the board runs to the
   edges. `.rptbar` directly above already does exactly this, with the same
   value, for the same reason - the sport strip is edge-to-edge and a board
   inset from it looked like a mistake. */
.content > .pos-wrap { margin: 0 -14px -14px; }

/* ---------- the WINDOW scrolls this board, not a pane inside it --------
   position.asp is `app` and not `app fill`. Removing the card left the board
   still ending in a scrollbar of its own, and left the pane stretching to the
   viewport whether or not the board filled it - 224.6px of empty pane under a
   one-league board, with the grand total floating at y=575 rather than pinned
   to anything. Handing the scroll to the window fixes both at once: the
   document is as tall as the board, so a short board simply ends.

   THIS IS NOT A NEW ARRANGEMENT. Everything below already exists and already
   works, fenced inside `@media (max-width: 720px)` further up - which is why
   the phone has scrolled this way all along. These three rules are that same
   branch, unfenced for this one board.

   The offsets are the whole trick, and the reason app.css's .app.fill note
   warns against doing this casually: with the window as the scrollport, a
   thead at `top: 0` pins behind the page head. .head is sticky at 0 and
   .rptbar is sticky at --bd-head-h, so the column head must clear BOTH, and a
   league band must clear the column head as well. Measured while scrolling:
   head 0-56.4, sport bar 56-103, column head 103-129, league band 129-161 -
   four sticky layers, none behind another. Both variables are measured by
   initHeadOffset() in js/ui.js; the :root values are the no-JS fallback.

   `overflow: visible` cannot be one-axis - per CSS Overflow, `visible` beside
   a non-visible value computes to `auto` - so this pane gives up horizontal
   scrolling. That is safe here and only here: the table is `table-layout:
   fixed; width: 100%`, measured with no horizontal overflow at 1440, 1280,
   1181, 1180, 1024, 900, 768, 700 and 390.

   The tfoot needs nothing: `.rpt tfoot td` is already `position: sticky;
   bottom: 0`, and sticky resolves against whatever the scrollport is - so the
   grand total simply re-pins to the bottom of the window. Measured at the
   foot of a full board: total 768-800, last row ending 768.4, clear of it. */
.rpt-wrap.pos-wrap { overflow: visible; max-height: none; height: auto; }
/* Both selectors named, and position re-stated: `.rpt th:first-child` is
   (0,2,1) and outranks `.rpt thead th` at (0,1,2), so naming only the latter
   loses the first column's stickiness - the trap the 720 branch documents
   after hitting it. */
.rpt.pos thead th,
.rpt.pos thead th:first-child {
  position: sticky;
  top: calc(var(--bd-head-h) + var(--bd-bar-h));
}
.rpt.pos tr.grp > td,
.rpt.pos tr.grp > td:first-child {
  top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th));
}

/* ---------- how the width is spent ------------------------------------
   MEASURED, at 1280 with the table at 1220. Ink is the rendered text, not the
   cell: rot 20px in a 73px column; team 140px average and 200px at the longest
   name, in 378px; At Risk 33px average, 43px at the widest figure, in 183px.
   That is 371px of 1220 - 30% of the board - standing empty in the three
   columns that need it least, while the three markets that ARE the report
   split 48% between them.

   So the three narrow columns are cut to what their content measures plus
   slack, and the rest goes to the markets. Rot holds three digits; At Risk
   holds one figure per game and is right-aligned, so its slack is leading.

   TEAM IS NOT SIZED FROM THE TEAM NAME. Cutting it to the 200px longest name
   plus slack - 22%, 275px - clipped two names on the first screen, because the
   name is not what is widest in that cell: the LAST line of a game also
   carries .gmeta, the ONE-SIDED flag and the ticket count, inline beside it.
   Measured: name 199.6 at the longest, .gmeta 131.7, plus 16 of padding, so
   the worst case is 347px and not 216. 28% is 350px at this width. The two
   that clipped were the two that carried a flag.

   Markets keep 19% each - 237px against 184px of content - so the recovered
   width still lands mostly where the report is.

   THIS SPLIT IS A WIDE-SCREEN SPLIT AND THE 721-1180 BLOCK PUTS IT BACK. The
   slack it spends only exists above about 1180; below that the team column has
   none to give, and taking 3 points off it there turns a pre-existing problem
   into a worse one. Measured, names clipped, original split vs this one:
   1280 0/0, 1152 0/1, 1024 1/2, 900 2/5, 768 6/12. The mid band keeps the old
   numbers exactly - see the restore in that media block, and the note there
   about why .gmeta is not being restacked to fix the underlying cause. */
.rpt.pos { table-layout: fixed; width: 100%; }
.rpt.pos col.c-rot  { width: 4%; }
.rpt.pos col.c-team { width: 28%; }
.rpt.pos col.c-mkt  { width: 19%; }
.rpt.pos col.c-risk { width: 11%; }

/* A fixed layout will not shrink a nowrap run, it just lets it out of the
   cell, so every cell that can hold a long name says what to do instead. */
.rpt.pos td, .rpt.pos th { overflow: hidden; }

/* ---------- the league banner ----------------------------------------
   The band is the fold control and the sticky header, so it keeps the .grp
   machinery it already had; this only changes what it looks like. Black bar,
   gold caption - the player board's, which is --bd-chrome and --bd-gold here
   under different names. */
/* THE ROW IS PAINTED AS WELL AS THE CELLS. The cells carry the colour because
   sticky lives on them, but they do not all end up the same height - the
   folded band's figure cell came out 32px against the name cell's 51 - and the
   19px of difference showed the page's own white ground through the middle of
   a black bar. Painting the row too costs nothing and closes any such gap. */
/* --bd-accent-chrome, NOT --bd-gold, and this was a real contrast failure
   rather than a preference. --bd-gold is defined in the on-white group of the
   light palette - #7A5C00, a dark olive meant to be READ ON PAPER - and it was
   being painted on --bd-chrome #24211C. Measured: 2.58:1, which fails WCAG AA
   for any text size.

   The theme already carries the token for exactly this situation, and says so
   in its own comment: --bd-accent-chrome, "brass on the warm near-black
   chrome, where the navy accent would vanish". #C9A227 on #24211C is 6.70:1.
   Dark theme was never affected - --bd-gold is #E8B93B there, on black - but
   it follows the same token now, which is #5B9DD9 on #000 and comparable.

   The league NAME took --bd-chrome-ink so the figure beside it could be the
   one accent-coloured run on the bar. Headings are the accent now, on every
   page (2026-09-15), and this bar sits on the chrome, so the name takes the
   chrome accent outright - the same yellow in both directions. */
.rpt.pos tr.grp:not(.ban) { background: var(--bd-chrome); }
.rpt.pos tr.grp:not(.ban) > td,
.rpt.pos tr.grp:not(.ban) > td:first-child {
  background: var(--bd-chrome);
  color: var(--bd-accent-chrome);
  border-top: 0;
  border-bottom: 0;
  vertical-align: middle;
}
.rpt.pos tr.grp:not(.ban) .gname {
  color: var(--bd-accent-chrome);
  font-family: var(--bd-font);
  letter-spacing: .04em;
}
.rpt.pos tr.grp:not(.ban) .disc { color: var(--bd-accent-chrome); }
/* The league's own exposure, right-aligned and quieter than the caption it
   shares a bar with - the answer to "how much", not a second heading. */
.rpt.pos tr.grp .lgrisk {
  float: right;
  font-family: var(--bd-mono);
  font-size: 12px;
  font-weight: 700;
  /* Was a hardcoded rgba(244,222,81,.82) - the player board's yellow, written
     as a literal, so it was the one colour on this page that did not move when
     the theme did. Same token as the caption above it. */
  color: var(--bd-accent-chrome);
}
.rpt.pos tr.grp .gcount { color: var(--bd-accent-chrome); }

/* The time slot inside a league. A label, not a fold, so it takes none of the
   band's weight - this is the player board's .lgbanner-when, given a line of
   its own because an agent report groups by kick-off and the player board
   does not. */
/* The time slot inside a league. IT HAD TO CHANGE COLOUR, not because the old
   one was ugly but because it stopped being a divider: it was --bd-raised
   #F4F1EA and the alternating game band is --bd-sunken #EFEBE1 - five to nine
   units apart per channel, which is no difference at all. A banner the eye
   cannot separate from the stripe below it is not marking anything.

   The accent tint separates by HUE as well as by tone, which is what makes it
   work here: everything else on this board is on the warm paper axis, so a
   cool blue-grey band reads as a different KIND of row rather than as a
   slightly different shade of the same one. --bd-phosphor on that ground
   measures 8.37:1. The 3px leading rule does the rest of the work and is what
   lets the ground stay this quiet - there are four of these visible at once on
   a normal board, and a solid band repeated four times would compete with the
   league bars it sits under.

   Both tokens carry into Noir on their own: the tint is rgba(91,157,217,.14)
   and the text #5B9DD9 there. */
.rpt.pos tr.grp.ban > td,
.rpt.pos tr.grp.ban > td:first-child {
  /* THE TINT IS LAID TWICE, and the second copy is not decoration. One coat of
     --bd-phosphor-soft over the page is #E8EAEB, which measures 1.01:1 against
     the #EFEBE1 game band - i.e. the two grounds are the same TONE and differ
     only in hue. Hue alone is not enough for a reader with any of the common
     colour deficiencies, and it is not enough at a glance for anyone.
     Compositing the token over itself takes .09 to .17 (and Noir's .14 to .26)
     without hardcoding a second colour that the theme could not follow: the
     ground becomes #DCE1E8, 1.11:1 against the band, and the text still
     measures 7.38:1. A gradient of one colour is the only way to say "this
     token, twice" in CSS that every target browser here already supports. */
  background-color: var(--bd-surface);
  background-image: linear-gradient(var(--bd-phosphor-soft), var(--bd-phosphor-soft)),
                    linear-gradient(var(--bd-phosphor-soft), var(--bd-phosphor-soft));
  /* ONE LEAGUE BAND LOWER THAN THE LEAGUE BAND. Every band cell inherits the
     same sticky offset from `.rpt.pos tr.grp > td` - the column head's
     bottom - so the moment a league and its first time slot were both
     pinned, the time slot painted over the league (measured: both at 129px).
     The time slot belongs UNDER the league it is in, so it pins one league
     band lower; --bd-row-h is the league band's height.

     AND THE GROUND IS OPAQUE NOW. The two coats of tint used to sit on
     nothing - fine while the band was in the flow, over the page - but a
     pinned band has rows scrolling beneath it and they showed straight
     through. --bd-surface goes under the same two coats, which on a white
     page is the identical colour, and on the dark direction is the panel.

     Sticky cells are bounded by the TABLE in Chrome, not by their row group,
     so neither band releases by itself: each stays pinned until the next
     band of its kind reaches the same offset and paints over it (later in
     the document wins at equal z-index). Rows are contiguous, so that is
     exactly the moment the band's last row leaves - no gap, no stale label
     over a row that is not its own. */
  top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th) + var(--bd-row-h));
  color: var(--bd-phosphor);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  font-size: var(--bd-head-size);
  height: 24px;
  padding-left: 22px;
  border-top: 1px solid var(--bd-rule);
  border-bottom: 1px solid var(--bd-rule);
  box-shadow: inset 3px 0 0 var(--bd-phosphor);
}

/* ---------- one team, one row ---------- */
.rpt.pos td.rot {
  font-family: var(--bd-mono);
  font-variant-numeric: tabular-nums;
  color: var(--bd-dim);
  font-size: 12px;
}
/* LEFT, not centred (asked for 2026-09-17): the team names read from a
   margin, as on allpending's selection. The head stays centred. */
.rpt.pos td.tm { font-weight: 700; text-align: left; }
.rpt.pos td.tm .nm {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* THE CREST SLOT. This report's data carries no logo path - see the note in
   position.asp - so this is a monogram off the team name, which needs no feed.
   It is the size .team-logo is on the player board, so a real crest drops in
   here unchanged if a path ever becomes available. */
.crest {
  display: inline-grid;
  place-items: center;
  width: 22px; height: 22px;
  margin-right: 7px;
  vertical-align: -6px;
  border-radius: 3px;
  background: var(--bd-sunken);
  color: var(--bd-muted);
  font-size: 9px; font-weight: 700;
  font-family: var(--bd-font);
  flex: 0 0 auto;
}

/* The game's own note - its ticket count and its flag - on the first line of
   the game. It is HERE and not in the At risk cell because sumCol() in
   js/ui.js totals that column out of the cell's textContent, and textContent
   includes a display:none span: a "26 tickets" beside the money read as
   2631500 and the filtered grand total came out at 17,638,250 against a book
   of 197,250. That only shows with the filter ON - the unfiltered view is
   restored from a photograph rather than recomputed - so it has to be measured
   with the filter on. */
/* THE COUNT IS A GLYPH AND A NUMBER, not a number and a word. "26 tickets"
   was the widest thing on the line that closes a game, and it repeated a noun
   the icon says in 13px. The word survives in .sr for the one reader that
   cannot see the glyph - the same trade .gcount makes on the band. */
.gmeta .tix { display: inline-flex; align-items: center; gap: 4px; }
.gmeta .tix .ico { width: 13px; height: 13px; flex: 0 0 auto; opacity: .8; }
.gmeta .tix.none { font-style: normal; }

.gmeta {
  float: right;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--bd-mono);
  font-size: 11px;
  color: var(--bd-dim);
  font-weight: 400;
}
.hotmark {
  display: inline-block;
  padding: 1px 5px;
  font-size: 9px;
  letter-spacing: .1em;
  background: var(--bd-down);
  color: #fff;
  font-weight: 700;
  font-family: var(--bd-font);
}
/* A rule down the leading edge of a flagged game, on the FIRST CELL only -
   `tr.hot > td` put an inset on all seven and the row came out striped at
   every column boundary. A <tr> cannot carry it: it paints nothing of its own
   in a table layout, the same reason the sticky bands live on their cells. */
.rpt.pos tbody tr.hot > td:first-child { box-shadow: inset 3px 0 0 var(--bd-down); }
/* WHICH LINE. The flag is decided on the spread, and the heavy side's spread
   cell is painted the badge's own red, so the reader sees which side of which
   market the book is on without reading the figures. The CELL, not the
   control inside it: .pbox and .pact keep their field ground and edges, so
   the red reads as a frame around the control and the figures stay on the
   ground they were tuned for. It was a 12% tint first, and on a phone that
   was invisible - the cell's padding there is 0 2px, so the tint had a
   two-pixel sliver to show in. The full colour, and 2px on every side of
   the cell in the phone block below, is what makes it a mark at all. */
.rpt.pos tbody tr.hot > td.mkt-s.hot-side { background: var(--bd-down); }

/* ---------- the pointer, on a board that already has two grounds -------
   THE BASE HOVER CANNOT WORK HERE AND IT IS A TIE, NOT A WEAK COLOUR.

     .rpt tbody tr:hover td     (0,2,3)   background: --bd-raised
     .rpt tbody tr.alt > td     (0,2,3)   background: --bd-sunken

   `:hover` counts as a class and a media query adds no specificity, so those
   are exactly equal - and the alt rule is 1028 lines later, so it wins. On
   every second game the highlight was not faint, it never applied at all.

   Winning the tie would not have been enough either: --bd-raised #F4F1EA on
   --bd-sunken #EFEBE1 is five to nine units per channel, the same non-
   difference that moved the timeslot banner off --bd-raised.

   So the tint is STACKED rather than substituted - the trick tr.grp:hover
   already uses further up, for this same problem, with a comment saying why.
   A translucent inset composites over whatever ground the row has, so a white
   row and a cream row light up identically and there is no cascade to win.

   The first cell is stated separately because it is already carrying a
   box-shadow: the ONE-SIDED flag's red edge. A blanket hover shadow on `td`
   replaces it, and a flagged game lost its stripe exactly when the reader
   pointed at it. Both are composed, flag first so it stays on top. */
@media (hover: hover) {
  .rpt.pos tbody tr[data-row]:hover > td {
    box-shadow: inset 0 0 0 999px var(--bd-phosphor-soft);
  }
  .rpt.pos tbody tr.hot[data-row]:hover > td:first-child {
    box-shadow: inset 3px 0 0 var(--bd-down),
                inset 0 0 0 999px var(--bd-phosphor-soft);
  }
}
/* ONE GAME IS ONE BLOCK, AND THE GAMES ARE SEPARATED. The lines within a game
   carry no rule between them - they are two halves of one thing - and the last
   line of each game closes with a heavier one than a row divider, so the eye
   lands on the event boundary rather than counting rows to find it.

   --bd-edge rather than --bd-rule: the same step up the band above it uses,
   which is what makes a game read as a unit between two of them. */
.rpt.pos tbody tr.ln > td { border-bottom: 0; }
.rpt.pos tbody tr.ln-last > td {
  border-bottom: 2px solid var(--bd-edge);
}

/* THE ALTERNATING BAND, painted on the ROW as well as on its cells - the same
   reason tr.grp above says so. position.asp puts `alt` on every line of every
   second GAME (both teams and the draw), so the band is the height of the
   event rather than of a row, and `.rpt tbody tr.alt > td` further up supplies
   the colour for the cells. The row paint closes any gap the cells leave when
   they do not all come out the same height. The phone block below needs it for
   a harder reason - see there. */
.rpt.pos tbody tr.alt { background: var(--bd-sunken); }

/* ---------- the price, and what is on it, side by side ---------------
   THE LINE BOX IS HALVED AND THE POSITION SITS BESIDE IT. It used to hold the
   price alone, at full width, with the money as a small line underneath - so
   the loudest thing in the row was the number the book is offering and the
   quietest was the number the agent is exposed to. One box now, split by an
   internal rule: price on the left, what is on it on the right.

   THE SPLIT IS NOT A HARD HALF. At 720 a market column is 115px, so half is
   51px - and "21x 41,200" with its chip needs about 70, which clipped. The
   price side is the one that gives: a price is four or five characters and
   the money is up to seven plus a chip. Measured across widths, price runs
   42->83px and the position 62->97px, with nothing clipped and nothing
   wrapped at any of them.

   THE FLEX ROW IS .mk AND NOT THE CELL. display:flex on a <td> takes it out
   of the table's column model - all three markets collapsed into the Spread
   column when the cell carried it. */
/* THE PAIR IS THE LINK, when there is anything to open. It is an <a> and not
   a <span> on any market carrying action, so the price half is a target too -
   on a phone that was a 60px box beside a 40px one that did nothing, and the
   dead half is exactly where a thumb lands when the eye is on the line.

   No underline: .rpt td a is underlined because a figure in a table needs to
   declare itself, and here the whole bordered pair already does. The cursor
   and the hover edge carry it instead.

   THAT RULE WAS WRITTEN AND IT DID NOT WIN. `a.mk` is (0,1,1) and `.rpt td a`
   above is (0,1,2), so every priced cell on this board was underlined anyway -
   and at `text-underline-offset: 3px` the line runs straight through the juice
   under the handicap, .pbox .j sitting 10px under a 12px .h. It read as a
   misprint rather than as an affordance. `.rpt td a.mk` is (0,2,2) and beats
   it; the :hover override has to be raised with it or the solid-on-hover half
   of the pair comes back on its own. */
.rpt.pos .mk { display: flex; align-items: stretch; width: 100%; }
.rpt td a.mk,
.rpt td a.mk:hover { text-decoration: none; }
a.mk { text-decoration: none; color: inherit; cursor: pointer; }
@media (hover: hover) {
  a.mk:hover .pbox, a.mk:hover .pact { border-color: var(--bd-phosphor); }
}
a.mk:focus-visible { outline: 2px solid var(--bd-focus); outline-offset: 1px; }

.pbox {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 0 1 auto;
  width: 46%;
  min-width: 0;
  box-sizing: border-box;
  min-height: 30px;
  margin: 0;
  padding: 1px 2px;
  background: var(--bd-field);
  border: 1px solid var(--bd-edge);
  border-right: 1px solid var(--bd-rule);
  border-radius: 3px 0 0 3px;
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
  text-align: center;
}
.pbox .h { font-size: 12px; font-weight: 700; color: var(--bd-ink); }
.pbox .j { font-size: 10px; color: var(--bd-muted); }
/* A one-part price - the money line, and the soccer draw, which IS a money
   line - is the main line of its cell and not juice under a handicap, so it
   wears the handicap's own size and ink. The player board makes the same call
   with .odds .ptb-j:only-child. */
.pbox .j:only-child { font-size: 12px; font-weight: 700; color: var(--bd-ink); }
.pbox.none { background: none; border-style: dashed; opacity: .45; }

/* What the agent has on that price. On the player board this space is the
   pick control; here it is the only thing the report exists to say.

   .pact, NOT .act. It shipped as a bare .act and allpending.asp's actions
   cells are th.act / td.act - so every delete cell on the pending report
   became a flex box with a field ground, a border and a 30px floor, sat at
   the top of its row, and the sticky header row carried a flex box in its
   last cell. Same shadowing that compat.css pulled four times: a bare class
   defined for one page is a rule for every page. Prefixed to the board. */
.pact {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1px 5px;
  flex: 1 1 auto;
  min-width: 0;
  box-sizing: border-box;
  min-height: 30px;
  margin: 0;
  padding: 1px 3px;
  background: var(--bd-field);
  border: 1px solid var(--bd-edge);
  border-left: 0;
  border-radius: 0 3px 3px 0;
  font-family: var(--bd-mono);
  font-size: 13px;
  color: var(--bd-ink);
  line-height: 1.05;
}
/* The chip and the figure are .pact's own flex items now - the anchor moved
   OUT to wrap the whole pair, so the gap and the wrap have to live here or
   the two run together as "14x24,850". */
/* THE COUNT IS A CHIP. It was a prefix inside the figure - "21x41,200" - so
   the money never read as a clean number, and the two could not break apart
   when a column was tight. It is phosphor because the figure is a link and
   phosphor is what a link is on this site. */
.pact .q {
  font-family: var(--bd-font);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--bd-phosphor-ink);
  background: var(--bd-phosphor);
  padding: 1px 4px;
  border-radius: 2px;
  margin: 0;
  flex: 0 0 auto;
}
/* THE POSITION MONEY IS ORANGE, every one of them. It is exposure - not a win
   and not a loss - so it gets a colour of its own rather than borrowing the
   up/down pair and implying a direction the figure does not have. See the
   --bd-pos note in theme.css.

   THE At Risk COLUMN IS DELIBERATELY NOT ORANGE. It is the total of these,
   and a total in the same colour as its parts stops reading as a total. */
.pact .m {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--bd-pos);
}
/* The side holding most of the game's spread money - the same hue carried
   further rather than a second colour, so the column stays one thing. */
.pact.big .m { color: var(--bd-pos-hi); }
.pact.zero {
  color: var(--bd-dim);
  background: none;
  border-style: dashed;
  font-weight: 400;
}

/* BETWEEN 721 AND 1180 THE PRICE GIVES UP MORE, AND THE CHIP SHRINKS.
   The market column is 16% of the width, so at 721 it is 105px of usable
   room. At the desktop split (46/54) the position side is 63px and the chip,
   its gap and a seven-figure sum need about 73 - so every figure on the board
   wrapped its chip onto a second line. Measured: 59 of 59 at 721, 37 of 59 at
   900, none from 1100 up. Four points off the price and two off the chip
   clears it without touching the widths where it already fits. */
@media (min-width: 721px) and (max-width: 1180px) {
  /* NOT TOUCHING .gmeta HERE. It floats right in the team cell, and between
     721 and 900 that starves the name - "Alabama Crimson Tide" gets 56px and
     shows as "Alabama Crim". Stacking it under the name was tried twice and
     broke the cell both times: a flex column on the <td> did not take, and
     the name ended up overlapping the note or vanishing behind it. It is a
     PRE-EXISTING condition of that width band, not something this change
     introduced, and it is not worth destabilising a working row for. Left
     alone deliberately.

     WHICH IS ALSO WHY THE COLUMN SPLIT REVERTS HERE. The wide split above cuts
     team 31 -> 28 to hand the slack to the markets, and that slack is a
     wide-screen fact: at 1280 the team column measured 350px against a 347px
     worst case, at 1024 it measures 278. Since .gmeta cannot be moved out of
     the name's line without the breakage described above, the only thing 28%
     does in this band is clip more names - 1 more at 1152, 1 more at 1024,
     3 more at 900, 6 more at 768. So this band keeps the numbers it was tuned
     with and the recovered width is spent only where it exists. */
  .rpt.pos col.c-rot  { width: 6%; }
  .rpt.pos col.c-team { width: 31%; }
  .rpt.pos col.c-mkt  { width: 16%; }
  .rpt.pos col.c-risk { width: 15%; }

  .rpt.pos .pbox { width: 34%; }
  .rpt.pos .pact { font-size: 12px; gap: 1px 4px; }
  .rpt.pos .pact .q { font-size: 8px; padding: 1px 3px; }
  .rpt.pos .pbox .h, .rpt.pos .pbox .j:only-child { font-size: 11px; }
  .rpt.pos .pbox .j { font-size: 9px; }
}

/* ---------- a rule between one market and the next -------------------
   Three markets side by side with nothing between them read as one run of
   numbers. The rule runs the full height of the board - heading and cell -
   and the team column takes one too, because that is where the figures
   start. */
.rpt.pos thead .h-team,
.rpt.pos thead .h-s,
.rpt.pos thead .h-t,
.rpt.pos thead .h-m,
.rpt.pos tbody td.tm,
.rpt.pos tbody td.mkt-s,
.rpt.pos tbody td.mkt-t,
.rpt.pos tbody td.mkt-m { border-right: 1px solid var(--bd-edge); }
.rpt.pos tbody td.mkt-s,
.rpt.pos tbody td.mkt-t,
.rpt.pos tbody td.mkt-m { padding: 3px 5px; }

/* ---------- the game's exposure ---------- */
.rpt.pos td.risk { font-family: var(--bd-mono); font-weight: 700; }
.rpt.pos td.risk.z { color: var(--bd-dim); font-weight: 400; }

/* ================================================================
   THE PHONE - DynamicOne's own rule, and the reason its board never
   runs off a screen: the team keeps a line to itself and the three
   prices drop underneath it as thirds.
   ================================================================ */
@media (max-width: 720px) {
  .rpt.pos { --pos-pad: 8px; }

  /* THE PANE MUST BE ALLOWED TO SHRINK. .panel-bd is display:flex and
     .rpt-wrap is a flex item, so its default min-width:auto pins it to its
     content's min-content - it cannot go narrower than the widest unbreakable
     thing inside it. Measured on the live board at 404px: the wrap was 675px
     inside a 374px pane, and the table, being width:100% of the WRAP, handed
     the row's six tracks 675px to divide. Every probe said the grid was
     equal - it was - it had simply been given the wrong width to be equal in.
     This is the .panel-bd flex-item hazard the narrow-width notes describe.

     Scoped to this page: the other reports use that pane as a horizontal
     scrollport, and this is not the width at which to change what they do. */
  .pos-wrap { min-width: 0; }

  /* THE BLANKET STOPS AT THE ROW GROUPS, and that is load-bearing - the same
     point `.rpt.pend` makes further up, learned the same way. `.rpt.pos tr` is
     (0,2,1) and so is `.rpt tr.is-off`, which hides the closed half of every
     band pair; equal specificity, and this block is later in the file, so a
     blanket wins and every league renders BOTH its open band and its folded
     one. So tr, th and td become blocks one at a time below, each with the
     guard its own hiding rule needs. */
  .rpt.pos, .rpt.pos thead, .rpt.pos tbody, .rpt.pos tfoot { display: block; }
  .rpt.pos colgroup { display: none; }

  /* ---- THE CAPTIONS ARE ONE STRIP, NOT A LINE PER ROW ----
     DynamicOne's board hides its Game / Team captions below lg
     (`d-none d-lg-block`) and keeps the three price captions as `col-4`s -
     one strip above the whole board. This is that, with the same thirds the
     rows below use.

     It replaces a caption printed on EVERY market cell, which is what made
     this view bulky: two extra caption lines per game, on sixteen games. The
     strip is sticky, so the three words stay put while the board scrolls -
     which is the only reason a per-row repeat was defensible in the first
     place. */
  .rpt.pos thead {
    display: block;
    position: sticky;
    top: calc(var(--bd-head-h) + var(--bd-bar-h));
    z-index: 12;
    background: var(--bd-colhead);
    border-bottom: 1px solid var(--bd-edge);
  }
  .rpt.pos thead tr {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    padding: 0 var(--pos-pad);
  }
  /* Addressed by class and never by position - `:nth-child` on a header row is
     the same footgun as `:nth-of-type` on the cells. */
  .rpt.pos thead th,
  .rpt.pos thead th:first-child {
    display: none;
    position: static; top: auto; left: auto;
    background: none; border: 0; padding: 0;
    height: var(--bd-head-th);
    line-height: var(--bd-head-th);
    text-align: center;
  }
  .rpt.pos thead th.h-s { display: block; grid-column: 1 / 3; }
  .rpt.pos thead th.h-t { display: block; grid-column: 3 / 5; }
  .rpt.pos thead th.h-m { display: block; grid-column: 5 / 7; }

  .rpt.pos tr.grp:not(.is-off):not(.is-hid) { display: block; }
  .rpt.pos tr.grp > td,
  .rpt.pos tr.grp > td:first-child {
    display: block;
    position: static; top: auto; left: auto;
    padding: 5px var(--pos-pad);
    height: auto;
  }
  .rpt.pos tr.grp:not(.ban) {
    position: sticky;
    top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-th-h));
    z-index: 11;
    background: var(--bd-chrome);
  }
  /* THE FOLDED BAND IS A FLEX ROW HERE, NOT SEVEN BLOCKS. The open band is one
     cell with colspan=7 and becomes a block cleanly; the folded one has all
     seven, and `display: block` on the cells stacked them into a 79px black
     column with a league name at the top and six empty lines under it. Only
     the name and the league's own total have anything to say.

     :not(.is-off) IS LOAD-BEARING AND WAS MISSING. `.rpt tr.is-off` hides the
     half of the band pair that is not in use and is (0,2,1); this rule is
     (0,3,1) and later in the file, so without the guard it won and EVERY
     league drew both its open band and its folded one, one under the other.
     The same trap `.rpt.pend` calls out a few hundred lines up - turning a row
     into a block is a more specific selector than the rule that hides it. */
  .rpt.pos tr.grp.grp-shut:not(.is-off) { display: flex; align-items: center; }
  .rpt.pos tr.grp.grp-shut:not(.is-off) > td { display: none; }
  .rpt.pos tr.grp.grp-shut:not(.is-off) > td:first-child { display: block; flex: 1 1 auto; min-width: 0; }
  .rpt.pos tr.grp.grp-shut:not(.is-off) > td.grpfig { display: block; flex: 0 0 auto; }

  /* ---- ONE LINE PER TEAM, THE PLAYER BOARD'S OWN SHAPE ----
     Below lg, DynamicOne gives the rotation and the team a row of their own
     (col-2 + col-10) and drops the three prices underneath as col-4 thirds.
     Six equal tracks say the same thing: 1 for the rotation, 4 for the name,
     1 for the share, then three price cells spanning two tracks each.

     minmax(0, 1fr) AND NOT 1fr. A bare `1fr` is `minmax(auto, 1fr)`, and that
     auto minimum is min-content - so one nowrap price wider than its share
     grows its track and shoves every column after it off the right edge. That
     is exactly what happened on the live board: "+1 1/2 +107" and "ov8 1/2 -110"
     are longer than the fixture's tidy "-3.5 -110", the Money, % and At Risk
     columns went off the edge, and every probe said the page was clean.

     Placement is by CLASS, never by :nth-of-type, which counts elements
     rather than classes: `.cell:nth-of-type(1)` matches the rotation cell. */
  .rpt.pos tbody tr[data-row]:not(.is-hid) {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    align-items: center;
    row-gap: 2px;
    padding: 4px var(--pos-pad) 6px;
    border-bottom: 1px solid var(--bd-rule);
    background: var(--bd-surface);
  }
  /* THE BAND HAS TO BE ON THE ROW HERE, not just on the cells. `td.tm` is
     `display: contents` below - it generates no box at all, so it paints no
     background, and a cell-only band came out with a hole across the team
     name and the closing strip. The row is a real grid box here and paints
     the full width.
     [data-row]:not(.is-hid) is carried over from the rule directly above,
     which sets --bd-surface at (0,4,2) and would otherwise win. */
  .rpt.pos tbody tr.alt[data-row]:not(.is-hid) { background: var(--bd-sunken); }
  /* EVERY ONE OF THESE IS SCOPED THROUGH `tbody tr[data-row] >`, and it is not
     decoration. The blanket rule directly below is (0,3,3); a bare
     `.rpt.pos td.tm` is (0,3,1) and LOSES to it. Two rules were silently
     dropped that way: `display: flex` on the team cell, so the game's flag and
     ticket count kept a line of their own above the name, and
     `td.risk:empty { display: none }`, so every second line of every game
     carried an empty row. Neither errors; both just cost vertical space. */
  .rpt.pos tbody tr[data-row] > td { display: block; border: 0; padding: 0; height: auto; }
  .rpt.pos tbody tr[data-row] > td.rot  { grid-column: 1 / 2; grid-row: 1; }
  /* The team cell is not a box here - its two children are grid items in
     their own right, so the NAME can keep the whole team span while the
     game's flag and ticket count go down to the line that closes the game. */
  .rpt.pos tbody tr[data-row] > td.tm { display: contents; }
  /* The share column is gone, so the name has the row to the trailing edge. */
  .rpt.pos td.tm > .nm {
    grid-column: 2 / 7; grid-row: 1;
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: 13px;
  }
  .rpt.pos tbody tr[data-row] > td.mkt-s { grid-column: 1 / 3; grid-row: 2; }
  .rpt.pos tbody tr[data-row] > td.mkt-t { grid-column: 3 / 5; grid-row: 2; }
  .rpt.pos tbody tr[data-row] > td.mkt-m { grid-column: 5 / 7; grid-row: 2; }
  /* The game's exposure closes its last line, right where the eye already is
     after reading the prices. An empty cell - every line but the first of a
     game - takes no row at all. */
  /* THE LINE THAT CLOSES A GAME: what is on it, and what it is worth.
     They are two separate grid items rather than one cell, because the At risk
     cell has to hold the money AND NOTHING ELSE - sumCol() in js/ui.js totals
     the column out of textContent, so a ticket count sharing that cell reads
     as part of the figure. Side by side on one row, they look like one line
     and stay two cells. */
  .rpt.pos td.tm > .gmeta {
    grid-column: 1 / 5; grid-row: 3;
    justify-content: flex-start;
    padding-top: 3px;
    border-top: 1px solid var(--bd-rule);
  }
  .rpt.pos tbody tr[data-row] > td.risk {
    grid-column: 5 / 7; grid-row: 3;
    text-align: right; font-size: 12px;
    padding-top: 3px;
    border-top: 1px solid var(--bd-rule);
  }
  .rpt.pos tbody tr[data-row] > td.risk:empty { display: none; }
  /* The divider is on the ROW here: below 720 a row is a grid box that paints
     the full width, and its cells no longer span it. */
  .rpt.pos tbody tr[data-row]:not(.is-hid) { border-bottom: 0; }
  .rpt.pos tbody tr.ln-last:not(.is-hid) { border-bottom: 2px solid var(--bd-edge); }
  .rpt.pos tbody tr[data-row] > td { border-bottom: 0; }

  /* The pair stays side by side here; the price gives up more of it, since
     a phone column is 59px of a 400px screen and the money is what is being
     read. The dividers go - the rows are grid boxes at this width and the
     cells no longer span the full height for a rule to run down. */
  .pbox { width: 40%; min-height: 27px; }
  .pact { min-height: 27px; font-size: 12px; }
  .pbox .h, .pbox .j:only-child { font-size: 11px; }
  .pbox .j { font-size: 9px; }
  .pact .q { font-size: 8px; padding: 0 3px; }
  .rpt.pos thead .h-team, .rpt.pos thead .h-s,
  .rpt.pos thead .h-t, .rpt.pos thead .h-m,
  .rpt.pos tbody td.tm { border-right: 0; }
  .rpt.pos tbody td.mkt-s, .rpt.pos tbody td.mkt-t { border-right: 1px solid var(--bd-rule); }
  .rpt.pos tbody td.mkt-m { border-right: 0; }
  .rpt.pos tbody td.mkt-s, .rpt.pos tbody td.mkt-t,
  .rpt.pos tbody td.mkt-m { padding: 0 2px; }
  /* the one-sided cell keeps a 2px ground on every side, or its red is a
     sliver down the two edges and no mark at all - see .hot-side above */
  .rpt.pos tbody tr.hot > td.mkt-s.hot-side { padding: 2px; }

  /* THE TEAM LINE IS ONE LINE. The game's flag and its ticket count sat in a
     block of their own above the name, which cost a whole line on the first
     line of every game - sixteen games, sixteen wasted lines. They belong
     beside the name, and the name is the half that gives way: a rotation
     number and a crest already identify the team, and the count and the flag
     do not shorten.

     The DOM order is meta-then-name because the desktop floats the meta right;
     `order` puts them back without a second markup. */
  .gmeta { float: none; }

  /* the grand total, pinned the way allpending pins its own */
  .rpt.pos { padding-bottom: 76px; }
  .rpt.pos tfoot {
    position: sticky;
    bottom: 0;
    z-index: 20;
    background: var(--bd-raised);
    border-top: 1px solid var(--bd-edge);
  }
  .rpt.pos tfoot tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 12px;
    padding: 0 var(--pos-pad);
  }
  .rpt.pos tfoot td,
  .rpt.pos tfoot td:first-child {
    display: block;
    position: static; left: auto;
    background: none; border: 0;
    text-align: right;
    white-space: normal;
  }
  .rpt.pos tfoot td:first-child { text-align: left; }
  .rpt.pos tfoot td.pad { display: none; }
}

@media screen and (max-width: 360px) {
  .rpt.pos { --pos-pad: 6px; }
  .crest { display: none; }
  .rpt.pos .pbox { width: 36%; }
  .rpt.pos .pact { font-size: 11px; }
}

/* ---------- the Position drill-down: a list of slips --------------------
   PendingByGameBetType.asp, in position.asp's dialog. Each ticket is the slip
   (.tk, above); these rules are only what the LIST adds: the agent band over
   a run of slips, the space between slips, the change-result form under one,
   and the totals pinned to the foot of the dialog's own scroller.

   No breakpoint, as before: a modal's width is not the viewport's, so a
   @media query keyed to the viewport is the wrong instrument for it - at
   1500px the dialog is still 620. The slip reads the same at 400 and 620. */
.tk-list { display: flex; flex-direction: column; }
.tk-band {
  padding: 6px var(--bd-pad);
  background: var(--bd-raised);
  border-top: 1px solid var(--bd-edge);
  border-bottom: 1px solid var(--bd-rule);
  font-size: 12px;
}
.tk-band:first-child { border-top: 0; }
.tk-list .tk + .tk { margin-top: 12px; }
/* the first name beside the login in the head, where the object has one */
.tk-acct .tk-nm { font-family: var(--bd-font); font-weight: 400; color: var(--bd-muted); }

/* The change-result form, revealed by ToggleEdit on position.asp, which
   toggles .is-on. It keeps every id the handlers there build. A ruled
   strip under the slip with the slip's own gutters. */
.tk-edit {
  display: none;
  gap: 6px; flex-wrap: wrap; align-items: center;
  padding: 8px var(--bd-pad) 10px;
  border-top: 1px solid var(--bd-rule);
}
.tk-edit.is-on { display: flex; }
.tk-edit .field { width: auto; min-width: 62px; flex: 0 1 auto; }
.tk-edit .inv { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; }

/* The totals, pinned to the foot of the dialog's own scroller. */
.tk-tot {
  position: sticky; bottom: 0; z-index: 2;
  display: flex; align-items: baseline; gap: 3px 10px; flex-wrap: wrap;
  margin-top: 12px;
  padding: 8px var(--bd-pad);
  background: var(--bd-raised);
  border-top: 1px solid var(--bd-edge);
  font-size: 12px;
}
.tk-tot .n { color: var(--bd-muted); display: inline-flex; align-items: center; gap: 5px; }
.tk-tot .n .ico { width: 14px; height: 14px; flex: 0 0 auto; opacity: .8; }
.tk-tot .fig { font-family: var(--bd-mono); font-weight: 700; white-space: nowrap; }
.tk-tot .fig:nth-of-type(1) { margin-left: auto; }
.tk-tot .fig b {
  font-weight: 400; font-size: 9px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--bd-muted); margin-right: 3px;
}

/* The dialog gives the list its own edges - the list draws its own rules. */
.modal-body:has(> .tk-list) { padding: 0; }

/* ---------- a segmented control that scrolls rather than shrinking ----
   .pseg was written for allpending's two-button Regular/Futures switch and
   has never had to hold more than two. position.asp's six sports measure
   342px, which does not fit a 320px phone and does not shrink - and below 720
   nothing scrolls sideways (body { overflow-x: clip }), so an overflowing
   control is not a scroll, it is a silent clip. */
.pseg-scroll {
  display: flex;
  min-width: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.pseg-scroll::-webkit-scrollbar { display: none; }
.pseg-scroll > .pseg { flex: 0 0 auto; }

/* ...AND ON A PHONE THE STRIP IS REPLACED, NOT SCROLLED.
   A scroller is right for a tablet and wrong for a phone. Measured at 390:
   six sports are 445px inside a 362px scroller - 83px hidden off the right,
   no scrollbar, and the last button starting at x=458. Nothing on screen says
   there is more; it simply reads as the page running off the edge.
   DynamicOne puts no sport strip on a phone at all, so this shows the same
   control the filter beside it uses. Exactly one of the two is ever on. */
/* ...AND THE STRIP IS REPLACED, NOT SCROLLED, WHEREVER IT DOES NOT FIT.
   1024 IS A MEASURED NUMBER, not a familiar one. The six sports are 445px and
   the strip shares .rptbar with a label, the filter and the fold button, so
   what matters is the room left after those - not the viewport. Hidden sports,
   measured: 185px at 721, 106px at 800, 6px at 900, 0 from 1024 up. Giving the
   strip a full line of its own was tried first and changed none of those
   numbers, because the bar is not wrapping at those widths.

   Below the swap it is the same control the filter beside it uses, which is
   also what DynamicOne does - it puts no sport strip on a small screen at all.
   Exactly one of the two is ever on. */
.sportsel { display: none; flex: 0 1 auto; min-width: 0; }
@media (max-width: 1023.98px) {
  .rptbar .pseg-scroll { display: none; }
  .rptbar .sportsel { display: block; }
}
/* On a phone the two controls take a line each, rather than two half-width
   ones neither of which can show what it holds. */
@media (max-width: 720px) {
  .rptbar .sportsel, .rptbar .wkpick.pick-filter { flex: 1 1 100%; width: auto; }
}


@media print {
  .side, .scrim, .head, .burger, .panel-act, .sheet, .sheet-back,
  .chart-panel, .no-print { display: none !important; }
  body { background: #fff; color: #000; font-size: 10pt; }
  .panel { border: 1px solid #999; box-shadow: none; break-inside: auto; }
  .content { padding: 0; }
  .rpt [data-p="2"], .rpt [data-p="3"], .rpt [data-dow] { display: table-cell !important; }
  /* The sub-row and the disclosure are screen furniture. The print block
     above forces every dropped column back into the table, so a sub-row
     on paper would print each account's figures a second time. The
     sticky band has to let go too, or it prints once per page break. */
  .rpt tr.sub, .disc { display: none !important; }
  .rpt tr.grp > td, .rpt tr.grp > td:first-child { position: static; }
  .rpt tr.grp-shut { display: none !important; }
  .rpt tr.is-hid { display: table-row !important; }
  .rptbar { display: none !important; }
  .rpt thead { display: table-header-group; }
  .rpt tfoot { display: table-footer-group; }
  .rpt th, .rpt td { height: auto; padding: 3px 5px; border-bottom: 1px solid #ccc; }
  .rpt thead th, .rpt td:first-child, .rpt tfoot td { position: static; background: #fff; }
  /* The narrow block hides "GRAND" to buy width on a phone. Paper has the
     width and no viewport to clip against, so the label prints in full. */
  .rpt tfoot .gt-wide { display: inline; }
  /* Release the scrollport, or the printout is clipped to the pane height
     and only the first screenful of accounts reaches the paper. */
  .rpt-wrap, .app.fill .panel.grow .rpt-wrap { overflow: visible; max-height: none; }
  .app.fill { height: auto; overflow: visible; }
  .app.fill .content { display: block; }
  .formatPositiveNumber, .formatNegativeNumber { color: #000 !important; }
  .formatNegativeNumber::before { content: "("; }
  .formatNegativeNumber::after { content: ")"; }
}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ================================================================
   ACCOUNT LISTS - the editable grid, the compact table, the account card
   (customers.asp, agents.asp, accountcard.asp, js/acctcard.js, js/autosave.js)
   .acl is both lists; .cust and .agt carry each page's measured breakpoint.

   THE GRID OR THE TABLE IS DECIDED BY WIDTH, AND THE WIDTH WAS
   MEASURED. Above the grid width the eleven columns fit on one line
   and every one of them is editable in place; below it the row is
   Acct, Balance, Settle, Credit and Limit as figures, and the card
   is the editor. The global tiers hide data-p="2" below 1180px; the
   first media rule below shows them again between the measured width
   and 1180, because this page's grid is narrower than the reports'
   and a landscape tablet has room for it. (0,2,1) beats the global
   (0,1,1) so order does not matter, but it is written after anyway.
   ================================================================ */

/* the site's switch at two thirds - a grid row is 30px tall */
.switch.sm { width: 40px; height: 18px; }
.switch.sm .slider::before { width: 13px; height: 13px; top: 1.5px; left: 2px; }
.switch.sm .slider::after { font-size: 7.5px; line-height: 16px; right: 5px; letter-spacing: .04em; }
.switch.sm input:checked + .slider::before { transform: translateX(21px); }
.switch.sm input:checked + .slider::after { left: 6px; }

/* the save-state chip: a dot and a word. Painted by the page's status()
   through js/autosave.js; empty means nothing has changed. */
.st {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--bd-dim); white-space: nowrap;
  transition: opacity .4s;
}
.st::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.st:empty::before { display: none; }
.st.dirty { color: var(--bd-pos); }
.st.saving::before { animation: st-pulse .8s infinite alternate; }
.st.saved { color: var(--bd-up); }
.st.failed { color: var(--bd-down); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.st.fade { opacity: .45; }
/* the dot alone, beside the login, for when the Status column is hidden */
.st.mini { display: none; font-size: 0; margin-left: 6px; vertical-align: middle; }
@keyframes st-pulse { from { opacity: .25; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .st.saving::before { animation: none; } }

/* the grid. (Column names are centred on every report now - see the
   thead rule above.) */
.rpt.acl td.ed { padding: 0 4px; }
.rpt.acl td.ed .field.sm { width: 100%; min-width: 58px; text-align: center; }
.rpt.acl td.ed .field.bad { border-color: var(--bd-down); }
.rpt.acl td.fig .v { display: none; font-family: var(--bd-mono); font-weight: 600; }
.rpt.acl td.fig .locked { display: inline-block; }
.rpt.acl .acct-btn {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; font-family: var(--bd-mono); font-weight: 600;
  color: var(--bd-phosphor); cursor: pointer;
}
.rpt.acl .acct-btn:hover { text-decoration: underline; }
.rpt.acl .acct-btn:focus-visible { outline: 2px solid var(--bd-focus); outline-offset: 2px; }
/* an inactive account. acct-off and not is-off: `.rpt tr.is-off` above hides
   the folded half of a report, at every width. */
.rpt.acl tbody tr.acct-off td { color: var(--bd-dim); }
.rpt.acl tr.acct-off .acct-btn { color: var(--bd-dim); text-decoration: line-through; text-decoration-thickness: 1px; }
.rpt.acl tr.is-sel td { background: var(--bd-phosphor-soft); }
.rpt.acl tr.is-sel td:first-child { background: var(--bd-phosphor-soft); }
/* the settings column before the account, and the button in it. The
   first column is no longer the sticky identity: the page scrolls with
   the window and never sideways, so nothing needs to stick to the left. */
.rpt.acl th:first-child, .rpt.acl td:first-child { position: static; border-right: 0; }
.rpt.acl .opencell { width: 34px; padding: 0 4px; text-align: center; }
.rpt.acl .open-card {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; margin: 0;
  background: none; border: 1px solid transparent; border-radius: var(--bd-radius);
  color: var(--bd-muted); cursor: pointer; vertical-align: middle;
}
.rpt.acl .open-card .ico { width: 16px; height: 16px; }
.rpt.acl .open-card:hover { border-color: var(--bd-edge); background: var(--bd-raised); color: var(--bd-ink); }
.rpt.acl .open-card:focus-visible { outline: 2px solid var(--bd-focus); outline-offset: 1px; }
.rpt.acl tr.is-sel .open-card { color: var(--bd-phosphor); border-color: var(--bd-phosphor); }
.rpt.acl td.acct { cursor: pointer; }
/* the agent-name row between one agent's accounts and the next, on the
   lists and on the Analysis reports. Asked for 2026-09-16: the raised
   surface (the light theme's #AADEEE) under the title colour (#7A5000),
   the name set like a report band. The chip markup stays; only its paint
   is taken off. (0,2,2) beats the sticky first cell's surface at (0,2,1). */
.rpt tr.band > td {
  background: var(--bd-raised);
  color: var(--bd-title);
  font-weight: 700;
  font-size: 11.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.rpt tr.band .chip { background: none; color: inherit; padding: 0; font-size: inherit; font-weight: inherit; }
/* customers.asp, asked for 2026-09-16: the agent's name sits in the middle
   of its band and a size up, and every money figure is bold - the balance,
   and Settle / Credit / Limit whether shown as a figure or in a field.
   (0,3,2) beats the (0,1,1) left alignment of td.txt; the .v rule outranks
   the .acl one at equal specificity by coming later. */
.rpt.cust tr.band > td { text-align: center; font-size: 13px; }
.rpt.cust td.fig, .rpt.cust td.fig .v, .rpt.cust td.fig .field { font-weight: 700; }

/* the grid, between the measured width and the global 1180 tier.
   MEASURED 2026-09-15 in the harness at 1366: the eleven columns need
   817px at min-content (60px fields, the 40px switch, a 23-character
   login in the account column). 817 + the content gutters and the panel
   border is 851; 880 leaves room for a scrollbar and a longer memo. */
@media (min-width: 881px) and (max-width: 1180px) {
  .rpt.cust [data-p="2"] { display: table-cell; }
}
/* the compact table */
@media (max-width: 880px) {
  .rpt.cust td.fig .field, .rpt.cust td.fig .locked { display: none; }
  .rpt.cust td.fig .v { display: inline; }
  .rpt.cust td.ed { padding: 0 8px; }
  .rpt.cust .st.mini { display: inline-flex; }
  .rpt.cust tbody tr[data-row] { cursor: pointer; }
  .rpt.cust tbody tr[data-row] td { height: 44px; }
}
/* agents.asp: six columns, so the grid holds to a narrower screen.
   MEASURED 2026-09-15 in the harness: min-content 560px. */
@media (min-width: 641px) and (max-width: 1180px) {
  .rpt.agt [data-p="2"] { display: table-cell; }
}
@media (max-width: 640px) {
  .rpt.agt td.fig .field, .rpt.agt td.fig .locked { display: none; }
  .rpt.agt td.fig .v { display: inline; }
  .rpt.agt td.ed { padding: 0 8px; }
  .rpt.agt .st.mini { display: inline-flex; }
  .rpt.agt tbody tr[data-row] { cursor: pointer; }
  .rpt.agt tbody tr[data-row] td { height: 44px; }
}
/* the comment in an agent's row: one line of a textarea, so a comment
   written with line breaks keeps them */
.rpt.acl td.ed textarea.field.sm {
  text-align: left; resize: none; height: 24px; line-height: 22px;
  padding: 0 6px; min-width: 140px; overflow: hidden; white-space: pre; display: block;
}

/* ---------- the WINDOW scrolls the page, not a pane inside it ----------
   .content.flow: the arrangement the board has (see .pos-wrap above), for
   the two account lists and the Analysis reports. The pane stops being a
   scrollport, the document is as tall as the table, and a short table
   simply ends. `overflow: visible` cannot be one-axis, so a table on a
   .flow page must fit its width: the lists were measured (grid 739px
   min-content, shown from 881; compact 267px) and the Analysis tables shed
   their data-p columns like every report. The panel releases its overflow
   or it becomes the sticky containing block and the column head scrolls
   away with it - the trap the 720 branch of the fixed shell documents. The
   column head then clears the page head AND the toolbar strip, both sticky
   above it; both selectors are named because `.rpt th:first-child`
   outranks `.rpt thead th`. */
.content.flow .panel { overflow: visible; }
.content.flow .rpt tr.grp > td,
.content.flow .rpt tr.grp > td:first-child {
  top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th));
}
/* THE WIDE REPORTS, from 1017px up. pweekly, aweekly and daily need 987, 856
   and 1009px of table between 721 and 1180 (measured 2026-09-16 at
   min-content), so on a tablet the pane keeps its sideways scroll and the
   fixed shell stays. From 1017 up it is released exactly the way the 720
   branch releases it for a phone - these are that branch's rules again,
   fenced from the other side. */
@media (min-width: 1017px) {
  .app.fill.fw { height: auto; overflow: visible; }
  .app.fill.fw .content { display: block; }
  .app.fill.fw .panel.grow { overflow: visible; }
  .content.fw .panel { overflow: visible; }
  /* THE RELEASED CARD GROWS TO ITS TABLE. Released means the pane's overflow
     is visible so the header pins to the window - which also means a table
     wider than the viewport is not clipped or scrolled, it just extends. If
     the card stays at viewport width the table hangs out past its right
     border ("bleeding out of the box"); sizing the card to its content keeps
     the grid inside the card and lets the PAGE scroll sideways to the rest.
     min-width:100% keeps a report that already fits filling the width rather
     than shrinking to its content (aweekly/daily fit at every desktop width;
     pweekly with every field on is the one that overflows). Below 1017 the
     pane is a contained scrollport (overflow auto), so this does not apply. */
  .content.fw, .content.fw .panel { width: max-content; min-width: 100%; }
  .content.fw .rpt-wrap { overflow: visible; max-height: none; height: auto; }
  .content.fw .rpt thead th,
  .content.fw .rpt thead th:first-child {
    position: sticky;
    top: calc(var(--bd-head-h) + var(--bd-bar-h));
  }
  .content.fw .rpt tr.grp > td,
  .content.fw .rpt tr.grp > td:first-child {
    top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th));
  }
}
.content.flow .rpt-wrap { overflow: visible; max-height: none; height: auto; }
.content.flow .rpt thead th,
.content.flow .rpt thead th:first-child {
  position: sticky;
  top: calc(var(--bd-head-h) + var(--bd-bar-h));
}
/* THE AGENT BAND STAYS UNDER THE HEADS (2026-09-16) until the agent's last
   customer has scrolled under it - at which moment the next agent's band
   arrives at the same line and covers it. Chrome bounds a sticky cell by
   the TABLE, not by its group (measured: one tbody per agent changes
   nothing), so a band cannot be pushed up by the next; covered is what is
   possible, and it reads the same: the band showing is always the agent
   whose customers are on screen. --bd-th-h is the measured head strip.
   Above the sticky first-column cells (10), below the heads (12). */
.content.flow .rpt.cust tr.band > td {
  position: sticky;
  top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-th-h));
  z-index: 11;
}
/* a form on the strip: the strip's own row, wrapping on a phone */
.rptbar .rbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex: 1 1 auto; min-width: 0; margin: 0; }
.rptbar .rgrp { display: inline-flex; align-items: center; gap: 10px; }
.rptbar .rgrp[hidden] { display: none; }
.rptbar .rbar[hidden] { display: none; }
.rptbar .rtext { width: 150px; flex: 0 1 150px; min-width: 0; text-align: left; }
.rptbar .rdate { width: auto; flex: 0 0 auto; height: 24px; padding: 0 6px; }

/* the toolbar: the same sticky strip pweekly.asp has, with the page's
   Show and Search on it. It spans both grid tracks so the card never sits
   beside it, and the grid's gap does the spacing the bar's own bottom
   margin would otherwise double. The card's sticky top clears it by the
   height js/ui.js measures into --bd-bar-h. */
.content.acl > .rptbar { grid-column: 1 / -1; margin-bottom: 0; flex-wrap: wrap; }
/* padding-right put back: .field.sm zeroes it and the select's arrow is
   painted where the text of "1000" would end */
.rptbar .lshow, .rptbar .rsel { width: auto; flex: 0 0 auto; padding-right: 24px; }
.rptbar .lsearch { width: 200px; flex: 0 1 200px; min-width: 0; text-align: left; }
/* the count and the pager, on the strip to the right of Search (moved up
   from under the table 2026-09-16). The strip wraps, and below 720 the
   pager takes a line of its own; js/ui.js re-measures the strip into
   --bd-bar-h, so the heads and the band still pin under it. */
.rptbar .pager { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; white-space: nowrap; }
.rptbar .pager .pg-info { font-size: 12px; color: var(--bd-muted); margin-right: 4px; }
.rptbar .pager .btn.is-dis { opacity: .45; pointer-events: none; }
@media (max-width: 720px) {
  .rptbar .lsearch { flex: 1 1 auto; }
  .rptbar .pager { flex: 0 0 100%; margin-left: 0; }
}

/* the card: beside the list where there is room, over it where not */
.content.acl { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: start; }
.pcard {
  background: var(--bd-surface);
  border: var(--bd-panel-border);
  border-radius: var(--bd-radius-lg);
  display: flex; flex-direction: column;
  overflow: hidden;
}
.pcard[hidden] { display: none; }
/* Three placements, by width.
   >= 1281  a side panel: the grid keeps its 817px and the card its 400.
   761-1280 a drawer over the list's right edge, behind a scrim - the
            grid would have to scroll sideways to share the width, and
            the card is where the editing is anyway.
   <= 760   the whole screen. */
.pcard-back { display: none; position: fixed; inset: 0; background: var(--bd-scrim); z-index: 94; }
@media (min-width: 1281px) {
  .content.acl.has-card { grid-template-columns: minmax(0, 1fr) 400px; }
  .pcard {
    position: sticky;
    top: calc(var(--bd-head-h) + var(--bd-bar-h, 0px) + 14px);
    max-height: calc(100vh - var(--bd-head-h) - var(--bd-bar-h, 0px) - 28px);
  }
}
@media (min-width: 761px) and (max-width: 1280px) {
  .pcard { position: fixed; top: 0; right: 0; bottom: 0; width: 420px; max-width: 100%; z-index: 95; border-radius: 0; border-width: 0 0 0 1px; }
  .content.acl.has-card .pcard-back { display: block; }
  body.pcard-open { overflow: hidden; }
}
@media (max-width: 760px) {
  .pcard { position: fixed; inset: 0; z-index: 95; border: 0; border-radius: 0; }
  body.pcard-open { overflow: hidden; }
}
/* the card header wears the column-head band's blue and yellow (asked for
   2026-09-16), not the chrome */
.pcard-hd {
  background: var(--bd-colhead); color: var(--bd-colhead-ink);
  padding: 10px 12px;
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-items: center;
  flex: 0 0 auto;
}
.pcard-hd .who { font-family: var(--bd-mono); font-weight: 700; font-size: 16px; }
.pcard-hd .bal { font-family: var(--bd-mono); font-weight: 700; font-size: 16px; text-align: right; }
.pcard-hd .bal .formatNegativeNumber { color: var(--bd-down-chrome); }
.pcard-hd .bal .formatPositiveNumber { color: var(--bd-up-chrome); }
.pcard-hd .bal .formatZero { color: var(--bd-colhead-ink); opacity: .7; }
.pcard-hd .meta { grid-column: 1 / -1; font-size: 11px; color: var(--bd-colhead-ink); opacity: .85; display: flex; flex-wrap: wrap; gap: 4px 14px; }
.pcard-hd .meta a { color: var(--bd-colhead-hi); margin-left: auto; }
/* MEMO, LAST LOGIN AND IP (2026-09-16): out of the band's 11px small print
   and into a block of their own between the band and the sections, so the
   identity reads before the first button and stays put while the sections
   scroll. One label column, fixed, so the three values line up; the IP can
   be a 39-character IPv6, so it may break anywhere rather than push the
   card wider. The chip says which kind it is. */
.pcard-info { flex: 0 0 auto; padding: 8px 12px; border-bottom: 1px solid var(--bd-rule); background: var(--bd-surface); display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 0 12px; align-items: center; }   /* center, not baseline: the Telegram row holds a switch, and a baseline put its label a line low */
/* one grid for all three rows, so the label column is exactly the widest
   label that is showing and every value starts on the same line. The rows
   are display:contents, so the line between them is drawn on the two
   cells, not on the row. */
.pcard-info .ir { display: contents; }
.pcard-info .ir[hidden] { display: none; }
.pcard-info .lbl, .pcard-info .val { margin: 0; padding: 6px 0; border-top: 1px solid var(--bd-rule); }
.pcard-info .ir:first-child .lbl, .pcard-info .ir:first-child .val { border-top: 0; }
.pcard-info .val { font-family: var(--bd-mono); font-variant-numeric: tabular-nums; font-size: 13px; color: var(--bd-ink); min-width: 0; }
.pcard-info #cmemo { font-weight: 700; }
.pcard-info .tm { color: var(--bd-dim); }
.pcard-info .ip { overflow-wrap: anywhere; }
/* The Telegram row's value: the switch, then the Alerts switch's own
   sentence in the small dim face, then the save chip. Wraps under the
   switch on a phone. A hidden section (Alerts, for now) stays display:none
   whatever .psec sets. */
.pcard-info .tgval { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px; font-family: var(--bd-font); font-weight: 400; }
.pcard-info .tgnote { font-size: 11px; color: var(--bd-dim); }
.psec[hidden] { display: none; }
/* an icon after a label - the ringing bell after Telegram */
.pcard-info .lbl .ico { width: 13px; height: 13px; margin-left: 5px; vertical-align: -2px; }
/* the type sits in the label cell, beside the word IP, in its own case */
.pcard-info .lbl .chip { margin-left: 6px; text-transform: none; letter-spacing: 0; vertical-align: 1px; }
/* .chip is display:inline-block, which beats the hidden attribute's UA
   rule - an empty hidden chip painted as a 12px pill after "IP". */
.pcard-info .chip[hidden] { display: none; }
.pcard-bd { overflow-y: auto; flex: 1 1 auto; padding: 10px; -webkit-overflow-scrolling: touch; }
/* THE WAY OUT IS A BAR AT THE FOOT (2026-09-16), outside the scrolling body
   so it is always on screen. It was a 24px X in the header's corner, and
   the corner is the last place a hand looking for "done" goes. One
   full-width button, the id acctcard.js has always bound; Escape and the
   scrim still close the card too. */
.pcard-ft { flex: 0 0 auto; padding: 10px; border-top: 1px solid var(--bd-rule); background: var(--bd-surface); }
.pcard-ft .btn { width: 100%; justify-content: center; padding: 9px 12px; font-size: 13px; }
/* The toggles' ON green (asked for 2026-09-16), so the way out reads as the
   positive action and not as one more grey control. Same hover treatment as
   .btn-pri, which base .btn:hover would otherwise turn grey. */
.pcard-ft .btn { background: var(--bd-up); border-color: var(--bd-up); color: var(--bd-up-ink); }
.pcard-ft .btn:hover { background: var(--bd-up); filter: brightness(1.12); }
.pcard-ft .btn .ico { color: inherit; }
.pcard:focus { outline: none; }

/* a section of the card */
.psec { border: 1px solid var(--bd-rule); border-radius: var(--bd-radius-lg); margin-bottom: 10px; background: var(--bd-surface); }
/* The section header. It was sticky for part of 2026-09-16 and asked
   back to plain the same day; the open section's hairline sits under the
   header, which is why .psec-bd carries none of its own. */
.psec-hd {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; background: var(--bd-surface); border: 0; color: inherit; font: inherit;
  cursor: pointer; text-align: left;
  border-radius: var(--bd-radius-lg);
}
/* The open section's header is the row-hover blue (asked for 2026-09-16),
   so the one section that is showing is named in colour as well as by its
   turned caret. Opaque, which the pinned header needs anyway. */
.psec.is-open .psec-hd { background: var(--bd-hover); border-bottom: 1px solid var(--bd-rule); border-radius: var(--bd-radius-lg) var(--bd-radius-lg) 0 0; }
.psec-hd:focus-visible { outline: 2px solid var(--bd-focus); outline-offset: -2px; }
.psec-hd h4 {
  margin: 0; flex: 1 1 auto;
  font-size: var(--bd-head-size); letter-spacing: var(--bd-head-track);
  text-transform: var(--bd-head-case); font-weight: var(--bd-head-weight);
  color: var(--bd-title);
}
.psec-hd .ico { transition: transform .15s; color: var(--bd-dim); flex: 0 0 auto; }
.psec.is-open .psec-hd .ico { transform: rotate(90deg); }
.psec-bd { display: none; padding: 4px 10px 12px; }
.psec.is-open .psec-bd { display: block; }
.pgrid { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); margin: 6px 0 10px; }
.pgrid .fgrp { margin-bottom: 0; }
.pgrid .sw-lbl { white-space: nowrap; }
.psec .field { text-align: left; }
/* a save chip inside a field label - the line set's, since its section is hidden */
.psec .lbl .st { margin-left: 8px; vertical-align: 1px; }
.psec .hint { font-size: 11px; color: var(--bd-dim); margin: 6px 0 0; }
.psec .sw-row { padding: 5px 0; }
/* a bar shaped like a section header that is a link to another page - the
   card's League limits, which live on editlimits.asp */
.psec-link { display: block; text-decoration: none; color: inherit; }
.psec-link .psec-hd { display: flex; }
.psec-link .psec-hd .ico { transform: none; color: var(--bd-title); }
.psec-link:hover .psec-hd { background: var(--bd-raised); }
.psec-link:hover { text-decoration: none; }
.psec-link[hidden] { display: none; }
.psec-go { font-size: 11px; font-weight: 700; color: var(--bd-phosphor); flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px; }
.psec-go .ico { width: 13px; height: 13px; }   /* the new-tab arrow after "Opens new tab" */

/* agentlimits.asp: the limit tables. Each cell prints the agent's own limit
   in brackets and then the field; the field is inline and sized to a
   six-digit figure, the small ones to an hour or a minute. */
.rpt.lim .lim-in { display: inline-block; width: 64px; text-align: right; margin-left: 4px; vertical-align: middle; }
.rpt.lim .lim-in.xs { width: 40px; }
.rpt.lim td.el { text-align: left; }
.rpt.lim td.el select.field { display: inline-block; width: auto; vertical-align: middle; }
.rpt.lim td.el span { margin-right: 10px; }
.psec #traj { margin-top: 10px; }
/* the permission switches, inline in the agent's card: one column, the
   card is 400px and the two-column list was laid out for a full panel */
.pcard .sw-list.two { grid-template-columns: 1fr; }

/* ---------- the card's sections as LEDGER ROWS (2026-09-16) ------------
   Every setting is one row: its label in a fixed left column, its control
   on the right, a hairline between rows - the same shape as the memo /
   last login / IP block above the sections. Chosen from four mockups
   ("B"), for every section.

   Done in CSS alone. The markup keeps its .pgrid > .fgrp > label + field
   and .sw-row > .switch + .sw-lbl shape: the section body is the grid,
   and every wrapper on the way down is a SUBGRID, so label cells across
   .pgrid groups, lone .fgrp rows and switch rows all share one column.
   The switch row's children are placed by column rather than by DOM
   order, which is what puts its label on the left without touching the
   fragment (or the four other pages that use .sw-row - everything here is
   scoped to the card). The label column is capped at 42% so a long label
   wraps instead of squeezing the field. Numbers right-align. */
.pcard .psec-bd { padding: 2px 10px 10px; }
.pcard .psec.is-open .psec-bd { display: grid; grid-template-columns: minmax(96px, 42%) minmax(0, 1fr); column-gap: 12px; align-items: center; }
.pcard .psec-bd > .pgrid,
.pcard .psec-bd > .sw-list { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; gap: 0; margin: 0; }
.pcard .psec-bd .fgrp,
.pcard .psec-bd .sw-row { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; column-gap: 12px; align-items: center; padding: 6px 0; margin: 0; border-top: 1px solid var(--bd-rule); }
/* the first row of a section carries no line, whichever wrapper it is in */
.pcard .psec-bd > .fgrp:first-child,
.pcard .psec-bd > .sw-row:first-child,
.pcard .psec-bd > .pgrid:first-child > .fgrp:first-child,
.pcard .psec-bd > .sw-list:first-child > .sw-row:first-child { border-top: 0; }
.pcard .sw-list .sw-row + .sw-row { border-top: 1px solid var(--bd-rule); }
.pcard .psec-bd .lbl,
.pcard .psec-bd .sw-lbl { margin: 0; font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700; color: var(--bd-title); white-space: normal; cursor: default; }
.pcard .psec-bd .sw-row .sw-lbl { grid-column: 1; grid-row: 1; cursor: pointer; }
.pcard .psec-bd .sw-row .switch { grid-column: 2; grid-row: 1; justify-self: end; }
.pcard .psec-bd .field { padding: 6px 9px; }
/* CENTRED (asked for 2026-09-16, "center all the text controls"): every
   field's text sits in the middle of its box, the way every report cell
   but the first column does. Figures keep tabular numerals. A textarea is
   prose and stays left. */
.pcard .psec-bd .field { text-align: center; }
.pcard .psec-bd .field[inputmode="decimal"] { font-variant-numeric: tabular-nums; }
.pcard .psec-bd textarea.field { text-align: left; }
.pcard .psec-bd .fgrp > .locked { justify-self: center; }
/* The drop boxes (Line style, the cash Type) drawn like the fields beside
   them (asked for 2026-09-16): the browser's own chrome is replaced by the
   field's border and padding and one chevron, an inline SVG so nothing is
   fetched. The value ellipsises rather than pushing the row. */
.pcard .psec-bd select.field {
  appearance: none; -webkit-appearance: none;
  height: 32px; padding: 0 30px 0 9px;
  font: inherit; font-size: 13px; color: var(--bd-ink);
  background-color: var(--bd-field);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231B324C' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat: no-repeat; background-position: right 9px center; background-size: 14px 14px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: pointer;
}
.pcard .psec-bd select.field { text-align: center; text-align-last: center; padding-left: 30px; }
.pcard .psec-bd select.field:focus { outline: 2px solid var(--bd-focus); outline-offset: -1px; border-color: var(--bd-focus); }

/* THE SECTION HEADERS WEAR THE THEME (asked for 2026-09-16, "the drop menus
   are not showing as themed"): the same navy band and light ink as the
   card's own header and every column head. Closed: the band's grey ink.
   Open: white, with the caret and the League limits arrow in the chrome
   yellow. The save chips take the -chrome money pair, which is tuned for
   this navy; the content pair is 2.6:1 on it. The light-blue open state
   this replaces lasted a day. */
.pcard .psec { border-color: var(--bd-colhead); }
.pcard .psec-hd { background: var(--bd-colhead); color: var(--bd-colhead-ink); }
.pcard .psec-hd h4, .pcard .psec-hd .ico { color: var(--bd-colhead-ink); }
.pcard .psec.is-open .psec-hd { background: var(--bd-colhead); border-bottom: 1px solid var(--bd-colhead); }
.pcard .psec.is-open .psec-hd h4 { color: var(--bd-colhead-hi); }
.pcard .psec.is-open .psec-hd .ico { color: var(--bd-accent-chrome); }
.pcard .psec-hd:focus-visible { outline-color: var(--bd-accent-chrome); }
.pcard .psec-hd .st { color: var(--bd-colhead-ink); }
.pcard .psec-hd .st.dirty { color: var(--bd-accent-chrome); }
.pcard .psec-hd .st.saved { color: var(--bd-up-chrome); }
.pcard .psec-hd .st.failed { color: var(--bd-down-chrome); }
.pcard .psec-link .psec-hd .ico, .pcard .psec-link .psec-go { color: var(--bd-accent-chrome); }
.pcard .psec-link:hover .psec-hd { background: var(--bd-colhead); filter: brightness(1.18); }
.pcard .psec-bd .fgrp:has(textarea) { align-items: start; }
.pcard .psec-bd .fgrp:has(textarea) .lbl { padding-top: 8px; }
/* rows that are not a setting span both columns: a hint, a button row,
   the cash result */
.pcard .psec-bd .hint,
.pcard .psec-bd .row.wrap,
.pcard .psec-bd #traj { grid-column: 1 / -1; }
.pcard .psec-bd .row.wrap { justify-content: flex-end; margin-top: 8px; }
.pcard .psec-bd .hint { margin-top: 6px; }
.pcard .sw-list.two .sw-row:nth-child(2) { border-top: 1px solid var(--bd-rule); }
@media (prefers-reduced-motion: reduce) { .psec-hd .ico { transition: none; } }


/* ======================================================================
   pweekly (.rpt.wk): THE BOOK'S TOTALS ABOVE THE ACCOUNTS - 2026-09-16
   ======================================================================
   The report opens with a "Summary" head bar and the book's "N Players"
   totals row, then the Acct column heads, then the agents. The ASP can only
   print the totals AFTER its loop, so they stay in <tfoot> in the markup
   and the TABLE MODEL moves them: with <thead> demoted to a row group and
   <tfoot> promoted to THE header group, the foot is laid out before every
   other row group (CSS 2.1 17.2 - a table has one header group, whichever
   section carries the display value), the thead follows it in document
   order, and sticky still works on the cells. Measured in Chrome at 1366:
   Summary bar 99-125, totals 125-152, Acct heads 152-178, band 178-205,
   all four pinned while the accounts scroll beneath.

   js/ui.js is unaffected: `thead th` is still the one row of column heads
   it maps by position, and it finds the grand row by data-row. */
.rpt.wk thead { display: table-row-group; }
.rpt.wk tfoot { display: table-header-group; }

/* the Summary bar: a <td> row painted as a column head. <td>, not <th>, so
   the `thead th` walks in ui.js see one head row - they map data-col by
   index, and a second row of <th> would shift every column. */
.rpt.wk tfoot tr.sumhd > td,
.rpt.wk tfoot tr.sumhd > td:first-child {
  position: sticky;
  top: 0;
  bottom: auto;
  z-index: 12;
  height: var(--bd-head-th);
  background: var(--bd-colhead);
  color: var(--bd-colhead-ink);
  font-size: var(--bd-head-size);
  font-weight: var(--bd-head-weight);
  text-align: center;
  border-top: 0;
  border-bottom: 1px solid var(--bd-edge);
  user-select: none;
}
.rpt.wk tfoot tr.sumhd > td:first-child { z-index: 14; text-align: left; }
/* the totals row, one head under the bar. Both selectors named: the base
   .rpt tfoot td pins to the BOTTOM at (0,1,2) and its 720 branch names the
   first cell at (0,2,2), so bottom is released and top set at (0,3,2)+. */
.rpt.wk tfoot tr[data-row="grand"] > td,
.rpt.wk tfoot tr[data-row="grand"] > td:first-child {
  position: sticky;
  top: var(--bd-head-th);
  bottom: auto;
  z-index: 12;
  background: var(--bd-raised);
  font-weight: 700;
  border-top: 0;
  border-bottom: 1px solid var(--bd-edge);
}
.rpt.wk tfoot tr[data-row="grand"] > td:first-child { z-index: 14; }
/* not a tap target: the row has no disc and opens nothing (ui.js setRow) */
.rpt.wk tfoot tr[data-row] { cursor: default; }
/* the account heads and the bands each move down by what now sits above */
.rpt.wk thead th,
.rpt.wk thead th:first-child { top: calc(var(--bd-head-th) + var(--bd-row-h)); }
.rpt.wk tr.grp > td,
.rpt.wk tr.grp > td:first-child { top: calc(var(--bd-head-th) + var(--bd-row-h) + var(--bd-head-th)); }

/* the agent band on the column-head navy, its name and count in the
   head's ink, its folded figures in the chrome money hues - the hard red
   is 2.4:1 on the navy. Left, not centred: it is a bar, not a cell. */
.rpt.wk tr.grp > td,
.rpt.wk tr.grp > td:first-child {
  background-color: var(--bd-colhead);
  background-image: none;
  color: var(--bd-colhead-ink);
  border-top-color: var(--bd-colhead);
  border-bottom-color: var(--bd-colhead);
  text-align: left;
}
.rpt.wk tr.grp .gname { color: var(--bd-colhead-ink); text-transform: none; letter-spacing: 0; font-size: 12px; font-weight: 500; }
.rpt.wk tr.grp .gcount, .rpt.wk tr.grp .disc { color: var(--bd-colhead-ink); }
.rpt.wk tr.grp .formatPositiveNumber { color: var(--bd-up-chrome); }
.rpt.wk tr.grp .formatNegativeNumber { color: var(--bd-down-chrome); }
.rpt.wk tr.grp .formatZero { color: var(--bd-colhead-ink); }
@media (hover: hover) {
  .rpt.wk tbody tr.grp:hover > td { background-color: var(--bd-colhead); }
}
/* the count lives on the subtotal row and on the folded band, not on the
   open band - the subtotal is right there when the agent is open */
.rpt.wk tr.grp-open .gcount { display: none; }

/* "<icon> 3 Players": the word is visible on this report, not
   screen-reader-only, on the subtotal, the book's totals and the folded band */
.rpt.wk .gcount .sr { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; }
.rpt.wk tr.subtotal .gcount, .rpt.wk tfoot .gcount { color: var(--bd-muted); font-weight: 400; white-space: nowrap; }
.rpt.wk tr.subtotal .gcount .ico, .rpt.wk tfoot .gcount .ico { width: 13px; height: 13px; vertical-align: -2px; margin-right: 3px; opacity: .8; }
.rpt.wk tr.subtotal .gcount b, .rpt.wk tfoot .gcount b { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--bd-ink); }

/* every cell centred; the identity column - Summary, Acct, the account
   names, the counts - stays left */
.rpt.wk th, .rpt.wk td { text-align: center; }
.rpt.wk th:first-child, .rpt.wk td:first-child { text-align: left; }

/* the released-pane branches, restated the way the column head and the
   bands have them above: the window is the scrollport there, so every
   pinned row clears .head and .rptbar first */
@media (max-width: 720px) {
  /* ONE LINE, or the heads pinned one --bd-row-h below it land on its
     second line. The 720 branch lets a tfoot first cell wrap for "GRAND
     TOTAL"; this cell is "<icon> 122 Players" and fits a 390 column. */
  .rpt.wk tfoot tr[data-row="grand"] > td:first-child { white-space: nowrap; }
  .rpt.wk tfoot tr.sumhd > td,
  .rpt.wk tfoot tr.sumhd > td:first-child { position: sticky; top: calc(var(--bd-head-h) + var(--bd-bar-h)); }
  .rpt.wk tfoot tr[data-row="grand"] > td,
  .rpt.wk tfoot tr[data-row="grand"] > td:first-child { position: sticky; top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th)); }
  /* 42px, NOT --bd-row-h: the narrow block above gives every cell
     height: 42px for touch, and the totals row is one of them. Measured at
     390: bar 163-189, totals 189-231, so the heads pin at 231. */
  .rpt.wk thead th,
  .rpt.wk thead th:first-child { top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th) + 42px); }
  .rpt.wk tr.grp > td,
  .rpt.wk tr.grp > td:first-child { top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th) + 42px + var(--bd-head-th)); }
}
@media (min-width: 1017px) {
  .content.fw .rpt.wk tfoot tr.sumhd > td,
  .content.fw .rpt.wk tfoot tr.sumhd > td:first-child { top: calc(var(--bd-head-h) + var(--bd-bar-h)); }
  .content.fw .rpt.wk tfoot tr[data-row="grand"] > td,
  .content.fw .rpt.wk tfoot tr[data-row="grand"] > td:first-child { top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th)); }
  .content.fw .rpt.wk thead th,
  .content.fw .rpt.wk thead th:first-child { top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th) + var(--bd-row-h)); }
  .content.fw .rpt.wk tr.grp > td,
  .content.fw .rpt.wk tr.grp > td:first-child { top: calc(var(--bd-head-h) + var(--bd-bar-h) + var(--bd-head-th) + var(--bd-row-h) + var(--bd-head-th)); }
}
