
:root{
  --bg:#F3F4F1;--panel:#FFFFFF;--sunk:#EAECE7;
  --line:#D8DBD3;--line-firm:#AFB4A7;
  --ink:#1A1D18;--ink-2:#525749;--ink-3:#818779;
  --brand:#1E4D3C;--brand-ink:#FFFFFF;
  --good:#2C6B4C;--good-bg:#E0EDE6;
  --warn:#8A5A10;--warn-bg:#F6EAD5;
  --bad:#9C3A2C;--bad-bg:#F5E2DE;
  --s2:8px;--s3:12px;--s4:18px;--s5:26px;--s6:38px;
  --shadow:0 1px 2px rgba(26,29,24,.05),0 4px 14px -6px rgba(26,29,24,.12);
  /* Figures are set in the page font, bold, not a monospace. Tabular numerals
     keep columns lined up without the typewriter look. */
  --num:'Archivo',system-ui,-apple-system,'Segoe UI',sans-serif;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
  font-family:'Archivo',system-ui,-apple-system,'Segoe UI',sans-serif;
  font-size:15px;line-height:1.5}
.mono{font-family:var(--num);font-weight:700;font-size:1.06em;font-variant-numeric:tabular-nums}

/* A rail rather than a bar. Twelve sections do not fit across the top beside a
   brand and a running figure, and the sidebar collapses to icons for anyone who
   would rather have the width back. The collapsed state is applied from
   localStorage in <head>, before first paint, so the rail never flashes wide. */
.shell{display:flex;align-items:flex-start;min-height:100vh}
.side{position:sticky;top:0;height:100vh;width:238px;flex:0 0 238px;
  background:var(--brand);color:var(--brand-ink);
  display:flex;flex-direction:column;overflow:hidden}
.content{flex:1;min-width:0}

.side-top{display:flex;align-items:center;gap:10px;padding:var(--s4) 12px var(--s3)}
.side .brand{font-weight:700;letter-spacing:-.01em;white-space:nowrap;font-size:.95rem}
.side-toggle{flex:0 0 34px;width:34px;height:34px;padding:0;display:grid;place-items:center;
  background:rgba(255,255,255,.1);border:0;border-radius:8px;color:inherit;cursor:pointer}
.side-toggle:hover{background:rgba(255,255,255,.2)}
.side-toggle:focus-visible{outline:2px solid #fff;outline-offset:2px}

.side nav{flex:1;min-height:0;overflow-y:auto;padding:0 10px var(--s3);
  display:flex;flex-direction:column;gap:2px}
.side nav a{display:flex;align-items:center;gap:11px;padding:8px 10px;border-radius:8px;
  color:rgba(255,255,255,.82);text-decoration:none;font-size:.9rem;white-space:nowrap}
.side nav a:hover{background:rgba(255,255,255,.11);color:#fff}
.side nav a.on{background:rgba(255,255,255,.17);color:#fff;font-weight:700}
.side nav a:focus-visible{outline:2px solid #fff;outline-offset:-2px}
.side svg{flex:0 0 19px;width:19px;height:19px;stroke:currentColor;fill:none;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
/* Small, uppercase and tracked out is already the hardest thing to read on a
   dark ground; at 50% white it measured 3.72:1, under the 4.5 a label needs,
   which is why these came out looking like a row of dashes. 75% is 6.25:1, and
   the tracking eases off to match. */
.side .group{font-size:.64rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  opacity:.75;padding:var(--s4) 10px 5px;white-space:nowrap}

/* The running supply, on every page. Bordered rather than coloured so it reads
   as a figure being watched, not as a warning. */
.side a.supply{display:flex;flex-direction:column;line-height:1.2;margin:0 12px var(--s3);
  padding:7px 10px;border:1px solid rgba(255,255,255,.28);border-radius:8px;
  text-decoration:none;color:inherit}
.side a.supply:hover{border-color:rgba(255,255,255,.7);background:rgba(255,255,255,.08)}
.side a.supply .k{font-size:.55rem;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;opacity:.72}
.side a.supply .v{font-family:var(--num);font-size:1.08rem;font-weight:700;
  font-variant-numeric:tabular-nums;color:#fff}
.side a.supply .short{display:none;font-family:var(--num);font-size:.78rem;
  font-weight:700;color:#fff;text-align:center}

.side-foot{margin-top:auto;padding:var(--s3) 12px var(--s4);
  border-top:1px solid rgba(255,255,255,.16);display:flex;flex-direction:column;gap:6px}
.side-foot .me{display:flex;align-items:center;gap:11px;padding:6px 10px;
  font-size:.86rem;opacity:.9;white-space:nowrap}
/* Sign out is given its own weight rather than sitting as one more link: it is
   the only item here that ends the session. */
.side-foot .signout{display:flex;align-items:center;gap:11px;padding:9px 10px;
  border-radius:8px;text-decoration:none;color:#fff;font-weight:700;font-size:.88rem;
  background:rgba(255,255,255,.13);border:1px solid rgba(255,255,255,.24);
  white-space:nowrap}
.side-foot .signout:hover{background:rgba(255,255,255,.24)}
.side-foot .signout:focus-visible{outline:2px solid #fff;outline-offset:2px}

/* Collapsed: icons only. Group headings become separators — a heading with no
   words under it is just noise, but the break between groups still helps. */
.rail .side{width:64px;flex-basis:64px}
.rail .side .lbl{display:none}
.rail .side-top{justify-content:center;padding-left:0;padding-right:0}
.rail .side nav a,
.rail .side-foot .me,
.rail .side-foot .signout{justify-content:center;padding-left:0;padding-right:0;gap:0}
.rail .side .group{height:1px;padding:0;margin:var(--s3) 12px;font-size:0;overflow:hidden;
  background:rgba(255,255,255,.2);opacity:1}
.rail .side a.supply{margin:0 8px var(--s3);padding:7px 4px;align-items:center}
.rail .side a.supply .k,.rail .side a.supply .v{display:none}
.rail .side a.supply .short{display:block}

/* A phone gets a top bar and a drawer, not a rail. An icons-only strip asks the
   reader to know what nine glyphs mean while taking a fifth of a narrow screen;
   a hamburger costs one tap and gives the width back. */
.topbar{display:none}
.backdrop{display:none}

@media(max-width:860px){
  .topbar{display:flex;align-items:center;gap:var(--s3);
    position:fixed;top:0;left:0;right:0;height:56px;z-index:40;
    padding:0 var(--s4);background:var(--panel);
    border-bottom:1px solid var(--line)}
  .topbar .brand{font-weight:700;color:var(--brand);letter-spacing:-.01em;
    margin-right:auto;white-space:nowrap}
  .topbar .chip{display:flex;align-items:center;gap:7px;padding:7px 12px;
    border-radius:20px;background:var(--brand);color:#fff;text-decoration:none;
    font-family:var(--num);font-size:.88rem;font-weight:700;
    white-space:nowrap}
  .topbar .burger{flex:0 0 40px;width:40px;height:40px;padding:0;display:grid;
    place-items:center;background:none;border:0;color:var(--ink);cursor:pointer}
  .topbar svg{width:22px;height:22px;stroke:currentColor;fill:none;
    stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}

  .content{padding-top:56px}

  /* Off-canvas. transform only, so opening does not reflow the page under it. */
  .side{position:fixed;top:0;bottom:0;left:0;z-index:60;
    width:min(280px,82vw);flex-basis:auto;height:100%;
    transform:translateX(-100%);transition:transform .18s ease}
  .nav-open .side{transform:none}
  .side-toggle{display:none}

  .nav-open .backdrop{display:block;position:fixed;inset:0;z-index:50;
    background:rgba(26,29,24,.45)}

  /* The rail is a desktop idea; on a phone it would hide the labels again. */
  .rail .side{width:min(280px,82vw)}
  .rail .side .lbl{display:inline}
  .rail .side .group{height:auto;padding:var(--s4) 10px 5px;font-size:.64rem;
    background:none;opacity:.75}
  .rail .side a.supply .k,.rail .side a.supply .v{display:block}
  .rail .side a.supply .short{display:none}
  .rail .side-top,.rail .side nav a,
  .rail .side-foot .me,.rail .side-foot .signout{
    justify-content:flex-start;padding-left:10px;padding-right:10px;gap:11px}
}
@media(prefers-reduced-motion:reduce){.side{transition:none}}

/* The day, as blocks. Sized to be read at a glance on a phone held at arm's
   length, which is where the operator actually checks them. */
.tiles{display:grid;gap:var(--s3);grid-template-columns:1fr}
@media(min-width:640px){.tiles{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1024px){.tiles{grid-template-columns:repeat(4,1fr)}}
/* A heading straight after the blocks needs more air than the default: the
   blocks are a band of colour and a section title butting up against them
   reads as a caption to the last one rather than the start of the next. */
.tiles + h2,.tiles + .page-head{margin-top:var(--s6)}
.tile{border-radius:14px;padding:var(--s5) var(--s4);color:#fff;
  box-shadow:0 2px 10px -4px rgba(26,29,24,.3)}
.tile .k{font-size:.92rem;opacity:.88;line-height:1.35}
.tile .v{font-family:var(--num);font-variant-numeric:tabular-nums;
  font-size:2.5rem;font-weight:700;letter-spacing:-.02em;line-height:1.15;
  margin-top:6px;word-break:break-all}
.tile .s{font-size:.8rem;opacity:.82;margin-top:6px}
/* The blocks were two blues and a blue-grey against a green-and-clay palette —
   the one place in the console that belonged to a different design. These are
   drawn from the palette itself: the brand green, a pine, an olive, the clay of
   --bad and the brass of --warn, each taken dark enough that white sits on it
   at 7:1 or better. Measured, not eyeballed: the old --added blue was 4.26,
   which is under the 4.5 an ordinary label needs. The lightest here is 7.4.
   Hue carries the meaning, so the four across a row stay distinguishable:
   olive is a plain figure, clay is points leaving, brass is points created,
   green is a balance being held. */
.tile.play{background:#3B4534}
.tile.won{background:#8E3A2A}
.tile.added{background:#6B4A12}
.tile.held{background:#17423E}
.tile.slate{background:var(--brand)}
/* A deep russet, distinct from the brass of the block beside it: two of them
   in one colour would read as a single figure split in half. 10.75:1. */
.tile.taken{background:#5C3324}
/* The queue block. Bordered rather than filled: it is a link to a job, not a
   figure to read, and it should not compete with the day's numbers. */
.tile.queue{background:var(--panel);color:var(--ink);border:1.5px solid var(--line-firm);
  text-decoration:none;display:block}
.tile.queue .k{color:var(--ink-2);opacity:1}
.tile.queue .s{color:var(--ink-3);opacity:1}
.tile.queue:hover{border-color:var(--brand)}
/* Green and red are reserved for the profit block, so that a glance at colour
   alone answers the only question with two answers on this page. It is the one
   green that is brighter than the brand, which is what makes it read as the
   answer rather than as another balance. */
.tile.profit{background:#1C6142}
.tile.loss{background:#8E3A2A}

/* Declaring and undoing happen over the list rather than on a page of their
   own — both are a glance at one row, and losing the list to see them costs
   the operator the context they were reading. The dialog fetches the page it
   would have gone to, so there is one copy of each form and both still work
   with JavaScript off. */
dialog.modal{border:0;padding:0;border-radius:12px;width:min(540px,94vw);
  background:var(--panel);color:var(--ink);
  box-shadow:0 24px 70px -24px rgba(26,29,24,.55)}
dialog.modal::backdrop{background:rgba(26,29,24,.5)}
.modal-body{padding:var(--s5)}
.modal-body h1{font-size:1.15rem;margin-bottom:var(--s3);padding-right:var(--s5)}
.modal-body .card{border:0;box-shadow:none;padding:0;background:none}
.modal-close{position:absolute;top:12px;right:12px;width:32px;height:32px;padding:0;
  display:grid;place-items:center;background:var(--sunk);border:0;border-radius:8px;
  color:var(--ink-2);cursor:pointer;font-size:1.1rem;line-height:1}
.modal-close:hover{background:var(--line);color:var(--ink)}

/* Results and View bids share one table shape, so a market sits in the same
   place on both and the operator does not have to re-find it. */
.markets .srno{width:44px;text-align:center;color:var(--ink-3);
  font-family:var(--num);font-weight:700;font-size:.86rem}
.markets td{vertical-align:middle}

/* How far through the day each market is, as the row's own ground. Strong
   enough to read down the table at a glance and tell the three apart without
   looking for them — a tint you have to hunt for is not doing the job.

   It still never replaces a label: the result chips and the buttons in the row
   say the same thing in words, so nothing here depends on seeing colour.

   Body text stays above 11:1 on all three. The faint --ink-3 used for the
   times would have dropped to 2.7:1 on these, so on a tinted row it steps up
   to --ink-2 — the tint made that text better rather than worse. */
.markets tr.r-done{background:rgba(44,107,76,.22)}
.markets tr.r-part{background:rgba(138,90,16,.26)}
.markets tr.r-todo{background:rgba(156,58,44,.20)}
.markets tr[class*="r-"] .srno{color:var(--ink-2)}

/* A chip needs its own edge once the ground behind it is coloured: a green
   pill on a green row is nearly the same value and reads as a smudge. */
.pill{box-shadow:inset 0 0 0 1px rgba(26,29,24,.10)}
/* The account state as its own switch. A button, so it can be pressed, but
   shaped exactly like the pill it replaces — the state is the control. */
button.pill.toggle{width:auto;border:0;cursor:pointer;font-family:'IBM Plex Mono',monospace;
  line-height:inherit}
button.pill.toggle:hover{box-shadow:inset 0 0 0 1.5px currentColor}
button.pill.toggle:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

/* The result and the button that undoes it sit on one line, and when the
   column is too narrow they wrap with a gap rather than colliding. .btn-sm
   carries a left margin for inline use, which a flex gap would double. */
.actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px}
.actions > *{margin-left:0}
.actions form{display:flex}

/* Filter tabs. The gap does the spacing, so btn-sm's own margin would push the
   whole row out of line with the table under it. */
.tabs{display:flex;flex-wrap:wrap;gap:var(--s2);margin:0 0 var(--s3)}
.tabs .btn-sm{margin-left:0}

/* Find a request: a name or mobile, and a day. Sits above the queue it filters
   rather than in a panel of its own — it is part of reading the table. */
.reqfilter,.filters{display:flex;align-items:flex-end;gap:var(--s3);flex-wrap:wrap}
.reqfilter{margin-bottom:var(--s4)}
.reqfilter > div{flex:1;min-width:150px}
.reqfilter label{margin-bottom:5px}
.reqfilter .btn-ghost{margin-left:0}

/* A declared result, read as one thing: the panel, and the digit it works out
   to. The ank is what most bets actually settle against, so it travels with
   the panel rather than sitting in a column of its own. */
.result{display:inline-flex;align-items:center;gap:7px;padding:3px 10px;
  border-radius:6px;background:var(--bad-bg);color:var(--bad);
  font-family:var(--num);font-variant-numeric:tabular-nums;
  font-weight:700;font-size:.96rem;white-space:nowrap}
.result em{font-style:normal;font-weight:600;opacity:.8}
.result em::before{content:'– '}

/* The two things you press on these pages. Given their own weight because the
   rest of the row is reference, and these are the actions. */
.btn-add,.btn-view{display:inline-block;padding:6px 13px;border-radius:6px;
  text-decoration:none;font-weight:700;font-size:.78rem;white-space:nowrap}
.btn-add{background:var(--warn-bg);color:var(--warn);
  border:1px solid rgba(138,90,16,.35);text-transform:capitalize}
.btn-add:hover{background:#efdcb8}
.btn-view{background:var(--brand);color:#fff}
.btn-view:hover{background:#173d2f}
.btn-add:focus-visible,.btn-view:focus-visible{outline:2px solid var(--brand);
  outline-offset:2px}

/* The payout chart. The field you type in has to stay legible whatever else
   goes, so it keeps a floor and the softer columns give way around it. */
.rates .about{font-size:.8rem;color:var(--ink-3)}
.rates .pays{width:130px}
.rates .pays input{min-width:84px;text-align:right;font-family:var(--num);font-weight:700;
  font-variant-numeric:tabular-nums;padding:9px 10px}
@media(max-width:640px){
  .rates .about,.rates .fair,.rates .verdict{display:none}
  .rates .pays{width:110px}
  .rates th,.rates td{padding:9px 7px}
}

main{max-width:1120px;margin:0 auto;padding:var(--s5)}
/* The bid report is the one page that wants every pixel: nine or twelve columns
   read far better side by side than behind a scrollbar. */
main.wide{max-width:none}
h1{font-size:1.35rem;margin:0 0 var(--s4);letter-spacing:-.015em}
/* Heading on the left, the day being looked at on the right — the shape the
   client's own results screen uses. */
.page-head{display:flex;align-items:flex-end;justify-content:space-between;
  gap:var(--s3);flex-wrap:wrap;margin-bottom:var(--s4)}
.page-head h1{margin:0}
.daypick{display:flex;align-items:center;gap:var(--s2)}
.daypick label{margin:0;font-size:.8rem;color:var(--ink-2);white-space:nowrap}
.daypick input{width:auto;padding:6px 10px;font-size:.88rem}
.daypick .btn-sm{margin-left:0}
/* The day the sheet is for, beside its name rather than in a sentence above it. */
h1 .when{font-weight:400;font-size:.85rem;color:var(--ink-3);margin-left:10px;
  font-family:var(--num);white-space:nowrap}
h2{font-size:.75rem;font-weight:700;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-3);margin:0 0 var(--s3)}

.grid{display:grid;gap:var(--s3)}
@media(min-width:720px){.grid.c2{grid-template-columns:repeat(2,1fr)}
  .grid.c3{grid-template-columns:repeat(3,1fr)}
  .grid.c4{grid-template-columns:repeat(4,1fr)}}

.card{background:var(--panel);border:1px solid var(--line);border-radius:7px;
  padding:var(--s4);box-shadow:var(--shadow)}
.stat .k{font-size:.68rem;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-3)}
.stat .v{font-family:var(--num);font-variant-numeric:tabular-nums;
  font-size:1.6rem;font-weight:700;margin-top:3px;letter-spacing:-.01em}
.stat .s{font-size:.8rem;color:var(--ink-3);margin-top:2px}
.v.good{color:var(--good)} .v.bad{color:var(--bad)}

/* A row of figures above a table reads as a caption to it, not as a dashboard,
   so the columns cap at 200px and the row sits at the left rather than being
   split across the full width into half-page slabs of mostly nothing.
   The column count is fixed rather than auto-fit: sized to their own content
   the cards wrap to ragged widths, and a short card stacked under a long one
   looks broken rather than deliberate. Two to a line on a phone, and a third
   that wraps is still exactly as wide as the two above it. */
.stats{display:grid;gap:var(--s3);grid-template-columns:repeat(2,minmax(0,200px))}
@media(min-width:720px){.stats{grid-template-columns:repeat(4,minmax(0,200px))}}
.stats .stat{padding:var(--s3) var(--s4)}
.stats .stat .v{font-size:1.35rem}
/* A figure that opens the list behind it. It looks like its neighbours until it
   is pointed at, and the chevron is the only standing hint. */
a.stat.link{display:block;text-decoration:none;color:inherit;cursor:pointer}
a.stat.link .k{display:flex;align-items:center;justify-content:space-between;gap:6px}
a.stat.link svg{flex:0 0 14px;width:14px;height:14px;stroke:currentColor;fill:none;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
a.stat.link:hover{border-color:var(--brand)}
a.stat.link:hover .k{color:var(--brand)}
a.stat.link:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
a.stat.link:active{transform:translateY(1px)}

/* Which sections one staff account may open. A grid of ticks rather than a
   second page each: the owner is comparing people, not editing one of them. */
.grant-head{display:flex;align-items:center;gap:var(--s3);margin-bottom:var(--s3)}
.grant-head .hint{flex:1}
.grants{display:grid;gap:var(--s2);grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:720px){.grants{grid-template-columns:repeat(5,minmax(0,1fr))}}
.grant{display:flex;align-items:center;gap:8px;margin:0;padding:7px 10px;
  border:1px solid var(--line);border-radius:8px;cursor:pointer;
  font-size:.86rem;text-transform:none;letter-spacing:0;color:var(--ink)}
.grant input{width:auto;margin:0;flex:0 0 auto}
.grant:has(input:checked){background:var(--good-bg);border-color:var(--good)}
.grant:focus-within{outline:2px solid var(--brand);outline-offset:1px}

/* Label beside field, the way the client's declare screen reads. A value that
   is fixed by the row you clicked is shown, not offered: an editable game
   picker on this screen is how a result lands on the wrong market. */
.field-row{display:grid;grid-template-columns:110px minmax(0,1fr);
  align-items:center;gap:var(--s3);margin-bottom:var(--s3)}
.field-row label{margin:0}

/* A box you type into with the list narrowing under it, rather than 220 lines
   of dropdown to scroll. The list is absolutely positioned, so it overlays the
   fields below instead of shoving the form down as it grows and shrinks. */
.combo{position:relative}
.combo input{width:100%}
.combo-list{position:absolute;z-index:40;left:0;right:0;top:calc(100% + 4px);
  margin:0;padding:4px;list-style:none;max-height:184px;overflow-y:auto;
  background:var(--panel);border:1px solid var(--line);border-radius:10px;
  box-shadow:0 18px 40px -18px rgba(26,29,24,.45)}
.combo-list li{padding:7px 10px;border-radius:7px;cursor:pointer;
  font-family:var(--num);font-weight:700;letter-spacing:.06em}
.combo-list li[aria-selected=true],.combo-list li:hover{background:var(--sunk)}
.combo-list li.none{color:var(--ink-3);cursor:default;font-family:inherit;
  letter-spacing:0}
.field-row .fixed{background:var(--sunk);border:1px solid var(--line);
  border-radius:7px;padding:9px 12px;color:var(--ink-2)}
input.fixed{width:100%;color:var(--ink-2)}
@media(max-width:520px){.field-row{grid-template-columns:1fr;gap:5px}}

/* One payout, one field. Label left, number right — the client wanted the
   working out gone, so there is no break-even column and no verdict pill to
   line up with. */
/* Each rate reads as a ratio per 10 points, bet : paid back, the way rate
   cards are written — "10 :" sits in front of the field. */
.rate-row{display:grid;grid-template-columns:minmax(0,1fr) 160px;
  align-items:center;gap:var(--s3);padding:9px 0;
  border-bottom:1px solid var(--line)}
.rate-row:last-child{border-bottom:0}
.rate-row label{margin:0;font-size:.95rem;color:var(--ink);
  text-transform:none;letter-spacing:0}
.rate-head{padding-top:0;font-size:.72rem;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3)}
.rate-head span:last-child{text-align:right}
.ratio{display:flex;align-items:center;gap:6px}
.ratio .one{flex:none;font-family:var(--num);font-weight:700;color:var(--ink-2);
  font-variant-numeric:tabular-nums}
.rate-row input,.rate-row .fixed{text-align:right;
  font-family:var(--num);font-weight:700;font-variant-numeric:tabular-nums}
.rate-row .fixed{justify-content:flex-end}
@media(max-width:520px){.rate-row{grid-template-columns:minmax(0,1fr) 136px}}

/* An eye or a person on a row: a look at what is behind it. */
.peek{display:inline-grid;place-items:center;width:30px;height:30px;padding:0;
  border:1px solid var(--line-firm);border-radius:7px;background:var(--sunk);
  color:var(--ink-2);text-decoration:none;cursor:pointer}
.peek:hover{background:var(--panel);color:var(--ink);border-color:var(--brand)}
/* Destructive row actions carry the same red the text buttons did. An icon on
   its own says less than a word, so every one of these keeps a title and an
   aria-label — the tooltip is the label, not decoration. */
.peek.danger{color:var(--bad);border-color:rgba(156,58,44,.45);background:var(--bad-bg)}
.peek.danger:hover{background:#f0cfc8;color:var(--bad);border-color:var(--bad)}
/* Approve and decline sit side by side and are the opposite of each other, so
   they are told apart by colour as well as by shape — a tick and a cross in the
   same grey would be two grey squares. */
.peek.good{color:var(--good);border-color:rgba(44,107,76,.45);background:var(--good-bg)}
.peek.good:hover{background:#cfe4d8;color:var(--good);border-color:var(--good)}
.peek.good svg,.peek.danger svg{stroke-width:2.1}
.peek:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.peek svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}

table{width:100%;border-collapse:collapse;font-size:.88rem}
th{font-family:'IBM Plex Mono',monospace;font-size:.62rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-3);text-align:left;
  padding:11px 11px;border-bottom:1.5px solid var(--line-firm);white-space:nowrap}
td{padding:12px 11px;border-bottom:1px solid var(--line)}
/* The outer edges. Cell padding spaces columns from each other; it does not
   keep the first and last off the card's own edge, which is where a right-hand
   button ends up sitting flush against the border. */
th:first-child,td:first-child{padding-left:16px}
th:last-child,td:last-child{padding-right:16px}
tbody tr:last-child td{border-bottom:0}
td.n{font-family:var(--num);font-weight:700;font-size:1.06em;font-variant-numeric:tabular-nums;
  text-align:right;white-space:nowrap}
.scroll{overflow-x:auto;background:var(--panel);border:1px solid var(--line);
  border-radius:7px;box-shadow:var(--shadow)}
/* Sortable headers read like the headers beside them until pointed at, and the
   arrow appears only on the column the table is sorted by. */
th button.sort{all:unset;cursor:pointer;display:inline-flex;align-items:center;gap:4px;
  font:inherit;letter-spacing:inherit;text-transform:inherit;color:inherit}
th button.sort:hover{color:var(--ink)}
th button.sort:focus-visible{outline:2px solid var(--brand);outline-offset:3px;border-radius:3px}
th button.sort:active{transform:translateY(1px)}
th[aria-sort=ascending] button.sort::after{content:'↑'}
th[aria-sort=descending] button.sort::after{content:'↓'}
.search-row{display:flex;align-items:center;gap:var(--s3);margin-bottom:var(--s4)}
.search-row label{margin:0;white-space:nowrap}
.search-row input{max-width:320px}
/* Every data table is a grid of boxes: a rule on all four sides of each cell,
   collapsed so neighbours share one line. A row of short facts with nothing
   between them reads as a single run-on line, which is the moment a value gets
   taken for the column beside it.

   The outermost edges are left off, because the card is already drawing them —
   a cell border there would sit a pixel inside the card's own and read as a
   doubled line. */
/* A row of buttons in a cell must not fold. The table is laid out at its
   min-content width now that nothing wraps, and a flex box that is allowed to
   wrap has a min-content of one item — so the actions stacked into a column and
   took the whole row down with them. Told not to fold, the cell asks for the
   width the buttons actually need and the row stays one line high. */
.scroll .actions{flex-wrap:nowrap}
.scroll th,.scroll td{border:1px solid var(--line)}
.scroll th:first-child,.scroll td:first-child{border-left:0}
.scroll th:last-child,.scroll td:last-child{border-right:0}
.scroll thead th{border-top:0;border-bottom:1.5px solid var(--line-firm)}
.scroll tbody tr:last-child td{border-bottom:0}

/* Wider across, tighter down. Each cell holds one short fact, so the room it
   needs is beside its neighbour rather than above the next row — and with the
   rules drawn in, the horizontal padding is what keeps a value off the line
   instead of crowding it. */
.scroll th{padding:9px 18px}
.scroll td{padding:8px 18px}
.scroll th:first-child,.scroll td:first-child{padding-left:20px}
.scroll th:last-child,.scroll td:last-child{padding-right:20px}

/* The dashboard panel is the one table sized to its content rather than to its
   card, so its right-hand edge stops short of the card and has to close itself
   — everywhere else the card is sitting right there to do it. */
.scroll.fit table{width:auto}
.scroll.fit th:last-child,.scroll.fit td:last-child{border-right:1px solid var(--line)}


/* The neutral pill carries its own ground: without one it reads as bare text
   beside a coloured pill in the same column, as though it were a different
   kind of thing rather than a different value. */
/* The second line under a value: the mobile under a name, the day under a
   market. The .opt ones are what the page can already tell you another way,
   and those are what a phone drops first. */
.sub{font-size:.78rem;color:var(--ink-3)}

.pill{display:inline-block;border-radius:20px;padding:2px 10px;
  font-family:'IBM Plex Mono',monospace;font-size:.66rem;font-weight:600;
  letter-spacing:.07em;text-transform:uppercase;
  background:var(--sunk);color:var(--ink-2)}
.pill.good{background:var(--good-bg);color:var(--good)}
.pill.warn{background:var(--warn-bg);color:var(--warn)}
.pill.bad{background:var(--bad-bg);color:var(--bad)}

/* Support: players waiting, a conversation, and the reply box under it. */
.side nav a .count{margin-left:auto;min-width:20px;padding:1px 6px;border-radius:999px;
  background:#fff;color:var(--brand);font-size:.72rem;font-weight:800;text-align:center}
.rail .side nav a .count{display:none}
tr.unread td{font-weight:700}
main.chat{max-width:780px}
.thread{list-style:none;margin:var(--s4) 0;padding:0;display:flex;flex-direction:column;gap:10px}
.thread .none{color:var(--ink-3)}
.msg{max-width:78%;padding:9px 13px 6px;border-radius:14px;box-shadow:var(--shadow);
  overflow-wrap:anywhere}
.msg p{margin:0;white-space:pre-wrap;line-height:1.45}
.msg .meta{display:block;margin-top:4px;font-size:.72rem;opacity:.72;text-align:right}
.msg.mine{align-self:flex-end;background:var(--brand);color:var(--brand-ink);
  border-bottom-right-radius:4px}
.msg.theirs{align-self:flex-start;background:var(--panel);border:1px solid var(--line);
  border-bottom-left-radius:4px}
/* As wide as the picture, with the picture filling it, whatever the caption. */
.msg:has(.pic){width:min(78%,320px)}
.msg .pic{display:block;margin:-2px -5px 6px;border-radius:10px;overflow:hidden}
.msg .pic img{display:block;width:100%;height:auto}
.msg .gone{display:block;margin-bottom:4px;font-size:.82rem;font-style:italic;opacity:.8}
.msg audio.voice{display:block;width:260px;max-width:100%;height:40px;margin:2px 0 6px}
.attach.mic.recording{background:var(--bad);border-color:var(--bad);color:#fff}
.composer{position:sticky;bottom:0;margin-top:var(--s3);padding:var(--s3) 0;background:var(--bg)}
.compose-row{display:flex;align-items:flex-end;gap:var(--s2)}
.composer textarea{flex:1;min-width:0;resize:vertical;font:inherit;font-size:.95rem;
  padding:10px 12px;border:1.5px solid var(--line-firm);border-radius:6px;
  background:var(--panel);color:var(--ink)}
.composer textarea:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.attach{flex:none;width:44px;height:44px;margin:0;display:grid;place-items:center;
  border:1.5px solid var(--line-firm);border-radius:6px;background:var(--panel);
  color:var(--ink-2);cursor:pointer}
.attach svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.attach:focus-within{outline:2px solid var(--brand);outline-offset:2px}
.chat-error{margin:0 0 6px;color:var(--bad);font-size:.88rem}
.picked{margin:4px 0 0;font-size:.8rem;color:var(--ink-3)}
.picked:empty{display:none}
.sr{position:absolute;width:1px;height:1px;margin:0;overflow:hidden;clip:rect(0 0 0 0);
  white-space:nowrap}

.alert{border-left:4px solid var(--bad);background:var(--bad-bg);
  padding:var(--s3) var(--s4);border-radius:0 6px 6px 0;margin-bottom:var(--s4)}
.alert.warn{border-color:var(--warn);background:var(--warn-bg)}

label{display:block;font-size:.68rem;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:var(--ink-3);margin-bottom:5px}
input,textarea{width:100%;font-size:1rem;padding:11px 12px;border:1.5px solid var(--line-firm);
  border-radius:6px;background:var(--panel);color:var(--ink)}
input:focus-visible,textarea:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
textarea{resize:vertical;line-height:1.5;min-height:120px}
button{font-family:inherit;font-weight:700;font-size:.92rem;border:none;border-radius:6px;
  padding:12px 18px;background:var(--brand);color:var(--brand-ink);cursor:pointer;width:100%}
button:active{transform:translateY(1px)}
button:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

/* width:auto matters on <button>: the base rule stretches buttons to fill their
   container, which would push any sibling action out of the row. */
.btn-sm{display:inline-block;width:auto;font-size:.75rem;font-weight:700;padding:5px 10px;
  border-radius:5px;background:var(--sunk);color:var(--ink-2);border:1px solid var(--line-firm);
  text-decoration:none;cursor:pointer;margin-left:6px;white-space:nowrap}
.btn-sm:hover{background:var(--panel);color:var(--ink)}
.btn-sm:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.btn-sm.danger{color:var(--bad);border-color:var(--bad)}
/* Quiet on purpose: it sits above the title, where a filled button would be
   read as the thing the page is for. */
.backlink{display:inline-flex;align-items:center;gap:4px;margin-bottom:var(--s2);
  color:var(--ink-2);text-decoration:none;font-size:.88rem}
.backlink:hover{color:var(--ink);text-decoration:underline}
.backlink:focus-visible{outline:2px solid var(--brand);outline-offset:3px;
  border-radius:4px}
.backlink svg{width:17px;height:17px;stroke:currentColor;fill:none;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}

.btn-ghost{display:inline-block;padding:12px 18px;border-radius:6px;background:var(--sunk);
  color:var(--ink-2);text-decoration:none;font-weight:700;font-size:.92rem}
/* Not .btn-sm: those carry red *text* on a pale ground, and a red background
   as well would leave red on red — an unreadable block where a label should be. */
/* The big red submit button. Excludes the two small shapes that carry the
   same word: btn-sm draws red on white, and peek is an icon square. Both
   are more specific than this rule reads, so they have to be named. */
button.danger:not(.btn-sm):not(.peek){background:var(--bad)}
.btn-sm.danger:hover{background:var(--bad-bg)}
select{width:100%;font-size:1rem;padding:11px 12px;border:1.5px solid var(--line-firm);
  border-radius:6px;background:var(--panel);color:var(--ink);font-family:inherit}
select:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.hint{font-size:.86rem;color:var(--ink-2);margin:var(--s2) 0 0}
form.card button{width:auto}

.login{max-width:340px;margin:14vh auto;padding:0 var(--s4)}
.login .card{padding:var(--s5)}
.field+.field{margin-top:var(--s3)}
.err{background:var(--bad-bg);color:var(--bad);border-radius:6px;
  padding:10px 12px;font-size:.88rem;margin-bottom:var(--s4)}
.foot{margin-top:var(--s5);color:var(--ink-3);font-size:.78rem;text-align:center}
/* Bid report: one column per game, numbers down it, total at the top. */
/* The bid report as one grid, the way the client's own report reads: a column
   per game, its total under the heading, and every number beneath that. One
   table rather than separate cards, so the columns line up and the whole book
   is on screen at once. */
.pivot{border-collapse:collapse;width:auto;font-size:.85rem}
.pivot th{border:1px solid var(--line-firm);background:var(--sunk);
  padding:7px 8px;text-align:center;white-space:nowrap;
  font-family:'IBM Plex Mono',monospace;font-size:.56rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-2)}
.pivot tr.totals td{border:1px solid var(--line-firm);background:var(--panel);
  padding:7px 8px;text-align:center;
  font-family:var(--num);font-variant-numeric:tabular-nums;
  font-size:1.06rem;font-weight:700}
/* Sized so the nine close-side games fit a laptop without scrolling. The open
   side has twelve and will still scroll — as the client's own report does. */
.pivot tbody td{border:1px solid var(--line-firm);vertical-align:top;padding:0;
  min-width:100px}
.pivot .pl{display:flex;justify-content:space-between;gap:10px;padding:3px 8px;
  font-family:var(--num);font-variant-numeric:tabular-nums;
  font-size:.88rem;font-weight:700}
.pivot .pl+.pl{border-top:1px solid var(--line)}
.pivot .pl .pick{color:var(--ink-2)}
.pivot .pl .amt{font-weight:600}
.pivot .none{padding:8px 12px;color:var(--ink-3);font-size:.78rem}
/* Total Play: the side's whole book, set off from the games it adds up. */
.pivot .grand{border-left:2px solid var(--ink-2)}
.pivot th.grand{color:var(--ink)}
.pivot tr.totals td.grand{font-size:1.18rem;white-space:nowrap}
.pivot tbody td.grand{min-width:96px}
.bidcol{background:var(--panel);border:1px solid var(--line);border-radius:7px;
  box-shadow:var(--shadow);overflow:hidden;display:inline-block;width:100%}
.bidcol-head{padding:10px 12px;background:var(--sunk);
  border-bottom:1.5px solid var(--line-firm)}
.bidcol-head .nm{font-size:.62rem;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-3)}
.bidcol-head .tot{font-family:var(--num);font-weight:700;font-variant-numeric:tabular-nums;
  font-size:1.05rem;font-weight:600;margin-top:2px;letter-spacing:-.01em}
.bidcol-head .sub{font-size:.72rem;color:var(--ink-3)}
.bidline{display:flex;justify-content:space-between;gap:var(--s3);padding:5px 12px;
  font-family:var(--num);font-weight:700;font-size:.88rem;font-variant-numeric:tabular-nums}
.bidline+.bidline{border-top:1px solid var(--line)}
.bidline .pick{color:var(--ink-2)}
.bidline .amt{font-weight:600}
.bidline.hot{background:var(--warn-bg)}
.bidline.hot .pick,.bidline.hot .amt{color:var(--warn)}
.bidempty{padding:10px 12px;font-size:.8rem;color:var(--ink-3)}

@media(prefers-reduced-motion:reduce){*{transition:none!important}}
/* The desktop-site band. Every column has to be on screen at once here — that
   is the whole reason someone switches a phone to desktop mode — so the type
   and the padding come down rather than the table being cut in half. */
@media(min-width:861px) and (max-width:1200px){
  table{font-size:.8rem}
  th{padding:7px 8px;font-size:.58rem}
  td{padding:8px}
  main{padding:var(--s4)}
  h1{font-size:1.2rem}
}

/* On a phone the table stays a table and stays its own size. Shrinking it to
   fit — 9px type, 2px of padding, columns free to wrap — did put every column
   on screen, but small enough that a figure had to be squinted at and a note
   came out as a column of broken phrases. A table is read to take a decision
   off, so it keeps the size it has at the desk and the screen moves under it
   instead.

   Cells are pinned to one line for that to hold: allowed to wrap, the browser
   settles the table at its min-content width — which is the shrinking again,
   arrived at from the other direction. */
@media(max-width:640px){
  /* The page keeps its margins. This has to live down here with the rest of
     the phone rules: the base main{} sits further down the sheet than the
     tablet block, so a copy of this up there never applied at all. */
  main{padding:var(--s4)}
  .scroll th,.scroll td{white-space:nowrap}

  /* A native date control has an intrinsic minimum width that a percentage
     cannot argue it out of. Sharing a row with the name box at 390px, it hung
     off the side of the card and off the screen. On a phone every filter field
     takes a row of its own and the buttons share the one underneath. The
     importants reach the widths the bets filter sets inline, which a class
     cannot otherwise beat. */
  .reqfilter > div,.filters > div{flex:1 1 100% !important;min-width:0 !important;
    width:auto !important}
  .reqfilter input,.filters input,.filters select{max-width:100%}
}

