/* GENERATED from Server/templates/admin.html by android-player/tools/sync_admin_ui.py — do not edit */
:root{
  --sb:env(safe-area-inset-bottom,0px);
  --sl:env(safe-area-inset-left,0px);
  --sr:env(safe-area-inset-right,0px);
  --g:#9BE36A; --r:#c31111; --bg:#141414; --s1:#111; --s2:#1a1a1a; --s3:#222;
  --bd:rgba(255,255,255,.09); --mu:rgba(255,255,255,.38);
  --nav:48px;
  /* Responsive defaults — overridden by setupResponsive() once JS
     runs. The fallbacks here ensure CSS that references these vars
     works even before JS executes (e.g. flash-of-unsized-content). */
  --vw: 100vw;
  --vh: 100vh;
  --screen-w: 100vw;
  --screen-h: 100vh;
  --aspect: 1.78;
  --is-landscape: 1;
  --scale: 0.5;
}

/* ══ ADAPTIVE TIER TWEAKS ══════════════════════════════════════════════
   data-tier is set on <html> by setupResponsive() based on actual
   viewport width. Use it to refine the layout beyond what media
   queries / clamp() express alone. */

/* Phone: tight spacing — every pixel counts on a 5" screen */
html[data-tier="phone"] .arow{ gap: 3px; }
html[data-tier="phone"] .ab{ padding: 6px 2px; min-height: 38px; font-size: 11px; }

/* Tablet: roomy default — what the existing rules target. No
   override needed; rules below are the baseline that other tiers
   adjust around. */

/* Desktop: bigger touch targets and slightly larger labels — the
   admin on an operator's PC monitor benefits from extra breathing
   room since they're reading at arm's length, not finger's reach. */
html[data-tier="desktop"] .ls-right .ab{ font-size: 13px; min-height: 52px; }
html[data-tier="desktop"] .ls-right .arow.ar-tall .ab{ min-height: 64px; }

/* TV / wall-mounted display: tap targets sized for finger-on-glass
   from a kiosk stand, labels readable from across the room. */
html[data-tier="tv"] .ls-right .ab{ font-size: 15px; min-height: 60px; padding: 12px 6px; }
html[data-tier="tv"] .ls-right .arow.ar-tall .ab{ min-height: 76px; }
html[data-tier="tv"] .ls-right .ab.ab-multiline{ font-size: 13px; }
html[data-tier="tv"] .ls-right .ab.fr.ab-multiline,
html[data-tier="tv"] .ls-right .ab.dk.ab-multiline{ font-size: 17px; }

/* iOS Safari belt-and-braces: where the existing rules use 100vh
   or 100dvh, also accept --vh (set by JS to visualViewport.height)
   as a fallback for cases where dvh isn't supported. The dvh rules
   still apply for browsers that have it; --vh covers older WebViews
   and embedded contexts (e.g. PWA "install" without dvh support). */
@supports not (height: 100dvh) {
  html[data-tier]{
    /* Use the JS-measured --vh when dvh isn't available. The
       fallback chain: var(--vh) → 100vh literal. */
    height: var(--vh, 100vh);
  }
}
*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%;overflow:hidden;background:var(--bg);color:#fff;
          font-family:'Rajdhani',sans-serif;}

/* ══ ROOT LAYOUT ══════════════════════════════════════════════════════════ */
/* Portrait: topbar + page + navbar stacked */
.root{
  display:grid;
  grid-template-rows:auto 1fr var(--nav);
  height:100dvh;
}

/* Landscape columns hidden in portrait */
.ls-scores,.ls-state,.ls-btns,.ls-scores-row{ display:none; }
.ls-card{ display:none; }
/* Multiplayer in landscape: swap the 2-player columns for the mp rows (#lsMpScore
   is populated by renderMpScore alongside #mpScore; .mp-mode set in render()). */
#lsMpScore{ display:none; }
/* Hide ONLY the 2-player scoreboard column; KEEP .ls-right (tabs + pot/foul/switch
   controls) so the operator can still score the multiplayer match in landscape. */
.ls-card.mp-mode > .ls-left{ display:none !important; }
.ls-card.mp-mode > #lsMpScore{
  display:flex; flex-direction:column; gap:12px;
  flex:1 1 0; min-width:0; padding:24px; box-sizing:border-box;
  justify-content:center; align-self:stretch;
  /* v5.10.319 — the zoom is set by _fitLsMpScore() at render time. It was a
     flat 1.3, which fits two or three players but pushes the DETECTOR strip
     (docked in last) off the bottom of a Team Doubles board — four player
     rows plus two team headers. Operator, RTX table 2 2026-09-18: 'the
     doubles admin page doesn't fit, detector balls at bottom is hidden'. */
  zoom:1.3;
  overflow-y:auto;   /* last-resort: reachable even if the zoom floor is hit */
}
/* Practice in landscape: swap the 2-player columns for the dedicated practice
   panel (#lsPracticePanel is populated by renderPracticePanel alongside the
   portrait #practicePanel; .practice-mode set in render()). Same idiom as mp. */
#lsPracticePanel{ display:none; }
/* Hide ONLY the 2-player scoreboard column; KEEP .ls-right (control/match/settings
   tabs + controls) so the operator can still use them in practice landscape. */
.ls-card.practice-mode > .ls-left{ display:none !important; }
.ls-card.practice-mode > #lsPracticePanel{
  display:flex; flex-direction:column; gap:16px;
  flex:1 1 0; min-width:0; padding:24px; box-sizing:border-box;
  justify-content:center; align-self:stretch;
  zoom:1.3;   /* scale up for landscape (fits beside the tabs/controls) */
}
/* Blue Ball Shootout panel — one renderer (renderShootout) feeds the portrait
   #shootoutPanel (in-flow, above the scorebox) and the landscape #lsShootout
   (fixed bar under the .ls-header — .root is display:none in landscape and the
   .ls-card grid has no spare cell, so the mirror floats over it). Both are
   hidden (class 'on' absent) whenever state.shootout is absent. */
#shootoutPanel{ display:none; }
#shootoutPanel.on{ display:block; margin:8px 0; }
#lsShootout{ display:none; }
@media (min-width:700px) and (orientation:landscape){
  #lsShootout.on{
    display:block; position:fixed; top:70px; left:50%; transform:translateX(-50%);
    z-index:600; width:min(720px, 90vw);
    box-shadow:0 12px 40px rgba(0,0,0,.6); border-radius:10px;
  }
}
/* Shared practice-panel styling (used by both portrait #practicePanel and
   landscape #lsPracticePanel — one renderer feeds both). */
.pr-panel{ display:flex; flex-direction:column; gap:12px; }
.pr-head{ display:flex; align-items:center; gap:12px; }
.pr-head .pr-avatar{ width:52px; height:52px; border-radius:50%; object-fit:cover;
  border:2px solid rgba(255,213,74,.5); flex:0 0 auto; background:#111; }
.pr-head .pr-who{ display:flex; flex-direction:column; min-width:0; }
.pr-name-row{ display:flex; align-items:center; justify-content:space-between; gap:8px; min-width:0; }
.pr-head .pr-name{ font-weight:800; font-size:18px; color:#fff;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1; min-width:0; }
.pr-mode-badge{
  flex:0 0 auto; display:inline-flex; align-items:center;
  background:rgba(155,227,106,.14); border:1.5px solid rgba(155,227,106,.5);
  color:#9BE36A; border-radius:20px; padding:3px 10px 3px 8px;
  font-size:11px; font-weight:700; letter-spacing:.5px; text-transform:uppercase;
  cursor:pointer; white-space:nowrap; transition:background .15s,border-color .15s; }
.pr-mode-badge:hover{ background:rgba(155,227,106,.25); border-color:#9BE36A; }
.pr-mode-sub{ font-size:11px; letter-spacing:1px; text-transform:uppercase;
  color:rgba(255,255,255,.4); margin-top:2px; }
/* Mode picker popover */
#prModePicker{
  position:absolute; z-index:9999;
  background:#1a241a; border:1.5px solid rgba(155,227,106,.5);
  border-radius:12px; padding:8px 6px; min-width:170px;
  box-shadow:0 8px 24px rgba(0,0,0,.6); display:none; flex-direction:column; gap:4px; }
#prModePicker.open{ display:flex; }
/* v5.11.315 — the control the remote's arrows are on, inside any popup */
.remote-focus, .modal :focus-visible, #lsMatchPickerModal :focus, #prModePicker :focus,
#qrLoginModal :focus-visible, #practiceModal :focus-visible, #rulesModal :focus-visible{
  outline: 3px solid #FFD54A !important; outline-offset: 2px; box-shadow: 0 0 0 6px rgba(255,213,74,.25) !important; }
.pr-mp-opt{
  display:block; width:100%; background:transparent; border:none;
  color:#fff; font-size:13px; font-weight:600; text-align:left;
  padding:9px 14px; border-radius:8px; cursor:pointer; letter-spacing:.3px; }
.pr-mp-opt:hover{ background:rgba(155,227,106,.15); color:#9BE36A; }
.pr-mp-opt.cur{ color:#9BE36A; background:rgba(155,227,106,.10); }
/* ── Landscape-only last-action + detector strip ── */
.pr-ls-last{ display:none; background:#0d0d0d; border:2px solid rgba(255,255,255,.10);
  border-radius:12px; padding:12px 18px; font-size:22px; font-weight:900; color:#fff;
  text-align:center; letter-spacing:.5px; min-height:52px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#lsPracticePanel .pr-ls-last{ display:flex; align-items:center; justify-content:center; }
.pr-ls-det-slot{ /* container for the relocated #lsDetVision in landscape practice */ }
#practicePanel .pr-ls-det-slot{ display:none; }
/* ── Two-column metric grid: break hero (left, 2 rows) + run / best (right) ── */
.pr-top-grid{
  display:grid; grid-template-columns:1fr 1fr;
  grid-template-rows:1fr 1fr; gap:10px; min-height:130px; }
.pr-brk-hero{
  grid-column:1; grid-row:1/3;
  background:rgba(255,213,74,.13); border:2px solid rgba(255,213,74,.55);
  border-radius:14px; display:flex; flex-direction:column;
  align-items:center; justify-content:center; padding:14px 10px; }
.pr-brk-hero .pr-val{ font-family:'Share Tech Mono',monospace; font-weight:900;
  font-size:72px; line-height:1; color:#FFD54A; }
.pr-brk-hero .pr-lbl{ font-size:11px; letter-spacing:1.5px; text-transform:uppercase;
  color:rgba(255,213,74,.65); margin-top:6px; font-weight:700; }
.pr-run-box{
  background:rgba(155,227,106,.14); border:2px solid rgba(155,227,106,.55);
  border-radius:14px; display:flex; flex-direction:column;
  align-items:center; justify-content:center; padding:10px 10px; }
.pr-run-box .pr-val{ font-family:'Share Tech Mono',monospace; font-weight:900;
  font-size:40px; line-height:1; color:#9BE36A; }
.pr-run-box .pr-lbl{ font-size:10px; letter-spacing:1.5px; text-transform:uppercase;
  color:rgba(155,227,106,.65); margin-top:5px; font-weight:700; }
.pr-best-brk{
  background:rgba(80,140,255,.11); border:1px solid rgba(100,160,255,.4);
  border-radius:14px; display:flex; flex-direction:column;
  align-items:center; justify-content:center; padding:10px 10px; }
.pr-best-brk .pr-val{ font-family:'Share Tech Mono',monospace; font-weight:900;
  font-size:36px; line-height:1; color:#7eb8ff; }
.pr-best-brk .pr-lbl{ font-size:10px; letter-spacing:1.5px; text-transform:uppercase;
  color:rgba(100,160,255,.65); margin-top:5px; font-weight:700; }
/* ── Stats rows ── */
.pr-stats{ display:flex; justify-content:space-around; gap:8px;
  background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.08);
  border-radius:12px; padding:10px 8px; }
.pr-stats .pr-stat{ text-align:center; }
.pr-stats .pr-stat b{ font-family:'Share Tech Mono',monospace; font-size:22px;
  font-weight:900; color:#fff; }
.pr-stats .pr-stat span{ display:block; font-size:10px; letter-spacing:1px;
  text-transform:uppercase; color:rgba(255,255,255,.45); margin-top:3px; }
.pr-stat-pots b{ color:#9BE36A !important; }
.pr-stat-miss b{ color:#ff7070 !important; }
.pr-stat-pct  b{ color:#FFD54A !important; }
.pr-breaks-hdr{ font-size:12px; letter-spacing:1px; text-transform:uppercase;
  color:rgba(255,255,255,.5); }
.pr-breaks{ display:flex; flex-wrap:wrap; gap:6px; }
/* v5.10.180 — break list as REVERSED-text boxes: solid green fill, dark text,
   squared box + border (operator: "reverse text with boxes around"). */
.pr-break-chip{ background:#9BE36A; border:1px solid #6fb840;
  color:#0b1a06; border-radius:4px; padding:5px 12px; font-weight:800; font-size:14px; }
.pr-break-chip small{ color:rgba(11,26,6,.65); font-weight:600; font-size:11px; }
.pr-breaks-empty{ font-size:13px; color:rgba(255,255,255,.3); font-style:italic; }

/* ── TOPBAR ── */
.topbar{
  display:flex;align-items:center;justify-content:space-between;
  padding:6px calc(var(--sl) + 12px) 6px 12px;
  background:var(--s1);border-bottom:1px solid var(--bd);
  font-size:13px;gap:10px;
}
.topbar-title{letter-spacing:2px;text-transform:uppercase;color:var(--mu);
              flex:1;text-align:left;}   /* v5.11.373 — beside the club logo: the scorer app's
                                            floating "Scorer" chip sits top-centre and hid it */
.topbar-logo-club,.topbar-logo-app{
  height:32px;width:auto;display:block;flex-shrink:0;
  /* Tiny brightness boost for the logos against dark UI */
  filter:drop-shadow(0 0 1px rgba(0,0,0,.5));
}
.topbar-logo-app{height:28px;}   /* aiSnooker logo is wide; constrain height */
.ws-row{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--mu);
        flex-shrink:0;}
.dot{width:7px;height:7px;border-radius:50%;background:#555;flex-shrink:0;}
.dot.live{background:var(--g);animation:blink 2s infinite;}
.dot.warn{background:#f90;}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.3}}

/* ── PAGES (single visible at a time) ── */
.page{display:none;overflow:hidden;height:100%;}
.page.on{display:flex;flex-direction:column;}

/* ── NAVBAR ── */
.navbar{
  display:flex;align-items:stretch;
  background:var(--s1);border-top:1px solid var(--bd);
  padding-bottom:var(--sb);
}
.nav-btn{
  flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;background:none;border:none;color:var(--mu);
  font-family:'Rajdhani',sans-serif;font-size:10px;letter-spacing:.5px;
  text-transform:uppercase;cursor:pointer;padding:6px 4px;
  transition:color .15s;border-top:2px solid transparent;
}
.nav-btn:active{background:rgba(255,255,255,.05);}
.nav-btn.on{color:var(--g);border-top-color:var(--g);}
.nav-icon{font-size:18px;line-height:1;}

/* ══ CONTROL PAGE — portrait ═════════════════════════════════════════════ */
/* portrait: scroll through info then controls */
.p-ctrl{overflow-y:auto;flex:1;min-height:0;}

/* score panel */
.score-panel{
  display:flex;flex-direction:column;gap:4px;
  padding:10px 12px 8px;background:var(--s2);
  border-bottom:1px solid var(--bd);flex-shrink:0;
}
.sp-names{display:flex;justify-content:space-between;align-items:baseline;gap:8px;}
.sp-scores{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:8px;}
.pb{display:flex;flex-direction:column;gap:3px;align-items:flex-start;}
.pb.r{align-items:flex-end;}
.pname{font-size:17px;font-weight:900;color:#aaa;
       white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
       flex:1;min-width:0;transition:color .3s;}
.pname.r{text-align:right;}
.pname.on{color:var(--g);}
.score{
  font-family:'Share Tech Mono',monospace;
  font-size:54px;font-weight:900;line-height:1;
  background:#252525;color:#ddd;border-radius:10px;
  padding:3px 12px;min-width:unset;display:inline-block;text-align:center;
  border:2px solid transparent;transition:all .3s;
}
.score.on{background:rgba(155,227,106,.1);color:var(--g);border-color:rgba(155,227,106,.3);}
.centre{display:flex;flex-direction:column;align-items:center;gap:3px;}
/* Match score (frames) — kept small/low-key so the NEXT ball is the hero. */
/* v5.9.100 — the match score wears the GOLD BOX in portrait too, echoing the
   landscape .ls-frames-big (same gradient, dark text, mono numerals) at
   portrait scale. It had been a tiny grey badge while the NEXT ball held the
   visual weight; the operator wants the same hierarchy in both layouts. */
.fbadge{font-size:20px;letter-spacing:1px;
        font-family:'Share Tech Mono',monospace;font-weight:900;
        color:#1a1a1a;
        background:linear-gradient(135deg, #FFD54A 0%, #E6A500 100%);
        border-radius:8px;padding:4px 14px;
        box-shadow:0 2px 12px rgba(255, 213, 74, .4),
                   inset 0 -3px 0 rgba(0,0,0,.15),
                   inset 0 1px 0 rgba(255,255,255,.4);}
.brk{font-family:'Share Tech Mono',monospace;font-size:30px;
     font-weight:900;color:var(--g);line-height:1;}
.brk-lbl{font-size:9px;color:var(--mu);letter-spacing:1px;}
/* NEXT ball (portrait pills row) — v5.9.100: back to the 52px pill size so
   the four indicators read as equal-weight chips again; the gold match-score
   box above carries the prominence now (operator request — the reverse of
   the earlier swap that made NEXT the big element). */
#pNxtBall{ width:52px; height:52px; font-size:12px; border-width:2px; }
.next-ball-wrap .next-ball-label{ font-size:11px; }

/* pills */
.pills{
  display:flex;gap:5px;justify-content:center;flex-wrap:wrap;
  padding:7px 10px 6px;border-bottom:1px solid var(--bd);flex-shrink:0;
}
/* Score pills (Diff / Rem / Reds) — sized to match the 52×52 .next-ball
   graphic so the four indicators read as a row of equal-weight chips.
   Layout: column flex with the label on top, the big number underneath.
   Box look mirrors the landscape .ls-pill tiles (same borders, gradient
   backgrounds, glow shadows, mono value font) at portrait 52px scale.
   Rem is green (was blue) at user request — green is the "remaining
   points on table" colour you'd see on a snooker baulk cloth, and reads
   as more positive than the prior cool blue. */
.pill{padding:6px 4px;border-radius:12px;font-size:10px;font-weight:700;
      letter-spacing:.8px;text-transform:uppercase;color:#cfcfcf;
      border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.06);
      white-space:nowrap;display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
      width:52px;height:52px;box-sizing:border-box;
      box-shadow:0 4px 12px rgba(0,0,0,.25);}
.pill-val{font-size:22px;font-weight:900;line-height:1;
          font-family:'Share Tech Mono',monospace;font-variant-numeric:tabular-nums;}
.pill.diff{color:#FFD54A;border-color:rgba(255,213,74,.45);
           background:linear-gradient(135deg, rgba(255,213,74,.18), rgba(255,213,74,.05));
           box-shadow:0 4px 16px rgba(255,213,74,.12);}
.pill.diff .pill-val{color:#FFD54A;}
.pill.reds{color:#FF6060;border-color:rgba(255,77,77,.45);
           background:linear-gradient(135deg, rgba(255,77,77,.18), rgba(255,77,77,.05));
           box-shadow:0 4px 16px rgba(255,77,77,.12);}
.pill.reds .pill-val{color:#FF6060;}
.pill.rem {color:#7EE787;border-color:rgba(126,231,135,.45);
           background:linear-gradient(135deg, rgba(126,231,135,.16), rgba(126,231,135,.04));
           box-shadow:0 4px 16px rgba(126,231,135,.10);}
.pill.rem  .pill-val{color:#7EE787;}
.pill.nxt {color:#CE93D8;border-color:rgba(206,147,216,.35);background:rgba(206,147,216,.09);}
/* v5.11.63 — the small pill form of the same message: centre it too, so the
   two portrait surfaces agree. .pill is a column flex, so text-align alone
   does nothing — it needs the cross-axis centred as well. */
.pill.snook{color:#FF6B6B;border-color:rgba(255,107,107,.4);background:rgba(255,107,107,.12);display:none;
            width:auto;height:auto;padding:5px 11px;
            text-align:center;align-items:center;justify-content:center;}
/* SHOT CLOCK tile — left end of the portrait #pills row, sized to match
   the 52px pills. Show/hide + colour are driven inline by _scStart()/
   _scStop()/_scRender() — this rule is placement/base appearance only. */
#shotClockBadge{
  width:52px;height:52px;box-sizing:border-box;
  border:2px solid rgba(126,231,135,.6);border-radius:12px;
  background:rgba(0,0,0,.55);color:#7EE787;
  font-family:'Share Tech Mono',monospace;font-weight:900;
  font-size:17px;line-height:48px;text-align:center;
  white-space:nowrap;pointer-events:none;transition:opacity .12s;
}
/* v5.9.112 — 2/3 in ANY practice mode (body.practice-clock set by render()),
   matching #prShotClockBadge so freeform and full_frame read the same. */
body.practice-clock #shotClockBadge{
  width:35px;height:35px;border-radius:8px;
  font-size:12px;line-height:31px;
}
/* Portrait PRACTICE shot clock — same tile look as #shotClockBadge but a bit
   wider so the "⏱ NN" fits, left-aligned above the practice panel (its own
   #prScWrap row; #pills is hidden in practice). Show/hide + colour driven
   inline by _scStart()/_scStop()/_scRender() like the other twins. */
/* v5.9.107 — practice clock at 2/3 the 2-player tile (52px → 35px):
   practice is untimed count-up, so the clock is ambient, not a deadline. */
#prShotClockBadge{
  display:inline-block;min-width:35px;height:35px;box-sizing:border-box;
  padding:0 9px;border:2px solid rgba(126,231,135,.6);border-radius:8px;
  background:rgba(0,0,0,.55);color:#7EE787;
  font-family:'Share Tech Mono',monospace;font-weight:900;
  font-size:13px;line-height:31px;text-align:center;letter-spacing:1px;
  white-space:nowrap;pointer-events:none;transition:opacity .12s;
}
/* The .snook pill keeps its old auto-size — it's a label, not a number tile. */

/* Next ball graphic */
.next-ball-wrap{display:flex;flex-direction:column;align-items:center;gap:4px;}
.next-ball-label{font-size:11px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:rgba(255,255,255,0.45);}
/* Portrait (phones): keep the Next ball round; its wrap starts at the 52px pill
   height but the label sits on top and the ball is free to extend a little below
   the Diff/Reds/Rem row. Portrait-only (.pills). */
.pills .next-ball{ width:25px; height:25px; font-size:8px; }
.pills .next-ball-wrap{ min-height:52px; gap:3px; justify-content:flex-start; }
.pills .next-ball-label{ font-size:9px; letter-spacing:1px; }
/* Match Setup player card: avatar + Avatar/Reset buttons on the top line, name
   box on its own line below so the login QR has room to be scanned. The row
   itself is a plain block — msModeChange()'s sh('mi-row-a','') reveal toggles
   block/none, so it can't wipe a layout. The inner .mi-ptop owns the flex. */
.mi-ptop{ display:flex; align-items:center; gap:10px; }
.mi-pbtns{ display:flex; flex-direction:column; gap:6px; flex:1; min-width:0; }
.mi-pbtns .ab{ flex:0 0 auto; min-height:0; font-size:12px; padding:9px 6px; }
/* v5.9.43 — the per-player Login button. Tinted so it reads as the primary
   action on the row: signing in is what attaches the match to a real account,
   and Avatar/Reset are the fallbacks.
   v5.9.45 — PORTRAIT ONLY. Portrait means the admin is being viewed on a
   phone, which is the player's own device: they can sign in to the cloud
   directly and a QR would just be a picture of a link they are already
   holding. Landscape is the table-side tablet, where the player is NOT the
   one looking at the screen — there the avatar QR is the right mechanism,
   and it stays exactly as it was in both orientations. */
/* v5.10.44 — VISIBLE IN BOTH ORIENTATIONS. This was portrait-only on the
   reasoning below (landscape = table-side tablet, so tap the avatar for the
   inline QR instead). But when the inline avatar QR fails to appear — which it
   did whenever a sign-in latch stuck, see v5.10.29 — landscape was left with
   NO route to player login at all, and the operator noticed the button had
   gone ("match setup modal used to have login button"). It is a small button
   that simply opens the same QR modal, so having it in landscape too costs
   nothing and removes the dead end. */
.mi-login-btn{
  display:block;
  background:rgba(123,183,255,.16); border-color:#5b8fd0; color:#cfe4ff; font-weight:700;
}
/* Match Setup avatars must be self-contained in PORTRAIT too. The base
   .ls-avatar / .ls-login-qr rules live inside a landscape-only @media block, so
   in portrait the avatar has no `position:relative`/`overflow:hidden` and the
   login QR (position:absolute; inset:0) escapes to fill the whole modal. These
   base rules give .mi-av its own positioning in every orientation. */
.mi-av{ position:relative; overflow:hidden; border-radius:50%; background:#222;
        display:flex; align-items:center; justify-content:center; }
.mi-av img{ width:100%; height:100%; object-fit:cover; }
.mi-av .ls-login-qr{ position:absolute; inset:0; padding:4px; box-sizing:border-box;
        display:none; align-items:center; justify-content:center; background:#fff; }
.mi-av .ls-login-qr[data-on="1"]{ display:flex; }
.mi-av .ls-login-qr img, .mi-av .ls-login-qr svg{ width:100%; height:100%; }
.mi-av .ls-login-qr .cap{ display:none; }
/* Root fix: the base .ls-avatar positioning lives in a landscape-only @media
   block, so in PORTRAIT any avatar's login QR (position:absolute; inset:0)
   escapes and covers the whole modal — this broke the practice modal (its Start
   button became unclickable under a full-screen QR). Contain every avatar in
   all orientations. Harmless in landscape (same values as the media rule). */
.ls-avatar{ position:relative; overflow:hidden; }
.next-ball{
  width:52px;height:52px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:900;color:#fff;letter-spacing:0.5px;
  box-shadow:0 3px 12px rgba(0,0,0,0.5),inset 0 1px 0 rgba(255,255,255,0.25);
  transition:background 0.3s,box-shadow 0.3s;
  border:2px solid rgba(255,255,255,0.15);
  position:relative;
}
/* Unified 3D billiard-ball overlay (same as the pot buttons): specular
   highlight + lower-edge shading layered over the per-colour gradient. */
.next-ball::before{
  content:'';position:absolute;inset:0;border-radius:50%;
  background:
    radial-gradient(circle at 30% 27%, rgba(255,255,255,.85) 0%, rgba(255,255,255,.22) 16%, rgba(255,255,255,0) 38%),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 55%, rgba(0,0,0,.38) 100%);
  pointer-events:none;
}
.next-ball.red   {background:radial-gradient(circle at 35% 35%,#e05555,#8b0000);box-shadow:0 3px 16px rgba(180,0,0,0.6),inset 0 1px 0 rgba(255,255,255,0.2);}
.next-ball.color {
  /* Colour wheel as a glossy 3D sphere: specular highlight + lower-edge shading
     layered over the conic mix of all six colours. */
  background:
    radial-gradient(circle at 34% 28%, rgba(255,255,255,.55) 0%, rgba(255,255,255,0) 40%),
    radial-gradient(circle at 50% 118%, rgba(0,0,0,.50) 0%, rgba(0,0,0,0) 58%),
    conic-gradient(
      #f9a825 0deg 60deg,
      #2e7d32 60deg 120deg,
      #5d4037 120deg 180deg,
      #1565c0 180deg 240deg,
      #e91e8c 240deg 300deg,
      #212121 300deg 360deg
    );
  box-shadow:0 3px 16px rgba(0,0,0,0.5),inset 0 1px 0 rgba(255,255,255,0.25);
}
.next-ball.yellow{background:radial-gradient(circle at 35% 35%,#f0d000,#a08000);box-shadow:0 3px 16px rgba(200,160,0,0.5),inset 0 1px 0 rgba(255,255,255,0.25);color:#333;}
.next-ball.green {background:radial-gradient(circle at 35% 35%,#28a850,#145a28);box-shadow:0 3px 16px rgba(0,140,50,0.5),inset 0 1px 0 rgba(255,255,255,0.2);}
.next-ball.brown {background:radial-gradient(circle at 35% 35%,#9a6030,#5a3010);box-shadow:0 3px 16px rgba(100,60,0,0.5),inset 0 1px 0 rgba(255,255,255,0.2);}
.next-ball.blue  {background:radial-gradient(circle at 35% 35%,#2080e0,#0040a0);box-shadow:0 3px 16px rgba(0,60,200,0.5),inset 0 1px 0 rgba(255,255,255,0.2);}
.next-ball.pink  {background:radial-gradient(circle at 35% 35%,#e060b0,#a02070);box-shadow:0 3px 16px rgba(180,0,120,0.5),inset 0 1px 0 rgba(255,255,255,0.2);}
.next-ball.black {background:radial-gradient(circle at 35% 35%,#444,#111);box-shadow:0 3px 16px rgba(0,0,0,0.7),inset 0 1px 0 rgba(255,255,255,0.15);}
.next-ball.free  {background:radial-gradient(circle at 35% 35%,#60c0a0,#208060);box-shadow:0 3px 16px rgba(0,150,100,0.5),inset 0 1px 0 rgba(255,255,255,0.2);}
.next-ball.ls    {width:44px;height:44px;font-size:11px;}

/* reds + colours */
.tbl-state{
  display:flex;flex-direction:column;align-items:center;gap:5px;
  padding:7px 12px;border-bottom:1px solid var(--bd);flex-shrink:0;
}
.reds-row{display:flex;gap:3px;flex-wrap:wrap;justify-content:center;}
.rd{width:13px;height:13px;border-radius:50%;
    background:#c62828;box-shadow:0 0 4px rgba(198,40,40,.5);transition:all .3s;}
.rd.gone{background:rgba(255,255,255,.07);box-shadow:none;}
.cols-row{display:flex;gap:6px;}
.cb{width:18px;height:18px;border-radius:50%;transition:all .3s;}
.cb.gone{opacity:.12;transform:scale(.7);}
.cb[data-c=yellow]{background:#f9a825;}.cb[data-c=green]{background:#2e7d32;}
.cb[data-c=brown]{background:#5d4037;}.cb[data-c=blue]{background:#1565c0;}
.cb[data-c=pink]{background:#e91e8c;}.cb[data-c=black]{background:#263238;border:1px solid #546e7a;}

/* ══ Detector-vision strip — live YOLO count vs server, between shots ══
   Shows what the detector currently SEES so the operator can spot a
   desync at a glance: red count beside the server's reds-left, and the
   six colours as balls that grey out (.cb.gone) when YOLO isn't seeing
   them. Data is the live_balls heartbeat (pushed ~1Hz between shots), so
   it dims to 'paused' during a shot when no fresh frame is arriving. */
/* v5.9.16 — wrap instead of running off the screen. This is a flex row of up
   to 15 reds plus the colours, the ball-on badge and the remaining count, and
   it never wrapped; the landscape override below scales every element up
   (26px reds, 30px cue ball, 36px badges, 22px numerals), so on a full rack it
   overflows the viewport and the right-hand end is simply not visible.
   max-width pins it to the parent so the wrap actually engages. */
.det-vision{display:flex;align-items:center;gap:10px;justify-content:center;
  flex-wrap:wrap;max-width:100%;row-gap:4px;
  padding:5px 12px;border-bottom:1px solid var(--bd);flex-shrink:0;
  font-size:12px;transition:opacity .3s;}
.det-vision .det-lbl{color:var(--muted);text-transform:uppercase;
  letter-spacing:.5px;font-size:10px;}
.det-red{display:inline-flex;align-items:center;gap:4px;font-weight:700;}
.det-red .rd{width:12px;height:12px;border-radius:50%;background:#c62828;}
.det-red.mismatch{color:#ffb300;}
.det-red.mismatch .rd{box-shadow:0 0 6px #ffb300;background:#ffb300;}
.det-cols{display:flex;gap:5px;}
.det-note{color:var(--muted);font-size:10px;font-style:italic;}
.det-vision.stale{opacity:.4;}
/* Detector "ball on" highlight + "Rem Reds Left" (parity with 2-player) */
.det-onball{display:inline-flex;align-items:center;gap:5px;}
.det-onball-lbl,.det-rem-lbl{color:var(--muted);text-transform:uppercase;
  letter-spacing:.5px;font-size:10px;}
.det-vision .det-nb{width:24px;height:24px;font-size:8px;border-width:1px;}
.det-rem{display:inline-flex;align-items:center;gap:4px;font-weight:700;}
.det-rem b{font-size:15px;}

/* v5.9.57 — the strip must never leave the screen.
   PORTRAIT puts it in the normal scrolling column, under the topbar, the score
   block and the pills row, so on a short tablet viewport it drops below the
   fold — and it does that exactly when it is most wanted, mid-frame, when the
   operator wants to know whether the detector still agrees with the score.
   Sticking it to the bottom edge keeps it visible while its natural position is
   off-screen; it rejoins the flow as soon as that position scrolls into view.
   Opaque background and a top rule because it now sits over content.
   LANDSCAPE doesn't need this — .ls-left reserves a grid row for it. */
#detVision{
  position:sticky; bottom:0; z-index:6;
  background:var(--bg);
  border-top:1px solid var(--bd);
}

/* ══ INFO-ZONE — replaces reds+colours area in portrait ══════════════
   Hosts stateful alerts (awaiting break, touching ball nomination,
   free ball pending, snookers required, post-foul reminder).
   Multiple banners can stack vertically. The zone collapses to
   zero height when nothing is active. */
.info-zone{
  display:flex; flex-direction:column; gap:6px;
  padding:8px 12px; border-bottom:1px solid var(--bd);
  flex-shrink:0;
}
.info-banner{
  display:flex; align-items:center; gap:8px;
  padding:7px 12px; border-radius:8px;
  font-size:13px; font-weight:700; letter-spacing:.2px;
  background:rgba(255,255,255,.06); border:1px solid rgba(255,255,255,.10);
  color:rgba(255,255,255,.92);
  box-sizing:border-box;
}
.info-banner .ib-icon{ font-size:16px; line-height:1; flex-shrink:0; }
.info-banner .ib-text{ flex:1; line-height:1.2; }
.info-banner b{ color:#fff; font-weight:900; }

/* Awaiting break-off — neutral with golden accent */
.ib-await{
  background:linear-gradient(180deg, rgba(217,119,6,.20), rgba(217,119,6,.10));
  border-color:rgba(255,180,80,.45);
  color:#ffe0a0;
}

/* Touching ball — cyan to match the armed-button glow */
.ib-touch{
  background:linear-gradient(180deg, rgba(20,184,166,.22), rgba(20,184,166,.10));
  border-color:rgba(94,234,212,.45);
  color:#bff5ec;
}

/* Free ball — purple to differentiate from touching */
.ib-free{
  background:linear-gradient(180deg, rgba(168,85,247,.22), rgba(168,85,247,.10));
  border-color:rgba(196,140,255,.45);
  color:#e8d4ff;
}

/* Free ball + touching ball nomination buttons (built by openFreeBall and
   openTouchingBall — one .fb-choice class, identical size and layout).
   Portrait keeps the previous inline sizing exactly; landscape grows them,
   because that layout is used on a tablet propped across the room and the
   operator is nominating a ball mid-frame with one hand.
   NOTE the #id selectors: the container is div.mbtns#freeBallChoices, and
   `.mbtns .ab` further down sets padding/font-size at higher specificity than
   a bare .fb-choice would have, so without the id these rules never apply. */
#freeBallChoices .fb-choice,
#tbChoices .fb-choice{
  padding:10px 16px; font-size:13px;
  border:1px solid rgba(255,255,255,.25);
}
@media (min-width:700px) and (orientation:landscape){
  #freeBallChoices, #tbChoices{ gap:14px; }
  #freeBallChoices .fb-choice,
  #tbChoices .fb-choice{
    padding:20px 30px; font-size:20px; font-weight:700;
    min-width:130px; min-height:66px; letter-spacing:.5px;
    border-width:2px; border-radius:10px;
  }
}
/* Short landscape (phone on its side): keep them big but not so tall the
   row wraps off the modal. */
@media (min-width:700px) and (orientation:landscape) and (max-height:700px){
  #freeBallChoices .fb-choice,
  #tbChoices .fb-choice{
    padding:14px 22px; font-size:17px; min-width:110px; min-height:52px;
  }
}

/* Snookers required — red urgency, pulses to match overlay's style */
.ib-snook{
  background:linear-gradient(180deg, rgba(196,18,48,.30), rgba(196,18,48,.15));
  border-color:rgba(255,80,80,.55);
  color:#ffd0d0;
  animation:pulse-soft 2.4s ease-in-out infinite;
}
/* v5.11.63 — CENTRED in portrait. .info-banner is a flex row built for
   icon + text + (sometimes) a button, so its text sits hard against the icon
   and reads as left-aligned — fine for the post-foul reminder, wrong for this
   one, which is a headline the operator reads from the table. Operator: "in
   portrait view the snooker required text should be centered". Only this
   banner changes; the others keep the row layout. */
.ib-snook{ justify-content:center; }
.ib-snook .ib-text{ flex:0 1 auto; text-align:center; }
@keyframes pulse-soft{
  0%,100%{ box-shadow:0 0 0 0 rgba(196,18,48,.30); }
  50%{    box-shadow:0 0 0 6px rgba(196,18,48,0); }
}

/* Post-foul reminder — green to suggest "you have an option" */
.ib-postfoul{
  background:linear-gradient(180deg, rgba(34,197,94,.20), rgba(34,197,94,.10));
  border-color:rgba(110,231,150,.45);
  color:#cef0d8;
}

/* Hide info-zone in landscape — landscape has its own dedicated
   layout with a centre-column area for these alerts (out of scope
   for this change). Keep it portrait-only for now. */
@media (min-width:700px) and (orientation:landscape){
  .info-zone{ display:none !important; }
}

/* last action */
.last-act{text-align:center;padding:5px 10px;font-size:13px;
          color:var(--mu);border-bottom:1px solid var(--bd);flex-shrink:0;}
.last-act .tag-ai{color:#7fff7f;font-size:10px;font-weight:700;letter-spacing:.5px;
                  background:rgba(0,160,0,.2);border:1px solid rgba(0,200,0,.3);
                  border-radius:4px;padding:1px 5px;margin-left:4px;vertical-align:middle;}
.last-act .tag-manual{color:#FFD54A;font-size:10px;font-weight:700;letter-spacing:.5px;
                      background:rgba(255,200,0,.15);border:1px solid rgba(255,200,0,.3);
                      border-radius:4px;padding:1px 5px;margin-left:4px;vertical-align:middle;}
/* The gap must not depend on the container. fmtLast() is shared — the practice
   panel calls it too (see its comment) — and that panel is neither .last-act
   nor .ls-last, so the scoped margins above never applied there and the tag
   butted straight onto the text: "Redmanual". */
.tag-ai, .tag-manual{margin-left:4px;}

/* controls */
.ctrl-btns{
  display:flex;flex-direction:column;gap:5px;
  padding:7px 8px calc(var(--sb) + 7px);flex-shrink:0;
}
.bgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;
       max-width:300px;margin:0 auto;width:100%;}
.bb{
  aspect-ratio:1/1;border-radius:50%;border:0;
  font-family:'Rajdhani',sans-serif;font-size:26px;font-weight:900;color:#fff;
  box-shadow:0 5px 14px rgba(0,0,0,.45);touch-action:manipulation;cursor:pointer;
  display:flex;align-items:center;justify-content:center;transition:transform .1s;
  position:relative;
}
.bb:active{transform:scale(.92);}
.bb.red{background:#c31111;}.bb.yellow{background:#e1b100;}.bb.green{background:#1aa04b;}
.bb.brown{background:#7b4a2c;}.bb.blue{background:#1f74d6;}.bb.pink{background:#d85bb3;}
.bb.black{background:#101010;border:1px solid rgba(255,255,255,.25);}
/* 3D billiard-ball overlay on the ball pot buttons: specular highlight +
   lower-edge shading over the flat colour. .ctrl buttons (Switch/Back) are
   NOT balls, so they're excluded; pointer-events:none keeps taps working. */
.bb:not(.ctrl)::before{
  content:'';position:absolute;inset:0;border-radius:50%;
  background:
    radial-gradient(circle at 30% 27%, rgba(255,255,255,.85) 0%, rgba(255,255,255,.22) 16%, rgba(255,255,255,0) 38%),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 55%, rgba(0,0,0,.38) 100%);
  pointer-events:none;
}
/* Generic 3D billiard overlay for small JS-generated ball dots that carry an
   inline `background:` (multiplayer potted-ball dots, NEXT-ball dot). */
.bdot3d{position:relative;box-shadow:0 2px 5px rgba(0,0,0,.45);}
.bdot3d::before{
  content:'';position:absolute;inset:0;border-radius:50%;
  background:
    radial-gradient(circle at 30% 27%, rgba(255,255,255,.85) 0%, rgba(255,255,255,.22) 16%, rgba(255,255,255,0) 38%),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 55%, rgba(0,0,0,.38) 100%);
  pointer-events:none;
}
/* Disabled: ball is not legal to pot right now (wrong phase, wrong
   colour in sequence, or non-nominated colour during free ball).
   Keeps the button visible but greyed out and non-tappable. */
.bb.disabled, .ls-pot.disabled{
  filter:grayscale(.85) brightness(.45) contrast(.85);
  opacity:.45;
  cursor:not-allowed;
  box-shadow:0 2px 6px rgba(0,0,0,.25);
  pointer-events:none;
  transform:none;
}
.bb.disabled:active, .ls-pot.disabled:active{ transform:none; }
.bb.ctrl{background:#282828;border:1px solid var(--bd);font-size:11px;flex-direction:column;gap:1px;line-height:1.1;}
/* Switch button is the primary turn-control action — make it the
   bright odd-one-out among the dark ball buttons. Almost white with
   dark text, large icon. */
.bb.ctrl.switch-btn{background:#f0f0f0;color:#111;border:1px solid rgba(0,0,0,.2);
                    box-shadow:0 5px 14px rgba(0,0,0,.45),inset 0 1px 0 rgba(255,255,255,.5);}
.bb.ctrl.switch-btn span.ico{font-size:28px;font-weight:900;}
/* Back button — clearly distinguished from Switch but obviously secondary.
   Amber arrow icon on a slightly lifted dark grey. */
.bb.ctrl.back-btn{background:#3a3a3a;border:1px solid rgba(255,255,255,.25);}
.bb.ctrl.back-btn span.ico{font-size:28px;font-weight:900;color:#ffb84a;}
.arow{display:flex;gap:4px;max-width:340px;margin:0 auto;width:100%;}
/* Taller action row — used for rows where labels need more breathing
   room (e.g. the "F&M Re-Spot" / "Free Ball" row). Buttons grow taller
   and labels can wrap. */
.arow.ar-tall .ab{ min-height:54px; padding:8px 4px; }
/* Multi-line label inside an .ab — allows <br> in the label to render
   as two lines instead of being collapsed by white-space:nowrap. The
   line-height and font-size are tuned so the two lines fit
   comfortably in the standard button height. */
.ab.ab-multiline{
  white-space:normal; line-height:1.05; font-size:11px;
  padding:6px 3px; word-break:keep-all;
}
.ab.ab-multiline br{ content:''; display:block; margin:1px 0; }
/* Primary-action multi-line variants: End Frame and End Match get a
   bigger font since they're prominent action buttons. The label
   needs to read clearly across the room as "this ends something". */
.ab.fr.ab-multiline,
.ab.dk.ab-multiline{
  font-size:13px; line-height:1.1; padding:6px 3px;
  font-weight:900; letter-spacing:.3px;
}
.ab{
  flex:1;min-width:0;padding:8px 3px;border-radius:9px;
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.13);
  color:#fff;font-family:'Rajdhani',sans-serif;font-size:12px;font-weight:700;
  touch-action:manipulation;cursor:pointer;min-height:42px;line-height:1.2;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:transform .1s;
}
.ab:active{transform:scale(.96);}
.ab:disabled{opacity:.35;filter:grayscale(1);cursor:not-allowed;}
/* Practice mode selector — active/selected state */
.pr-mode-btn.sel{
  background:rgba(88,200,88,.13);
  border-color:rgba(88,200,88,.55);
  color:#9be36a;
}
.pr-mode-btn.sel .pr-mode-dot{ color:#9be36a; }
.pr-mode-btn{ text-align:left; border-radius:10px; padding:10px 13px; line-height:1.4; }
.pr-mode-dot{ display:inline-block; width:14px; }
/* Touching Ball button — armed state. The flag is one-shot, consumed
   on the next shot. While armed, a cyan accent + soft pulse tells the
   operator at a glance that the next shot will bypass foul checks. */
.ab.tb-armed{
  background:linear-gradient(180deg, #14b8a6 0%, #0f766e 100%);
  border:2px solid #5eead4;
  color:#fff;
  box-shadow:0 0 0 2px rgba(94,234,212,.35),
             0 0 16px rgba(20,184,166,.5),
             inset 0 1px 0 rgba(255,255,255,.25);
  animation:tb-pulse 1.2s ease-in-out infinite;
}
@keyframes tb-pulse{
  0%,100%{box-shadow:0 0 0 2px rgba(94,234,212,.35),0 0 16px rgba(20,184,166,.5),inset 0 1px 0 rgba(255,255,255,.25);}
  50%    {box-shadow:0 0 0 3px rgba(94,234,212,.6),0 0 24px rgba(20,184,166,.8),inset 0 1px 0 rgba(255,255,255,.25);}
}
.ab.f4{background:#7b4a2c;border-color:rgba(255,255,255,.3);}
.ab.f5{background:#1f74d6;border-color:rgba(255,255,255,.3);}
.ab.f6{background:#d85bb3;border-color:rgba(255,255,255,.35);}
.ab.f7{background:#101010;border-color:rgba(255,255,255,.3);}
/* Foul X — bright red with a soft glow so it's instantly visible on any
   background colour. Larger than before (52 vs 44) for at-a-glance use. */
.foul-x{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
        font-size:52px;font-weight:900;line-height:1;color:#ff2a2a;
        text-shadow:0 0 8px rgba(255,42,42,.9), 0 0 2px rgba(255,42,42,1);
        pointer-events:none;}
/* Label sits in a small dark pill so it reads cleanly on top of the X
   regardless of the underlying ball-colour background. */
/* v5.9.103 — labels read "Foul-4".."Foul-7" now (was "F4"), three times the
   text in the same four-across row: a step down in size and spacing so the
   word fits the pill, and nowrap so a tight tier ellipsizes instead of
   breaking "Foul-" onto its own line. */
.foul-num{position:relative;z-index:1;font-size:14px;font-weight:900;letter-spacing:.3px;
          white-space:nowrap;
          background:rgba(0,0,0,.6);padding:2px 6px;border-radius:5px;}
.ab.foul-btn{position:relative;overflow:hidden;}
.ab.r {background:rgba(195,17,17,.65);border-color:rgba(255,100,100,.4);}
/* v5.11.214 — '−1 Red' (the AI counted a red too many): amber on dark so the
   flipped key is noticed, in both layouts and both skins. */
.ab.r.red-minus, .ls-right .ab.r.red-minus, body.theme-light .ab.r.red-minus{
  background:#2a1a00 !important; border:2px solid #FFB020 !important; color:#FFD27A !important; font-weight:800;}
/* End Frame — amber gradient. "Stage complete" feel; warmer and
   more inviting than End Match which is the more terminal action. */
.ab.fr{
  background:linear-gradient(180deg, #d97706 0%, #92400e 100%);
  border:1px solid rgba(255,180,80,.6);
  color:#fff;
  box-shadow:0 4px 10px rgba(217,119,6,.30),
             inset 0 1px 0 rgba(255,255,255,.25),
             inset 0 -2px 0 rgba(0,0,0,.18);
  text-shadow:0 1px 2px rgba(0,0,0,.45);
}
.ab.fr:active{ filter:brightness(.92); }
/* End Match — deep red with bright outline. The terminal action
   for the whole match; clearly more weighty than End Frame. */
.ab.dk{
  background:linear-gradient(180deg, #b91c1c 0%, #6b0e0e 100%);
  border:1px solid rgba(255,90,90,.6);
  color:#fff;
  box-shadow:0 4px 10px rgba(185,28,28,.35),
             inset 0 1px 0 rgba(255,255,255,.25),
             inset 0 -2px 0 rgba(0,0,0,.20);
  text-shadow:0 1px 2px rgba(0,0,0,.5);
}
.ab.dk:active{ filter:brightness(.92); }
.ab.ai-on {background:rgba(0,140,0,.35);border-color:#00c800;color:#7fff7f;}
.ab.ai-off{background:rgba(80,0,0,.35);border-color:#c80000;color:#ff8080;}

/* no-match setup */
.setup-box{
  display:flex;flex-direction:column;gap:10px;
  padding:16px 14px;background:var(--s2);border-radius:10px;margin:12px 10px;
}
.setup-lbl{font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(--mu);}
.inp{width:100%;background:#242424;border:1px solid var(--bd);border-radius:7px;
     color:#fff;font-family:'Rajdhani',sans-serif;font-size:16px;padding:8px 11px;outline:none;}
.inp:focus{border-color:var(--g);}

/* Cloud-member autocomplete picker */
.memb-inp.cloud-locked{ border-color:#64b4ff; padding-right:80px; }
.memb-tag{
  position:absolute; right:8px; top:50%; transform:translateY(-50%);
  background:rgba(100,180,255,.18); color:#64b4ff;
  border:1px solid rgba(100,180,255,.4); border-radius:5px;
  padding:2px 8px; font-size:10px; font-weight:700;
  letter-spacing:.5px; pointer-events:none;
}
.memb-drop{
  position:absolute; top:100%; left:0; right:0; z-index:50;
  background:#1a1a1a; border:1px solid var(--bd); border-radius:7px;
  margin-top:2px; max-height:240px; overflow-y:auto;
  box-shadow:0 6px 20px rgba(0,0,0,.5);
}
.memb-row{
  padding:8px 12px; cursor:pointer; font-size:13px;
  border-bottom:1px solid rgba(255,255,255,.05);
  display:flex; align-items:center; justify-content:space-between; gap:8px;
}
.memb-row:hover{ background:rgba(100,180,255,.1); }
.memb-row .memb-name{ color:#fff; font-weight:600; }
.memb-row .memb-meta{ color:var(--mu); font-size:11px; }
.memb-row.no-results{ color:var(--mu); cursor:default; font-style:italic; }
.memb-row.no-results:hover{ background:transparent; }

/* Handicap input pair — blue (A) / yellow (B) accents like create.php */
.hcap-inp{
  flex:1; text-align:center; font-weight:700;
}
.hcap-inp.hcap-a{
  background:linear-gradient(180deg, rgba(37,99,235,.25), rgba(30,64,175,.10)) !important;
  border-color:rgba(96,165,250,.4) !important;
  color:#dbeafe !important;
}
.hcap-inp.hcap-b{
  background:linear-gradient(180deg, rgba(234,179,8,.25), rgba(161,98,7,.10)) !important;
  border-color:rgba(250,204,21,.4) !important;
  color:#fef3c7 !important;
}
.hcap-inp::placeholder{ opacity:.6; }
/* Wrapper column — label on top, input below. Two columns share the
   row width equally via flex:1. Labels are colour-keyed to match the
   input's tint (blue for A, yellow for B). */
.hcap-col{
  flex:1; display:flex; flex-direction:column; gap:3px; min-width:0;
}
.hcap-lbl{
  font-size:10px; font-weight:700; letter-spacing:1px; text-transform:uppercase;
  color:rgba(255,255,255,.55); text-align:center; line-height:1;
}
.hcap-col:has(.hcap-a) .hcap-lbl{ color:#60a5fa; }
.hcap-col:has(.hcap-b) .hcap-lbl{ color:#fbbf24; }

/* Coin toss animation — a realistic vertical flip.
   Real coins tumble end-over-end around their horizontal axis, not
   around the vertical axis (which would be a card-flip motion).
   The coin also travels through an arc (up, peak, down) to feel
   like a real toss. */
#ctCoin{ perspective: 800px; transform-style: preserve-3d; }
#ctCoin > * { transform-style: preserve-3d; backface-visibility: visible; }
@keyframes coinFlip {
  0%   { transform: translateY(0)     rotateX(0deg)    scale(1); }
  20%  { transform: translateY(-60px) rotateX(360deg)  scale(1.12); }
  50%  { transform: translateY(-90px) rotateX(900deg)  scale(1.18); }
  80%  { transform: translateY(-50px) rotateX(1440deg) scale(1.10); }
  100% { transform: translateY(0)     rotateX(1800deg) scale(1); }
}
#ctCoin.spinning > * { animation: coinFlip 1.8s cubic-bezier(.32,.18,.36,1) forwards; }
#ctCoin.win > * { animation: coinWin .6s ease-out forwards; }
@keyframes coinWin {
  0%   { transform: scale(1); filter: drop-shadow(0 0 0 rgba(255,213,74,0)); }
  50%  { transform: scale(1.18); filter: drop-shadow(0 0 18px rgba(255,213,74,.9)); }
  100% { transform: scale(1.08); filter: drop-shadow(0 0 12px rgba(255,213,74,.6)); }
}
.btn-go{width:100%;padding:12px;background:var(--g);color:#111;
        font-family:'Rajdhani',sans-serif;font-size:16px;font-weight:900;
        border:none;border-radius:8px;cursor:pointer;letter-spacing:1px;text-transform:uppercase;}
.btn-go:active{transform:scale(.98);}

/* ══ MATCHES PAGE ════════════════════════════════════════════════════════ */
.p-matches{overflow-y:auto;padding:12px 10px calc(var(--sb) + 12px);}
.section-hd{font-size:11px;letter-spacing:2px;text-transform:uppercase;
            color:var(--mu);margin:12px 0 8px;}
.match-card{
  display:flex;justify-content:space-between;align-items:center;
  padding:12px 14px;background:var(--s2);border-radius:10px;
  border:1px solid var(--bd);margin-bottom:8px;cursor:pointer;
}
.match-card:active{background:var(--s3);}
.match-card.cur{border-color:var(--g);}
.mc-left .mc-players{font-size:16px;font-weight:700;}
.mc-left .mc-meta{font-size:12px;color:var(--mu);margin-top:2px;}
.mc-score{font-family:'Share Tech Mono',monospace;font-size:24px;font-weight:900;color:var(--g);}
.no-matches{text-align:center;padding:40px;color:var(--mu);font-size:15px;}

/* ══ SETTINGS PAGE ═══════════════════════════════════════════════════════ */
.p-settings{overflow-y:auto;padding:12px 10px calc(var(--sb) + 12px);}
.setting-row{
  display:flex;justify-content:space-between;align-items:center;
  padding:14px 14px;background:var(--s2);border-radius:10px;
  border:1px solid var(--bd);margin-bottom:8px;
}
.sr-label{font-size:15px;font-weight:700;}
.sr-sub  {font-size:12px;color:var(--mu);margin-top:2px;}
.toggle{
  width:46px;height:26px;border-radius:999px;border:none;cursor:pointer;
  background:#444;position:relative;transition:background .2s;flex-shrink:0;
}
.toggle::after{
  content:'';position:absolute;left:3px;top:3px;
  width:20px;height:20px;border-radius:50%;background:#fff;transition:left .2s;
}
.toggle.on{background:var(--g);}
.toggle.on::after{left:23px;}
.cloud-status{
  padding:12px 14px;background:var(--s2);border-radius:10px;
  border:1px solid var(--bd);margin-bottom:8px;
}
.cs-label{font-size:12px;letter-spacing:1px;color:var(--mu);margin-bottom:4px;}
.cs-val{font-size:14px;font-weight:700;word-break:break-all;}
.cs-val.ok{color:var(--g);}
.cs-val.off{color:var(--mu);}


/* ══ LANDSCAPE ════════════════════════════════════════════════════════════ */
@media (min-width:700px) and (orientation:landscape){
  html,body{
    height:100vh;            /* fallback for browsers without dvh */
    height:100dvh;           /* dynamic viewport — accounts for address bar */
    overflow:hidden;
    background:#0e0e0e;
  }
  body{ margin:0; padding:0; }
  .root{ display:none !important; }
  .corr-toast,.corr-toast.on{ position:fixed; }

  /* ── 1920×1080 layout ─────────────────────────────────────────────────
     3-column left panel: Player A | Centre | Player B
     Fixed right panel: 360px with tabs
     Header: 60px spanning full width
  ─────────────────────────────────────────────────────────────────── */
  .ls-card{
    display:grid;
    grid-template-columns: 1fr 360px;
    grid-template-rows: 60px 1fr;
    width:100vw;
    height:100vh;            /* fallback */
    height:100dvh;           /* dynamic — fits visible viewport on tablets */
    padding:0; gap:0; overflow:hidden;
    position:fixed; top:0; left:0;
    transition: grid-template-columns .2s ease;
  }
  /* Widen the right panel on Matches/Settings (toggled by lsTab). Higher
     specificity than the per-tier `.ls-card` widths so it overrides them all;
     capped at 48vw so the live-scoring view keeps the majority of the screen. */
  .ls-card.ls-wide{ grid-template-columns: 1fr min(680px, 48vw); }

  /* ── HEADER ──────────────────────────────────────────────────────── */
  .ls-header{
    grid-column: 1 / -1;
    display:flex; align-items:center; gap:16px;
    padding:0 24px;
    background:#0a0a0a; border-bottom:2px solid rgba(255,255,255,.1);
  }
  .ls-header img{ height:46px; width:auto; display:block; flex-shrink:0; }
  .ls-header .ls-header-title{
    flex:1; text-align:left; font-size:22px; font-weight:700;   /* v5.11.373 — beside the logo, see .topbar-title */
    letter-spacing:4px; text-transform:uppercase; color:rgba(255,255,255,.4);
  }
  .ls-header .ws-row{
    display:flex; align-items:center; gap:8px;
    font-size:15px; color:rgba(255,255,255,.45); flex-shrink:0;
  }
  .ls-header .ws-row .dot{ width:14px; height:14px; border-radius:50%; background:#555; }
  .ls-header .ws-row .dot.live{ background:#9BE36A; animation:blink 2s infinite; }
  .ls-header .ws-row .dot.warn{ background:#f90; }
  @keyframes blink{0%,100%{opacity:1}50%{opacity:.35}}

  /* ── LEFT PANEL ──────────────────────────────────────────────────── */
  .ls-left{
    display:grid;
    /* avatar+score+stats | centre | stats+score+avatar */
    grid-template-columns: minmax(0,1fr) auto minmax(0,1fr);
    /* row1 auto = full-width LAST ACTION bar (order:-1); row2 = player cards + centre;
       row3 = detector strip. minmax(0,1fr) on the MIDDLE (NOT plain 1fr) lets it
       shrink below its content's min-height so it never overflows and COVERS the
       detector strip — the player sections have overflow:hidden and clip gracefully.
       Plain 1fr = minmax(auto,1fr) can't shrink, so tall stat tables spilled over the
       bottom detector row (covered it in fullscreen).
       row3 = 56px: RESERVE the detector strip's height at all times — even while
       it's display:none — so toggling it on never grows the grid past the
       viewport and clips it off the bottom, and the scoreboard doesn't jump when
       it appears. The middle row (minmax 0) always yields the 56px.
       v5.9.57 — was minmax(56px,auto). The `auto` max meant a wrapped strip
       could push the row taller than its reservation, which is the one way this
       row could still be driven off the bottom. #lsDetVision no longer wraps
       (see its rule), so a fixed height is now safe and, unlike minmax, is a
       number the rest of the layout can count on. */
       v5.9.61 — the SLACK now lives in row4, not row2.
       Row2 was minmax(0,1fr), so the player row grew to eat every spare pixel
       and the dead space opened up INSIDE it, underneath the shot-history
       boxes — pushing the detector strip onto the bottom edge and stranding
       roughly 40% of the panel as black between the two. v5.9.60 added a small
       fixed reserve at the foot, which moved the strip up a few pixels but left
       that pool untouched, because the 1fr simply reclaimed it.
       Now: row2 is minmax(0,auto) — tall enough for its content, no more — and
       row4 is an empty minmax(0,1fr) that absorbs whatever is left. Content
       packs to the top, the detector strip sits directly under the history it
       is read against, and all the emptiness collects at the bottom where it
       costs nothing.
       Both tracks keep a 0 minimum, so on a short screen row4 collapses first
       and then row2 can still shrink rather than overflowing the panel — the
       property the original minmax(0,1fr) was there to guarantee.
       v5.9.94 — the 56px row3 reservation is GONE: #lsDetVision lives inside
       .ls-centre now (under the match score box), so the strip no longer
       occupies a grid row of its own and the old reservation was just a dead
       band above the slack row. */
    grid-template-rows: auto minmax(0,auto) minmax(0,1fr);
    padding:16px 28px 12px;
    gap:10px 20px;
    overflow:hidden;
    border-right:2px solid rgba(255,255,255,.08);
    background:#141414;
    box-sizing:border-box;
    position:relative;       /* anchor for .ls-stats-toggle */
  }

  .ls-player-section{
    display:flex; flex-direction:column; align-items:center;
    gap:8px; min-width:0; overflow:hidden;
    position:relative;          /* v5.11.15 — anchor for the corner clock */
  }
  /* v5.11.15 — SHOT CLOCK IN THE OUTER TOP CORNER, with this frame's average
     underneath. Operator: 'show the shot timer over the players top left and
     right corner and maybe their current frame average shot time'. Absolutely
     positioned so it costs the column no height — the avatar, name and score
     keep every pixel they had. A sits top-LEFT, B top-RIGHT, so both badges
     hug the outside edges of the board and read as a matched pair. */
  .ls-corner-clock{
    position:absolute; top:0; z-index:3;
    display:flex; flex-direction:column; align-items:flex-start;
    gap:1px; line-height:1; pointer-events:none;
  }
  .ls-player-section:not(.right) .ls-corner-clock{ left:0;  align-items:flex-start; }
  .ls-player-section.right       .ls-corner-clock{ right:0; align-items:flex-end;   }
  /* Compact inside the badge — the standalone 30px centred clock was sized
     for the middle of the column, not a corner. */
  .ls-corner-clock .ls-clock{
    font-size:clamp(20px,1.9vw,30px); letter-spacing:2px;
    text-align:inherit !important; min-height:0;
  }
  .ls-corner-clock .ls-avg{
    font-family:'Share Tech Mono',monospace;
    font-size:clamp(10px,.85vw,14px); letter-spacing:1px; white-space:nowrap;
    color:rgba(255,255,255,.45); font-weight:700;
  }
  /* The badge takes no layout space, so it stays put when stats collapse —
     unlike the old in-column clock, which had to be hidden to free height. */
  .ls-left.stats-collapsed .ls-corner-clock{ display:flex; }

  /* v5.11.22 — APPLY HANDICAP is a decision, not a footnote. It was a 13px
     native checkbox on a dim line; on the wall TV and on a tablet it was easy
     to miss and easy to mis-tap. Operator: 'make the apply handicap check box
     much bigger'. The whole row is the tap target, the box itself is 30px,
     and a ticked row turns green so the state reads from across the room.
     Same class on all four instances (three setup panels + Match Info). */
  .hcap-apply{
    display:flex; align-items:center; flex-wrap:wrap; gap:10px 14px;
    margin:6px 0 8px; padding:10px 14px;
    border:2px solid rgba(255,255,255,.28); border-radius:12px;
    background:rgba(255,255,255,.06);
    font-size:clamp(17px,1.5vw,22px); font-weight:800; letter-spacing:.3px;
    color:#fff; cursor:pointer; user-select:none;
  }
  .hcap-apply input[type=checkbox]{
    width:30px; height:30px; margin:0; flex:0 0 auto; cursor:pointer;
    accent-color:#30D158;
  }
  .hcap-apply:has(input:checked){
    border-color:#30D158; background:rgba(48,209,88,.14);
  }
  /* v5.11.89 — 'Use Elo handicap' sits beside 'Apply handicap' */
  .hcap-ticks{ display:flex; flex-wrap:wrap; gap:8px; width:100%; }
  .hcap-ticks > .hcap-apply{ flex:1 1 220px; }
  .hcap-apply.hcap-elo input[type=checkbox]{ accent-color:#60a5fa; }
  .hcap-apply.hcap-elo:has(input:checked){ border-color:#60a5fa; background:rgba(96,165,250,.14); }
  .ls-meta{ font-size:12px; color:rgba(255,255,255,.6); min-height:14px; line-height:1.25; margin-top:3px; }
  /* v5.11.23 — New Match panel layout. */
  .nm{ width:100%; display:flex; flex-direction:column; gap:8px; }
  .nm-row{ display:flex; gap:8px; width:100%; align-items:flex-start; }
  .nm-bestof, .nm-mode{ flex-direction:column; gap:3px; }
  .nm-col{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:3px; }
  .nm .hcap-lbl{ font-size:12px; letter-spacing:1.2px; }
  .nm-a > .hcap-lbl{ color:#60a5fa; }
  .nm-b > .hcap-lbl{ color:#fbbf24; }
  /* players: bigger text, room on the right for the Login button */
  .nm-players .inp.memb-inp{
    font-size:clamp(17px,1.5vw,22px); font-weight:700;
    padding:12px 92px 12px 12px; min-height:52px;
  }
  .nm-a .inp.memb-inp{ border-color:rgba(96,165,250,.55); }
  .nm-b .inp.memb-inp{ border-color:rgba(250,204,21,.55); }
  .nm-select{
    font-size:clamp(16px,1.4vw,20px); font-weight:700; padding:10px 12px;
    background:#242424; color:#fff;
  }
  .nm-custom{ font-size:clamp(16px,1.4vw,20px); padding:10px 12px; }
  /* handicap steppers: [−] [ value ] [+], all three big enough for a finger */
  .hcap-stepper{ display:flex; align-items:stretch; gap:6px; }
  .hcap-stepper .hcap-inp{
    font-size:clamp(24px,2.4vw,36px); padding:8px 4px; min-width:0;
    -moz-appearance:textfield; appearance:textfield;
  }
  .hcap-stepper .hcap-inp::-webkit-outer-spin-button,
  .hcap-stepper .hcap-inp::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
  .hcap-btn{
    flex:0 0 clamp(46px,4.4vw,66px); font-size:clamp(24px,2.4vw,34px); font-weight:900;
    line-height:1; border-radius:10px; cursor:pointer; user-select:none;
    border:1px solid rgba(255,255,255,.28); background:rgba(255,255,255,.08); color:#fff;
  }
  .hcap-btn:active{ transform:scale(.96); }
  .hcap-col:has(.hcap-a) .hcap-btn{ border-color:rgba(96,165,250,.5); color:#dbeafe; }
  .hcap-col:has(.hcap-b) .hcap-btn{ border-color:rgba(250,204,21,.5); color:#fef3c7; }
  .nm .btn-go{ font-size:clamp(17px,1.5vw,22px); padding:14px; }
  .hcap-apply .hcap-hint{
    flex-basis:100%; font-size:12px; font-weight:500; letter-spacing:0;
    color:rgba(255,255,255,.55); margin-top:-2px;
  }

  /* v5.11.16 — FRAME / MATCH elapsed, under the match score. Two filled
     boxes, big mono digits, a label above each so neither needs a 'F:'/'M:'
     prefix to be understood. Frame is gold (it belongs to the frame score it
     sits under); match is blue, so a glance tells them apart by colour before
     the label is even read. Sized in vw so the wall TV gets the full 52px and
     a tablet still fits both on one line. */
  .ls-timers{
    display:flex; justify-content:center; align-items:stretch;
    gap:clamp(8px,1vw,18px); margin-top:clamp(4px,.7vh,10px);
  }
  .ls-tbox{
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:clamp(1px,.2vh,4px);
    padding:clamp(4px,.7vh,11px) clamp(10px,1.3vw,24px);
    border-radius:clamp(8px,.9vw,16px);
    min-width:clamp(84px,9vw,158px);
    background:rgba(255,213,74,.10); border:1px solid rgba(255,213,74,.32);
  }
  .ls-tbox-m{ background:rgba(110,175,255,.10); border-color:rgba(110,175,255,.32); }
  .ls-tlbl{
    font-size:clamp(9px,.78vw,14px); letter-spacing:2px; font-weight:800;
    text-transform:uppercase; color:rgba(255,255,255,.55); line-height:1;
  }
  .ls-tval{
    font-family:'Share Tech Mono',monospace; font-weight:700; line-height:1;
    font-size:clamp(26px,3vw,54px); letter-spacing:2px;
  }
  .ls-tbox-f .ls-tval{ color:var(--y); }
  /* v5.11.22 — frame not started yet: dim the digits, extend the label. */
  .ls-tbox-f.t-unstarted .ls-tval{ opacity:.45; }
  .ls-tbox-f.t-unstarted .ls-tlbl::after{ content:' \00b7 at break-off'; letter-spacing:1px; text-transform:none; }
  .ls-tbox-m .ls-tval{ color:#9ecbff; }
  /* Right section: content is still centered, stat table labels/values mirror */
  .ls-player-section.right{ align-items:center; }

  /* Stats-collapsed mode — stats tables and shot clocks hidden,
     score/avatar/name grow to fill the freed space. Operator
     toggles via the show-stats button (top-right of left panel). */
  .ls-left.stats-collapsed .ls-stat-table{
    display:none;
  }
  .ls-left.stats-collapsed .ls-score{
    font-size:clamp(120px, 14vw, 260px);
    padding:clamp(3px, .4vw, 8px) clamp(10px, 1.2vw, 24px);
  }
  .ls-left.stats-collapsed .ls-pname{
    font-size:clamp(28px, 3vw, 56px);
  }
  .ls-left.stats-collapsed .ls-avatar{
    width:clamp(150px, 14vw, 240px);
    height:clamp(150px, 14vw, 240px);
  }
  .ls-left.stats-collapsed .ls-frames-big{
    font-size:clamp(64px, 7vw, 130px);
  }
  .ls-left.stats-collapsed .ls-brk{
    font-size:clamp(80px, 9vw, 170px);
  }

  /* Show/hide stats toggle — sits in the top-right corner of the
     left panel. Small pill button, low-key when stats are showing,
     more prominent when collapsed (so operator remembers to bring
     them back). */
  .ls-stats-toggle{
    position:absolute; top:8px; right:12px;
    background:rgba(255,255,255,.06);
    border:1px solid rgba(255,255,255,.15);
    color:rgba(255,255,255,.7);
    padding:6px 12px; border-radius:8px;
    font-family:'Rajdhani',sans-serif;
    font-size:11px; font-weight:700; letter-spacing:1.5px;
    text-transform:uppercase; cursor:pointer;
    z-index:5;
    transition:background .15s, color .15s;
  }
  .ls-stats-toggle:hover{
    background:rgba(255,255,255,.12); color:#fff;
  }
  /* v5.11.257 — the shot history only shows when the stats panel is collapsed
     (operator: 'hide the ball history if show stats active'): with the stats
     table up there is no room for it beside the names, so it stays hidden until
     the stats are hidden, where the freed space is used by the history grid. */
  .ls-left:not(.stats-collapsed) .ls-shot-history{ display:none !important; }
  /* v5.11.258 — stream-only box: the AI toggle has nothing to switch */
  body.stream-box #togAI, body.stream-box #lsTogAI{ display:none !important; }
  .ls-left.stats-collapsed .ls-stats-toggle{
    background:rgba(255,213,74,.18);
    border-color:rgba(255,213,74,.45);
    color:#FFD54A;
  }

  /* Frame Scores history button — sits below Hide Stats toggle */
  /* v5.11.56 — beside Hide Stats, not under it: two stacked pills used two
     rows of a corner the scores want. Operator: "make the Show/Hide Stats /
     Frames buttons on one row instead of two". */
  .ls-frame-scores-btn{
    position:absolute; top:8px; right:108px;
    background:rgba(255,255,255,.04);
    border:1px solid rgba(255,255,255,.12);
    color:rgba(255,255,255,.55);
    padding:4px 10px; border-radius:7px;
    font-family:'Rajdhani',sans-serif;
    font-size:10px; font-weight:700; letter-spacing:1.2px;
    text-transform:uppercase; cursor:pointer;
    z-index:5;
    transition:background .15s, color .15s, border-color .15s;
  }
  .ls-frame-scores-btn:hover{
    background:rgba(255,255,255,.10); color:#fff;
    border-color:rgba(255,255,255,.25);
  }
  .ls-frame-scores-btn.has-frames{
    color:rgba(155,227,106,.8);
    border-color:rgba(155,227,106,.25);
  }

  /* SHOT CLOCK badge — top-LEFT corner of the card, just below the header,
     vertically aligned with the Hide Stats / 📋 Frames buttons.
     Direct child of .ls-card (NOT .ls-left) so it survives the mode swaps
     that hide .ls-left — multiplayer (.mp-mode) and practice (.practice-mode)
     both hide the left column, but the shot clock must keep showing. Absolute
     (relative to the position:fixed .ls-card) so it doesn't take a grid cell.
     top:74px clears the 60px header + the ~14px inset that used to sit inside
     .ls-left. Show/hide + colour are driven inline by _scStart()/_scStop()/
     _scRender() — this rule is placement and base appearance only. */
  .ls-card > #lsShotClockBadge{
    position:absolute; top:74px; left:12px; z-index:5;
    font-family:'Share Tech Mono',monospace; font-weight:900;
    font-size:clamp(70px,9vw,130px); letter-spacing:3px; line-height:1;  /* v5.3.85: ~5x bigger (was 26px) so the 20s shot clock reads from the table */
    padding:14px 28px; border-radius:16px;
    background:rgba(0,0,0,.55); color:#7EE787;
    border:2px solid rgba(126,231,135,.6);
    box-shadow:0 6px 20px rgba(0,0,0,.45);
    pointer-events:none; transition:opacity .12s;
  }
  /* v5.9.107/112 — 2/3 size in ANY practice mode (body.practice-clock covers
     full_frame too, which renders the 2-player panels and never gets
     .practice-mode): count-up ambience, not a countdown for the room. */
  body.practice-clock .ls-card > #lsShotClockBadge{
    font-size:clamp(46px,6vw,86px); letter-spacing:2px;
    padding:9px 18px; border-radius:12px;
  }

  /* Frame scores dropdown panel */
  .ls-frame-scores-panel{
    display:none;
    position:absolute; top:62px; right:8px;
    width:220px;
    background:#1a1a1a;
    border:1px solid rgba(255,255,255,.12);
    border-radius:10px;
    box-shadow:0 8px 32px rgba(0,0,0,.6);
    z-index:20;
    overflow:hidden;
  }
  .ls-frame-scores-panel.open{ display:block; }
  .ls-fsp-header{
    padding:8px 12px 6px;
    font-size:10px; font-weight:700; letter-spacing:2px;
    text-transform:uppercase; color:rgba(255,255,255,.45);
    border-bottom:1px solid rgba(255,255,255,.07);
  }
  .ls-fsp-body{
    max-height:240px; overflow-y:auto;
  }
  .ls-fsp-row{
    display:grid; grid-template-columns:28px 1fr auto;
    align-items:center; gap:6px;
    padding:6px 12px;
    border-bottom:1px solid rgba(255,255,255,.04);
    font-size:12px;
  }
  .ls-fsp-row:last-child{ border-bottom:none; }
  .ls-fsp-num{
    color:rgba(255,255,255,.35); font-weight:700;
    font-size:10px; letter-spacing:1px;
  }
  .ls-fsp-scores{
    font-family:'Share Tech Mono',monospace;
    color:#fff; font-size:13px; font-weight:700;
    letter-spacing:1px;
  }
  .ls-fsp-winner{
    font-size:10px; font-weight:700; letter-spacing:1px;
    text-transform:uppercase;
    color:rgba(155,227,106,.8);
    background:rgba(155,227,106,.08);
    border:1px solid rgba(155,227,106,.18);
    border-radius:4px; padding:2px 6px;
    white-space:nowrap;
  }
  .ls-fsp-empty{
    padding:14px 12px;
    font-size:11px; color:rgba(255,255,255,.3);
    text-align:center; font-style:italic;
  }

  /* Avatar — clickable */
  .ls-avatar{
    /* v5.10.334 — was 130px. Shrunk to give the shot history the height,
       but floored at a size the login QR still scans from: Match Setup
       already renders the same QR in an 88px box and is known good, so
       96px keeps a margin and leaves room for the caption strip that the
       88px variant has to drop. Operator: 'make the avatar smaller just
       enough to display QR for logins so there's more room for history
       boxes'. */
    width:96px; height:96px; border-radius:50%;
    background:#222; border:3px solid rgba(255,255,255,.15);
    overflow:hidden; flex-shrink:0; position:relative;
    display:flex; align-items:center; justify-content:center;
    transition:border-color .3s, box-shadow .3s;
    cursor:pointer;
    -webkit-tap-highlight-color: rgba(155,227,106,.3);
    user-select:none; -webkit-user-select:none;
  }
  .ls-avatar:hover{ border-color:rgba(255,255,255,.35); }
  .ls-avatar:active{ transform:scale(.96); }
  .ls-avatar.active{
    border-color:#9BE36A;
    box-shadow:0 0 24px rgba(155,227,106,.3);
  }
  /* img must not capture pointer events — clicks must reach the div */
  .ls-avatar img{ width:100%; height:100%; object-fit:cover; pointer-events:none; }
  /* Empty-slot "Scan to log in" QR overlay — shown over the avatar when a
     player card has no assigned player. pointer-events:none so a tap still
     falls through to the avatar div (tap-to-choose stays available). Hidden
     until the pairing session returns a QR (data-on="1"); when the cloud
     login service isn't live it stays empty and the default avatar shows. */
  /* v5.10.334 — padding down with the avatar, so the QR itself did not
     shrink by as much as its box. */
  .ls-login-qr{ position:absolute; inset:0; display:none; align-items:center;
    justify-content:center; background:#fff; padding:5px; box-sizing:border-box;
    pointer-events:none; }
  .ls-login-qr[data-on="1"]{ display:flex; }
  .ls-login-qr img, .ls-login-qr svg{ width:100%; height:100%; }
  .ls-login-qr .cap{ position:absolute; bottom:1px; left:0; right:0; text-align:center;
    font-size:9px; font-weight:800; color:#111; letter-spacing:.4px; text-transform:uppercase; }
  .ls-avatar:has(.ls-login-qr[data-on="1"]){ border-radius:14px; background:#fff; }
  /* Match Setup avatars: drop the overlay caption (it scrambled the small QR)
     and tighten padding so the QR fills more of the 88px box and stays scannable. */
  .mi-av .ls-login-qr{ padding:4px; }
  .mi-av .ls-login-qr .cap{ display:none; }

  /* Name */
  .ls-pname{
    flex-shrink:0;   /* v5.11.239 — overflow:hidden let a tight column squash the name; the history gives way instead */
    font-size:clamp(22px, 2.2vw, 38px); font-weight:900; color:#666;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
    letter-spacing:1px; max-width:100%; text-align:center;
  }
  .ls-pname.on{ color:#9BE36A; }

  /* Score box — the headline element; intentionally heavier than the
     surrounding stats. Already weight 900, but we add a subtle dark
     text-shadow + a stronger outer glow so the digits feel weighty
     and dominant against the rest of the panel. */
  /* Score box — the headline element. Scales with viewport width
     via clamp() so wider screens get a bigger, more dominant score.
     Floor 96px (small landscape tablets), cap 200px (4K displays).
     11vw is the sweet spot — at 1920px it hits ~211px and clamps;
     at 1280px it hits ~141px which is bigger than the old fixed 128. */
  .ls-score{
    font-family:'Arial Black','Arial Bold',Arial,sans-serif;
    font-size:clamp(100px, 12vw, 210px);
    font-weight:900;
    font-weight:900;
    line-height:1; color:#000; background:#fff;
    border-radius:clamp(8px, .8vw, 14px);
    padding:clamp(2px, .3vw, 6px) clamp(8px, 1vw, 18px);
    display:inline-block; width:auto; align-self:center; text-align:center;
    box-shadow:0 6px 24px rgba(0,0,0,.65),
               0 0 0 2px rgba(255,255,255,.06),
               inset 0 -3px 0 rgba(0,0,0,.08);
    text-shadow:0 1px 0 rgba(0,0,0,.10);
    letter-spacing:-1px;
    transition:background .25s, color .25s, box-shadow .25s;
  }
  .ls-score.on{
    background:#9BE36A; color:#111;
    box-shadow:0 10px 36px rgba(155,227,106,.45),
               0 0 0 2px rgba(155,227,106,.15),
               inset 0 -4px 0 rgba(0,0,0,.10);
  }

  /* ── Shot history boxes (landscape) ───────────────────────────────── */
  .ls-shot-history{
    /* v5.10.334 — 12 across by 5 down. Same sixty-box budget as the 15x4
       it replaces and the 10x6 the TV layout used, but a squarer block
       that reads at arm's length on a tablet and from across a room on a
       TV, which is what let the separate big-screen layout go away. Sixty
       is the display budget the server also trims to; see the note on
       renderShotHistory for why that is the right number. */
    display:grid;
    grid-template-columns:repeat(var(--shot-cols,12), var(--shot-box,18px));
    grid-template-rows:repeat(var(--shot-rows,5), var(--shot-box,18px));
    gap:2px;
    justify-content:center;
    width:100%;
    padding:2px 0;
  }
  .ls-shot-box{
    width:18px; height:18px;
    border-radius:50%;
    border:1px solid rgba(255,255,255,.12);
    background:rgba(255,255,255,.04);
    display:flex; align-items:center; justify-content:center;
    font-size:5px; font-weight:800;
    color:rgba(255,255,255,.5);
    overflow:hidden; position:relative; cursor:default;
  }
  .ls-shot-box.empty{ border-color:rgba(255,255,255,.06); background:transparent; }
  /* Ball circle inside box */
  .ls-shot-box .shb{
    width:86%; height:86%; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    /* 3D sphere: soft drop shadow + bright top highlight + dark lower-inner
       shading so the flat disc reads as a rounded ball. */
    box-shadow:0 2px 5px rgba(0,0,0,.55),
               inset 0 2px 3px rgba(255,255,255,.40),
               inset 0 -3px 5px rgba(0,0,0,.45);
    font-size:7px; font-weight:900; line-height:1; color:#fff;
    flex-shrink:0;
  }
  /* Radial gradients: bright specular at top-left → deep base colour, giving
     each potted ball a glossy spherical look (matches .next-ball). */
  .shb.c-red   {background:radial-gradient(circle at 34% 28%,#f15a5a,#8b0000);}
  .shb.c-yellow{background:radial-gradient(circle at 34% 28%,#ffe24d,#9c7c00);color:#111;}
  .shb.c-green {background:radial-gradient(circle at 34% 28%,#34c264,#0a4a1f);}
  .shb.c-brown {background:radial-gradient(circle at 34% 28%,#a86e3e,#3f2208);}
  .shb.c-blue  {background:radial-gradient(circle at 34% 28%,#3f95ec,#053067);}
  .shb.c-pink  {background:radial-gradient(circle at 34% 28%,#f274c4,#9a2870);}
  .shb.c-black {background:radial-gradient(circle at 34% 28%,#5a5a5a,#080808);
                box-shadow:0 2px 5px rgba(0,0,0,.8),
                           inset 0 2px 3px rgba(255,255,255,.18),
                           inset 0 -3px 5px rgba(0,0,0,.6),
                           0 0 0 2px #888;}
  .shb.c-white {background:radial-gradient(circle at 34% 28%,#ffffff,#c4c4c4);color:#333;}
  /* Outcome types (no ball) */
  .ls-shot-box.t-miss  { background:rgba(255,255,255,.06); border-color:rgba(255,255,255,.2); color:rgba(255,255,255,.4); font-size:7px; font-weight:900; }
  /* v5.10.335 — a foul is a SOLID RED SQUARE with the value on it.
     v5.10.333 drew a red X behind the number. Two problems, both visible on
     iCue-s4: the opponent-turn mark is already a red cross, so the row had
     two crosses meaning different things, and in the light theme the X never
     painted at all because the theme's own .ls-shot-box background rule sat
     later in the cascade and replaced it. Operator: 'the F does not stand
     out'.
     Shape is the strongest cue available here. Every other box in the strip
     is a circle, so a filled SQUARE is found by the eye before any glyph is
     read, at any size, and it cannot be confused with the opponent cross.
     Solid fill rather than a tint, white numerals, and a dark rim so it holds
     on both the dark board and the blue one.
     Appearance lives in ONE rule; a skin overrides --foul-fill / --foul-edge
     and nothing else. */
  .ls-shot-box.t-foul{
    --foul-fill:#E11D1D;
    --foul-edge:#ff8a8a;
    background:var(--foul-fill);
    border:1px solid var(--foul-edge);
    border-radius:22%;
    color:#fff;
    font-weight:900;
    font-size:calc(var(--shot-box, 18px) * .62);
    line-height:1;
    letter-spacing:-.02em;
    box-shadow:0 0 0 1px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.45);
    text-shadow:0 1px 2px rgba(0,0,0,.55);
  }
  /* v5.11.302 — scratch: a white ball on the red foul square, value in red on the ball. */
  .ls-shot-box.t-foul.t-scratch .shb{
    width:80%; height:80%;
    color:#C8102E; text-shadow:none;
    font-size:calc(var(--shot-box, 18px) * .46); font-weight:900;
  }
  .ls-shot-box.t-undo  { background:rgba(255,200,0,.1); border-color:rgba(255,200,0,.3); color:#ffd54a; }
  .ls-shot-box.t-switch{ background:rgba(80,160,255,.1);border-color:rgba(80,160,255,.3);color:#90caf9; }
  /* Opponent's turn slot — red ✕ marking the shooter's pot/miss on the other row */
  .ls-shot-box.t-opp   { background:rgba(200,30,30,.10); border-color:rgba(200,30,30,.35); color:#ff5252; font-size:9px; font-weight:800; }

  /* Shot clock — always visible, dimmed when inactive (keeps layout stable) */
  .ls-clock{
    font-size:30px; font-weight:700;
    letter-spacing:3px; font-family:'Share Tech Mono',monospace;
    text-align:center; min-height:1.2em;
    color:rgba(255,255,255,.2);        /* dim placeholder */
    transition:color .25s;
  }
  .ls-clock.active{ color:var(--y); } /* bright when this player's turn */

  /* ── STATS TABLE ─────────────────────────────────────────────────── */
  /* Two-column table: label | value
     Player A (left): label on left, value on RIGHT (toward centre)
     Player B (right): value on LEFT (toward centre), label on right
     achieved with direction:rtl on the right-player table rows        */
  .ls-stat-table{
    width:100%; border-collapse:collapse; margin-top:8px;
    table-layout:fixed;
  }
  .ls-stat-table td{
    padding:7px 6px;
    border-bottom:1px solid rgba(255,255,255,.06);
  }
  .ls-stat-table .st-lbl{
    font-size:14px; letter-spacing:1.5px; text-transform:uppercase;
    color:rgba(255,255,255,.55); font-weight:700; white-space:nowrap;
    text-align:center !important;
  }
  .ls-stat-table .st-val{
    font-size:24px; font-weight:700; color:rgba(255,255,255,.9);
    font-family:'Share Tech Mono',monospace;
    white-space:nowrap;
    text-align:center !important;
  }
  .ls-stat-table .st-val.hi{ color:#FFD54A; }
  .ls-stat-table .st-val.good{ color:#9BE36A; }
  .ls-stat-table .st-val.bad{ color:#FF6060; }
  /* Percentage bar fill — used for Pot % and Long pot % rows.
     Renders a thin horizontal gradient underlay scaled to the
     percentage value. The td's value text floats above. */
  .ls-stat-table td.st-val-pct{
    position:relative;
    overflow:hidden;
  }
  .ls-stat-table td.st-val-pct .pct-bar{
    position:absolute; left:0; top:auto; bottom:2px;
    height:3px; width:0%;
    background:linear-gradient(90deg, rgba(155,227,106,.40), rgba(155,227,106,.85));
    border-radius:2px;
    transition:width .6s ease;
    pointer-events:none;
  }
  .ls-stat-table td.st-val-pct .pct-sub{ font-size:.68em; opacity:.65; margin-left:4px; }
  .ls-stat-table td.st-val-pct .pct-bar.b{
    /* For player B (right side), bar grows from right edge inward */
    left:auto; right:0;
    background:linear-gradient(270deg, rgba(155,227,106,.40), rgba(155,227,106,.85));
  }
  /* Match column gets a dimmer bar */
  .ls-stat-table .st-val-match.st-val-pct .pct-bar{
    background:linear-gradient(90deg, rgba(255,255,255,.18), rgba(255,255,255,.45));
  }
  .ls-stat-table .st-val-match.st-val-pct .pct-bar.b{
    background:linear-gradient(270deg, rgba(255,255,255,.18), rgba(255,255,255,.45));
  }
  /* Match stats column — dimmer, pill background to read as context */
  .ls-stat-table .st-val-match{
    font-size:18px; font-weight:600;
    color:rgba(255,255,255,.55);
    background:rgba(0,0,0,.25);
    border-radius:6px;
  }
  .ls-stat-table .st-val-frame{
    text-align:center !important;
    background:rgba(255,255,255,.04);
    border-radius:6px;
    font-weight:500;     /* not bold — readable but un-emphasised */
  }

  /* Section headers always centred, span all 3 columns */
  .ls-stat-table .st-stat-section-hdr{ text-align:center; }

  /* Column header row labels — centered, dimmer */
  .st-col-hdr{
    font-size:11px; letter-spacing:2px; text-transform:uppercase;
    color:rgba(255,255,255,.45); font-weight:800;
    text-align:center !important;
    padding:6px 6px !important; border-bottom:1px solid rgba(255,255,255,.10) !important;
  }
  /* Distinguish "This Match" header column with a subtle highlight */
  .ls-player-section.left .st-col-hdr:first-child,
  .ls-player-section.right .st-col-hdr:last-child{
    background:rgba(255,255,255,.03);
  }

  /* Match stats hidden until at least one frame is complete */
  .ls-match-stats{ display:none; }
  .ls-match-stats.visible{ display:table-row-group; }

  /* ── CENTRE COLUMN ───────────────────────────────────────────────── */
  .ls-centre{
    display:flex; flex-direction:column; align-items:center;
    gap:8px; flex-shrink:0; min-width:220px;
  }
  /* Centre stacking order (top→bottom):
     NEXT ball → BREAK score → break balls → Frame # → timers →
     match score → pills → snook. */
  .ls-centre .ls-next-brk      { order: -2; }  /* NEXT ball + BREAK (side by side) → top */
  /* NEXT ball and BREAK score share one horizontal row */
  .ls-centre .ls-next-brk{
    display:flex; align-items:center; justify-content:center;
    gap:clamp(14px, 1.8vw, 40px); flex-wrap:wrap;
  }
  /* .ls-brk-who, .ls-balls-row (break balls) stay order 0 */
  .ls-centre .ls-pills-row     { order: 1; }   /* Diff/Rem/Reds → above Frame # */
  .ls-centre .ls-bo            { order: 2; }   /* Frame # → below the pills */
  .ls-centre .ls-score-wrap    { order: 3; }   /* match-score wrapper — below Frame # */
  /* v5.9.102 — timers UNDER the match score box (same order as the wrapper,
     later in source, so they render after it). */
  .ls-centre .ls-timers        { order: 3; }
  .ls-centre .ls-snook         { order: 4; }
  /* v5.11.57 — LAST ACTION is the last thing in the column. The centre is a
     flex column with explicit order values (NEXT -2 … snook 4), so v5.11.56's
     element, carrying no order, defaulted to 0 and landed mid-column.
     Operator: "the last action line is at wrong place, need to be all the way
     in bottom". */
  .ls-centre .ls-last          { order: 9; }
  /* … and the two corner buttons come off the corner into this column.
     v5.11.57 put them at the top, above NEXT/BREAK; the operator then asked
     for the very bottom instead: "the Show/hide Stats two buttons move to
     very bottom under last action". Order 10 — after .ls-last (9). */
  .ls-centre .ls-topbtns       { order: 10; margin-top:6px; }
  .ls-centre .ls-topbtns{
    display:flex; align-items:center; justify-content:center; gap:8px;
    position:relative;                 /* the Frames dropdown anchors here */
  }
  .ls-centre .ls-stats-toggle,
  .ls-centre .ls-frame-scores-btn{
    position:static; top:auto; right:auto;   /* no longer corner overlays */
  }
  /* the row sits at the foot of the column, so the Frames list opens UPWARDS
     — anchored to the bottom of the row rather than hanging off-screen. */
  .ls-centre .ls-frame-scores-panel{ top:auto; bottom:34px; right:auto; left:50%;
                                     transform:translateX(-50%); }

  /* Frame counter (MATCH SCORE) — demoted below the NEXT ball: SMALLER now. */
  .ls-frames-big{
    display:inline-flex; align-items:baseline; gap:8px;
    font-size:clamp(26px, 2.6vw, 46px); font-weight:900; color:#fff;
    font-family:'Share Tech Mono',monospace; line-height:1;
    background:linear-gradient(135deg, #FFD54A 0%, #E6A500 100%);
    color:#1a1a1a;
    padding:clamp(4px, .4vw, 8px) clamp(10px, 1vw, 18px);
    border-radius:8px;
    box-shadow:0 2px 12px rgba(255, 213, 74, .4),
               inset 0 -3px 0 rgba(0,0,0,.15),
               inset 0 1px 0 rgba(255,255,255,.4);
  }
  .ls-frames-big .sep{ font-size:.5em; color:rgba(0,0,0,.55); font-weight:700; }

  .ls-bo{
    background:#484848; color:#ddd; font-weight:900; border-radius:6px;
    padding:4px 18px; font-size:18px; letter-spacing:1px;
  }

  /* Current break */
  .ls-brk{
    font-size:clamp(64px, 7vw, 130px);
    font-weight:900; color:#9BE38D; line-height:1;
    font-family:'Share Tech Mono',monospace; text-align:center;
  }
  .ls-brk .lbl{
    font-size:12px; letter-spacing:3px; opacity:.6; display:block;
    text-align:center; text-transform:uppercase;
  }
  .ls-brk-who{ display:none; }  /* player name is already on the big player cards — hide the centre one above the next ball */

  /* Next ball — promoted to the BIG, prominent element (bigger than score). */
  .ls-next-row{ display:flex; flex-direction:column; align-items:center; gap:6px; }
  .next-ball.ls-lg{
    width:clamp(140px, 13vw, 210px); height:clamp(140px, 13vw, 210px);
    border-radius:50%;
    box-shadow:0 10px 34px rgba(0,0,0,.55); font-size:clamp(28px, 2.6vw, 44px);
  }

  /* Pills — DIFF / REDS / REM as a single horizontal row.
     Previously stacked vertically (3 rows ≈ 160px height) — now
     side by side saves ~100px of vertical space on small tablets. */
  .ls-pills-row{
    display:flex; flex-direction:row; gap:6px;
    align-items:stretch; width:100%;
    margin-top:6px;
  }
  .ls-pill{
    border:1px solid rgba(255,255,255,.2);
    background:rgba(255,255,255,.06);
    padding:14px 12px;
    border-radius:14px;
    font-weight:900; color:#cfcfcf;
    display:flex; flex-direction:column; align-items:center;
    justify-content:center;
    gap:5px;
    flex:1;
    min-height:84px;
    box-shadow:0 4px 12px rgba(0,0,0,.25);
  }
  .ls-pill .pill-lbl{
    font-size:12px; letter-spacing:1.5px; text-transform:uppercase;
    opacity:.7; font-weight:700;
  }
  .ls-pill .pill-val{
    font-size:44px; line-height:1; font-weight:900;
    font-family:'Share Tech Mono',monospace;
    font-variant-numeric:tabular-nums;
  }
  .ls-pill.diff{
    color:#FFD54A;
    border-color:rgba(255,213,74,.45);
    background:linear-gradient(135deg, rgba(255,213,74,.18), rgba(255,213,74,.05));
    box-shadow:0 4px 16px rgba(255,213,74,.12);
  }
  .ls-pill.reds{
    color:#FF6060;
    border-color:rgba(255,77,77,.45);
    background:linear-gradient(135deg, rgba(255,77,77,.18), rgba(255,77,77,.05));
    box-shadow:0 4px 16px rgba(255,77,77,.12);
  }
  .ls-pill.rem{
    color:#7EE787;
    border-color:rgba(126,231,135,.45);
    background:linear-gradient(135deg, rgba(126,231,135,.16), rgba(126,231,135,.04));
    box-shadow:0 4px 16px rgba(126,231,135,.10);
  }

  /* Snooker badge */
  /* v5.11.60 — snookers required is the one line on this column that changes
     how the rest of the frame is played, and it was a small dark pill that read
     like every other chip. Double size on a light red ground so it is seen from
     the table. Operator: "make snooker required display double size and light
     red background". Only shows at all when the count is above zero (.on). */
  .ls-snook{
    display:none; padding:10px 26px; border-radius:999px;
    /* v5.11.61 — reversed out: RED ground, light letters, label and count in
       the same colour. v5.11.60 had it the other way round (light ground, dark
       red ink) and the number was a deeper red than the word, so the line read
       as two pieces. Operator: "Snookers: 7 all in same reversed letters but
       background color to red". */
    background:linear-gradient(180deg,#e53935 0%,#b3261e 100%);
    color:#fff; font-weight:900;
    font-size:30px; line-height:1.1; letter-spacing:.5px;
    border:1px solid rgba(255,255,255,.35);
    box-shadow:0 2px 12px rgba(200,30,30,.45);
    text-shadow:0 1px 2px rgba(0,0,0,.35);
  }
  .ls-snook b{ color:#fff; }   /* the count reads as part of the same line */
  .ls-snook.on{ display:block; }

  /* ── BALLS ROW (spans all 3 columns) ─────────────────────────────── */
  .ls-balls-row{
    grid-column: 1 / -1;
    display:flex; align-items:center; justify-content:center; gap:20px;
  }
  .ls-reds{ display:flex; gap:5px; flex-wrap:wrap; justify-content:center; }
  /* Break ball dots — same as broadcast overlay */
  .ls-bb{
    border-radius:50%;display:inline-flex;align-items:center;justify-content:center;
    width:30px;height:30px;
    font-size:12px;font-weight:900;color:#fff;flex-shrink:0;
    box-shadow:0 2px 6px rgba(0,0,0,.6),inset 0 1px 0 rgba(255,255,255,.2);
  }
  .ls-rd{
    width:18px; height:18px; border-radius:50%;
    background:#c62828; box-shadow:0 0 5px rgba(198,40,40,.5); transition:all .3s;
  }
  .ls-rd.gone{ background:rgba(255,255,255,.07); box-shadow:none; }
  .ls-cols{ display:flex; gap:10px; }
  .ls-cb{ width:26px; height:26px; border-radius:50%; transition:all .3s; }
  .ls-cb.gone{ opacity:.18; transform:scale(.75); filter:grayscale(60%); }
  .ls-cb[data-c=yellow]{background:#f9a825;} .ls-cb[data-c=green]{background:#2e7d32;}
  .ls-cb[data-c=brown]{background:#5d4037;}  .ls-cb[data-c=blue]{background:#1565c0;}
  .ls-cb[data-c=pink]{background:#e91e8c;}   .ls-cb[data-c=black]{background:#263238;border:1px solid #546e7a;}

  /* Last action tags (AI / manual) — same style as portrait .last-act tags */
  .ls-last .tag-ai{
    color:#7fff7f; font-size:11px; font-weight:700; letter-spacing:.5px;
    background:rgba(0,160,0,.2); border:1px solid rgba(0,200,0,.3);
    border-radius:6px; padding:2px 8px; margin-left:8px; vertical-align:middle;
  }
  .ls-last .tag-manual{
    color:#FFD54A; font-size:11px; font-weight:700; letter-spacing:.5px;
    background:rgba(255,200,0,.15); border:1px solid rgba(255,200,0,.3);
    border-radius:6px; padding:2px 8px; margin-left:8px; vertical-align:middle;
  }
  /* v5.11.56 — inside the centre column and styled like portrait's .last-act:
     a muted line under a hairline, not a heavy black bar. */
  .ls-last{
    width:100%; margin-top:auto; align-self:stretch;
    font-size:15px; font-weight:700;
    color:var(--mu); text-align:center;
    padding:8px 12px 2px;
    background:transparent;
    border:none; border-top:1px solid var(--bd);
    border-radius:0;
    letter-spacing:.5px;
    /* v5.9.138 — one line, never reflowing: nowrap + ellipsis guarantee it.
       v5.11.56 drops the 64px box height with the black bar it belonged to;
       a hairline-topped line sizes to its own text like portrait does. */
    box-sizing:border-box;
    display:flex; align-items:center; justify-content:center;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }

  /* ── Landscape detector ball view ──────────────────────────────────────
     v5.9.94 — sits in the CENTRE COLUMN directly under the match score box
     (it is a .ls-centre flex item now, not a full-width .ls-left grid row).
     order:3 matches the match-score wrapper; being later in source, it renders
     after the score and before .ls-snook (order:4). Sized down from the old
     full-width strip so one count + six dots fit the ~300px column; if a
     narrow tier still can't fit, it scrolls sideways rather than wrapping —
     same height-stability reasoning as v5.9.57. */
  #lsDetVision{
    /* v5.9.100 — order 1 (was 3): sits directly UNDER the Diff/Rem/Reds pill
       row (also order 1, earlier in source), above the Frame # line. */
    order: 1;
    align-self: stretch;
    max-width: 100%;
    justify-content: center;
    background: rgba(0,0,0,.25);
    border: 1px solid rgba(255,255,255,.08);
    border-radius: 10px;
    padding: 5px 10px;
    gap: 8px;
    row-gap: 6px;   /* `gap` resets .det-vision's row-gap — restate (v5.9.16) */
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }
  #lsDetVision::-webkit-scrollbar{ display:none; }
  #lsDetVision .det-lbl{ font-size:12px; }
  #lsDetVision .rd{ width:20px; height:20px; }
  #lsDetVision .cb{ width:24px; height:24px; }
  #lsDetVision .det-cols{ gap:6px; }
  #lsDetVision b{ font-size:18px; }
  #lsDetVision .det-nb{ width:30px; height:30px; font-size:10px; }
  #lsDetVision .det-onball-lbl, #lsDetVision .det-rem-lbl{ font-size:12px; }

  /* ── RIGHT PANEL: tabs + buttons ─────────────────────────────────── */
  .ls-right{
    display:flex; flex-direction:column;
    padding:0; overflow:hidden; gap:0;
    background:#1a1a1a; box-sizing:border-box;
  }
  .ls-tabs{
    display:flex; width:100%; flex-shrink:0;
    border-bottom:1px solid rgba(255,255,255,.12); background:#111;
  }
  .ls-tab{
    flex:1; padding:12px 4px; font-size:12px; font-weight:700; letter-spacing:.5px;
    text-transform:uppercase; color:rgba(255,255,255,.4);
    background:transparent; border:none; cursor:pointer;
    border-bottom:2px solid transparent; transition:all .2s;
  }
  .ls-tab.on{ color:#9BE36A; border-bottom-color:#9BE36A; }
  .ls-tab:active{ background:rgba(255,255,255,.05); }
  .ls-panel{
    display:none; flex-direction:column; align-items:center;
    gap:8px; padding:12px 10px; overflow-y:auto;
    width:100%; flex:1; box-sizing:border-box;
  }
  .ls-panel.on{ display:flex; }


  /* Ball buttons */
  .ls-right .bgrid{
    display:grid; grid-template-columns:repeat(3,1fr);
    gap:6px; width:100%; flex-shrink:0;
  }
  .ls-right .bb{
    width:100%; height:auto; aspect-ratio:1/1;
    border-radius:50%; font-size:34px;
    border:0; box-shadow:0 5px 14px rgba(0,0,0,.4);
    touch-action:manipulation; box-sizing:border-box;
    display:flex; align-items:center; justify-content:center;
    color:#fff; font-weight:900; cursor:pointer;
  }
  .ls-right .bb:active{ transform:scale(.95); }
  .ls-right .bb.ctrl{ font-size:10px; }
  .ls-right .bb.ctrl span.ico{ font-size:28px; }
  .ls-right .bb.ctrl.switch-btn{ background:#f0f0f0; color:#111; border:1px solid rgba(0,0,0,.2); }
  .ls-right .bb.ctrl.switch-btn span.ico{ font-size:30px; }
  .ls-right .bb.ctrl.back-btn{ background:#3a3a3a; border:1px solid rgba(255,255,255,.25); }
  .ls-right .bb.ctrl.back-btn span.ico{ font-size:30px; color:#ffb84a; }

  /* Action buttons */
  .ls-right .arow{
    display:flex; gap:4px; justify-content:center; flex-wrap:nowrap; width:100%;
  }
  .ls-right .ab{
    background:rgba(255,255,255,.10); border:1px solid rgba(255,255,255,.18);
    color:#fff; padding:10px 4px; border-radius:10px; font-weight:900; font-size:12px;
    touch-action:manipulation; min-height:48px; line-height:1.1;
    box-sizing:border-box; flex:1 1 0; min-width:0;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; cursor:pointer;
  }
  /* Tall row variant for landscape — used on the action rows where
     labels deserve more height ("Free Ball", "F&M Re-Spot", etc.) */
  .ls-right .arow.ar-tall .ab{ min-height:60px; padding:10px 4px; }
  /* Multi-line label support — same idea as portrait but with the
     bigger landscape base font. Wraps cleanly across two lines. */
  .ls-right .ab.ab-multiline{
    white-space:normal; line-height:1.05; font-size:11px;
    word-break:keep-all; padding:8px 3px;
  }
  /* Primary-action multi-line buttons (End Frame / End Match) —
     read clearly from across the room. */
  .ls-right .ab.fr.ab-multiline,
  .ls-right .ab.dk.ab-multiline{
    font-size:14px; line-height:1.1; padding:8px 4px;
    font-weight:900; letter-spacing:.3px;
  }
  .ls-right .ab:active{ transform:scale(.97); }
  .ls-right .ab.r{ background:#c31111; border-color:rgba(255,255,255,.22); }
  /* End Frame / End Match — explicit landscape overrides because
     .ls-right .ab base background was winning on source order over
     the .ab.fr/.ab.dk gradient rules. Match portrait styles exactly. */
  .ls-right .ab.fr{
    background:linear-gradient(180deg, #d97706 0%, #92400e 100%);
    border:1px solid rgba(255,180,80,.6);
    color:#fff;
    box-shadow:0 4px 10px rgba(217,119,6,.30),
               inset 0 1px 0 rgba(255,255,255,.25),
               inset 0 -2px 0 rgba(0,0,0,.18);
    text-shadow:0 1px 2px rgba(0,0,0,.45);
  }
  .ls-right .ab.fr:active{ filter:brightness(.92); }
  .ls-right .ab.dk{
    background:linear-gradient(180deg, #b91c1c 0%, #6b0e0e 100%);
    border:1px solid rgba(255,90,90,.6);
    color:#fff;
    box-shadow:0 4px 10px rgba(185,28,28,.35),
               inset 0 1px 0 rgba(255,255,255,.25),
               inset 0 -2px 0 rgba(0,0,0,.20);
    text-shadow:0 1px 2px rgba(0,0,0,.5);
  }
  .ls-right .ab.dk:active{ filter:brightness(.92); }
  .ls-right .ab.f4{ background:#7b4a2c; border-color:rgba(255,255,255,.3); }
  .ls-right .ab.f5{ background:#1f74d6; border-color:rgba(255,255,255,.3); }
  .ls-right .ab.f6{ background:#d85bb3; border-color:rgba(255,255,255,.35); }
  .ls-right .ab.f7{ background:#101010; border-color:rgba(255,255,255,.3); }
  .ls-right .ab.ai-on{ background:rgba(0,140,0,.35); border-color:#00c800; color:#7fff7f; }
  .ls-right .ab.ai-off{ background:rgba(80,0,0,.35); border-color:#c80000; color:#ff8080; }
  .ls-right .ab:disabled{ opacity:.45; filter:grayscale(1); cursor:not-allowed; }
  .ls-divider{ width:100%; border:none; border-top:1px solid rgba(255,255,255,.1); margin:2px 0; flex-shrink:0; }
}

/* Smaller landscape screens */
@media (min-width:700px) and (max-width:1280px) and (orientation:landscape){
  .ls-card{ grid-template-columns: 1fr 300px; }
  .ls-last{ font-size:14px; }
  .ls-right .bb{ font-size:26px; }
  .ls-right .ab{ font-size:11px; min-height:44px; }
  .ls-avatar{ width:80px; height:80px; }
  /* Tighten centre column padding on mid-size tablets */
  .ls-left{ padding:10px 20px 8px; gap:7px 16px; }
  .ls-pill .pill-val{ font-size:24px; }
  .ls-pill{ min-height:46px; padding:6px 8px; }
}

/* Small landscape tablets (768×1024 rotated = 1024×768, or iPad mini etc.)
   Primary target: viewport height < 700px in landscape.
   Goal: fit everything above the fold with no scrolling. */
@media (min-width:700px) and (orientation:landscape) and (max-height:700px){
  .ls-card{
    grid-template-rows: 48px 1fr;   /* shrink header row */
  }
  .ls-header{ padding:0 16px; }
  .ls-header img{ height:36px; }
  .ls-header .ls-header-title{ font-size:16px; letter-spacing:2px; }
  .ls-left{ padding:8px 16px 6px; gap:5px 14px; }
  .ls-player-section{ gap:4px; }
  .ls-avatar{ width:60px; height:60px; }
  .ls-pname{ font-size:clamp(14px,1.6vw,22px); }
  .ls-score{ font-family:'Arial Black','Arial Bold',Arial,sans-serif; font-size:clamp(80px,10vw,180px); padding:3px 10px; }
  .ls-clock{ font-size:11px; }
  /* v5.11.15 — the corner badge needs its own shrink here: .ls-corner-clock
     .ls-clock out-specifies the bare .ls-clock rule above, so without this it
     would keep its full size on exactly the screens with no room for it. */
  .ls-corner-clock .ls-clock{ font-size:15px; letter-spacing:1px; }
  .ls-corner-clock .ls-avg{ font-size:9px; letter-spacing:.5px; }
  /* v5.11.16 — the timer boxes get their own shrink here too; the clamps
     above are viewport-WIDTH based and this query is about a short viewport,
     so a wide-but-short screen would otherwise keep the full 54px digits. */
  .ls-timers{ gap:8px; margin-top:2px; }
  .ls-tbox{ padding:3px 10px; min-width:76px; border-radius:8px; }
  .ls-tlbl{ font-size:8px; letter-spacing:1px; }
  .ls-tval{ font-size:22px; letter-spacing:1px; }
  .ls-brk{ font-size:clamp(28px,4vw,52px); }
  .ls-bo{ font-size:13px; }
  .ls-next-row{ gap:3px; }
  .next-ball.ls-lg{ width:clamp(96px,11vw,120px); height:clamp(96px,11vw,120px); font-size:24px; }
  .ls-pills-row{ gap:5px; margin-top:4px; }
  .ls-pill{ min-height:62px; padding:9px 8px; border-radius:10px; }
  .ls-pill .pill-val{ font-size:32px; }
  .ls-pill .pill-lbl{ font-size:11px; }
  .ls-snook{ font-size:22px; padding:7px 18px; }   /* v5.11.60 — still double the old 12px */
  .ls-balls-row{ gap:12px; }
  .ls-reds{ gap:3px; }
  .ls-rd{ width:14px; height:14px; }
  .ls-cb{ width:20px; height:20px; }
  .ls-cols{ gap:6px; }
  .ls-last{ font-size:13px; height:auto; padding:6px 10px 2px; }
  .ls-stat-table{ font-size:11px; }
  .st-val{ font-size:13px !important; }
  /* Smaller right panel on tight screens */
  .ls-card{ grid-template-columns: 1fr 260px; }
  .ls-tab{ padding:8px 4px; font-size:10px; }
  .ls-right .bb{ font-size:22px; }
  .ls-right .ab{ font-size:10px; min-height:38px; padding:6px 4px; }
  /* Correction toast fills right panel at correct offset */
  .corr-toast{ top:48px; height:calc(100dvh - 48px); max-height:calc(100dvh - 48px); }
}

/* ══ AWAITING-BREAK BANNER — keep the nav tabs tappable ═══════════════════
   The full-screen #awaitingBreakBanner (position:fixed; z-index:2000) must not
   cover the CONTROL / MATCHES / SETTINGS tab bar.
   • Portrait: the tabs are the bottom .navbar (height var(--nav)=48px + safe
     area). The banner's inline geometry stops above it via
     bottom:calc(var(--nav) + var(--sb)), leaving the navbar exposed & tappable.
   • Landscape: the tabs are .ls-tabs at the TOP of the right panel, and
     .ls-card is position:fixed (its own stacking context) — so bumping a child
     z-index can't lift the tabs above the body-level banner. Instead we drop
     the banner's top edge below the header + tab bar (and reset bottom to 0,
     since landscape has no bottom navbar) so the tab bar stays tappable while
     the banner still covers the rest of the screen. */
@media (min-width:700px) and (orientation:landscape){
  /* v5.11.9 — COMPACT CENTRED CARD, not a panel-filling sheet.
     It used to cover everything left of the right-hand panel (right:48vw),
     which hid the player score boxes underneath — and those are exactly what
     an operator needs to read at this moment, to check the handicap before
     the break. Operator: "put the awaiting break off in the middle of
     landscape admin and smaller so both player score boxes are still visible
     to verify handicap at break off".
     Centred on the viewport with no full-screen backdrop, so both score
     plates stay bright and legible either side of it. The card keeps its own
     radial-gradient ground, so it still reads as a modal rather than as part
     of the board. Everything inside is scaled to the smaller box; the inline
     font sizes are vw-based and would otherwise overflow it. */
  /* v5.11.10 — centred in the LEFT window and lifted toward the top.
     Centring on the whole viewport put it over the middle column, hiding the
     frame score. left:0 + right:<control panel width> + margin-inline:auto
     centres it across the board area only; a fixed top keeps the frame score
     and the detector strip below it visible. Operator: 'center in the left
     window only and towards the top so the frame score is visible'. */
  #awaitingBreakBanner{
    top:clamp(70px,9vh,128px) !important; bottom:auto !important;
    left:0 !important; right:360px !important;   /* .ls-card right column */
    transform:none;
    width:min(46vw,600px); margin-inline:auto;
    max-height:72vh; overflow:auto;
    padding:clamp(13px,1.7vh,22px) clamp(18px,2vw,30px) !important;
    gap:clamp(8px,1.1vh,15px) !important;
    border:1px solid rgba(255,213,74,.50); border-radius:18px;
    box-shadow:0 24px 70px rgba(0,0,0,.65);
  }
  /* Matches/Settings widen the right column, so follow it. */
  body:has(.ls-card.ls-wide) #awaitingBreakBanner{ right:min(680px,48vw) !important; }
  #awaitingBreakBanner > div:first-of-type{ font-size:clamp(15px,1.3vw,21px) !important; }
  /* v5.11.26 - the BREAKER'S NAME is the thing this card exists to show.
   Operator: 'make the awaiting for break off screen with a much bigger the
   breakers name text'. The name takes its own line at up to ~100px and
   'to break' drops to a caption under it; long names wrap instead of
   overflowing the card. */
#awaitingBreakBanner > div:nth-of-type(2){ font-size:clamp(16px,1.5vw,24px) !important; font-weight:700 !important; color:rgba(255,255,255,.85) !important; }
#awaitingBreakBanner #awaitingBreakName{ display:block; font-size:clamp(48px,5.6vw,104px); font-weight:900; color:#fff; line-height:1.02; letter-spacing:.5px; max-width:100%; overflow-wrap:anywhere; margin-bottom:.12em; text-shadow:0 3px 14px rgba(0,0,0,.55); }
  #awaitingBreakBanner #awaitingBreakScore{
    font-size:clamp(17px,1.65vw,27px) !important; border-width:2px !important;
    border-radius:10px !important;
    padding:clamp(4px,.6vh,8px) clamp(10px,1.1vw,18px) !important; }
  /* v5.11.11 — the rack status is the line the operator is waiting on, so it
     is the one element that should NOT shrink with the card. Operator: 'make
     the Setting Up Rack box bigger'. */
  #awaitingBreakBanner #awaitingBreakStatus{
    font-size:clamp(26px,2.6vw,42px) !important;
    padding:clamp(13px,1.6vh,22px) clamp(26px,2.8vw,46px) !important;
    border-width:3px !important; width:100%; box-sizing:border-box; }
  #awaitingBreakBanner .ab{
    font-size:clamp(12px,1.1vw,17px) !important; min-height:0 !important;
    padding:clamp(7px,.8vh,11px) clamp(10px,1.1vw,18px) !important; }
}

/* ── v5.11.48 — Match Setup at the table ─────────────────────────────────
   Landscape is a TV or a wide tablet: the card was a 480px phone column, so
   the operator scrolled past the players to reach Best of and the handicaps.
   Wider card, the two players side by side, each handicap under its player.
   Portrait is untouched — there the stack is right.                      */
.mi-hcap-inline{ margin-top:8px; }
.mi-hcap-inline .hcap-stepper{ display:flex; align-items:stretch; gap:6px; }
.mi-hcap-inline .hcap-inp{ flex:1; min-width:0; text-align:center;
  font-size:22px; font-weight:800; padding:6px 4px;
  -moz-appearance:textfield; appearance:textfield; }
.mi-hcap-inline .hcap-inp::-webkit-outer-spin-button,
.mi-hcap-inline .hcap-inp::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
.mi-hcap-inline .hcap-btn{ flex:0 0 46px; font-size:22px; font-weight:900;
  line-height:1; border-radius:9px; cursor:pointer; padding:0;
  border:1px solid rgba(255,255,255,.28); background:rgba(255,255,255,.08); color:#fff; }
.mi-hcap-inline .hcap-btn:active{ transform:scale(.96); }
#mi-hcap-a .hcap-btn{ border-color:rgba(96,165,250,.5); color:#dbeafe; }
#mi-hcap-b .hcap-btn{ border-color:rgba(250,204,21,.5); color:#fef3c7; }
/* the remote's highlight, same light green as the scoring-check toast */
#matchInfoModal :focus{ outline:3px solid #34d17a !important; outline-offset:2px; }
/* v5.11.50 — the highlight was a TRANSLUCENT green, so on the game-mode
   dropdown (amber ground, near-black text) it came out as dark-on-dark and the
   selection could not be read. Operator: 'use very light green background for
   selected game mode instead of dark green, it's not visible with dark
   letters'. Solid light green + dark ink, and the option list follows it. */
#matchInfoModal .inp:focus, #matchInfoModal select:focus,
#matchInfoModal textarea:focus{
  background:#d9f7e3 !important; color:#06331a !important;
  border-color:#34d17a !important; }
#matchInfoModal select:focus option{ background:#ffffff; color:#06331a; }
#matchInfoModal button:focus{
  background:linear-gradient(180deg,#c9f7d4 0%,#7fdf9e 100%) !important;
  color:#06331a !important; }
@media (min-width:700px) and (orientation:landscape){
  #matchInfoModal > div{ width:min(96vw,980px) !important; max-width:980px !important;
                         padding:22px 26px !important; }
  #mi-players{ display:flex; gap:18px; align-items:flex-start; }
  #mi-players > div{ flex:1 1 0; min-width:0; }
  /* v5.11.51 — the avatar IS the login QR: a player scans it with a phone, so
     it wants the room, not a thumbnail. v5.11.48 shrank it to 76px to make the
     two columns fit; the columns are 460px wide, so it can have far more. The
     three word buttons beside it only need to be tappable, not wide — they were
     eating the width the QR should have. Operator: 'make the avatar/QR display
     larger and make the 3 buttons on each side much narrower'. */
  #mi-players .mi-av{ width:150px !important; height:150px !important; }
  #mi-players .mi-ptop{ gap:12px; align-items:center; }
  #mi-players .mi-pbtns{ flex:0 0 108px; max-width:108px; gap:8px; }
  #mi-players .mi-pbtns .ab{ font-size:12px; padding:10px 4px; width:100%;
                             white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  /* the QR needs quiet space around it to scan; a bigger circle can afford it */
  #mi-players .mi-av .ls-login-qr{ padding:10px; }
}

/* ── v5.11.53 — Edit Score boxes ──────────────────────────────── */
#editStatsModal .es-field, #scoreOneModal .es-field{ display:flex; flex-direction:column; gap:6px; }
#editStatsModal .es-lbl, #scoreOneModal .es-lbl{
  font-size:15px; font-weight:800; color:#fff; letter-spacing:.2px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
#editStatsModal .es-stepper, #scoreOneModal .es-stepper{ display:flex; align-items:stretch; gap:6px; }
#editStatsModal .es-stepper .inp, #scoreOneModal .es-stepper .inp{
  flex:1; min-width:0; font-family:monospace; font-size:26px; font-weight:800;
  text-align:center; padding:6px 2px;
  -moz-appearance:textfield; appearance:textfield; }
#editStatsModal .es-stepper .inp::-webkit-outer-spin-button, #scoreOneModal .es-stepper .inp::-webkit-outer-spin-button,
#editStatsModal .es-stepper .inp::-webkit-inner-spin-button, #scoreOneModal .es-stepper .inp::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
#editStatsModal .es-btn, #scoreOneModal .es-btn{
  flex:0 0 46px; font-size:24px; font-weight:900; line-height:1; padding:0;
  border-radius:9px; cursor:pointer; color:#fff;
  border:1px solid rgba(255,255,255,.28); background:rgba(255,255,255,.08); }
#editStatsModal .es-btn:active, #scoreOneModal .es-btn:active{ transform:scale(.96); }
#editStatsModal .es-apply, #scoreOneModal .es-apply{
  background:linear-gradient(180deg,#c9f7d4 0%,#7fdf9e 100%) !important;
  color:#06331a !important; border:1px solid rgba(6,90,40,.55) !important; }
#editStatsModal .es-apply:active, #scoreOneModal .es-apply:active{ filter:brightness(.94); }
/* the remote's highlight, as on the toast and Match Setup */
#editStatsModal :focus, #scoreOneModal :focus{ outline:3px solid #34d17a !important; outline-offset:2px; }
#editStatsModal .inp:focus, #scoreOneModal .inp:focus{ background:#d9f7e3 !important; color:#06331a !important; }

/* ══ MODALS ══════════════════════════════════════════════════════════════ */
.modal{display:none;position:fixed;inset:0;background:rgba(0,0,0,.76);
       z-index:10000;align-items:flex-start;justify-content:center;padding:18px;
       overflow-y:auto;-webkit-overflow-scrolling:touch;}
.modal.on{display:flex;}
/* v5.11.381 — a popup is centred by its own auto margins, not by align-items: a box taller than the
   screen then starts at the top and the page scrolls to its buttons (auto margins drop to 0 and the box
   overflows downward). align-items:center clipped BOTH ends of a tall popup — the camera preview on an
   iPad in a browser had no reachable Close button. openModal() sets the same alignment inline. */
.modal > *{ margin:auto; flex-shrink:0; }
/* v5.11.327 — the one-player score popup sits at the TOP: a tablet's keyboard rises from the bottom */
#scoreOneModal{ padding-top:max(12px, env(safe-area-inset-top, 0px)) !important; }
#scoreOneModal > *{ margin:0 auto; }
/* v5.11.378 — PORTRAIT: EVERY popup sits at the top, the same way. A phone's keyboard takes the lower
   half of the screen, so a centred dialog with a text box (Edit score, operator PIN, race-to…) had its
   lower half — the other fields and the Apply button — under the keyboard. Scrolls if taller than the screen. */
@media (orientation:portrait){
  .modal{ padding-top:max(14px, env(safe-area-inset-top, 0px)); }
  .modal > *{ margin:0 auto; }
}
/* v5.11.269 — GLOBAL (these used to sit inside the landscape block, so in portrait the live-view window and the 3rd-party panel were unstyled) */
  /* v5.11.264 — POOL match: rack buttons replace the ball grid; no AI toggle */
  body.sport-pool #ls-panel-ctrl > .bgrid, body.sport-pool #ctrlBtns .bgrid, body.sport-pool #lsTogAI, body.sport-pool #togAI,
  body.sport-pool .ls-shot-history{ display:none !important; }
  /* v5.11.264 — LIVE VIEW: small picture-in-picture of the outgoing stream (1 fps JPEG tap) */
  #lsPip{ display:none; position:fixed; right:12px; bottom:12px; width:clamp(200px, 22vw, 400px); z-index:9000;
    background:#000; border:1px solid rgba(255,255,255,.25); border-radius:10px; overflow:hidden; box-shadow:0 10px 40px rgba(0,0,0,.6); }
  #lsPip.on{ display:block; }
  #lsPip img{ display:block; width:100%; aspect-ratio:16/9; object-fit:contain; background:#000; }
  #lsPip .pip-bar{ display:flex; align-items:center; justify-content:space-between; padding:3px 6px 3px 10px; font-size:11px; color:rgba(255,255,255,.75); background:rgba(0,0,0,.6); }
  #lsPip .pip-bar button{ background:none; border:0; color:#fff; font-size:15px; cursor:pointer; padding:0 4px; }
  body.dp-follow .box-pool-panel .pool-row{ display:none; }         /* v5.11.269 — followed match: no rack buttons */
  #tpModal{ align-items:flex-start !important; padding-top:max(8px, env(safe-area-inset-top)); }   /* top of the screen: the keyboard must not cover it */
  #tpModal .mbox{ max-width:520px; width:94%; padding:16px; }
  #tpModal .tp-card{ background:rgba(31,182,182,.08); border:1px solid rgba(31,182,182,.45); border-radius:12px; padding:12px; margin:10px 0; }
  #tpModal .tp-names{ font-weight:900; font-size:18px; }
  #tpModal .tp-racks{ font-family:'Share Tech Mono',monospace; font-size:34px; font-weight:900; color:#e9b949; text-align:center; margin:4px 0; }
  #tpModal .tp-sub{ font-size:13px; opacity:.75; }
  #tpModal .tp-row{ display:flex; gap:8px; margin-top:10px; }
  #tpModal .tp-row .ab{ flex:1; }
  #tpModal .tp-keep{ display:none; background:rgba(220,38,38,.22); border:2px solid #ef4444; border-radius:10px; padding:10px; margin-top:10px; }
  #tpModal .tp-keep .ab{ background:#dc2626; border-color:#f87171; color:#fff; font-weight:900; }
  /* v5.11.276 — following a 3rd-party match: Control shows only its card, no scoring UI */
  body.dp-follow #pg-ctrl .p-ctrl > :not(.js-dp-card), body.dp-follow #ls-panel-ctrl > :not(.js-dp-card), body.dp-follow #ctrlBtns{ display:none !important; }
  body.dp-follow .ls-next-brk, body.dp-follow .ls-pills-row, body.dp-follow #lsBreakBallsRow, body.dp-follow .ls-score, body.dp-follow .ls-stat-table, body.dp-follow .ls-corner-clock{ display:none !important; }   /* tablet: snooker-only parts; the racks box stays */
  .dp-card{ display:none; } body.dp-follow .dp-card{ display:block; background:rgba(31,182,182,.08); border:1px solid rgba(31,182,182,.45); border-radius:12px; padding:12px; margin:8px; text-align:center; }
  .dp-card .dp-names{ font-weight:900; font-size:18px; } .dp-card .dp-racks{ font-family:'Share Tech Mono',monospace; font-size:40px; font-weight:900; color:#e9b949; }
  .dp-card .dp-sub{ font-size:13px; opacity:.75; } .dp-card .dp-row{ display:flex; gap:8px; margin-top:10px; } .dp-card .dp-row .ab{ flex:1; }
  /* v5.11.289 — pool: no snooker scoreboard furniture (next ball, break, reds/rem/diff, detector, snooker stats) */
  body.sport-pool #awaitingBreakBanner, body.sport-pool #ibAwait, body.dp-follow #awaitingBreakBanner, body.sport-pool #brk, body.sport-pool .brk-lbl, body.sport-pool #pills, body.sport-pool #detVision, body.sport-pool #lsDetVision, body.sport-pool .ls-next-brk, body.sport-pool .ls-pills-row, body.sport-pool #lsBreakBallsRow, body.sport-pool .ls-stat-table, body.sport-pool .det-row, body.sport-pool #lsDetRow{ display:none !important; }
  /* v5.11.291 — the app's pool panel, scoped to the box panel */
  .box-pool-panel{ flex-direction:column; gap:8px; padding:8px 4px; width:100%; }
  .box-pool-panel .arow{ display:flex; gap:6px; }
  .box-pool-panel .arow .ab{ flex:1; min-height:48px; font-size:14px; }
  .box-pool-panel .pool-tag{ text-align:center; font-weight:700; letter-spacing:2px; color:var(--mu,#9aa); font-size:12px; text-transform:uppercase; }
  .box-pool-panel .pool-top{ display:grid; grid-template-columns:1fr auto 1fr; gap:8px; align-items:stretch; }
  .box-pool-panel .pool-top-start{ grid-template-columns:1fr auto; }
  .box-pool-panel .pool-mid{ display:flex; flex-direction:column; gap:8px; justify-content:center; align-items:center; }
  .box-pool-panel .pool-mid .bb{ width:74px; max-width:74px; font-size:12px; display:flex; flex-direction:column; align-items:center; justify-content:center; min-height:64px; }
  .box-pool-panel .pool-mid .bb .ico{ font-size:22px; line-height:1; }
  .box-pool-panel .bpw{ border:0; border-radius:14px; color:#fff; font-weight:700; cursor:pointer; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; padding:10px 6px; min-height:160px; box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 4px 12px rgba(0,0,0,.35); }
  .box-pool-panel .bpw:disabled{ opacity:.35; }
  .box-pool-panel .bpw.pw-a{ background:linear-gradient(180deg,#2f86e8,#1559b0); }
  .box-pool-panel .bpw.pw-b{ background:linear-gradient(180deg,#f0c22a,#c48f00); color:#1b1400; }
  .box-pool-panel .bpw .pw-n{ font-family:'Share Tech Mono',monospace; font-size:44px; line-height:1; }
  .box-pool-panel .bpw .pw-name{ font-size:14px; letter-spacing:.5px; text-transform:uppercase; text-align:center; }
  .box-pool-panel .bpw b{ font-size:11px; letter-spacing:1.5px; opacity:.85; }
  .box-pool-panel .bpw.brk{ box-shadow:0 0 0 3px #9BE36A,0 0 18px rgba(155,227,106,.45); }
  .box-pool-panel .pool-start{ min-height:110px; font-size:18px; flex-direction:column; display:flex; align-items:center; justify-content:center; gap:6px; }
  .box-pool-panel .pool-start b{ font-size:13px; opacity:.75; letter-spacing:1px; }
  .box-pool-panel .pool-quick{ display:grid; grid-template-columns:1fr; gap:8px; }
  .box-pool-panel .pq-cell{ display:flex; gap:6px; } .box-pool-panel .pq-cell .ab{ flex:1; min-height:40px; font-size:12px; padding:6px 4px; }
  .box-pool-panel .pq-gold{ background:linear-gradient(180deg,#FFD54A,#E6A500) !important; color:#1b1400 !important; border-color:transparent !important; font-weight:800; }
  .box-pool-panel .pool-howq{ display:flex; flex-direction:column; gap:6px; border:2px solid #FFD54A; border-radius:10px; padding:8px; background:rgba(255,213,74,.08); }
  .box-pool-panel .ph-q{ font-size:13px; text-align:center; color:#FFD54A; font-weight:700; letter-spacing:.5px; }
  .box-pool-panel .ph-badge{ font-size:10px; font-weight:800; letter-spacing:1px; color:#111; background:#FFD54A; border-radius:4px; padding:1px 5px; margin-left:6px; }
  .box-pool-panel .pool-stats{ width:100%; border-collapse:collapse; font-size:13px; }
  .box-pool-panel .pool-stats th{ font-size:10px; letter-spacing:1px; text-transform:uppercase; color:var(--mu,#9aa); font-weight:700; padding:4px 2px; text-align:center; }
  .box-pool-panel .pool-stats th:first-child, .box-pool-panel .pool-stats td:first-child{ text-align:left; }
  .box-pool-panel .pool-stats td{ padding:5px 2px; border-top:1px solid rgba(255,255,255,.1); text-align:center; font-family:'Share Tech Mono',monospace; }
  .box-pool-panel .pool-log{ display:flex; flex-direction:column; gap:2px; max-height:220px; overflow:auto; border-top:1px solid rgba(255,255,255,.1); padding-top:6px; }
  .box-pool-panel .pl-row{ display:grid; grid-template-columns:42px 1fr auto auto; gap:8px; align-items:baseline; font-size:13px; padding:3px 2px; cursor:pointer; }
  .box-pool-panel .pl-no, .box-pool-panel .pl-at{ font-family:'Share Tech Mono',monospace; color:var(--mu,#9aa); }
  .box-pool-panel .pl-dur{ font-family:'Share Tech Mono',monospace; color:#FFD54A; }
  .box-pool-panel{ display:none !important; flex-direction:column; gap:10px; padding:6px 4px; }
  body.sport-pool .box-pool-panel{ display:flex !important; }
  body.sport-pool:not(.dp-follow) #ls-panel-ctrl > :not(.box-pool-panel), body.sport-pool #ctrlBtns{ display:none !important; }   /* v5.11.281 — pool: rack panel only */   /* v5.11.267 — never shown for snooker, whatever the layout rules say */
  body.nm-sport-pool .hcap-ticks, body.nm-sport-pool .nm-hcaps:not(.nm-pool), body.nm-sport-pool #ls-hcap-spot, body.nm-sport-pool #m-hcap-spot{ display:none !important; }   /* v5.11.285 — pool: game spot, not snooker points */
/* v5.11.287 — ball picker: a select whose open list shows each choice's ball (a native list cannot show images) */
.bp-hit{position:absolute;inset:0;z-index:3;cursor:pointer;background:transparent;border:0;padding:0}
.bp-menu{position:fixed;z-index:100010;background:#15191f;border:1px solid rgba(255,255,255,.18);border-radius:12px;padding:6px;box-shadow:0 18px 50px rgba(0,0,0,.65);max-height:70vh;overflow:auto}
.bp-item{display:flex;align-items:center;gap:12px;width:100%;padding:10px 12px;border:0;border-radius:9px;background:transparent;color:#eee;font:inherit;font-size:16px;font-weight:700;text-align:left;cursor:pointer}
.bp-item:hover,.bp-item.on{background:rgba(31,182,182,.16);color:#fff}
.bp-ball{position:relative;width:28px;height:28px;flex:0 0 28px}
.bp-ball img{width:28px;height:28px;display:block}
.bp-ball i{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font:900 13px/1 Arial,sans-serif;font-style:normal;color:#fff;text-shadow:0 1px 1px rgba(0,0,0,.45)}
.bp-scrim{position:fixed;inset:0;z-index:100009;background:rgba(0,0,0,.35)}
  /* v5.11.288 — Sport + Game side by side, no labels (as the app); the older rows stay as hidden value holders */
  .nm-row.nm-sportgame{ display:flex; gap:8px; } .nm-row.nm-sportgame .sel-ico{ flex:1 1 0; }
  .nm-row.nm-sport, .nm-row.nm-mode, .nm-row.nm-pool:has(.sel-ico [id$='-inp-pool-game']){ display:none !important; }
  .sg-lt{ position:absolute; left:10px; top:50%; transform:translateY(-50%); width:22px; height:22px; z-index:2; pointer-events:none; display:flex; align-items:center; justify-content:center; font:900 12px/1 Arial,sans-serif; color:#fff; text-shadow:0 1px 1px rgba(0,0,0,.45); }
  .sg-lt:empty{ display:none; }
  .nm-row .sel-ico{ flex:1 1 0; min-width:0; position:relative; display:flex; }
  .nm-row .sel-ico img{ position:absolute; left:10px; top:50%; transform:translateY(-50%); width:22px; height:22px; pointer-events:none; z-index:1; }
  .nm-row .sel-ico .inp{ flex:1; min-width:0; margin:0; padding-left:44px !important; }
  .pgames{ display:flex; gap:6px; flex:1; flex-wrap:wrap; }
  .pgame{ flex:1 1 0; min-width:68px; display:flex; flex-direction:column; align-items:center; gap:4px; padding:8px 4px; border-radius:10px; border:1px solid rgba(255,255,255,.18); background:rgba(255,255,255,.05); color:#ddd; font-weight:700; font-size:12px; cursor:pointer; }
  .pgame.on{ border-color:#1fb6b6; background:rgba(31,182,182,.16); color:#fff; box-shadow:0 0 0 1px #1fb6b6 inset; }
  .pball{ width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center; box-shadow:inset -4px -5px 8px rgba(0,0,0,.35), 0 1px 3px rgba(0,0,0,.5); }
  .pball b{ width:17px; height:17px; border-radius:50%; background:#f4f1ea; color:#111; font-size:10px; font-weight:900; display:flex; align-items:center; justify-content:center; }
  .pball.cn b{ background:#d22; color:#fff; }
  .nm-row.nm-pool{ display:none !important; } body.nm-sport-pool .nm-row.nm-pool{ display:flex !important; } body.nm-sport-pool .nm-mode, body.nm-sport-pool .nm-bestof, body.nm-sport-pool .nm-bestof-custom{ display:none !important; }
  /* (v5.11.267 - the body class must never share a name with the row class: `.nm-pool{display:none}` matched body.nm-pool and blanked the screen) */
.mbox{max-width:460px;width:100%;background:#111;
      border:1px solid rgba(255,255,255,.13);border-radius:16px;padding:22px;text-align:center;}
.mbox h2{font-size:20px;margin-bottom:10px;}
.mbox p {color:rgba(255,255,255,.78);line-height:1.5;margin-bottom:16px;font-size:14px;}
.mbtns{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;}
.mbtns .ab{flex:0 0 auto;padding:10px 18px;font-size:14px;}

/* correction toast — shown for needs_review, count_mismatch, etc.
   Restyled in v1.4.13.6: bigger, brighter, longer dwell so it can't
   be missed. Positions itself in the upper-centre of the screen
   rather than tucked above the (now-hidden) bottom nav. */
.corr-toast{
  display:none;position:fixed;top:80px;left:50%;
  transform:translateX(-50%);background:#1a1a1a;
  border:2px solid #FFD54A;border-radius:14px;
  padding:18px 24px;min-width:340px;max-width:92vw;z-index:9998;text-align:center;
  box-shadow:0 8px 32px rgba(0,0,0,.6), 0 0 0 1px rgba(255,213,74,.2);
  animation:corrPulse 1.6s ease-in-out infinite;
}
.corr-toast.on{display:block;}
@keyframes corrPulse{
  0%,100%{ box-shadow:0 8px 32px rgba(0,0,0,.6), 0 0 0 1px rgba(255,213,74,.2); }
  50%   { box-shadow:0 8px 32px rgba(0,0,0,.6), 0 0 0 6px rgba(255,213,74,.35); }
}
.corr-head{font-size:13px;letter-spacing:2px;text-transform:uppercase;
           color:#FFD54A;margin-bottom:8px;font-weight:700;}
.corr-msg{font-size:18px;font-weight:700;margin-bottom:14px;line-height:1.3;}
.corr-btns{display:flex;flex-direction:column;gap:8px;}
/* v5.11.46 — the remote can drive this toast, so the highlighted button has
   to read from across the room. Operator: 'maybe have a different light green
   background on highlighted button' — light green against the toast's amber
   frame and its dark ground, with dark ink so the label stays legible, plus a
   ring and a slight lift. :focus, not :focus-visible: the focus is set
   programmatically when the toast opens and :focus-visible ignores that. */
.corr-btns .ab:focus{
  background:linear-gradient(180deg,#c9f7d4 0%,#7fdf9e 100%) !important;
  color:#06331a !important; font-weight:900;
  outline:3px solid #34d17a; outline-offset:2px;
  border-color:rgba(6,90,40,.55) !important;
  box-shadow:0 0 18px rgba(52,209,122,.55); transform:scale(1.02);
}
.corr-btns .ab{padding:12px 16px;font-size:15px;width:100%;
               white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}

/* Landscape: position the scoring-check toast as a tall column
   overlay over the right control panel. The right panel is 360px
   wide (see .ls-card grid-template-columns); we match that width and
   position right:0 below the 60px header. Each button gets generous
   height so all label text shows clearly and the operator can tap
   without aiming. Cramped horizontal-row layout (still used in
   portrait) makes long labels like "Foul+R" / "Free Ball" /
   "Ignore" hard to read. */
@media (min-width:700px) and (orientation:landscape){
  .corr-toast{
    top:60px; right:0; left:auto; bottom:0;
    transform:none;
    width:360px; max-width:360px; min-width:360px;
    height:calc(100vh - 60px);
    height:calc(100dvh - 60px);    /* dynamic viewport variant */
    max-height:calc(100vh - 60px);
    max-height:calc(100dvh - 60px);
    border-radius:0;          /* flush against right edge */
    border-top:none;          /* header already provides separator */
    border-right:none;        /* flush against right edge */
    padding:24px 18px;
    display:none;             /* still toggled by .on */
    flex-direction:column;
    overflow-y:auto;
  }
  .corr-toast.on{display:flex;}
  .corr-head{font-size:14px; margin-bottom:12px;}
  .corr-msg{font-size:20px; margin-bottom:20px; flex-shrink:0;}
  .corr-btns{gap:12px; flex:1;}
  .corr-btns .ab{
    padding:18px 16px; font-size:17px; min-height:64px;
    border-radius:10px;
    /* Allow text wrap so long labels never get truncated */
    white-space:normal; text-overflow:clip; overflow:visible;
    line-height:1.2;
  }
}

/* CENTURY popup modal — full-screen overlay, dramatic display.
   Was previously a small banner pill at top of screen, but per
   operator feedback a 100-break is a celebration moment that
   deserves real visual weight. Auto-dismisses after 4.5s but is
   click/tap dismissable for the impatient. */
#century{
  display:none;position:fixed;inset:0;z-index:2147483645;
  background:radial-gradient(circle at center,
    rgba(255,213,74,.18) 0%, rgba(0,0,0,.85) 70%);
  align-items:center;justify-content:center;flex-direction:column;
  cursor:pointer;
  animation:centuryPulse 1.4s ease-in-out infinite alternate;
}
#century.on{display:flex;}
#century .cent-num{
  font-size:180px;font-weight:900;color:gold;
  text-shadow:0 0 30px rgba(255,213,74,.7),0 4px 10px rgba(0,0,0,.6);
  font-family:'Share Tech Mono',monospace;letter-spacing:8px;
  font-variant-numeric:tabular-nums;
}
#century .cent-lbl{
  font-size:64px;font-weight:900;color:#FFD54A;letter-spacing:12px;
  text-transform:uppercase;text-shadow:0 4px 12px rgba(0,0,0,.7);
  margin-top:8px;
}
#century .cent-sub{
  font-size:22px;color:rgba(255,255,255,.65);margin-top:16px;
  letter-spacing:2px;
}
@keyframes centuryPulse{
  from{ box-shadow:inset 0 0 80px rgba(255,213,74,.15); }
  to  { box-shadow:inset 0 0 200px rgba(255,213,74,.4); }
}
/* ── v5.10.282 — Detector strip: the RED count is what the operator checks
   against the score, so the red dot and its numeral grow and the six colour
   dots shrink to fit the same row. Row height is unchanged: it is already
   set by the 30px (landscape) / 24px (portrait) ball-on badge, and the red
   dot is sized to that badge, never above it. Applies to both strips
   (#detVision portrait, #lsDetVision landscape); ids beat the earlier rules. */
#detVision .det-red .rd{ width:22px; height:22px; }
#detVision .det-red b{ font-size:17px; line-height:1; }
#detVision .cb{ width:13px; height:13px; }
#detVision .det-cols{ gap:4px; }
#lsDetVision .det-red .rd{ width:30px; height:30px; }
#lsDetVision .det-red{ gap:6px; }
#lsDetVision .det-red b{ font-size:24px; line-height:1; }
#lsDetVision .cb{ width:17px; height:17px; }
#lsDetVision .det-cols{ gap:4px; }

/* == v5.11.78 - REMOTE KEY BADGES ========================================
   A small dark key in the corner of every admin button the remote can press,
   showing the remote's own symbol for it, so the two can be matched without
   the printed sheet. Operator: 'snip the buttons from the remote and put the
   button icon onto the admin button text'. Drawn, not cropped: the only
   photo of the handset carries the label chips baked over each key.       */
.ab:has(> .rk), .bb:has(> .rk){ position:relative; }
.rk{
  position:absolute; top:3px; right:4px; z-index:3; pointer-events:none;
  font:900 10px/1.35 'Rajdhani', system-ui, sans-serif; letter-spacing:.2px;
  padding:0 4px; border-radius:4px; color:#f2f2f2; background:#151515;
  border:1px solid rgba(255,255,255,.28); box-shadow:0 1px 2px rgba(0,0,0,.5);
  opacity:.88; white-space:nowrap; }
.bb > .rk{ top:8%; right:10%; }
@media (min-width:700px) and (orientation:landscape){
  .ls-card.ls-bigctl #ls-panel-ctrl .rk{ font-size:clamp(9px, 1.5vh, 15px); padding:0 5px; top:4px; right:5px; }
  .ls-card.ls-bigctl #ls-panel-ctrl .bb > .rk{ top:9%; right:11%; }
}
@media (max-width:699px){ .rk{ display:none; } }
/* v5.11.80 — Settings > Appearance > Remote key badges (per device) */
html[data-rk="off"] .rk{ display:none; }

/* == v5.11.67 - LARGE CONTROLS ==========================================
   Operator, from the iCue TV: 'they prefer to have the control panel and
   buttons bigger and easier to see'. Then, on the first draft: 'I prefer the
   previous layout with red, switch and back button the largest buttons on
   one row, these are probably the most used buttons, make sure the score
   boxes can still hold 3 digits when someone makes a century break'.

   KEYS. Red, Switch and Back keep the top row they always had, now as the
   three biggest circles in the panel. The six colours sit in one row below
   them, then fouls, the in-play actions, the utility row and the end row.
   Every height is a clamp() on the viewport height, so the same rules fit a
   1080p TV and an 800px-tall tablet.

   WIDTH. On a 1920 TV the panel is 680px, the width Matches and Settings
   already use. It narrows along a line to about 330px on an 1180 tablet and
   300px at 1024, close to the old 360/300. A tablet cannot have both: at
   Matches' 48vw each player card was ~150px, and a 3-digit score in 150px is
   ~45px tall. Matches and Settings keep their own 48vw.

   TWO SHAPES. Below 1580px wide the balls keep the old 3 x 3 grid - Red,
   Switch, Back on top - at whatever size the panel allows. From 1580px (TVs,
   big monitors) there is room for more: Red, Switch and Back become three big
   circles across the top and the six colours run in one row beneath them.

   SCORES. A plate's font is capped to what its CARD can hold. 0-99 share one
   cap so the digits don't change size mid-frame; 100+ gets a tighter one so
   three digits still fit. Container units (cqi) measure the card itself.

   Everything is scoped to .ls-bigctl; remove that class from the card and the
   old 360px panel comes back exactly.                                      */
@media (min-width:700px) and (orientation:landscape){
  body:has(.ls-card.ls-bigctl){ --rw: clamp(300px, calc(46.875vw - 220px), 680px); }
  body:has(.ls-card.ls-bigctl.ls-wide){ --rw: min(680px, 48vw); }
  .ls-card.ls-bigctl{
    grid-template-columns: 1fr var(--rw);
    --kg:       clamp(6px, .9vh, 10px);     /* gap between keys */
    --k-ball:   11.5vh;                     /* 3 x 3 grid: every ball */
    --k-big:    19.5vh;                     /* wide: Red / Switch / Back */
    --k-small:  12vh;                       /* wide: the six colours */
    --k-foul-h: clamp(54px, 8.5vh, 100px);
    --k-play-h: clamp(58px, 9vh, 116px);
    --k-util-h: clamp(44px, 6.5vh, 76px);
    --k-end-h:  clamp(58px, 9vh, 116px);
  }
  /* things that were sized to the old 360px column follow the new one */
  body:has(.ls-card.ls-bigctl) #awaitingBreakBanner{ right:var(--rw) !important; }
  body:has(.ls-card.ls-bigctl) .corr-toast{
    width:var(--rw); max-width:var(--rw); min-width:var(--rw); }

  .ls-card.ls-bigctl .ls-tab{
    font-size:clamp(12px, 1.6vh, 18px); padding:clamp(10px,1.3vh,15px) 6px; }
  .ls-card.ls-bigctl #ls-panel-ctrl{
    gap:var(--kg); padding:clamp(8px,1.1vh,12px) clamp(8px,.8vw,14px);
    /* labels below are capped by the PANEL's width (cqi) as well as the screen
       height: the vh sizes suit a 680px TV panel, but in a ~330px tablet panel
       they cut 'Handicap' to 'Handi...'. On the TV the vh value is the smaller
       one, so nothing there changes. */
    container-type:inline-size; }

  /* -- balls, 3 x 3 (below 1580px): the old grid, Red / Switch / Back on top -- */
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid{
    grid-template-columns:repeat(3, minmax(0,1fr)); gap:var(--kg);
    max-width:none; width:100%; }   /* the portrait base caps .bgrid at 300px */
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb{
    max-width:var(--k-ball); justify-self:center; align-self:center;
    font-size:clamp(26px, 5vh, 52px); }
  /* v5.11.80 - Switch / Back: icon and word scaled up to fill the circle.
     The icon carries class=ico: the remote-key badge (.rk) is the button's
     first child since v5.11.78, so span:first-child had been sizing the
     BADGE, not the glyph.
     (operator: 'make the Switch and Back icon and text bigger') */
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb.ctrl{ font-size:clamp(13px, 2.1vh, 23px); line-height:1; gap:clamp(1px,.4vh,5px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb.ctrl span.ico{
    font-size:clamp(32px, 5.6vh, 60px); line-height:1; }

  .ls-card.ls-bigctl #ls-panel-ctrl .arow{ gap:var(--kg); max-width:none; }   /* base .arow caps at 340px */
  /* v5.11.67 - with the QR tile gone the four rows share the rest of the
     panel's height: each starts at its own minimum and grows by its weight,
     up to a cap. The keys fill their row (height:auto + stretch). */
  .ls-card.ls-bigctl #ls-panel-ctrl > .arow{ align-items:stretch; flex:1 1 auto; }
  .ls-card.ls-bigctl #ls-panel-ctrl > .arow:has(.foul-btn){ flex-grow:1;   min-height:var(--k-foul-h); max-height:clamp(54px, 12vh, 132px); }
  .ls-card.ls-bigctl #ls-panel-ctrl > .arow:has(.act-big) { flex-grow:1.3; min-height:var(--k-play-h); max-height:clamp(58px, 14vh, 150px); }
  .ls-card.ls-bigctl #ls-panel-ctrl > .arow:not(.ar-tall) { flex-grow:.7; min-height:var(--k-util-h); max-height:clamp(44px, 8.5vh, 96px); }
  .ls-card.ls-bigctl #ls-panel-ctrl > .arow:has(.ab.fr)   { flex-grow:1.3; min-height:var(--k-end-h);  max-height:clamp(58px, 14vh, 150px); }
  .ls-card.ls-bigctl #ls-panel-ctrl > .arow .ab{ height:auto !important; min-height:0 !important; }

  /* -- fouls -- */
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.foul-btn{
    height:var(--k-foul-h); min-height:var(--k-foul-h);
    border-radius:clamp(10px,1.4vh,16px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.foul-btn .foul-num{
    font-size:clamp(13px, min(2.8vh, 5cqi), 32px); padding:3px 10px; border-radius:7px; }
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.foul-btn .foul-x{ font-size:clamp(46px, 7.4vh, 84px); }

  /* -- Touching Ball / Free Ball / F&M Re-Spot (Pause in shot-clock games) -- */
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.act-big,
  .ls-card.ls-bigctl #ls-panel-ctrl #lsPauseSlot{
    height:var(--k-play-h); min-height:var(--k-play-h);
    border-radius:clamp(10px,1.4vh,16px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.act-big{
    /* v5.11.70 - the icon is a layer behind the words (admin-skins.css), so
       the label has the whole key: capped by the PANEL width, ~31px on the
       TV, ~17px on an 1180 tablet. The icon fills most of the key height. */
    font-size:clamp(13px, min(3.6vh, 5.2cqi), 32px) !important;
    padding-left:clamp(3px,.4vw,8px); padding-right:clamp(3px,.4vw,8px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.act-big .act-ico{ font-size:clamp(36px, min(11vh, 22cqi), 118px); }
  .ls-card.ls-bigctl #ls-panel-ctrl #lsPauseSlot{ font-size:clamp(11px, min(2vh, 4.5cqi), 22px); }

  /* -- +1 Red / Toss / Re-rack / +Red / -Red -- */
  .ls-card.ls-bigctl #ls-panel-ctrl .arow:not(.ar-tall) .ab{
    height:var(--k-util-h); min-height:var(--k-util-h);
    font-size:clamp(11px, min(2.4vh, 3.9cqi), 26px); border-radius:clamp(9px,1.2vh,14px); }

  /* -- End Match / Replays / Handicap / Audio / End Frame -- */
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.fr,
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.dk,
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.act-glyph{
    height:var(--k-end-h); min-height:var(--k-end-h);
    border-radius:clamp(10px,1.4vh,16px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.fr.ab-multiline,
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.dk.ab-multiline{
    font-size:clamp(12px, min(2.7vh, 4.8cqi), 30px); line-height:1.08; }
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.act-glyph.act-stack{ gap:clamp(2px,.4vh,6px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.act-glyph.act-stack .act-ico{ font-size:clamp(18px, min(4vh, 7cqi), 46px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .ab.act-glyph.act-stack .act-txt{ font-size:clamp(9px, min(2.1vh, 3.4cqi), 24px); }


  /* -- the left side: player cards sized to themselves, not the screen -- */
  .ls-card.ls-bigctl .ls-player-section{ container-type:inline-size; }
  .ls-card.ls-bigctl .ls-left .ls-pname{ font-size:clamp(14px, min(2.2vw, 15cqi), 38px); }
  .ls-card.ls-bigctl .ls-left.stats-collapsed .ls-pname{ font-size:clamp(15px, min(3vw, 15cqi), 56px); }
  .ls-card.ls-bigctl .ls-left.stats-collapsed .ls-avatar{
    width:clamp(96px, min(14vw, 64cqi), 240px);   /* 96 = smallest size the login QR is known to scan at */
    height:clamp(96px, min(14vw, 64cqi), 240px); }
  /* a narrow card has no room beside the QR for the corner clocks, so the QR
     starts below them instead of on top of them */
  @container (max-width: 300px){
    .ls-card.ls-bigctl .ls-left.stats-collapsed .ls-avatar{ margin-top:clamp(30px, 4.4vh, 44px); }
  }
  /* Score plates. Arial Black digits are ~0.68em wide; the plate adds ~54px of
     padding and border. So n digits fit at (100cqi - 54px) / (0.68 n):
     two digits = 70cqi - 38px, three = 47cqi - 26px (both rounded down).
     data-digits is written by the render code with the score itself. */
  .ls-card.ls-bigctl .ls-left .ls-score{
    font-size:min(clamp(100px, 12vw, 210px), calc(70cqi - 38px)); }
  .ls-card.ls-bigctl .ls-left.stats-collapsed .ls-score{
    font-size:min(clamp(120px, 14vw, 260px), calc(70cqi - 38px)); }
  .ls-card.ls-bigctl .ls-left .ls-score[data-digits="3"]{
    font-size:min(clamp(100px, 12vw, 210px), calc(47cqi - 26px)); }
  .ls-card.ls-bigctl .ls-left.stats-collapsed .ls-score[data-digits="3"]{
    font-size:min(clamp(120px, 14vw, 260px), calc(47cqi - 26px)); }

  /* -- the centre column: narrower, with its contents sized to it --
     Operator: 'maybe we can also shrink the middle column. have Rem and Diff
     box in one row so they can be also larger. have the Red box and Detector
     box in same row ...'. Its width was whatever its widest child needed,
     and the three pills in a row made that 474px on the TV. It now has a set
     width, ~380px on the TV and ~275px on an 1180 tablet, and everything
     inside sizes to it (cqi = 1% of the column), so the player cards on
     either side get the difference. */
  .ls-card.ls-bigctl{ --cw: clamp(230px, calc(14vw + 110px), 420px); }
  .ls-card.ls-bigctl .ls-left{ grid-template-columns: minmax(0,1fr) var(--cw) minmax(0,1fr); }
  .ls-card.ls-bigctl .ls-centre{ width:var(--cw); min-width:0; max-width:var(--cw); container-type:inline-size; }
  .ls-card.ls-bigctl .ls-centre > *{ max-width:100%; }

  /* NEXT ball and BREAK share a row, so both give way to the column */
  .ls-card.ls-bigctl .next-ball.ls-lg{
    width:min(clamp(140px, 13vw, 210px), 44cqi); height:min(clamp(140px, 13vw, 210px), 44cqi);
    font-size:min(clamp(28px, 2.6vw, 44px), 9cqi); }
  .ls-card.ls-bigctl .ls-centre .ls-next-brk{ gap:clamp(10px, 5cqi, 40px); flex-wrap:nowrap; }
  .ls-card.ls-bigctl .ls-centre .ls-brk{ font-size:min(clamp(64px, 7vw, 130px), 34cqi); }
  .ls-card.ls-bigctl .ls-left.stats-collapsed .ls-centre .ls-brk{ font-size:min(clamp(80px, 9vw, 170px), 34cqi); }
  /* a century break is three digits wide: smaller, so the row still fits */
  .ls-card.ls-bigctl .ls-centre .ls-brk:has(#lsBrkVal[data-digits="3"]),
  .ls-card.ls-bigctl .ls-left.stats-collapsed .ls-centre .ls-brk:has(#lsBrkVal[data-digits="3"]){
    font-size:min(clamp(64px, 7vw, 130px), 24cqi); }

  /* timers: two boxes across the narrower column */
  .ls-card.ls-bigctl .ls-centre .ls-timers{ width:100%; gap:clamp(6px, 2.4cqi, 16px); }
  .ls-card.ls-bigctl .ls-centre .ls-tbox{ flex:1 1 0; min-width:0; padding-left:clamp(4px, 2.4cqi, 20px); padding-right:clamp(4px, 2.4cqi, 20px); }
  .ls-card.ls-bigctl .ls-centre .ls-tval{ font-size:min(clamp(26px, 3vw, 54px), 10cqi); letter-spacing:1px; }
  .ls-card.ls-bigctl .ls-centre .ls-tlbl{ font-size:min(clamp(9px, .78vw, 14px), 3.4cqi); }

  /* pills: Rem | Diff on the first row, Reds | Detector on the second
     (20 columns so the second row can split 35/65) */
  .ls-card.ls-bigctl .ls-pills-row{
    display:grid; grid-template-columns:repeat(20, minmax(0,1fr));
    gap:clamp(5px, 2cqi, 8px); }
  .ls-card.ls-bigctl .ls-pills-row > .ls-pill{ min-width:0; min-height:clamp(54px, 18cqi, 84px);
    padding:clamp(5px, 2cqi, 12px) clamp(4px, 2cqi, 12px); }
  .ls-card.ls-bigctl .ls-pills-row > .ls-pill .pill-lbl{ font-size:clamp(10px, 3.4cqi, 15px); }
  .ls-card.ls-bigctl .ls-pills-row > .ls-pill.rem { grid-row:1; grid-column:1 / span 10; }
  .ls-card.ls-bigctl .ls-pills-row > .ls-pill.diff{ grid-row:1; grid-column:11 / span 10; }
  .ls-card.ls-bigctl .ls-pills-row > .ls-pill.rem .pill-val,
  .ls-card.ls-bigctl .ls-pills-row > .ls-pill.diff .pill-val{ font-size:clamp(28px, 15cqi, 64px); }
  .ls-card.ls-bigctl .ls-pills-row > .ls-pill.reds{ grid-row:2; grid-column:1 / span 7; }
  .ls-card.ls-bigctl .ls-pills-row > .ls-pill.reds .pill-val{ font-size:clamp(26px, 12cqi, 54px); }
  /* no detector on screen (no match, or no detector): Reds takes the row */
  .ls-card.ls-bigctl .ls-pills-row:not(:has(> #lsDetVision.in-pills:not([style*="none"]))) > .ls-pill.reds{
    grid-column:1 / -1; }

  /* the detector box: big red ball and count on the left, the word DETECTOR
     beside it on the first line, the small colour balls on the second.
     The page shows and hides it with an inline display, so the grid is only
     forced while that inline value is not 'none'. */
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills{
    grid-row:2; grid-column:8 / span 13;
    grid-template-columns:auto minmax(0,1fr); grid-template-rows:auto auto;
    align-items:center; justify-items:start; justify-content:start;
    column-gap:clamp(6px, 2.6cqi, 14px); row-gap:clamp(2px, 1cqi, 6px);
    padding:clamp(5px, 2cqi, 10px) clamp(6px, 2.6cqi, 14px);
    border-radius:14px; margin:0; min-width:0; max-width:none; overflow:hidden; }
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills:not([style*="none"]){ display:grid !important; }
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills > .det-red{ grid-row:1 / span 2; grid-column:1; gap:clamp(4px, 1.6cqi, 10px); }
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills > .det-red .rd{ width:clamp(22px, 10cqi, 46px); height:clamp(22px, 10cqi, 46px); }
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills > .det-red b{ font-size:clamp(22px, 11cqi, 48px); line-height:1; }
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills > .det-lbl{ grid-row:1; grid-column:2; align-self:end;
    font-size:clamp(9px, 3.2cqi, 14px); letter-spacing:1px; }
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills > .det-cols{ grid-row:2; grid-column:2; align-self:start; gap:clamp(2px, .9cqi, 5px); }
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills > .det-cols .cb{ width:clamp(9px, 3.6cqi, 20px); height:clamp(9px, 3.6cqi, 20px); }
  .ls-card.ls-bigctl .ls-pills-row > #lsDetVision.in-pills > *:not(.det-red):not(.det-lbl):not(.det-cols){ grid-column:1 / -1; }

  /* -- ball history: 8 across, 8 down when the card has the height, else 7 --
     v5.11.80, operator: 'make ball history 8x8 instead or 8x7 if filled up it
     would up one row if not enough room'. Eight columns make each ball
     (card width - seven 2px gaps) / 8 wide, between 14 and 44px; the row
     count and a height cap are decided by _histGridFor() in JS, which
     measures the space left under the score and writes --hb and the row
     count inline (CSS cannot see the card's free height). When the grid is
     full, renderShotHistory drops whole ROWS off the top so the newest
     entries always start on a fresh row. A manual size from Settings still
     wins but can never overflow the card. */
  .ls-card.ls-bigctl .ls-player-section{ --hb: clamp(14px, calc((100cqi - 14px) / 8), 44px); }
  body[style*="--shot-box"] .ls-card.ls-bigctl .ls-player-section{ --hb: min(var(--shot-box), calc((100cqi - 14px) / 8)); }
  .ls-card.ls-bigctl .ls-player-section .ls-shot-history{
    grid-template-columns:repeat(8, var(--hb)); grid-template-rows:repeat(8, var(--hb)); }
  .ls-card.ls-bigctl .ls-player-section .ls-shot-box{ width:var(--hb); height:var(--hb); font-size:calc(var(--hb) * .30); }
  .ls-card.ls-bigctl .ls-player-section .ls-shot-box .shb{ font-size:calc(var(--hb) * .36); }
  .ls-card.ls-bigctl .ls-player-section .ls-shot-box.t-foul{ font-size:calc(var(--hb) * .62); }
  .ls-card.ls-bigctl .ls-player-section .ls-shot-box.t-scratch .shb{ font-size:calc(var(--hb) * .46); }
  .ls-card.ls-bigctl .ls-player-section .ls-shot-box > span{
    font-size:calc(var(--hb) * .48) !important; top:calc(var(--hb) * .07) !important;
    right:calc(var(--hb) * .12) !important; }
}
/* the wide shape - TVs and big monitors, where the panel reaches ~520px+ */
@media (min-width:1580px) and (orientation:landscape){
  .ls-card.ls-bigctl{
    --k-play-h: clamp(62px, 10vh, 116px);
    --k-end-h:  clamp(62px, 10vh, 116px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid{ grid-template-columns:repeat(6, minmax(0,1fr)); }
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb{ max-width:var(--k-small); font-size:clamp(24px, 4.4vh, 46px); }
  /* DOM order is red, switch, back, then the colours, so spanning two of the
     six columns puts the first three on row one with no placement rules. */
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb.red,
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb.ctrl{ grid-column:span 2; max-width:var(--k-big); }
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb.red{ font-size:clamp(40px, 7vh, 76px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb.ctrl{ font-size:clamp(16px, 2.8vh, 30px); line-height:1; gap:clamp(2px,.5vh,6px); }
  .ls-card.ls-bigctl #ls-panel-ctrl .bgrid .bb.ctrl span.ico{ font-size:clamp(40px, 7.4vh, 80px); }
}

/* v5.11.228-portrait-nm — the portrait Matches page uses the landscape New
   Match form; its rules live in the landscape media query, so the portrait
   page gets the same look here at phone sizes. */
#pg-matches .nm{ width:100%; display:flex; flex-direction:column; gap:8px; box-sizing:border-box; }
#pg-matches .nm-row{ display:flex; gap:8px; width:100%; align-items:flex-start; }
#pg-matches .nm-bestof, #pg-matches .nm-mode{ flex-direction:column; gap:3px; }
#pg-matches .nm-col{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:3px; }
#pg-matches .nm .hcap-lbl{ font-size:12px; letter-spacing:1.2px; }
#pg-matches .nm-a > .hcap-lbl{ color:#60a5fa; }
#pg-matches .nm-b > .hcap-lbl{ color:#fbbf24; }
#pg-matches .nm-players .inp.memb-inp{ font-size:16px; font-weight:700; padding:12px 10px; min-height:48px; width:100%; box-sizing:border-box; margin:0; }
#pg-matches .nm-a .inp.memb-inp{ border-color:rgba(96,165,250,.55); }
#pg-matches .nm-b .inp.memb-inp{ border-color:rgba(250,204,21,.55); }
#pg-matches .nm-select{ font-size:16px; font-weight:700; padding:10px 12px; background:#242424; color:#fff; width:100%; margin:0; }
#pg-matches .nm-custom{ font-size:16px; padding:10px 12px; width:100%; margin:0; }
#pg-matches .hcap-ticks{ display:flex; flex-wrap:wrap; gap:8px; width:100%; }
#pg-matches .hcap-ticks > .hcap-apply{ flex:1 1 150px; }
#pg-matches .hcap-apply{ display:flex; align-items:center; flex-wrap:wrap; gap:6px 10px; margin:0; padding:10px 12px;
  border:2px solid rgba(255,255,255,.28); border-radius:12px; background:rgba(255,255,255,.06);
  font-size:16px; font-weight:800; color:#fff; cursor:pointer; user-select:none; box-sizing:border-box; }
#pg-matches .hcap-apply input[type=checkbox]{ width:26px; height:26px; margin:0; flex:0 0 auto; accent-color:#30D158; }
#pg-matches .hcap-apply:has(input:checked){ border-color:#30D158; background:rgba(48,209,88,.14); }
#pg-matches .hcap-apply.hcap-elo input[type=checkbox]{ accent-color:#60a5fa; }
#pg-matches .hcap-apply.hcap-elo:has(input:checked){ border-color:#60a5fa; background:rgba(96,165,250,.14); }
#pg-matches .hcap-apply .hcap-hint{ flex-basis:100%; font-size:11px; font-weight:500; color:rgba(255,255,255,.55); }
#pg-matches .nm-hcaps .hcap-col{ flex:1 1 0; min-width:0; }
#pg-matches .hcap-stepper{ display:flex; align-items:stretch; gap:6px; }
#pg-matches .hcap-stepper .hcap-inp{ font-size:26px; padding:8px 4px; min-width:0; flex:1 1 auto; margin:0; text-align:center;
  -moz-appearance:textfield; appearance:textfield; }
#pg-matches .hcap-stepper .hcap-inp::-webkit-outer-spin-button,
#pg-matches .hcap-stepper .hcap-inp::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
#pg-matches .hcap-btn{ flex:0 0 44px; font-size:26px; font-weight:900; line-height:1; border-radius:10px; cursor:pointer;
  border:1px solid rgba(255,255,255,.28); background:rgba(255,255,255,.08); color:#fff; }
#pg-matches .hcap-btn:active{ transform:scale(.96); }
#pg-matches .hcap-col:has(.hcap-a) .hcap-btn{ border-color:rgba(96,165,250,.5); color:#dbeafe; }
#pg-matches .hcap-col:has(.hcap-b) .hcap-btn{ border-color:rgba(250,204,21,.5); color:#fef3c7; }
#pg-matches .nm .btn-go{ font-size:18px; padding:14px; margin:0; }
