/* THE TYPEFACES, SERVED FROM HERE (np52). Every page used to open with a
   stylesheet from fonts.googleapis.com — a second and third origin (the CSS,
   then fonts.gstatic.com for the files), each a DNS lookup and a TLS
   handshake from a phone in Lagos, and the stylesheet blocked the first
   paint until it came back. On a slow line that was the blank white second
   before anything showed. Now they come from this origin with everything
   else, cached for a year, and `swap` means the text draws at once in the
   phone's own font and changes face when the file lands.
   Archivo is ONE variable file for every weight (the old link asked for six
   separate ones); latin-ext carries the ₦ sign, so it is the second file
   every screen uses. IBM Plex Mono is the receipt's font — the 32 columns
   are measured in it — and only a screen that uses it downloads it.
   Both are SIL Open Font Licence: fonts/OFL-*.txt. betlist.css is the one
   sheet all four apps load, which is why they are declared here. */
@font-face{font-family:'Archivo';font-style:normal;font-display:swap;font-weight:100 900;
  src:url(fonts/archivo-latin-wght-normal.woff2) format('woff2-variations'),url(fonts/archivo-latin-wght-normal.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Archivo';font-style:normal;font-display:swap;font-weight:100 900;
  src:url(fonts/archivo-latin-ext-wght-normal.woff2) format('woff2-variations'),url(fonts/archivo-latin-ext-wght-normal.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:400;
  src:url(fonts/ibm-plex-mono-latin-400-normal.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:500;
  src:url(fonts/ibm-plex-mono-latin-500-normal.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:600;
  src:url(fonts/ibm-plex-mono-latin-600-normal.woff2) format('woff2')}
/* THE SLIP'S FONT (np61): 'NP Receipt' is Iosevka Bold, subset to the
   characters a slip prints (6KB; fonts/OFL-Iosevka.txt). The owner asked for
   bigger, all-bold type for older customers, and the 32 columns cannot move —
   three ten-character selection cells per line is the whole receipt design.
   Iosevka's letters are half an em wide where IBM Plex Mono's are 0.6, so the
   same 32 columns fit the same paper at a FIFTH more font size: capitals go
   from 7.3px to 9.3px tall on a 58mm roll, in a face drawn bold. */
@font-face{font-family:'NP Receipt';font-style:normal;font-display:swap;font-weight:700;
  src:url(fonts/iosevka-receipt-700.woff2) format('woff2')}

/* THE BETLIST, STYLED ONCE — the component betlist.js renders, on all three
   screens that show it: the back office, the agent desk and the shop counter.

   TWO STYLESHEETS, ONE COMPONENT. admin.css names its surfaces --paper,
   --card, --edge, --edge2, --text, --dim, --faint; styles.css (the punter,
   agent and counter sheet) names them --bg, --s1, --s2, --line, --on, --on2,
   --on3. Neither defines the other's names — test_css_tokens.py caught four
   of the back office's own rules reaching for the punter sheet's --bg and
   --line2 once already, and an undefined var() falls back to nothing with
   nothing on screen looking broken.

   So this file does not pick a side. Every rule below is written against
   --btl-* tokens, and those are resolved on .btlroot with a fallback chain:
   the back office's name first, the counter's second, a literal last. On
   /admin --card is defined and wins; on /agent and /pos it is not, so --s1
   does. Adding a third app means adding one more link, in one place.

   A class name here is a global identifier with no compiler, and this
   codebase has been bitten three times by a reused one (CLAUDE.md's trap
   note: .slipbar, .pip, and the data-a action `quick`). Everything is
   prefixed `btl`, and every name was grepped against both stylesheets
   before it was written. */

.btlroot{
  --btl-card:  var(--card,  var(--s1,   #141417));
  --btl-card2: var(--edge2, var(--s2,   #1B1B1F));
  --btl-edge:  var(--edge,  var(--line, #26262B));
  --btl-text:  var(--text,  var(--on,   #F4F4F6));
  --btl-dim:   var(--dim,   var(--on2,  #9B9BA5));
  --btl-faint: var(--faint, var(--on3,  #6E6E78));
  color: var(--btl-text);
}

/* THE HOST'S BARE ELEMENT RULES MUST NOT REACH IN HERE, and one of them did.
   agent.css styles `table` and `td` directly — `table{min-width:560px}` and
   `td{white-space:nowrap}` — because the agent desk predates this component
   and had every right to. The result, measured on the real screen rather than
   guessed at: the money table inside an opened slip came out 560px wide in a
   300px column and printed itself straight over the match list beside it. The
   back office, whose stylesheet has no bare table rule, showed the same markup
   correctly — which is exactly the kind of difference "make the three look
   alike" is asking us to make impossible.

   So the component states every property it depends on rather than inheriting
   it. `:where()` keeps the specificity of this block at .btlroot alone, so the
   real rules below still win by document order and nothing here has to be
   fought with !important. */
.btlroot :where(table){width:100%;border-collapse:collapse;min-width:0;
  font-size:inherit;table-layout:auto}
.btlroot :where(th,td){padding:0;border:0;white-space:normal;vertical-align:middle;
  text-align:left;font-size:inherit;font-weight:inherit;color:inherit;background:transparent}

/* ---------------------------------------------------------------- head */
.btlhead{display:flex;align-items:flex-start;gap:12px;margin:0 0 14px}
.btlhead h2{margin:0;font-size:17px;font-weight:800;letter-spacing:-.02em}
.btlhead p{margin:3px 0 0;font-size:12px;color:var(--btl-dim);max-width:62ch}
.btlsp{flex:1;display:flex;justify-content:flex-end;gap:8px}

/* ------------------------------------------------------------- the tiles */
.btlgrid{display:grid;gap:9px;margin-bottom:14px;
  /* auto-fit rather than a fixed four, so the row fills at any width and a
     phone gets two up instead of four unreadable slivers. */
  grid-template-columns:repeat(auto-fit,minmax(168px,1fr))}
.btlbox{border:1px solid var(--btl-edge);border-radius:11px;padding:11px;min-width:0}
.btlbox small{display:block;font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--btl-faint);font-weight:800}
.btlbox b{display:block;font-size:20px;font-weight:800;margin-top:2px;letter-spacing:-.025em;
  font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.btlbox i{font-style:normal;font-size:11px;color:var(--btl-dim)}
/* Open exposure is the one figure on this screen a trader acts on, so it
   carries the alert border whenever there is any exposure at all. */
.btlbox.alert{border-color:var(--rd)}
.btlbox.alert b{color:var(--rd)}

/* ------------------------------------------------------------- the buckets */
.btltabs{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-bottom:10px}
/* One row since np88: Won, Lost and Void sit after a thin divider, so they
   read as "inside Settled" without a second row that only appeared after a
   tap. The row WRAPS rather than scrolling sideways: a tab off the right
   edge behind a hidden scrollbar is a filter nobody finds (np67's lesson). */
.btltabsep{width:1px;align-self:stretch;margin:4px 2px;background:var(--btl-edge)}
.btltab{flex:none;display:flex;align-items:center;gap:6px;padding:7px 13px;border-radius:999px;
  background:var(--btl-card);border:1px solid var(--btl-edge);color:var(--btl-dim);
  font:inherit;font-size:12px;font-weight:700;cursor:pointer}
.btltab[aria-pressed="true"]{background:var(--or);border-color:var(--or);color:#fff}
.btlcount{font-size:10.5px;font-weight:800;padding:1px 6px;border-radius:999px;
  background:var(--btl-card2);color:var(--btl-dim);font-variant-numeric:tabular-nums}
.btltab[aria-pressed="true"] .btlcount{background:rgba(255,255,255,.22);color:#fff}

/* The device and address column — back office only (see betlist.js
   accessCell). A device shared by accounts is the strong signal and gets the
   flag; an address only ever gets a count. */
.btlmono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;letter-spacing:.02em}
.btlsub2{font-size:10.5px;color:var(--btl-faint);margin-top:2px;white-space:nowrap}
.btlflag{display:inline-block;font-size:10px;font-weight:800;padding:1px 6px;border-radius:999px;
  background:var(--rd);color:#fff;vertical-align:1px}

/* ------------------------------------------------------------- the filters */
.btlbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:11px}
.btlbar input,.btlbar select{background:var(--btl-card);border:1px solid var(--btl-edge);
  color:var(--btl-text);border-radius:9px;padding:7px 10px;font:inherit;font-size:12.5px;min-height:38px}
.btlbar input#btl_q{min-width:190px;flex:1 1 190px;max-width:320px}
.btlbar label{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--btl-dim)}
.btlbtn{background:var(--btl-card);border:1px solid var(--btl-edge);color:var(--btl-text);
  border-radius:9px;padding:7px 13px;font:inherit;font-size:12px;font-weight:700;
  min-height:38px;cursor:pointer}
.btlbtn:disabled{opacity:.45;cursor:default}
/* Play these numbers again (np83) — the punter's own slip, opened. */
.btlreplay{margin-top:12px;width:100%;min-height:44px;border-radius:10px;
  border:1px solid var(--or,#FF6A00);background:var(--or,#FF6A00);color:#fff;
  font:inherit;font-size:14px;font-weight:800;cursor:pointer}
.btlreplay:active:not([disabled]){transform:scale(.98)}
.btlreplay:disabled{opacity:.5;cursor:default}
.btlshare{margin-top:8px;width:100%;min-height:44px;border-radius:10px;
  border:1px solid #25D366;background:transparent;color:#25D366;
  font:inherit;font-size:14px;font-weight:800;cursor:pointer}
.btlshare:disabled{opacity:.5;cursor:default}
/* The Search button (np76) — the one toolbar action, so it reads as one. */
.btlbtn.btlgo{background:var(--or,#FF6A00);border-color:var(--or,#FF6A00);color:#fff}

/* ------------------------------------------------------------- the table */
.btlwrap{border:1px solid var(--btl-edge);border-radius:11px;overflow:auto;max-height:60vh;
  container-type:inline-size}
.btltable{width:100%;border-collapse:collapse;font-size:12.5px;
  /* Ten columns do not fit a phone and must not be allowed to try: the
     wrapper scrolls sideways on its own rather than the page doing it. */
  min-width:820px}
.btltable th{text-align:left;font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--btl-faint);font-weight:800;padding:9px 11px;background:var(--btl-card2);
  border-bottom:1px solid var(--btl-edge);position:sticky;top:0;z-index:1;white-space:nowrap}
.btltable td{padding:9px 11px;border-bottom:1px solid var(--btl-card2);
  font-variant-numeric:tabular-nums;vertical-align:middle}
.btltable td.r,.btltable th.r{text-align:right}
.btltable tr:last-child td{border-bottom:0}

.btlrow.tap{cursor:pointer}
.btlrow.tap:hover{background:var(--btl-card2)}
/* The clicked row and its panel read as one object: the row loses its
   bottom border into the panel, and both carry the same tint. */
.btlrow.on{background:color-mix(in srgb,var(--or) 9%,transparent)}
.btlrow.on td{border-bottom-color:transparent;font-weight:600}
.btlslotrow td{border-bottom:0}
.btlslot:not(:empty){border-bottom:1px solid var(--btl-card2);
  background:color-mix(in srgb,var(--or) 4%,transparent);padding:14px 11px}
/* THE PANEL IS AS WIDE AS THE BOX YOU CAN SEE, not as the table (np88).
   The table is wider than the box on a laptop (twelve columns), and the
   panel spanned all of it, so every figure in its right-hand column sat
   off the right edge. 100cqw is the box (.btlwrap is the container), and
   sticky keeps the panel in view if the table is scrolled sideways. */
.btlslot{position:sticky;left:0;width:100cqw;max-width:100%;box-sizing:border-box}
.btldmoney{min-width:0}

.btlbadge{display:inline-block;padding:2px 7px;border-radius:999px;font-size:9.5px;
  font-weight:900;letter-spacing:.05em;text-transform:uppercase;white-space:nowrap}
.btlbadge.ok{background:rgba(47,191,113,.16);color:var(--gr)}
.btlbadge.no{background:rgba(240,80,77,.15);color:var(--rd)}
.btlbadge.wait{background:rgba(255,122,26,.16);color:var(--or)}
.btlbadge.grey{background:var(--btl-card2);color:var(--btl-dim)}
.btlpaid{color:var(--gr)}
.btlnote{margin:0 0 9px;font-size:11.5px;color:var(--btl-dim)}
.btldim2{display:inline-block;margin-left:6px;font-size:10.5px;color:var(--btl-faint)}

.btlempty,.btlwait{padding:22px 12px;text-align:center;font-size:12.5px;color:var(--btl-dim)}
.btlerr{border:1px solid var(--rd);border-radius:9px;padding:9px 12px;font-size:12px;color:var(--rd)}

.btlpage{display:flex;align-items:center;gap:9px;margin-top:9px;font-size:11.5px;
  color:var(--btl-dim);flex-wrap:wrap}
/* The pager above the table (np77) sits between the filters and the rows. */
.btlpage.btlpagetop{margin:0 0 9px}
.btlpage label{display:flex;align-items:center;gap:6px}
.btlpage select{background:var(--btl-card);border:1px solid var(--btl-edge);
  color:var(--btl-text);border-radius:9px;padding:5px 8px;font:inherit;font-size:12px;min-height:34px}
.posapp .btlpage select{min-height:48px}

/* ------------------------------------------------------- the opened slip */
/* Money on the left, the matches on the right — and one column under
   1100px, because eleven fixtures in a 300px-wide table is unreadable and
   none of these three screens is always on a desk. */
.btldetail{display:grid;grid-template-columns:minmax(230px,300px) 1fr;gap:18px;align-items:start}
/* THE MATCHES FIRST when the panel is one column (np88): the owner opens a
   slip to see its events, and under 1100px they sat below the money,
   off the bottom of the table's box. Side by side, they are already in
   view on the right. */
@media (max-width:1100px){ .btldetail{grid-template-columns:1fr} .btldevents{order:-1} }
.btldetail h4{margin:0 0 2px;font-size:12.5px;font-weight:800}

.btlmini{width:100%;border-collapse:collapse;font-size:12px}
.btlmini td{padding:4px 0;border-bottom:1px solid var(--btl-card2)}
.btlmini td.r{text-align:right;font-variant-numeric:tabular-nums}
.btlmini tr:last-child td{border-bottom:0}

/* The per-event table sits INSIDE a row of the table above it, so it must
   not inherit the sticky header that would then float over the page. */
.btltab2{width:100%;border-collapse:collapse;font-size:12px}
.btltab2 th{text-align:left;font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--btl-faint);font-weight:800;padding:6px 9px;position:static;background:transparent;
  border-bottom:1px solid var(--btl-edge)}
.btltab2 td{padding:6px 9px;border-bottom:1px solid var(--btl-card2)}
.btltab2 td.r,.btltab2 th.r{text-align:right}
/* A missed leg is the one thing somebody looking at a lost slip is actually
   looking for. */
.btltab2 tr.btlout td{color:var(--rd)}
.btlbanker{display:inline-block;margin-left:6px;padding:1px 6px;border-radius:999px;
  font-size:9px;font-weight:800;letter-spacing:.06em;
  background:color-mix(in srgb,var(--or) 18%,transparent);color:var(--or)}
/* The panel mark, in the one colour neither verdict uses. See the note in
   betlist.js: this says who decided the leg, never what they decided. */
.btlpanel{display:inline-block;margin-left:6px;padding:1px 6px;border-radius:999px;
  font-size:9px;font-weight:800;letter-spacing:.06em;
  background:rgba(79,163,247,.16);color:#4FA3F7}

/* A DRAWN BALL, on the back office's expanded 5/90 slip.
   Square-ish rather than round, deliberately: the punter app's .dnum is the
   round one a customer sees, and these two screens are read side by side at
   a counter often enough that making them identical would invite "that is
   not what my slip says". Same PADDING though — two digits, always, so a
   column of numbers lines up and 07 never reads as a different kind of
   thing from 21. */
.btlball{display:inline-block;min-width:24px;margin-right:4px;padding:2px 5px;
  border-radius:6px;text-align:center;font-weight:800;font-size:12px;
  font-variant-numeric:tabular-nums;
  background:var(--btl-card2);border:1px solid var(--btl-edge);color:var(--btl-dim)}
/* A ball the punter had. The only colour on this table, because it is the
   only thing anybody is looking for. */
.btlball.on{background:color-mix(in srgb,var(--gr) 20%,transparent);
  border-color:var(--gr);color:var(--gr)}

/* A counter is a touchscreen and the back office is a mouse. 48px is the
   floor styles.css already sets for every control inside .posapp; applying
   it here keeps the filter row tappable on the till without making the back
   office's own controls oversized. */
.posapp .btlbar input,.posapp .btlbar select,.posapp .btlbtn,
.posapp .btltab{min-height:48px}

@media (max-width:560px){
  .btlhead{flex-wrap:wrap}
  .btlgrid{grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}
  .btlbar input#btl_q{max-width:none}
}

/* ======================================================================
   THE MONEY MOVEMENTS TABLE — every movement with the balance before and
   after it, on the counter's own report and in the back office's financial
   report.

   IT LIVES IN THIS FILE FOR THE REASON THE BETLIST DOES. It was written in
   styles.css first, which is the punter/agent/counter sheet — and
   `admin.html` loads admin.css and this file and NOTHING ELSE. So the back
   office's financial report would have rendered every one of these rules
   as nothing: an unstyled table on a desktop, and no stacked layout at all
   on a phone. That is `.kpis` again, the third time (CLAUDE.md's trap
   note), and the class fence did not catch it because its list is curated
   and these names were new. They are on the list now.

   SO IT DOES NOT PICK A SIDE, exactly as the betlist above does not:
   every rule is written against `--mny-*`, resolved with a fallback chain
   — the back office's token first, the counter's second, a literal last.
   On /admin --card is defined and wins; on /pos it is not, so --s1 does.
   ====================================================================== */
/* The opened profile's statement, under the list rather than inside it —
   see the note in admin.js's financeHtml for the bug that caused. */
.finpanel{margin:16px 0 0;padding:14px;border:1px solid var(--mny-edge);
  border-radius:12px;background:var(--mny-card)}
.finpanel .mgroup:last-child{margin-bottom:0}

.mgroup,.finpanel{
  --mny-card:  var(--card,  var(--s1,   #141417));
  --mny-card2: var(--edge2, var(--s2,   #1B1B1F));
  --mny-edge:  var(--edge,  var(--line, #26262B));
  --mny-text:  var(--text,  var(--on,   #F4F4F6));
  --mny-dim:   var(--dim,   var(--on2,  #9B9BA5));
  --mny-faint: var(--faint, var(--on3,  #6E6E78));
  color: var(--mny-text);
}

/* The back office styles bare `table`/`td` for its own .dt tables, so this
   one states what it depends on rather than inheriting it — the same
   lesson the betlist learned from agent.css's `table{min-width:560px}`
   printing itself over the match list beside it. */
.mtab :where(th,td){border:0;background:transparent}

/* THE MONEY MOVEMENTS REPORT.

   THE OWNER ASKED FOR TWO THINGS HERE: that it be responsive "just as we
   have the betslip screen", and that it show every movement with the
   balance before and after each one. The second was a server question (a
   shop has two ledgers and the report was showing one — see
   reports._shop_groups); this is the first.

   ONE MARKUP, TWO LAYOUTS. Above 700px it is a real table with real
   columns, which is what a report on a 1,400px counter screen should be
   and what the betslip screen already is. Below it, every row becomes a
   stacked card and the `data-l` attribute on each cell becomes its label —
   so a cashier on a phone reads the same six figures down the screen
   instead of dragging a table sideways. The alternative, a horizontally
   scrolling table, is what the betslip does inside its own box and is
   fine for a dense grid nobody reads on a phone; a cash report IS read on
   a phone, by an agent checking a shop from the road. */
.mgroup{margin:0 0 18px}
.mghead h3{margin:0;font-size:13.5px;font-weight:800;color:var(--mny-text)}
.mghead h3 span{font-weight:600;color:var(--mny-faint);font-size:11.5px;margin-left:6px}
.mghead p{margin:3px 0 8px;font-size:11.2px;color:var(--mny-faint);max-width:62ch}

.mtabwrap{border:1px solid var(--mny-edge);border-radius:11px;overflow:auto}
.mtab{width:100%;border-collapse:collapse;font-size:12.5px}
.mtab th{text-align:left;font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--mny-faint);padding:8px 11px;background:var(--mny-card2);white-space:nowrap;
  border-bottom:1px solid var(--mny-edge);position:sticky;top:0;z-index:1}
.mtab td{padding:9px 11px;border-bottom:1px solid var(--mny-edge);vertical-align:top}
.mtab tr:last-child td{border-bottom:0}
.mtab td.r,.mtab th.r{text-align:right}
.mtab td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
.mtab .mref{font-size:11px;color:var(--mny-dim);font-variant-numeric:tabular-nums}
.mtab .mdim{display:block;font-size:10.4px;color:var(--mny-faint)}

@media (max-width:700px){
  /* THE SAME ROWS, STACKED. `display:block` on the parts and a label from
     `data-l` — the table's own headings, so the two layouts can never
     disagree about what a column is called. */
  .mtabwrap{border:0;border-radius:0;overflow:visible}
  .mtab thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  .mtab,.mtab tbody,.mtab tr,.mtab td{display:block;width:100%}
  .mtab tr{border:1px solid var(--mny-edge);border-radius:11px;padding:4px 2px;margin:0 0 8px}
  .mtab td{border-bottom:0;padding:5px 11px;display:flex;gap:10px;
    align-items:baseline;justify-content:space-between;text-align:right}
  .mtab td::before{content:attr(data-l);font-size:10.2px;letter-spacing:.06em;
    text-transform:uppercase;color:var(--mny-faint);text-align:left;flex:none}
  .mtab td.r{text-align:right}
  .mtab .mdim{display:inline;margin-left:6px}
  /* The first cell is the row's headline, so it reads as one rather than
     as a labelled field. */
  .mtab td:first-child{display:block;text-align:left;padding-top:8px}
  .mtab td:first-child::before{display:none}
}

/* ═══════════════════════════════════════════════════════ the bell
   One inbox, four apps — see frontend/notify.js.

   IT LIVES HERE AND NOT IN styles.css FOR A REASON THIS FILE ALREADY
   CARRIES. The money-movements table was written into styles.css and then
   used by the back office's financial report, and admin.html loads
   admin.css and betlist.css and nothing else — so every rule resolved to
   nothing, on a desktop and on a phone, and looked like a design decision.
   betlist.css is the one sheet all four apps load.

   SAME FALLBACK-CHAIN TOKENS as the betlist and the money table above, so
   the bell takes each app's own palette without any of them having to
   define a new name. --npn-* resolves through the punter sheet's names,
   then the back office's, then a literal. */
:root{
  --npn-card:  var(--card,  var(--s1,   #141417));
  --npn-card2: var(--edge2, var(--s2,   #1B1B1F));
  --npn-edge:  var(--edge,  var(--line, #26262B));
  --npn-text:  var(--text,  var(--on,   #F4F4F6));
  --npn-dim:   var(--dim,   var(--on2,  #9B9BA5));
  --npn-faint: var(--faint, var(--on3,  #6E6E78));
  --npn-accent:var(--or,    var(--acc,  #FF6A00));
  --npn-good:  var(--gr,    var(--good, #27C281));
}

/* THE MOUNT MUST BE POSITIONED, and that is not styling — it is the whole
   anchor. `.npn-panel` is position:absolute, which resolves against the
   nearest POSITIONED ancestor, so without this the sheet hangs off whatever
   happens to be relative further up: the app bar in one app, the document in
   another. This codebase has paid for a containing block once already — the
   thermal slip printed half a metre of blank paper because a transformed
   ancestor captured a position:fixed escape.

   Caught by test_no_screen_uses_a_class_its_stylesheets_never_heard_of,
   which went red on `npmount` having no rule at all. */
.npmount{position:relative;display:inline-flex;align-items:center}

.npbell{position:relative;display:inline-flex;align-items:center;
  justify-content:center;width:38px;height:38px;border-radius:11px;
  border:1px solid transparent;background:transparent;color:var(--npn-dim);
  cursor:pointer;padding:0;transition:color .14s,background .14s}
.npbell:hover{color:var(--npn-text);background:var(--npn-card2)}
/* The press state every primary control in this product got after the
   "two taps, one ticket" session: a tap must answer in the same frame. */
.npbell:active{transform:scale(.94);transition:none}
.npbell[aria-expanded="true"]{color:var(--npn-text);background:var(--npn-card2);
  border-color:var(--npn-edge)}
.npbell-dot{position:absolute;top:3px;right:2px;min-width:16px;height:16px;
  padding:0 4px;border-radius:9px;background:var(--npn-accent);color:#14100B;
  font-size:10px;font-weight:800;font-style:normal;line-height:16px;
  text-align:center;font-variant-numeric:tabular-nums}

.npn-panel{position:absolute;top:calc(100% + 8px);right:0;width:min(380px,calc(100vw - 24px));
  max-height:min(70vh,560px);display:flex;flex-direction:column;
  background:var(--npn-card);border:1px solid var(--npn-edge);border-radius:14px;
  box-shadow:0 18px 48px rgba(0,0,0,.46);z-index:400;overflow:hidden;
  text-align:left}
.npn-head{display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:10px 12px;border-bottom:1px solid var(--npn-edge)}
.npn-tabs{display:flex;gap:4px}
.npn-tabs button{border:0;background:transparent;color:var(--npn-dim);
  font-size:12.5px;font-weight:700;padding:5px 9px;border-radius:8px;cursor:pointer}
.npn-tabs button.on{background:var(--npn-card2);color:var(--npn-text)}
.npn-link{border:0;background:transparent;color:var(--npn-accent);
  font-size:11.5px;font-weight:700;cursor:pointer;padding:4px 2px}

.npn-scroll{overflow-y:auto;flex:1;min-height:0}
.npn-grp{padding:8px 0 2px}
.npn-grp h4{margin:0;padding:6px 12px 4px;font-size:10.5px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--npn-faint);font-weight:800}
.npn-grp ul{list-style:none;margin:0;padding:0}
.npn-row{display:grid;grid-template-columns:8px 1fr;gap:9px;align-items:start;
  padding:9px 12px;cursor:pointer;border-left:2px solid transparent}
.npn-row:hover{background:var(--npn-card2)}
.npn-row:active{background:var(--npn-card2);transform:none}
/* UNREAD IS A RULE AND A WEIGHT, NOT A COLOUR ALONE. Colour alone fails
   for the part of any Nigerian shop's customers who cannot tell these two
   oranges apart, and fails completely on a monochrome terminal screen. */
.npn-new{border-left-color:var(--npn-accent);background:rgba(255,106,0,.045)}
.npn-new .npn-txt b{color:var(--npn-text)}
.npn-pip{width:8px;height:8px;border-radius:50%;margin-top:5px;
  background:var(--npn-faint)}
.npn-MONEY{background:var(--npn-good)}
.npn-RESULTS{background:var(--npn-accent)}
.npn-PRODUCT{background:var(--npn-faint)}
.npn-OPS{background:#7A8CFF}
.npn-txt{display:flex;flex-direction:column;gap:2px;min-width:0}
.npn-txt b{font-size:13px;font-weight:700;color:var(--npn-dim);line-height:1.3}
.npn-body{font-size:12px;color:var(--npn-dim);line-height:1.4}
.npn-when{font-size:10.5px;color:var(--npn-faint);font-style:normal}

.npn-empty{padding:30px 16px;text-align:center;display:flex;
  flex-direction:column;gap:4px}
.npn-empty b{font-size:13.5px;color:var(--npn-text)}
.npn-empty span{font-size:12px;color:var(--npn-faint)}

.npn-set{border-top:1px solid var(--npn-edge);background:var(--npn-card2)}
.npn-set summary{padding:9px 12px;font-size:12px;font-weight:700;
  color:var(--npn-dim);cursor:pointer;list-style:none}
.npn-set summary::-webkit-details-marker{display:none}
.npn-set summary::after{content:'▾';float:right;color:var(--npn-faint)}
.npn-sw{display:grid;grid-template-columns:auto 1fr;gap:9px;align-items:start;
  padding:8px 12px;cursor:pointer}
.npn-sw input{margin-top:2px;accent-color:var(--npn-accent);width:16px;height:16px}
.npn-sw span{display:flex;flex-direction:column;gap:1px;min-width:0}
.npn-sw b{font-size:12.5px;font-weight:700;color:var(--npn-text)}
.npn-sw i{font-size:11px;font-style:normal;color:var(--npn-faint);line-height:1.35}
.npn-master{border-bottom:1px solid var(--npn-edge)}
.npn-note{margin:0;padding:4px 12px 11px;font-size:10.5px;color:var(--npn-faint);
  line-height:1.4}

/* AT PHONE WIDTH THE SHEET IS THE SCREEN. A 380px dropdown anchored to a
   bell in a 390px header hangs off the edge; this is the width every
   punter actually sees. */
@media (max-width:460px){
  .npn-panel{position:fixed;top:auto;bottom:0;left:0;right:0;width:100%;
    max-height:78vh;border-radius:16px 16px 0 0;
    padding-bottom:env(safe-area-inset-bottom,0px)}
}

@media (prefers-reduced-motion:reduce){
  .npbell,.npbell:active{transition:none;transform:none}
}

/* ======================================================================
   MY BETS ON A PHONE — the punter's own slips (betlist.js audience
   "player"). Nine columns do not fit 390px, and a sideways-scrolling table
   is a table a customer reads one column of. So below 700px each slip is a
   card: the ticket and its status on top, what it was on, then stake /
   could win / won side by side with their names over them, then when.
   Scoped to .btlplayer, so the counter and the back office keep their
   table at every width.
   ====================================================================== */
/* A customer's page scrolls as a page; a box scrolling inside it is two
   scrollbars on a phone. */
.btlplayer .btlwrap{max-height:none}
@media (max-width:700px){
  .btlplayer .btlwrap{overflow:visible;border:0;background:none}
  .btlplayer .btltable,.btlplayer .btltable tbody{display:block;min-width:0;width:auto}
  .btlplayer .btlslot{max-width:100%;overflow-x:auto}
  .btlplayer .btltabs{flex-wrap:wrap}
  .btlplayer .btltable thead{display:none}
  .btlplayer .btltable tr.btlrow{display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px 10px;
    padding:12px;margin:0 0 8px;border:1px solid var(--btl-edge);border-radius:12px;
    background:var(--btl-card)}
  .btlplayer .btltable tr.btlrow.on{border-color:var(--btl-text);margin-bottom:0;
    border-radius:12px 12px 0 0}
  .btlplayer .btltable td{display:block;padding:0;border:0}
  .btlplayer .btltable td:nth-child(1){grid-column:1 / 3;font-size:13.5px}
  .btlplayer .btltable td:nth-child(2){grid-column:3;justify-self:end}
  .btlplayer .btltable td:nth-child(3),
  .btlplayer .btltable td:nth-child(4){grid-column:1 / 4;color:var(--btl-dim)}
  .btlplayer .btltable td:nth-child(5),
  .btlplayer .btltable td:nth-child(6),
  .btlplayer .btltable td:nth-child(7){text-align:left;margin-top:4px;font-weight:700}
  .btlplayer .btltable td:nth-child(5)::before{content:'Stake'}
  .btlplayer .btltable td:nth-child(6)::before{content:'Could win'}
  .btlplayer .btltable td:nth-child(7)::before{content:'Won'}
  .btlplayer .btltable td:nth-child(5)::before,
  .btlplayer .btltable td:nth-child(6)::before,
  .btlplayer .btltable td:nth-child(7)::before{display:block;font-size:9.5px;font-weight:600;
    letter-spacing:.08em;text-transform:uppercase;color:var(--btl-faint)}
  .btlplayer .btltable td:nth-child(8){grid-column:1 / 3;font-size:11px;color:var(--btl-faint)}
  .btlplayer .btltable td:nth-child(9){grid-column:3;font-size:11px;color:var(--btl-faint);
    text-align:right}
  .btlplayer .btltable tr.btlslotrow{display:block}
  .btlplayer .btltable tr.btlslotrow td{display:block}
  .btlplayer .btltable tr.btlrow.on + tr.btlslotrow .btlslot{border:1px solid var(--btl-text);
    border-top:0;border-radius:0 0 12px 12px;margin-bottom:8px}
  .btlplayer .btlbar{flex-wrap:wrap}
  .btlplayer .btlbar input#btl_q,.btlplayer .btlbar select{flex:1 1 100%}
}

/* ── a file with a support message (frontend/attach.js) ──────────────────
   Here for the bell's reason: the punter app, the agent desk and the back
   office all show it, and this is the one sheet all of them load. Same
   fallback-chain tokens (--npn-*), prefixed `npatt`, grepped first. */
.npatt{display:flex;align-items:center;gap:10px;margin-top:8px;padding:8px;border-radius:10px;
  border:1px solid var(--npn-edge);background:var(--npn-card2);max-width:320px}
.npatt-img{width:64px;height:64px;object-fit:cover;border-radius:7px;background:var(--npn-card);flex:none}
.npatt-pdf{width:64px;height:64px;border-radius:7px;display:grid;place-items:center;flex:none;
  font-weight:900;font-size:13px;letter-spacing:.06em;color:var(--npn-accent);background:var(--npn-card);
  border:1px solid var(--npn-edge)}
.npatt-meta{display:grid;gap:2px;min-width:0;flex:1}
.npatt-meta b{font-size:13px;color:var(--npn-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.npatt-meta small{font-size:11px;color:var(--npn-dim)}
.npatt-open,.npatt-x{flex:none;border:1px solid var(--npn-edge);background:var(--npn-card);color:var(--npn-text);
  border-radius:8px;padding:7px 11px;font:inherit;font-size:12px;font-weight:700;cursor:pointer;min-height:36px}
.npatt-x{font-size:18px;line-height:1;padding:6px 11px}
.npatt-open:active,.npatt-x:active{transform:scale(.96)}
.npatt-pick{display:inline-flex;align-items:center;gap:8px;margin:2px 0 10px;padding:9px 13px;border-radius:10px;
  border:1px dashed var(--npn-edge);background:transparent;color:var(--npn-text);font:inherit;font-size:13px;
  font-weight:700;cursor:pointer;min-height:44px}
.npatt-pick:active{transform:scale(.98)}
.npatt-file{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.npatt-flag{margin-top:6px;padding:7px 9px;border-radius:8px;font-size:12px;line-height:1.45;
  border:1px solid var(--npn-accent);color:var(--npn-text);background:var(--npn-card)}
.npatt-done{margin-top:6px;font-size:12px;color:var(--npn-good);font-weight:700}
/* In the compose box, waiting for Send: dashed, because it is not sent. */
.npatt-pending{border-style:dashed;margin:2px 0 10px}
/* The punter sheet sizes no bare svg, so the icon in the pick button would
   draw at the svg's own 300x150 default. */
.npatt-pick svg{width:18px;height:18px;flex:none}

/* ======================================================================
   THE SIGNED CASH-UP (np61) — frontend/cashup.js, on the counter, the agent
   desk and the back office. Written against --cus-* tokens with the same
   fallback chain as the betlist and the money report: the back office's
   name first, the counter's and agent desk's second, a literal last.
   Every name prefixed `cus-`, grepped against all four sheets first.

   THE STAMP is the owner's word made literal — "a stamp to confirm both
   shop and agents agrees". An ink-coloured double border, the DATE as the
   biggest thing in it, both names under it. Tilted two degrees, the way a
   hand-pressed stamp lands; no animation.
   ====================================================================== */
.cus-box,.cus-card,.cus-list,.cus-remit{
  --cus-card:  var(--card,  var(--s1,   #141417));
  --cus-edge:  var(--edge,  var(--line, #26262B));
  --cus-text:  var(--text,  var(--on,   #F4F4F6));
  --cus-dim:   var(--dim,   var(--on2,  #9B9BA5));
  --cus-faint: var(--faint, var(--on3,  #6E6E78));
  --cus-ink:   var(--gr, #1F9D57);
  --cus-warn:  var(--ye, #C98A00);
  --cus-bad:   var(--rd, #D9433F);
  --cus-acc:   var(--or, #FF6A00);
  color: var(--cus-text);
}
.cus-box,.cus-card{border:1px solid var(--cus-edge);border-radius:12px;background:var(--cus-card);
  padding:12px 13px;margin:9px 0;display:flex;flex-direction:column;gap:5px}
.cus-list{display:flex;flex-direction:column;gap:0}
.cus-head{font-size:12px;color:var(--cus-dim);margin-bottom:3px}
.cus-row{display:flex;justify-content:space-between;align-items:baseline;gap:12px;font-size:13.5px}
.cus-row span{color:var(--cus-dim)}
.cus-row b{font-variant-numeric:tabular-nums;font-weight:700;text-align:right}
.cus-row.cus-plus b{color:var(--cus-ink)}
.cus-row.cus-minus b,.cus-row.cus-short b{color:var(--cus-bad)}
.cus-row.cus-zero b{color:var(--cus-ink)}
.cus-row.cus-total{border-top:1px solid var(--cus-edge);padding-top:7px;margin-top:3px;font-size:16px}
.cus-row.cus-total b{color:var(--cus-warn)}
.cus-lab{font-size:12px;color:var(--cus-dim);margin-top:4px}
.cus-in{width:100%;box-sizing:border-box;min-height:44px;padding:9px 11px;border-radius:9px;
  border:1px solid var(--cus-edge);background:transparent;color:var(--cus-text);font:inherit;font-size:15px}
.cus-btn{min-height:46px;padding:10px 14px;border-radius:10px;border:0;cursor:pointer;
  background:var(--cus-acc);color:#fff;font:inherit;font-weight:800;font-size:14px}
.cus-btn.ghost{background:transparent;color:var(--cus-text);border:1px solid var(--cus-edge)}
.cus-btn:active:not([disabled]){transform:scale(.98);filter:brightness(.92)}
.cus-btn[disabled]{opacity:.55;cursor:default}
.cus-acts{display:flex;gap:8px;flex-wrap:wrap}
.cus-acts .cus-btn{flex:1 1 140px}
.cus-note{margin:2px 0 0;font-size:12px;line-height:1.45;color:var(--cus-faint)}
.cus-msg{display:none;font-size:12.5px;color:var(--cus-bad)}
.cus-left{font-size:12.5px;font-weight:700;color:var(--cus-warn)}
.cus-left:empty{display:none}
.cus-top{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.cus-top b{font-size:13.5px;letter-spacing:.02em}
.cus-top span{font-size:12.5px;color:var(--cus-dim)}
.cus-pill{margin-left:auto;font-style:normal;font-size:11px;font-weight:800;letter-spacing:.05em;
  text-transform:uppercase;padding:2px 8px;border-radius:999px;border:1px solid var(--cus-edge);color:var(--cus-dim)}
.cus-agreed .cus-pill{color:var(--cus-ink);border-color:var(--cus-ink)}
.cus-pending{border-color:var(--cus-warn)}
.cus-pending .cus-pill{color:var(--cus-warn);border-color:var(--cus-warn)}
.cus-plain{opacity:.92}
.cus-rejected .cus-pill{color:var(--cus-bad);border-color:var(--cus-bad)}
.cus-done{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:14px;align-items:center}
.cus-done > div:first-child{display:flex;flex-direction:column;gap:4px;min-width:0}
@media (max-width:520px){.cus-done{grid-template-columns:minmax(0,1fr)}.cus-stamp{justify-self:center}}
.cus-stamp{border:3px double var(--cus-ink);color:var(--cus-ink);border-radius:8px;padding:7px 11px 8px;
  text-align:center;transform:rotate(-2deg);min-width:150px;max-width:100%;box-sizing:border-box;
  font-family:'NP Receipt','IBM Plex Mono',ui-monospace,monospace;font-weight:700;line-height:1.25}
.cus-stamp-top{font-size:11px;letter-spacing:.14em}
.cus-stamp-date{font-size:21px;letter-spacing:.04em;margin:2px 0}
.cus-stamp-time{font-size:11px;opacity:.85}
.cus-stamp-sig{display:flex;justify-content:space-between;gap:8px;font-size:11px;border-top:1px dashed var(--cus-ink);
  margin-top:4px;padding-top:3px;text-align:left}
.cus-stamp-sig b{font-weight:700;overflow-wrap:anywhere;text-align:right}
/* THE WEEK A CASH-UP IS FOR (np102): the label both sides picked, on the
   card's top line and in the stamp, and what the period's tickets were week
   by week beneath it. Prefixed `cus-` like the rest, grepped first. */
.cus-wk{font-size:12px;font-weight:800;letter-spacing:.03em;color:var(--cus-acc);
  border:1px solid var(--cus-acc);border-radius:999px;padding:1px 8px}
.cus-stamp-wk{font-size:13px;letter-spacing:.1em;border-top:1px dashed var(--cus-ink);
  margin-top:4px;padding-top:3px}
/* THE REMITTANCE STAMP (np103): an agent's payment to head office, stamped
   like a cash-up. `.cus-remit` is the row of them and carries the tokens
   (the cash-up boxes carry their own); a part payment wears the warning
   colour all through by re-pointing the one ink token. Grepped first. */
.cus-remit{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-start;margin:6px 0 8px}
.cus-stamp.cus-part{--cus-ink:var(--cus-warn)}
.cus-stamp-amt{font-size:12px;margin-top:2px;overflow-wrap:anywhere}
.cus-stamp-wk small{display:block;font-size:10px;letter-spacing:.04em;opacity:.85;font-weight:700}
.cus-stamp-owe{font-size:11px;letter-spacing:.08em;margin-top:3px}
.cus-wks{display:flex;flex-direction:column;gap:3px;border-top:1px dashed var(--cus-edge);
  margin-top:3px;padding-top:6px}
.cus-wks .cus-row{font-size:12.5px}
.cus-mix{margin:2px 0 0;font-size:12px;line-height:1.45;font-weight:700;color:var(--cus-warn)}
.cus-sel{width:100%;box-sizing:border-box;min-height:44px;padding:9px 11px;border-radius:9px;
  border:1px solid var(--cus-edge);background:var(--cus-card);color:var(--cus-text);font:inherit;font-size:15px}

/* ==========================================================================
   TRANSACTIONS AS A TABLE (np78) — frontend/txlist.js, on the punter app,
   the counter and the agent desk. Prefixed `txl-`: `.tx` is already the
   punter app's menu text, and a class name is a global identifier.
   ========================================================================== */
.txl{
  --txl-card:  var(--card,  var(--s1,   #141417));
  --txl-card2: var(--edge2, var(--s2,   #1B1B1F));
  --txl-edge:  var(--edge,  var(--line, #26262B));
  --txl-text:  var(--text,  var(--on,   #F4F4F6));
  --txl-dim:   var(--dim,   var(--on2,  #9B9BA5));
  --txl-faint: var(--faint, var(--on3,  #6E6E78));
  color:var(--txl-text);display:flex;flex-direction:column;gap:10px;font-variant-numeric:tabular-nums;
  margin-top:12px}
.txl table,.txl td,.txl th{min-width:0;white-space:normal}
.txl-head{display:flex;align-items:flex-start;gap:10px;flex-wrap:wrap}
.txl-head>div{flex:1 1 220px}
.txl-head h2{margin:0;font-size:18px;font-weight:800}
.txl-head p{margin:3px 0 0;font-size:12.5px;color:var(--txl-dim)}
.txl-books{display:flex;gap:4px;padding:4px;border-radius:12px;background:var(--txl-card)}
.txl-books button{flex:1;min-height:44px;border:0;border-radius:9px;background:transparent;
  color:var(--txl-dim);font:inherit;font-size:13.5px;font-weight:800;cursor:pointer}
.txl-books button.on{background:var(--or,#FF6A00);color:#fff}
.txl-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px}
.txl-tile{padding:12px;border-radius:12px;background:var(--txl-card);border:1px solid var(--txl-edge);
  display:flex;flex-direction:column;gap:3px}
.txl-tile small,.txl-sum small{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--txl-dim);font-weight:800}
.txl-tile b{font-size:21px;font-weight:800}
.txl-tile.bonus b{color:var(--ye,#FFC400)}
.txl-tile i{font-style:normal;font-size:11.5px;color:var(--txl-dim)}
.txl-sumh{font-size:11px;font-weight:800;color:var(--txl-dim);letter-spacing:.08em;text-transform:uppercase}
.txl-sum{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;padding:9px 11px;
  border-radius:10px;background:var(--txl-card)}
.txl-sum span{display:flex;flex-direction:column;gap:2px;min-width:0}
.txl-sum b{font-size:13px;overflow-wrap:anywhere}
.txl-in{color:var(--gr,#2FBF71)}
.txl-note{margin:0;font-size:12px;color:var(--txl-dim)}
.txl-chips{display:flex;gap:6px;overflow-x:auto;padding-bottom:2px}
.txl-chips button{flex:none;min-height:40px;padding:0 13px;border-radius:999px;border:1px solid var(--txl-edge);
  background:var(--txl-card);color:var(--txl-dim);font:inherit;font-size:12.5px;font-weight:700;cursor:pointer}
.txl-chips button[aria-pressed="true"]{background:var(--or,#FF6A00);border-color:var(--or,#FF6A00);color:#fff}
.txl-bar{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end}
.txl-f{display:flex;flex-direction:column;gap:4px;font-size:10.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--txl-dim);font-weight:800;flex:1 1 140px;min-width:0}
.txl-f input,.txl-f select,.txl-page select{background:var(--txl-card);border:1px solid var(--txl-edge);
  color:var(--txl-text);border-radius:10px;padding:0 10px;font:inherit;font-size:14px;font-weight:600;
  letter-spacing:0;text-transform:none;min-height:44px;width:100%;box-sizing:border-box}
.txl-page select{width:auto;min-height:36px;font-size:12.5px}
.txl-btn{background:var(--txl-card);border:1px solid var(--txl-edge);color:var(--txl-text);border-radius:10px;
  padding:0 14px;font:inherit;font-size:13px;font-weight:800;min-height:44px;cursor:pointer}
.txl-btn:disabled{opacity:.45;cursor:default}
.txl-btn.txl-go{background:var(--or,#FF6A00);border-color:var(--or,#FF6A00);color:#fff}
.txl-page{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;color:var(--txl-dim)}
.txl-page label{display:flex;align-items:center;gap:6px}
.txl-page .txl-btn{min-height:36px}
.txl-sp{flex:1}
.txl-err{border:1px solid var(--rd,#F0504D);border-radius:10px;padding:9px 12px;font-size:12.5px;color:var(--rd,#F0504D)}
.txl-wrap{border:1px solid var(--txl-edge);border-radius:12px;overflow:auto}
.txl-table{width:100%;border-collapse:collapse;font-size:13px}
.txl-table th{text-align:left;font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--txl-faint);
  font-weight:800;padding:9px 10px;background:var(--txl-card2);border-bottom:1px solid var(--txl-edge);white-space:nowrap}
.txl-table td{padding:10px;border-bottom:1px solid var(--txl-card2);vertical-align:top}
.txl-table .r{text-align:right;white-space:nowrap}
.txl-row{cursor:pointer}
.txl-row:hover,.txl-row.on{background:var(--txl-card)}
.txl-when b,.txl-type b{display:block;font-weight:700}
.txl-when span,.txl-dim{display:block;font-size:11.5px;color:var(--txl-dim);overflow-wrap:anywhere}
/* np120: a winning ticket on a shop's balance — moves it by nothing, and
   says in green where its winnings went. Greped first: `txl-win` is new. */
.txl-win{display:block;margin-top:2px;font-size:12px;font-weight:700;color:var(--gr,#2FBF71);overflow-wrap:anywhere}
.txl td.txl-when{white-space:nowrap}
.txl td.txl-ref{white-space:nowrap}
.txl-refsub{display:none}
.txl-hmk{display:none}
.txl-dim2{color:var(--txl-dim)}
.txl-amt{font-weight:800}
.txl-ref{font-size:12px;color:var(--txl-dim);overflow-wrap:anywhere}
.txl-chip{display:inline-block;font-size:9px;letter-spacing:.08em;font-weight:800;color:var(--ye,#FFC400);
  border:1px solid var(--ye,#FFC400);border-radius:999px;padding:1px 6px;vertical-align:1px}
/* np98: which of an agent's balances a row moved — a word, so a shop's
   row and his own trading balance's row are never told apart by colour. */
.txl-bal{display:block;margin-top:2px;font-size:11px;font-weight:800;letter-spacing:.02em;
  color:var(--txl-faint);overflow-wrap:anywhere}
.txl-open td{background:var(--txl-card);padding:8px 10px 12px}
.txl-open dl{margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px}
.txl-open dt{font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--txl-faint);font-weight:800}
.txl-open dd{margin:2px 0 0;font-size:13px;overflow-wrap:anywhere}
.txl-empty{text-align:center;padding:22px 10px !important;color:var(--txl-dim)}
/* TICKETS (np91): a result is a word and a colour, never the colour alone. */
.txl-st{display:inline-block;font-size:11px;font-weight:800;letter-spacing:.04em;border-radius:999px;
  padding:2px 8px;border:1px solid var(--txl-edge);color:var(--txl-dim);white-space:nowrap}
.txl-st-won{color:var(--gr,#2FBF71);border-color:var(--gr,#2FBF71)}
.txl-st-lost{color:var(--txl-dim)}
.txl-st-void{color:var(--ye,#FFC400);border-color:var(--ye,#FFC400)}
.txl-st-open{color:var(--txl-text)}
.txl-st-pend{color:var(--or,#FF6A00);border-color:var(--or,#FF6A00)}
.txl-table tfoot td{padding:10px;background:var(--txl-card2);font-size:12.5px}
/* A PHONE: the reference column folds into the type cell's second line,
   so Date, Type, Amount, Before and After fit without a sideways scroll. */
@media (max-width:640px){
  .txl-table th.txl-ref,.txl-table td.txl-ref{display:none}
  .txl-refsub{display:block}
  .txl-bar .txl-go{flex:1 1 100%}
  /* Five columns on a phone: the mark moves to the heading, the columns
     are fixed, and a long reference is cut rather than widening the row. */
  .txl-mk{display:none}
  .txl-hmk{display:inline}
  .txl-table{table-layout:fixed;font-size:11.5px}
  .txl-table th.txl-c-date{width:40px}
  .txl-table th.txl-c-amt{width:88px}
  .txl-table th.txl-c-bef{width:80px}
  .txl-table th.txl-c-aft{width:86px}
  .txl-table td.r{font-size:11px;letter-spacing:-.01em}
  .txl-table td,.txl-table th{padding:9px 4px}
  .txl-type .txl-dim{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .txl-table{font-size:12px}
  .txl-table td,.txl-table th{padding:9px 6px}
  .txl-sum{grid-template-columns:repeat(2,minmax(0,1fr))}
}
.posapp .txl-btn,.posapp .txl-f input,.posapp .txl-f select,.posapp .txl-chips button{min-height:48px}
/* The counter's customer statement: the PIN box and the printed preview. */
.txl-pin{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end}
