/* ---------------------------------------------------------------- base */
:root{
  --bg:#0d1411; --bg2:#131d19; --card:#16211c; --line:#25342c;
  --ink:#e8f2ec; --muted:#8ea79a; --accent:#3ddc84; --accent2:#38bdf8;
  --home:#3ddc84; --away:#fbbf24;
  --goal:#22c55e; --saved:#38bdf8; --blocked:#a78bfa; --post:#f472b6; --off:#94a3b8;
  --danger:#ef4444;
  --r:14px;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
/* The page itself never scrolls sideways — anything wide (a table, a map)
   scrolls inside its own box. `clip`, not `hidden`: hidden makes the body a
   scroll container, which breaks the sticky top bar. */
body{
  overflow-x:clip;
  background:var(--bg); color:var(--ink);
  font:15px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  -webkit-text-size-adjust:100%; padding-bottom:40px;
  -webkit-tap-highlight-color:transparent;
}
h1,h2,h3{margin:0 0 10px}
h1{font-size:24px} h2{font-size:19px} h3{font-size:15px;letter-spacing:.02em}
.muted{color:var(--muted)} .small{font-size:12.5px} .strong{font-weight:700}
.hidden{display:none !important}
code{background:#0b120f;padding:1px 5px;border-radius:5px;font-size:12px}

/* -------------------------------------------------------------- topbar */
#topbar{
  position:sticky; top:0; z-index:30; background:rgba(13,20,17,.94);
  backdrop-filter:blur(8px); border-bottom:1px solid var(--line);
  display:flex; align-items:center; flex-wrap:nowrap; gap:12px; padding:6px 14px;
  padding-top:calc(6px + env(safe-area-inset-top));
}
/* The tabs always win the space; the brand gives way, then disappears. */
.brand{display:flex; align-items:center; gap:6px; min-width:0; flex:0 1 auto;
  font-weight:800; letter-spacing:-.01em; overflow:hidden}
.brand-mark{flex:none}
.brand-name{white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.tabs{display:flex; gap:6px; margin-left:auto; flex:0 0 auto; flex-wrap:nowrap}
/* Thinner pills, mostly vertically: the height of this bar is stolen from the
   pitch on every page. 34px of tappable height is still comfortably past the
   ~30px a thumb needs, and the horizontal padding stays generous so the pill
   still reads as a pill rather than bare text. */
.tab{
  color:var(--muted); text-decoration:none; font-weight:600; font-size:13.5px;
  padding:4px 12px; border-radius:999px; border:1px solid transparent;
  white-space:nowrap; line-height:1.5;
}
.tab.active{color:#06120c; background:var(--accent); border-color:var(--accent)}
.lbl-short{display:none}

.view{display:none; max-width:1100px; margin:0 auto; padding:14px}
.view.on{display:block}

/* --------------------------------------------------------------- bits */
.btn{
  font:inherit; font-weight:700; border-radius:10px; border:1px solid var(--line);
  background:var(--bg2); color:var(--ink); padding:10px 14px; cursor:pointer;
}
.btn:active{transform:translateY(1px)}
.btn-go{background:var(--accent); color:#06120c; border-color:var(--accent)}
.btn-stop{background:#ef4444; color:#fff; border-color:#ef4444}
.btn-ghost{background:transparent}
.btn.small{padding:6px 10px; font-size:12.5px}
.btn .ico{margin-right:4px}
.linkbtn{background:none;border:0;color:var(--muted);font:inherit;font-size:12.5px;
  text-decoration:underline;cursor:pointer;padding:4px 2px}
.linkbtn.danger{color:#f87171}
.iconbtn{background:none;border:0;color:var(--muted);font-size:15px;cursor:pointer;padding:4px 7px;border-radius:8px}
.iconbtn.danger:hover{color:#fff;background:var(--danger)}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:14px;margin-bottom:14px}
.pill{margin-left:auto;font-size:12px;color:var(--muted);background:var(--bg2);
  border:1px solid var(--line);border-radius:999px;padding:3px 10px}
.empty{color:var(--muted);font-style:italic;padding:10px 2px;font-size:13.5px}
.hint{color:var(--muted);font-size:13px;margin:6px 2px 10px;text-align:center}
.hint.tight{margin:8px 0 6px}

/* inline-grid with 1fr columns: every segment is the same width AND as wide as
   the longest label, so the divider sits dead centre and nothing wraps. Flex
   couldn't do both at once — equal widths forced the labels onto two lines. */
.seg{display:inline-grid;grid-auto-flow:column;grid-auto-columns:1fr;align-items:stretch;
  background:var(--bg2);border:1px solid var(--line);border-radius:11px;padding:3px;gap:3px}
.seg.wide{display:grid;width:100%;margin-bottom:12px}
/* min-width:0 is what actually makes flex:1 split the row evenly — without it a
   longer label claims more space and the divider sits off-centre. Labels wrap
   rather than shrink the button, so both halves always match. */
.seg-btn{min-width:0;display:flex;align-items:center;justify-content:center;
  text-align:center;font:inherit;font-weight:700;font-size:13.5px;border:0;border-radius:8px;
  background:transparent;color:#c3d5cb;padding:10px 12px;cursor:pointer;
  white-space:normal;line-height:1.2;overflow-wrap:break-word}
.seg-btn:not(.active):hover{color:var(--ink)}
.seg-btn.active{background:var(--accent);color:#06120c}
#team-seg .seg-btn[data-team="away"].active{background:var(--away);color:#231a00}

/* --------------------------------------------------------- scoreboard */
.scoreboard{display:grid;grid-template-columns:1fr auto 1fr;gap:10px;align-items:start;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:12px;margin-bottom:12px}
/* Name, score and scorers are laid out by the SCOREBOARD's grid rather than
   each side's own box, so the two names share one row and set its height
   between them. Both scores then start on the same line whatever the names
   do — which is what the old fixed-height name box was for, and it clipped a
   three-line club name behind the score to get it. */
.score-team{display:contents}
.st-name,.st-goals,.scorers{text-align:center;min-width:0}
.st-name[data-team="home"],.st-goals[data-team="home"],.scorers[data-team="home"]{grid-column:1}
.st-name[data-team="away"],.st-goals[data-team="away"],.scorers[data-team="away"]{grid-column:3}
.st-name{grid-row:1} .st-goals{grid-row:2} .scorers{grid-row:3}
.clock-block{grid-column:2;grid-row:1 / span 3}
.st-name{font-size:12.5px;color:var(--muted);font-weight:700}
.st-goals{font-size:38px;font-weight:800;line-height:1.05}
#sb-home .st-goals{color:var(--home)} #sb-away .st-goals{color:var(--away)}
.clock-block{text-align:center;padding-top:2px}
.clock{font:inherit;font-size:30px;font-weight:800;font-variant-numeric:tabular-nums;
  letter-spacing:.01em;background:none;border:0;color:var(--ink);padding:0 4px;cursor:pointer;
  border-bottom:1px dashed transparent}
.clock:hover{border-bottom-color:var(--line)}
body.clock-running .clock{color:var(--accent)}
.clock-controls{display:flex;gap:6px;justify-content:center;margin-top:6px}
/* A native select left-aligns its text and reserves room for the system arrow,
   so it can never be truly centred. Draw the label ourselves, centred with the
   chevron as one group, and lay the real select over it, invisible, to handle
   the tap and the picker. */
.period-wrap{position:relative;display:inline-flex;align-items:center;justify-content:center;
  gap:7px;background:var(--bg2);border:1px solid var(--line);border-radius:10px;
  padding:12px 16px;min-width:118px;cursor:pointer}
.period-wrap:focus-within{border-color:var(--accent)}
.period-label{font-size:15px;font-weight:800;color:var(--ink);white-space:nowrap}
.period-chev{width:11px;height:7px;flex:none;fill:none;stroke:var(--muted);stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.period{position:absolute;inset:-1px;opacity:0;cursor:pointer;   /* -1px covers the border */
  appearance:none;-webkit-appearance:none;border:0;font:inherit}

.controls{display:flex;gap:8px;flex-wrap:wrap;align-items:center;justify-content:center;
  margin-bottom:4px}
#btn-undo{white-space:nowrap}
/* Undo becomes Redo after it fires: the same button, but plainly a different one
   to press, so nobody taps it twice expecting two shots to come off. */
.btn.is-redo{border-color:var(--accent);color:var(--accent);background:rgba(61,220,132,.10)}

/* -------------------------------------------------------------- pitch */
.svg-host{display:block;width:100%}
.pitch-svg{display:block;width:100%;height:auto;margin:0 auto}
.pitch-svg[data-mode="full"]{max-width:min(100%,46vh)}
.turf{fill:#123020}
.stripe{fill:#14361f;opacity:.55}
.ln{fill:none;stroke:rgba(255,255,255,.62);stroke-width:.28}
.dot{fill:rgba(255,255,255,.72)}
.goalbox{fill:rgba(255,255,255,.14);stroke:rgba(255,255,255,.7);stroke-width:.24}
.pitch-tag{fill:rgba(255,255,255,.42);font-size:1.9px;letter-spacing:.5px;text-anchor:middle}
.pitch-svg{cursor:crosshair}
/* The opponent's pitch is a cooler, lighter green — different enough to notice
   at a glance without leaving the colour of grass. The name on the pitch is the
   cue that actually matters; this just backs it up. */
.pitch-svg.team-away .turf{fill:#173f35}
.pitch-svg.team-away .stripe{fill:#1b4a3d}

/* markers */
/* Every marker is ringed in its side's colour — the same green and amber the
   scoreboard and the shot log already use for home and away. The fill still
   says what happened to the shot; the ring says who took it, which the mirrored
   half used to convey by position alone and the near half not at all.

   These sit ABOVE .mk-hot deliberately: it is the same specificity, so the
   highlighted shot's white ring only keeps winning while it comes later. */
.mk circle{stroke-width:.3;opacity:.92}
.mk-team-home circle{stroke:var(--home)}
.mk-team-away circle{stroke:var(--away)}
/* Not in the goal mouth. That view is drawn in its own, much smaller user
   units — roughly a fifth of the pitch's — so the ring above lands as a fat
   halo swallowing the marker rather than an outline around it. It has nothing
   to say there either: a goal-mouth diagram is one team's shots at a time, so
   the colour would be the same on every dot. A hairline keeps them off the net
   and that is all that is wanted.

   `:not(.mk-hot)` so the highlighted shot keeps its white ring. */
.goal-svg .mk:not(.mk-hot) circle{stroke:#0b120f;stroke-width:.05}

/* stacked shots read as a denser blob rather than hiding one another */
.mk-dim{opacity:.3}
.mk-other text{opacity:1}
.mk-hot circle{opacity:1;stroke:#fff;stroke-width:.34}
.mk-halo{fill:none;stroke:#fff;stroke-width:.3;opacity:.85}
.goal-svg .mk-hot circle{stroke-width:.06}
.goal-svg .mk-halo{stroke-width:.055}
/* never let a plotted shot swallow a tap meant for the pitch underneath */
/* On the TRACKER a dot never takes a tap. Tapping the pitch means "log a shot",
   shots cluster on the same spots, and two attempts from the same yard of the
   six-yard box are common — so a dot that intercepted would cost a shot at the
   moment there is least time to notice. Tried it the other way; this is right.

   A VIEWER logs nothing, so there is no tap for a dot to steal and the bubble
   costs them nothing. Same rule on the report's maps, which are read-only for
   everyone. */
#pitch-host .mk,.sheet-pitch .mk{pointer-events:none}
body[data-role="viewer"] #pitch-host .mk{pointer-events:auto;cursor:pointer}
.mk text{fill:#08110c;text-anchor:middle;font-weight:800;paint-order:stroke}
.mk-goal circle{fill:var(--goal)} .mk-saved circle{fill:var(--saved)}
.mk-blocked circle{fill:var(--blocked)} .mk-post circle{fill:var(--post)}
.mk-off circle{fill:var(--off)}

/* ---------------------------------------------------------- goal mouth */
.goal-svg{display:block;width:100%;height:auto;max-height:40vh;cursor:crosshair}
.miss-zone{fill:#0f1a15}
.ground{fill:#123020}
.goalline{stroke:rgba(255,255,255,.6);stroke-width:.05}
.net{fill:rgba(255,255,255,.06)}
.mesh{stroke:rgba(255,255,255,.16);stroke-width:.02}
.guide{stroke:rgba(255,255,255,.22);stroke-width:.03;stroke-dasharray:.12 .12}
.frame{fill:none;stroke:#f4f7f5;stroke-width:.13;stroke-linecap:square}
.goal-tag{fill:rgba(255,255,255,.35);font-size:.28px}
.place-mark{fill:#fff;stroke:#0b120f;stroke-width:.05}

/* --------------------------------------------------------------- flow */
.flow{position:fixed;inset:0;z-index:60;background:rgba(4,8,6,.72);
  display:flex;align-items:flex-end;justify-content:center}
.flow-sheet{background:var(--card);border:1px solid var(--line);
  border-radius:18px 18px 0 0;width:100%;max-width:640px;max-height:94vh;
  display:flex;flex-direction:column;padding:14px;
  padding-bottom:calc(14px + env(safe-area-inset-bottom));
  box-shadow:0 -18px 50px rgba(0,0,0,.55)}
.flow-head{display:flex;align-items:flex-start;gap:10px;margin-bottom:12px}
.flow-step{font-size:11.5px;color:var(--muted);font-weight:700;letter-spacing:.06em;text-transform:uppercase}
.flow-title{font-size:20px;font-weight:800}
.flow-meta{margin-left:auto;display:flex;align-items:center;gap:6px}
.badge{display:inline-flex;align-items:center;gap:5px;background:var(--bg2);
  border:1px solid var(--line);border-radius:999px;padding:4px 10px;font-size:12px;
  font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap;max-width:100%}
.badge .crest{width:15px;height:15px;flex:none}
.badge .badge-txt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.badge.team-home{color:var(--home);border-color:rgba(61,220,132,.4);
  background:rgba(61,220,132,.10)}
.badge.team-away{color:var(--away);border-color:rgba(251,191,36,.4);
  background:rgba(251,191,36,.10)}
.flow-body{overflow-y:auto;-webkit-overflow-scrolling:touch;flex:1}
.flow-foot{display:flex;align-items:center;gap:10px;margin-top:12px;padding-top:10px;border-top:1px solid var(--line)}
.crumbs{margin-left:auto;display:flex;align-items:center;gap:5px;font-size:11.5px;color:var(--muted);flex-wrap:wrap}
.crumb.on{color:var(--accent)} .crumb.cur{color:var(--ink);font-weight:700}
.crumb-sep{opacity:.4}

.numgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(78px,1fr));gap:8px}
.wide-note{grid-column:1/-1}
.numbtn{background:var(--bg2);border:1px solid var(--line);border-radius:12px;
  padding:10px 6px;cursor:pointer;color:var(--ink);font:inherit;display:flex;
  flex-direction:column;align-items:center;gap:2px;min-height:64px;justify-content:center}
.numbtn:active{background:var(--accent);color:#06120c}
.numbtn .n{font-size:22px;font-weight:800;line-height:1}
.numbtn .nm{font-size:10.5px;color:var(--muted);text-align:center;line-height:1.15;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.numbtn:active .nm{color:#06120c}
.numbtn.other{border-style:dashed}

.typegrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px}
.typebtn{background:var(--bg2);border:1px solid var(--line);border-radius:12px;color:var(--ink);
  font:inherit;font-weight:700;padding:16px 10px;cursor:pointer}
.typebtn:active{background:var(--accent);color:#06120c}

.outcomes{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:7px;margin-top:4px}
.ocbtn{font:inherit;font-weight:800;font-size:13px;border-radius:11px;padding:13px 6px;cursor:pointer;
  border:1px solid var(--line);background:var(--bg2);color:var(--muted)}
.ocbtn:disabled{opacity:.35;cursor:not-allowed}
.ocbtn:not(:disabled).oc-goal{color:var(--goal);border-color:var(--goal)}
.ocbtn:not(:disabled).oc-saved{color:var(--saved);border-color:var(--saved)}
.ocbtn:not(:disabled).oc-blocked{color:var(--blocked);border-color:var(--blocked)}
.ocbtn:not(:disabled).oc-post{color:var(--post);border-color:var(--post)}
.ocbtn:not(:disabled).oc-off{color:var(--off);border-color:var(--off)}
.ocbtn.suggest{box-shadow:0 0 0 2px rgba(61,220,132,.28)}
.goal-host{background:#0b1310;border:1px solid var(--line);border-radius:12px;padding:4px}

/* --------------------------------------------------- edit a logged shot */
.editlist{list-style:none;margin:0 0 14px;padding:0}
.editlist li{display:flex;align-items:center;gap:10px;padding:13px 4px;
  border-bottom:1px solid var(--line);cursor:pointer}
.editlist li:active{background:var(--bg2)}
.el-label{color:var(--muted);font-size:12.5px;min-width:88px;flex:none}
.el-value{flex:1;font-weight:700;min-width:0}
.el-go{color:var(--muted);font-size:18px;flex:none}
.editactions{display:flex;gap:8px;flex-wrap:wrap}
.editactions .btn{flex:1;min-width:140px}
.danger-btn{color:#f87171;border-color:#5b2020}
.btn.wide{width:100%;margin-top:4px}
.numbtn.picked,.typebtn.picked{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
.ocbtn.picked{box-shadow:inset 0 0 0 2px currentColor}
.sheet-pitch{background:#0b1310;border:1px solid var(--line);border-radius:12px;padding:6px}
.sheet-pitch .pitch-svg{max-width:min(100%,38vh);margin:0 auto}
.timeedit{display:flex;gap:8px;align-items:center;margin-bottom:10px}
.time-input{flex:1;min-width:0;font:inherit;font-size:28px;font-weight:800;text-align:center;
  font-variant-numeric:tabular-nums;background:var(--bg2);color:var(--ink);
  border:1px solid var(--line);border-radius:11px;padding:12px 8px}
.nudges{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.nudges .btn{padding:11px 4px;font-size:13px}
.presets{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:6px;margin-bottom:12px}
.presets .btn{padding:11px 6px;font-size:13px}
.tight-top{margin:12px 2px 6px}
.halflen{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--muted);
  margin-bottom:12px;flex-wrap:wrap}
.halflen input{width:72px;font:inherit;font-size:15px;font-weight:700;text-align:center;
  background:var(--bg2);color:var(--ink);border:1px solid var(--line);border-radius:9px;padding:8px}
/* On iOS a plain select opens the system wheel picker, which is exactly the control
   wanted here; elsewhere it's a normal dropdown. */
.wheelwrap{display:flex;justify-content:center;padding:6px 0 2px}
.numwheel{font:inherit;font-size:34px;font-weight:800;font-variant-numeric:tabular-nums;
  text-align:center;text-align-last:center;background:var(--bg2);color:var(--ink);
  border:1px solid var(--line);border-radius:14px;padding:16px 28px;min-width:160px}
.numwheel:focus{outline:0;border-color:var(--accent)}

/* end half / end game, spanning the scoreboard */
.match-actions{grid-column:1 / -1;display:flex;gap:8px;justify-content:center;
  margin-top:10px;padding-top:10px;border-top:1px solid var(--line)}

/* running shots-per-team comparison on the tracker */
.tally{display:flex;flex-direction:column;gap:6px;margin:4px 0 12px}
/* One line per team, always: the club name gives way, the numbers never do. */
.tally-row{display:flex;align-items:center;gap:9px;flex-wrap:nowrap;font-size:12.5px;
  color:var(--muted);padding:7px 8px;border-radius:10px;background:var(--bg2);
  border:1px solid transparent;overflow:hidden}
.tally-row.current{border-color:var(--line)}
.tally-row .badge{margin-right:2px;min-width:0;flex:0 1 auto}
.tally-n{white-space:nowrap;flex:none;display:inline-flex;align-items:center;gap:3px}
.tally-n b{color:var(--ink);font-size:14px;font-weight:800}
/* Emoji render tall for their point size; hold them to the line's rhythm. */
/* Abbreviations now, not emoji — sized and weighted to match the xG label
   beside them so the four read as one set of labels rather than three pictures
   and a word. */
.tally-n .ti{font-style:normal;font-size:11px;line-height:1;font-weight:800;
  color:var(--muted);letter-spacing:.02em}

/* the number chip on each log row, coloured like its dot on the pitch */
.rownum{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;
  border-radius:50%;font-size:11.5px;font-weight:800;color:#08110c;flex:none;
  background:var(--off)}
.rownum[data-oc="goal"]{background:var(--goal)}
.rownum[data-oc="saved"]{background:var(--saved)}
.rownum[data-oc="blocked"]{background:var(--blocked)}
.rownum[data-oc="post"]{background:var(--post)}
.rownum[data-oc="off"]{background:var(--off)}
/* The chip is filled by the outcome and ringed by the team, in the same green
   and amber the team pills and the pitch use — so whose shot it was reads from
   the left edge of the row without following the pill across it. A box-shadow
   rather than an outline: it is guaranteed to follow the circle everywhere. */
.rownum[data-team="home"]{box-shadow:0 0 0 1.5px var(--card),0 0 0 3.5px var(--home)}
.rownum[data-team="away"]{box-shadow:0 0 0 1.5px var(--card),0 0 0 3.5px var(--away)}

/* the key to those chips, sat above the shot list */
.legend-track{display:flex;flex-direction:column;align-items:flex-start;
  margin:2px 0 12px;gap:6px;font-size:10.5px}
.lg-row{display:flex;flex-wrap:wrap;align-items:center;gap:7px 12px}
/* All five outcomes belong on one line — split across two they stop reading as
   one scale. The short labels above are what buys the width. */
.lg-fill{flex-wrap:nowrap;gap:9px;max-width:100%;overflow-x:auto;scrollbar-width:none}
.lg-fill::-webkit-scrollbar{display:none}
.lg-fill .lg{white-space:nowrap}
/* the oldest small phones need a shade less of everything to hold the line */
@media (max-width:345px){
  .legend-track{font-size:9px}
  .lg-fill{gap:6px}
  .lg-fill .lg{gap:4px}
  .lg-fill .sw{width:8px;height:8px}
}
.legend-track .lg-lead{font-weight:800;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);opacity:.8}
/* a swatch drawn the way a chip is: filled by the outcome, ringed by the team */
.sw-ring{background:var(--bg2);margin-right:3px}
.sw-ring[data-team="home"]{box-shadow:0 0 0 1px var(--card),0 0 0 2.5px var(--home)}
.sw-ring[data-team="away"]{box-shadow:0 0 0 1px var(--card),0 0 0 2.5px var(--away)}

/* ------------------------------------------------- highlighted shot bar */
.hot-bar{display:flex;align-items:center;gap:8px;margin-top:8px;padding:8px 10px;
  background:var(--card);border:1px solid var(--accent);border-radius:12px;font-size:12.5px}
.hot-bar.hidden{display:none}
.hot-n{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;
  border-radius:50%;background:var(--accent);color:#06120c;font-weight:800;flex:none}
.hot-txt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.logrow.hot{background:rgba(61,220,132,.10);border-radius:8px}
.logrow.hot .who{color:var(--accent)}

/* --------------------------------------------------------------- log */
.log{list-style:none;margin:0;padding:0}
.logrow{display:flex;align-items:center;gap:8px;padding:8px 4px;border-bottom:1px solid var(--line);font-size:13px}
.logrow[data-edit]{cursor:pointer}
.logrow[data-edit]:active{background:var(--bg2)}
.logrow:last-child{border-bottom:0}
.logrow .t{font-variant-numeric:tabular-nums;color:var(--muted);min-width:70px;white-space:nowrap}
.logrow .who{font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.logrow .ty{color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.logrow .oc{margin-left:auto}
@media (max-width:620px){
  .logrow.arch{flex-wrap:wrap}
  /* Outcome pill sits on the top line so the shot type gets real width on the
     bottom one — squeezed into a leftover column it truncated to "Ri…". */
  /* No edit button on the row itself any more — the swipe carries it — so the
     club name gets that column back. */
  .logrow:not(.arch){display:grid;gap:4px 8px;align-items:center;
    grid-template-columns:auto minmax(0,1fr) auto;
    grid-template-areas:"time team oc" "who who ty"}
  .logrow .ty{text-align:right}
  /* every cell must be allowed to shrink, or a long club name in a pill pushes
     the outcome chip and the edit button off the screen */
  .logrow:not(.arch) > *{min-width:0}
  .logrow .t{grid-area:time} .logrow .badge{grid-area:team;justify-self:start}
  .logrow .who{grid-area:who} .logrow .ty{grid-area:ty;text-align:right}
  .logrow .oc{grid-area:oc;margin-left:0}
}
/* ---- a shot row you can swipe aside to delete ---- */
.swipe-row{position:relative;overflow:hidden;border-bottom:1px solid var(--line)}
.swipe-row:last-child{border-bottom:0}
/* The row rides over its own delete button, so it has to be opaque; a see-through
   highlight would show the red through the row itself. */
.swipe-row > .logrow{border-bottom:0;background:var(--card);position:relative;z-index:1;
  transition:transform .18s ease;touch-action:pan-y}
.swipe-row > .logrow.hot{background:#1a3426}
.swipe-row > .logrow.sliding{transition:none}      /* follow the finger, don't chase it */
.swipe-row.open > .logrow{transform:translateX(-124px)}
.row-actions{position:absolute;top:0;right:0;bottom:0;display:flex}
.row-act{width:62px;padding:0;border:0;color:#fff;cursor:pointer;
  display:flex;align-items:center;justify-content:center}
.row-act svg{width:21px;height:21px;fill:currentColor}
.row-act:active{filter:brightness(.85)}
/* grey for the reversible one, red for the one that isn't */
.row-edit{background:#3a4a42}
.row-del{background:var(--danger)}

/* ---- home on the left, away on the right ----
   Which side the chip sits on is the fastest read of whose shot it was, so the
   row keeps a chip-width margin at both ends and the chip takes one of them. */
/* Room for the chip plus a clear gap, so the time and the outcome pill stop
   running up against it at either end. */
.swipe-row > .logrow{padding-left:45px;padding-right:45px}
.swipe-row .rownum{position:absolute;top:50%;transform:translateY(-50%)}
/* 6px, not flush: the team ring stands 3.5px proud of the chip and the row clips
   whatever hangs over its edge. */
.swipe-row .logrow[data-side="home"] .rownum{left:6px}
.swipe-row .logrow[data-side="away"] .rownum{right:6px}

.oc{font-weight:800;font-size:11.5px;border-radius:999px;padding:3px 9px;white-space:nowrap;
  border:1px solid currentColor}
.oc-goal{color:var(--goal)} .oc-saved{color:var(--saved)} .oc-blocked{color:var(--blocked)}
.oc-post{color:var(--post)} .oc-off{color:var(--off)}
.recent{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:12px;margin-top:14px}

/* ------------------------------------------------------------- roster */
/* One team at a time — the tab at the top of the page picks which, and the
   line-up pitch below follows the same choice. */
.roster-grid{display:block}
.team-card{display:none}
.team-card.on{display:block}
/* The squad folds away so the pitch under it is a scroll away rather than a
   whole roster away. */
.collapse-btn{background:none;border:0;color:var(--muted);cursor:pointer;flex:none;
  padding:8px 6px;margin:-6px -4px -6px 0;border-radius:8px;
  display:inline-flex;align-items:center;justify-content:center}
.collapse-btn .chev{width:14px;height:9px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;transition:transform .15s}
.team-card.collapsed .collapse-btn .chev{transform:rotate(-90deg)}
.team-card.collapsed .card-body{display:none}
.team-card.collapsed .card-head{margin-bottom:0}
.card-head{display:flex;align-items:center;gap:10px;margin-bottom:10px}
/* Crests come in every shape — tall shields, wide ovals, circles. A fixed square
   box with object-fit:contain gives them all the same footprint so rows line up
   whatever the club. */
/* A default size matters: every other rule here is context-specific, so a crest
   in a context nobody styled — the stats table — rendered at its natural 128px.
   The context rules below out-specify this one. */
.crest{display:inline-block;object-fit:contain;vertical-align:middle;width:18px;height:18px}
.tbl .crest{width:18px;height:18px;margin-right:6px;flex:none}
.card-head .crest{width:36px;height:36px;flex:none}
.st-name .crest{width:26px;height:26px}
/* Wrap rather than truncate — a club name is worth two lines. min-width:0 on both
   the flex container and the text is what actually lets it shrink; flex items
   default to min-width:auto and will happily overflow the column instead. */
/* Fixed height, top-aligned: both crests land on the same line and both scores
   start at the same y, whether a club name runs to one line or two. */
.st-name{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
  gap:4px;line-height:1.2;min-height:62px;min-width:0;max-width:100%}
.st-text{min-width:0;max-width:100%;white-space:normal;overflow-wrap:break-word}
.team-shots-head{margin:16px 0 8px}
.tbl-keeper{max-width:560px}
.keeper-hint{margin:14px 0 0}

/* a shot's note, tucked under its own row */
.note-row td{border-top:1px dashed var(--line);font-size:12.5px;color:var(--muted);
  padding:5px 8px 7px;white-space:normal}
.note-label{display:inline-block;font-size:10px;font-weight:800;text-transform:uppercase;
  letter-spacing:.06em;color:var(--accent);margin-right:7px}

/* the note field in the entry flow */
.note-input{width:100%;font:inherit;font-size:15px;background:var(--bg2);color:var(--ink);
  border:1px solid var(--line);border-radius:11px;padding:11px;resize:none}
.note-input:focus{outline:0;border-color:var(--accent)}
.note-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;
  margin:6px 2px 12px}
.nm-short,.bd-short,.oc-short,.nm-num,.th-short{display:none}
.list-head{display:flex;align-items:center;gap:7px;margin:16px 0 8px}
.list-head .crest{width:20px;height:20px}
.logobar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.team-name-input{font:inherit;font-size:17px;font-weight:800;background:transparent;
  border:0;border-bottom:1px dashed var(--line);color:var(--ink);padding:4px 2px;min-width:0;flex:1}
.team-name-input:focus{outline:0;border-bottom-color:var(--accent)}
.oppbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px;
  padding-bottom:10px;border-bottom:1px dashed var(--line)}
[data-saved-select]{flex:1;min-width:150px;font:inherit;font-size:13px;padding:9px 8px}
[data-saved-select]:disabled{color:var(--muted)}
.oppstate{font-size:11.5px;font-weight:700;white-space:nowrap}
.oppstate.ok{color:var(--accent)} .oppstate.warn{color:var(--away)}

/* Native selects paint themselves white regardless of the page, so the chrome
   comes off and the chevron is drawn back on by hand. */
.add-row select,[data-saved-select]{appearance:none;-webkit-appearance:none;
  background-color:var(--bg2);color:var(--ink);border:1px solid var(--line);
  border-radius:9px;cursor:pointer;padding-right:26px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%238ea79a' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 9px center;background-size:11px 7px}
.add-row select:focus,[data-saved-select]:focus{outline:none;border-color:var(--accent2)}
/* the options list itself, for the browsers that honour it */
.add-row select option,[data-saved-select] option{background:var(--card);color:var(--ink)}

.add-row{display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap}
.add-row input,.add-row select{font:inherit;background:var(--bg2);border:1px solid var(--line);
  border-radius:9px;color:var(--ink);padding:9px 10px;min-width:0}
.add-row .num{width:62px;text-align:center;font-weight:800}
.add-row .nm{flex:1;min-width:96px}
/* Sized so "DEF/MID" still reads in full while #, name, Pos and Add stay on one
   line at 375px — the widest label drives the width, not the placeholder. */
.add-row .ps{width:82px;font-size:12.5px;padding-left:8px;padding-right:19px;
  background-position:right 6px center}
.add-row input[name="type"]{flex:1;min-width:150px}
.players{list-style:none;margin:0 0 10px;padding:0}
.players li{display:flex;align-items:center;gap:10px;padding:7px 2px;border-bottom:1px solid var(--line)}
.jersey{display:inline-flex;align-items:center;justify-content:center;min-width:34px;height:34px;
  border-radius:9px;background:var(--bg2);border:1px solid var(--line);font-weight:800}
.pname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pos{font-size:11px;font-weight:800;letter-spacing:.04em;width:68px;text-align:center;
  color:var(--muted)}
.pos-GK,.pos-DEF,.pos-MID,.pos-FWD,.pos-DEF-MID,.pos-MID-FWD{border-radius:999px;padding:3px 0;
  border:1px solid var(--line)}
/* Hybrids carry twice the text in the same pill. */
.pos-DEF-MID,.pos-MID-FWD{font-size:9.5px;letter-spacing:.02em}
.pos-GK{background:rgba(56,189,248,.15);border-color:var(--saved);color:var(--saved)}
.players li.is-gk .jersey{border-color:var(--saved);color:var(--saved)}

/* The row is the button: make that legible without shouting. */
.players li.prow{cursor:pointer;border-radius:10px}
.players li.prow:hover,.players li.prow:focus-visible{background:rgba(255,255,255,.04);outline:none}
.players li.prow:focus-visible{box-shadow:inset 0 0 0 2px var(--accent)}
.rowedit{width:22px;text-align:right;color:var(--muted);font-size:13px;opacity:.5}
.players li.prow:hover .rowedit{opacity:1}

/* ---- player edit sheet ---- */
.pl-row{display:flex;gap:10px;align-items:flex-end}
.pl-field{display:block;margin-bottom:14px}
.pl-num{width:88px;flex:0 0 88px}
.pl-name{flex:1 1 auto;min-width:0}
.pl-label{display:block;font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);margin-bottom:5px}
.pl-field input{width:100%;font:inherit;font-size:16px;padding:10px 12px;border-radius:10px;
  background:var(--panel);color:var(--text);border:1px solid var(--line)}
/* Six choices: three across, back-to-front reading order. */
.posgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.posbtn{font:inherit;font-size:14px;font-weight:800;letter-spacing:.04em;padding:12px 2px;
  border-radius:10px;cursor:pointer;background:var(--panel);color:var(--muted);
  border:1px solid var(--line)}
.posbtn.on{background:rgba(56,189,248,.16);border-color:var(--saved);color:var(--saved)}
.pl-foot{display:flex;align-items:center;justify-content:space-between;gap:12px}
.bulk{margin-bottom:8px}
.bulk summary{cursor:pointer;color:var(--muted);font-size:13px}
.bulk textarea{width:100%;font:inherit;font-size:13px;background:var(--bg2);border:1px solid var(--line);
  border-radius:9px;color:var(--ink);padding:9px;margin-bottom:8px}
.switch{display:inline-flex;align-items:center;gap:10px;cursor:pointer;font-weight:700;
  font-size:14px;padding:6px 2px}
.switch input{width:20px;height:20px;accent-color:var(--accent);cursor:pointer}
/* the extra-time length hangs under the switch that turns extra time on */
.fmtgrid{display:flex;flex-direction:column;gap:2px;margin-bottom:2px}
.fmtgrid .halflen{margin:2px 0 8px 30px}
.chips{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:10px}
.subhead{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);
  margin:14px 0 8px}
.pickgroup{margin-bottom:14px}
.pickgroup h4{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin:0 0 8px}
.chips-pick{display:flex;flex-wrap:wrap;gap:6px}
.chippick{font:inherit;font-size:13px;font-weight:700;border-radius:999px;padding:9px 14px;
  cursor:pointer;background:var(--bg2);border:1px solid var(--line);color:var(--muted)}
.chippick.on{background:var(--accent);border-color:var(--accent);color:#06120c}
.chip{display:inline-flex;align-items:center;gap:4px;background:var(--bg2);border:1px solid var(--line);
  border-radius:999px;padding:6px 6px 6px 12px;font-size:13px;font-weight:600}
.chip-x{background:none;border:0;color:var(--muted);cursor:pointer;font-size:12px;padding:0 4px}

/* ------------------------------------------------------------- report */
.report-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.statusline{font-size:12.5px;font-weight:600;margin:10px 2px 0;color:var(--muted)}
.statusline.ok{color:var(--accent)}
.statusline.warn{color:var(--away)}
.statusline.bad{color:#f87171}
.statusline a{color:var(--accent);font-weight:800}
#game-label{flex:1;min-width:200px;font:inherit;background:var(--bg2);border:1px solid var(--line);
  border-radius:9px;color:var(--ink);padding:10px}
.report-head{text-align:center;margin-bottom:6px}
/* a team's shots, two tables abreast */
.shots-2up{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));
  gap:14px;align-items:start}
.sb{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;
  gap:18px;margin:14px auto 0;max-width:760px}
.sb-team{display:flex;align-items:center;gap:14px;min-width:0;font-size:19px;font-weight:700}
.sb-team.home{flex-direction:row-reverse;text-align:right}
.sb-team .crest{width:40px;height:40px;flex:none}
.sb-name{min-width:0;overflow-wrap:break-word;line-height:1.2}
.sb-mid{text-align:center}
.sb-score{font-size:40px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums;
  display:flex;align-items:center;justify-content:center;gap:10px;white-space:nowrap}
.sb-dash{color:var(--muted);font-weight:600}
.sb-home{color:var(--home)} .sb-away{color:var(--away)}
.sb-status{margin-top:6px;font-size:13px;font-weight:600;color:var(--muted);
  display:flex;align-items:center;justify-content:center;gap:6px;flex-wrap:wrap}
.sb-clock{color:var(--ink);font-variant-numeric:tabular-nums}
.sb-live{display:inline-flex;align-items:center;gap:5px;padding:2px 8px 2px 7px;border-radius:999px;
  background:rgba(239,68,68,.14);color:#f87171;font-size:11px;font-weight:800;
  text-transform:uppercase;letter-spacing:.06em}
.sb-live::before{content:"";width:6px;height:6px;border-radius:50%;background:#ef4444;
  animation:live-pulse 1.6s ease-in-out infinite}
@keyframes live-pulse{50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.sb-live::before{animation:none}}
.sb-goals{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:18px;
  align-items:start;margin:14px auto 0;max-width:760px;font-size:13px;color:var(--muted);
  font-variant-numeric:tabular-nums}
.sb-scorers.home{text-align:right}
.sb-scorers.away{text-align:left}
.sb-scorer{line-height:1.5}
.sb-scorer b{color:var(--ink);font-weight:700}
.sb-ball{width:15px;height:15px;margin-top:2px;fill:none;stroke:var(--muted);stroke-width:1.3}
.sb-ball path{fill:var(--muted);stroke:none}
/* A phone: the crest stands over the name so a long club name gets the column. */
@media (max-width:560px){
  .sb,.sb-goals{gap:10px}
  .sb-team,.sb-team.home{flex-direction:column;text-align:center;gap:6px;font-size:14px}
  .sb-team .crest{width:34px;height:34px}
  .sb-score{font-size:32px;gap:6px}
  .sb-goals{font-size:12px}
}
/* Fixed four across so eight tiles make two even rows — auto-fit was leaving a
   single tile stranded on a row of its own. */
.statgrid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-bottom:14px}
/* Nine tiles with xG on: three across divides evenly where four leaves an orphan. */
.statgrid.has-xg{grid-template-columns:repeat(3,minmax(0,1fr))}
@media (max-width:520px){
  .statgrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .statgrid.has-xg{grid-template-columns:repeat(3,minmax(0,1fr))}
  .statgrid.has-xg .sv{font-size:18px}
  .statgrid.has-xg .sl{font-size:10px}
}
.stat{background:var(--bg2);border:1px solid var(--line);border-radius:11px;padding:9px 6px;
  text-align:center}
.sv{font-size:21px;font-weight:800} .sl{font-size:11px;color:var(--muted)}

/* One rule for these tiles: a COLOUR MEANS AN OUTCOME, and it is the same
   colour that outcome has on the pitch and in the legend. Everything else on
   the grid is deliberately colourless.

   It had drifted badly. Green meant "goal" and also "accuracy" and
   "conversion"; xG's blue was byte-identical to on-target's; "shots" had no
   rule at all and "off target" had a text colour but no tint, so two of the
   eight simply looked forgotten. */

/* The five outcomes, tinted alike. */
.stat-goal{background:rgba(34,197,94,.12);border-color:rgba(34,197,94,.45)}
.stat-goal .sv{color:var(--goal)}
.stat-saved{background:rgba(56,189,248,.12);border-color:rgba(56,189,248,.45)}
.stat-saved .sv{color:var(--saved)}
.stat-blocked{background:rgba(167,139,250,.12);border-color:rgba(167,139,250,.45)}
.stat-blocked .sv{color:var(--blocked)}
.stat-post{background:rgba(244,114,182,.12);border-color:rgba(244,114,182,.45)}
.stat-post .sv{color:var(--post)}
/* Off target was the one outcome with no tint, which is why it read as unstyled
   rather than as grey-because-it-missed. */
.stat-off{background:rgba(148,163,184,.12);border-color:rgba(148,163,184,.45)}
.stat-off .sv{color:var(--off)}

/* Shots is the sum of those five, not a sixth one. No hue to borrow, so it
   takes weight instead: full-strength ink and a solid edge, the headline the
   others break down. */
.stat-shots{background:var(--bg2);border-color:var(--line)}
.stat-shots .sv{color:var(--ink)}

/* Accuracy, conversion and xG are worked out FROM the outcomes — they are not
   outcomes, so they take no outcome colour. Marked by form rather than hue: a
   dashed edge, which is how the Stats page already marks its per-60 rates. */
.stat-rate,.stat-xg{background:none;border-style:dashed;border-color:var(--line)}
.stat-rate .sv,.stat-xg .sv{color:var(--ink)}
.stat-rate .sl,.stat-xg .sl{font-style:italic}
.tally-n.xg b{color:var(--accent2)}
/* the one label that is a word, so it needs the type treatment the emoji don't */
.tally-n.xg .ti{font-size:11px;font-weight:800;color:var(--accent2);opacity:.75;
  letter-spacing:.01em}
td.xg{font-variant-numeric:tabular-nums}
.tbl-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin-bottom:14px}
.tbl{width:100%;border-collapse:collapse;font-size:13px}
.tbl th,.tbl td{white-space:nowrap}
/* Headings keep the casing they are written with. `xG` and `SoT` are names
   with a small letter inside them, and uppercasing turned them into XG and SOT
   — different abbreviations from the ones the rest of the app uses. The plain
   words read better in sentence case too. */
.tbl th{text-align:left;color:var(--muted);font-size:11px;text-transform:none;letter-spacing:.03em;
  padding:6px 8px;border-bottom:1px solid var(--line)}
.tbl td{padding:7px 8px;border-bottom:1px solid var(--line)}
.tbl td.num{font-weight:800;color:var(--muted)}
.tbl.compact{max-width:420px}
@media print{.tbl-wrap{overflow:visible}}
/* Football's own notation: 2 (4) — the score, then the shootout in brackets. */
.pk-score{font-size:.6em;font-weight:800;color:var(--muted);vertical-align:middle}
.pk-line{margin:8px 0 0;text-align:center;font-weight:800;font-size:13.5px;color:var(--accent)}
.pk-headline{margin:0 0 12px;font-size:16px;font-weight:700}
.pk-headline strong{font-size:19px}
/* the quiet print under a map: what it is, and what it isn't */
/* Sits with the legend it belongs to, not with whatever comes next. It had
   8px above and nothing below, so it drifted away from "numbers = …" and ended
   up almost touching the next section's heading — reading as that section's
   subtitle rather than as a note about the map above it. */
.mapnote{margin:3px 2px 18px;font-size:11.5px;line-height:1.35;color:var(--muted);font-style:italic}
/* charts inside a report card: figure's default margin is 40px a side */
.overview-map figure{margin:0 0 14px}
.overview-map figcaption{font-size:11.5px;color:var(--muted);text-transform:uppercase;
  letter-spacing:.05em;margin-bottom:6px}
/* map beside the shot log — stacked on a phone, side by side once there's room */
/* min-width:0 on the children is essential: grid items default to min-width:auto,
   so a table whose min-content is wider than its track overflows the card instead
   of fitting inside it. */
.overview-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:16px;align-items:start}
.overview-grid > *{min-width:0}
.overview-log{overflow:hidden}
.overview-map .tbl-wrap{margin-bottom:12px}
.overview-log h3{margin:14px 0 8px}
.overview-log h3:first-child{margin-top:0}
.overview-log .list-head:first-child{margin-top:0}
@media (min-width:900px){
  .overview-grid{grid-template-columns:minmax(0,300px) minmax(0,1fr);gap:22px}
}

/* ---- report tabs ----------------------------------------------------
   Full Time / each half over the match card, home / away over the teams.
   The segmented control the tracker already uses, so it reads as the same
   kind of switch. Every pane is in the page; only the chosen one shows. */
.rtabs{margin-bottom:16px}
.rtabs .seg-btn{gap:8px}
.rtabs .seg-btn .crest{width:22px;height:22px;flex:none}
.rtabs .seg-btn[data-tab="away"].active{background:var(--away);color:#231a00}
.rt-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tab-pane:not(.on){display:none}
.tab-pane.on{animation:pane-in .18s ease-out}
@keyframes pane-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.tab-pane.on{animation:none}}

.report-top .report-head{margin-bottom:0}

/* The pitch and the numbers side by side once there is room for both;
   one above the other on a phone. */
.match-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:22px;align-items:start}
.map-period .pitch-svg{max-width:min(100%,360px);margin:0 auto}
.match-map .legend{justify-content:center}
.match-map .mapnote{text-align:center;margin-bottom:0}
@media (min-width:760px){
  .match-grid{grid-template-columns:minmax(0,5fr) minmax(0,6fr);gap:32px}
  /* held to the screen's height so the whole pitch is in view at once */
  .map-period .pitch-svg{max-width:min(100%,max(320px,52vh))}
}

/* ---- head to head ----
   A line a stat: home value, the label, away value, and a bar out from the
   middle each way — the bigger number fills its half. */
.cmp{display:flex;flex-direction:column;gap:13px;min-width:0}
.cmp-head{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:12px;
  padding-bottom:12px;border-bottom:1px solid var(--line);font-size:12px;font-weight:800;
  text-transform:uppercase;letter-spacing:.04em}
.cmp-team{display:flex;align-items:center;gap:7px;min-width:0}
.cmp-team > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cmp-team .crest{width:20px;height:20px;flex:none}
.cmp-team.home{color:var(--home)}
.cmp-team.away{color:var(--away);flex-direction:row-reverse;text-align:right}
.cmp-row{display:grid;grid-template-columns:4em minmax(0,1fr) 4em;align-items:baseline;row-gap:6px}
.cmp-v{font-size:17px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--muted)}
.cmp-v.lead{color:var(--ink);font-weight:800}
.cmp-v.away{text-align:right}
.cmp-l{text-align:center;font-size:12.5px;color:var(--muted)}
.cmp-row.big .cmp-v{font-size:26px;line-height:1.1}
.cmp-row.big .cmp-v.home{color:var(--home)}
.cmp-row.big .cmp-v.away{color:var(--away)}
.cmp-row.big .cmp-l{font-weight:700;color:var(--ink)}
.cmp-bar{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;gap:4px;height:6px}
.cmp-bar > span{display:flex;background:var(--bg2);border-radius:99px;overflow:hidden}
.cmp-bar > span:first-child{justify-content:flex-end}
.cmp-bar i{display:block;height:100%;border-radius:99px;transition:width .3s ease}
.cmp-bar .h{background:var(--home)}
.cmp-bar .a{background:var(--away)}
/* the team's two maps, side by side under its totals */
.team-maps{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));
  gap:16px;align-items:start;margin-bottom:4px}
.team-maps figure{margin:0}
.team-maps figcaption{font-size:11.5px;color:var(--muted);text-transform:uppercase;
  letter-spacing:.05em;margin-bottom:6px}
.team-maps .pitch-svg{max-width:min(100%,300px);margin:0 auto}
.team-maps .goal-svg{max-width:100%}
.legend{display:flex;flex-wrap:wrap;gap:12px;margin-top:12px;font-size:11.5px;color:var(--muted)}
.lg{display:inline-flex;align-items:center;gap:5px}
.sw{width:10px;height:10px;border-radius:50%;display:inline-block;background:currentColor}
.sw.oc-goal{background:var(--goal)} .sw.oc-saved{background:var(--saved)}
.sw.oc-blocked{background:var(--blocked)} .sw.oc-post{background:var(--post)} .sw.oc-off{background:var(--off)}

/* --------------------------------------------------------- update bar */
.update-bar{position:fixed;left:12px;right:12px;margin:0 auto;
  width:fit-content;max-width:min(92vw,520px);
  bottom:calc(16px + env(safe-area-inset-bottom));z-index:70;
  display:flex;align-items:center;gap:10px;
  background:var(--card);border:1px solid var(--accent);border-radius:14px;
  padding:10px 10px 10px 16px;font-size:13px;font-weight:600;
  box-shadow:0 12px 34px rgba(0,0,0,.5)}
.update-bar.hidden{display:none}
.update-bar span{flex:1;min-width:0}
.update-bar .btn{flex:none;white-space:nowrap}

/* -------------------------------------------------------------- toast */
.toast{position:fixed;left:12px;right:12px;margin:0 auto;width:fit-content;
  max-width:min(92vw,440px);bottom:calc(24px + env(safe-area-inset-bottom));z-index:80;
  transform:translateY(20px);
  background:#0b120f;border:1px solid var(--accent);color:var(--ink);border-radius:999px;
  padding:11px 18px;font-size:13px;font-weight:600;opacity:0;pointer-events:none;
  transition:opacity .18s,transform .18s;text-align:center}
.toast.on{opacity:1;transform:translateY(0)}
body.has-update .toast{bottom:calc(86px + env(safe-area-inset-bottom))}

@media (max-width:520px){
  .brand{font-size:14px}
  .tab{padding:6px 10px;font-size:12.5px}
  .seg-btn{font-size:13px;padding:10px 8px}
  .st-goals{font-size:32px} .clock{font-size:26px}
  .st-name{font-size:11.5px}
  .scoreboard{gap:6px;padding:10px 8px}
  .period-wrap{padding:12px 12px;min-width:104px}
  .period-label{font-size:14px}
  .clock-controls{gap:4px}
  .view{padding:10px}
}
/* Below this the wordmark costs more than it's worth — keep just the ball. */
@media (max-width:470px){
  #topbar{gap:8px}
  .brand-name{display:none}
  /* Six tabs were 11px wider than an iPhone, and that sliver was enough for
     the whole page to slide sideways under a thumb scrolling down. The row
     now shares out the width it has instead of claiming what it wants. */
  /* Packed to the right, not spread: a parent sees four tabs, not six, and
     spreading those left gulfs between them. */
  .tabs{flex:0 1 auto; min-width:0; gap:2px}
  .tab{padding:6px 8px}
}
/* Six tabs: shorten the longest ones rather than let anything wrap. */
@media (max-width:560px){
  .lbl-long{display:none}
  .lbl-short{display:inline}
}
/* Tightest phones: tighten the row itself as well. */
@media (max-width:360px){
  #topbar{padding-left:8px;padding-right:8px;gap:6px}
  .tab{padding:6px 5px;font-size:11.5px}
}

/* -------------------------------------------------------------- print */
@media print{
  body{background:#fff;color:#111;padding:0}
  #topbar,.no-print,.flow,.toast,.update-bar,.match-actions{display:none !important}
  .view:not(.on){display:none !important}
  .view.on{display:block !important;max-width:none;padding:0}
  .card{background:#fff;border-color:#ddd;break-inside:avoid;page-break-inside:avoid}
  /* tabs are a screen control: on paper every pane prints, one after another */
  .rtabs{display:none}
  .tab-pane{display:block !important;animation:none}
  .muted,.sl,.tbl th,.cmp-l{color:#555}
  .turf{fill:#f2f7f3} .stripe{fill:#e8f1ea}
  .ln{stroke:#7d8b83} .dot{fill:#7d8b83} .goalbox{fill:#e5e9e7;stroke:#7d8b83}
  .pitch-tag,.goal-tag{fill:#999}
  .miss-zone{fill:#f6f7f6} .ground{fill:#eef3ef} .net{fill:#fafafa}
  .mesh{stroke:#ddd} .guide{stroke:#ccc} .frame{stroke:#333} .goalline{stroke:#999}
}

/* ===================================================================
   HOSTED MODE — sign-in, read-only viewing, and the live badge.
   Everything here is inert in the private single-user copy: with no
   server to answer /api/auth/me, no data-role is ever set and the
   viewer rules below never match.
   =================================================================== */

/* ---- signing in --------------------------------------------------- */

/* [hidden] is display:none in the UA stylesheet, which any author rule of equal
   specificity beats — so `.signin { display: flex }` quietly made the overlay
   permanent and no amount of signing in could dismiss it. Guard every element
   that gets a display, not just this one. */
[hidden] { display: none !important; }

.signin {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--bg, #0d1117);
}
body.locked { overflow: hidden; }

.signin-card {
  width: 100%;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: center;
}
.signin-mark { font-size: 44px; line-height: 1; }
.signin-card h1 { margin: 0; font-size: 26px; letter-spacing: -0.01em; }
.signin-card .muted { margin: 0 0 10px; }
.signin-label {
  text-align: left;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.7;
}
#signin-pw {
  width: 100%;
  padding: 12px 14px;
  font-size: 17px;             /* 16px or more, or iOS zooms the page on focus */
  border-radius: 8px;
}
.signin-msg { margin: 4px 0 0; font-size: 13px; min-height: 1.2em; opacity: 0.75; }

/* ---- the live badge ------------------------------------------------ */

/* The middle column, stacked for viewers: time, then half, then status.
   Left as one centred line they sat side by side, which widened the column and
   squeezed the team names either side of it. */
body[data-role="viewer"] .clock-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Tight: the clock and the half are one reading, so they sit together. The
     status badge is a separate thought and gets its own space below. */
  gap: 0;
}
/* Its own margin was stacking on top of the column gap — two spacings doing
   the same job is what left the half floating away from the clock. */
body[data-role="viewer"] .clock-controls { margin-top: 0; }
/* During a break the badge is the whole story. The period label would name a
   half that has not kicked off, and the clock is parked at the time the next
   one starts from — a number that looks like a running score of the match and
   is not one. Both stand down until play restarts. */
body[data-role="viewer"].game-break #clock,
body[data-role="viewer"].game-break .clock-controls { display: none !important; }
body[data-role="viewer"] #live-status { margin-top: 8px; }
body[data-role="viewer"] #clock          { order: 1; }
body[data-role="viewer"] .clock-controls { order: 2; }
body[data-role="viewer"] #live-status    { order: 3; }
body[data-role="viewer"] .final-report   { order: 4; }

/* The box is the affordance for a tappable control. A viewer's period is a
   label — nothing to open — so the panel, the border, the minimum width and
   the padding all go, leaving the words. */
body[data-role="viewer"] .period-wrap {
  background: none;
  border: 0;
  padding: 0;
  min-width: 0;
  cursor: default;
}
body[data-role="viewer"] .period-label {
  font-weight: 600;
  color: var(--muted);
}

.live-status {
  display: inline-block;
  margin: 0;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid currentColor;
  line-height: 1.5;
  max-width: 100%;
}
.live-status.is-live  { color: #35c37d; }
.live-status.is-idle  { color: #8b98a5; }
/* Amber, not red: the tracker being out of signal is expected at a field and
   is not an error — it just means what's on screen has stopped being current. */
.live-status.is-stale { color: #d99a2b; text-transform: none; letter-spacing: 0; }
.live-status.is-off   { color: #d9663b; text-transform: none; letter-spacing: 0; }

/* ---- read-only ------------------------------------------------------
   Viewers see the whole match — pitch, shot map, scoreboard, clock,
   report, past games — and can change none of it. The controls are
   hidden rather than disabled: a greyed-out row of buttons they can
   never use is just clutter on a phone screen. */

body[data-role="viewer"] #btn-clock,
body[data-role="viewer"] #btn-undo,
body[data-role="viewer"] #archive-game,
body[data-role="viewer"] .tab[data-view="settings"],
body[data-role="viewer"] #track-hint,
body[data-role="viewer"] #view-settings,
body[data-role="viewer"] #team-seg,
body[data-role="viewer"] #pitch-seg,
/* Roster page: everything that edits a squad. The squads themselves stay
   visible — a parent wants to see who wore 9. */
body[data-role="viewer"] .logobar,
body[data-role="viewer"] .oppbar,
body[data-role="viewer"] .add-row,
body[data-role="viewer"] .bulk,
body[data-role="viewer"] .clear-team,
body[data-role="viewer"] #shared-roster { display: none !important; }

/* The Rosters tab is squad management. A viewer who wants to know who wore 9
   reads it off the report, where it is attached to actual shots. */
body[data-role="viewer"] .tab[data-view="roster"],
body[data-role="viewer"] #view-roster { display: none !important; }

/* Same rule as the past-game report: a viewer gets Print / PDF and nothing
   that writes, imports, or exports. */
body[data-role="viewer"] #game-label,
body[data-role="viewer"] #export-csv,
body[data-role="viewer"] #export-json,
body[data-role="viewer"] #import-json,
body[data-role="viewer"] #offline-status { display: none !important; }

/* With no game there is nothing to label, save, export or print, so the whole
   action bar goes and the empty state speaks for the page. */
/* Nothing to label, save, export or print when no game is being tracked. */
body.no-live-game #view-report .card.no-print { display: none !important; }

/* A viewer's action bar holds one button. A card around a single button is
   chrome around nothing, so the panel drops away and the button stands on its
   own, centred. Admins keep the card -- theirs holds six controls and a status
   line, which does need a container. */
body[data-role="viewer"] #view-report .card.no-print,
body[data-role="viewer"] #past-actions {
  background: none;
  border: 0;
  padding: 0;
  margin-top: 18px;
}
body[data-role="viewer"] .report-actions { justify-content: center; }

.empty-live { text-align: center; padding: 40px 24px; }
.empty-live h2 { margin: 0 0 6px; }
.empty-live p { margin: 0 0 18px; }
.empty-live .btn { text-decoration: none; display: inline-block; }

/* Readable, not editable. */
body[data-role="viewer"] .team-name-input,
body[data-role="viewer"] .players { pointer-events: none; }
body[data-role="viewer"] .team-name-input {
  border-color: transparent;
  background: transparent;
}

/* ---- features that only exist when the app is served from a laptop ----
   Both of these talk to serve.py, which is not what is running here:
   /logo-fetch proxies an arbitrary URL (it is not on the public server on
   purpose — see the plan), and the shared roster reads and writes a
   roster.json next to serve.py. Left visible they are buttons that always
   fail. The hosted app has no need for the second one anyway: rosters now
   travel to the server with the game. */
body[data-role] #shared-roster,
body[data-role] [data-logo-url] { display: none !important; }

/* Setting the clock, ending a half and ending a game all change the match.
   The period LABEL stays — a viewer needs to know it is the second half —
   but the select behind it and the chevron that advertises it both go. */
body[data-role="viewer"] #btn-clock-set,
body[data-role="viewer"] .match-actions,
body[data-role="viewer"] .period-chev,
body[data-role="viewer"] #period-select { display: none !important; }

/* The pitch stays visible and stays legible — it simply stops accepting
   taps, so a viewer can never open the shot flow at all. */
body[data-role="viewer"] #pitch-host { pointer-events: none; }

/* Half of that hint is still true for a viewer; the swipe half is not. */
body[data-role="viewer"] .admin-only { display: none !important; }

/* The shot log stays tappable for viewers — tapping a row lights that shot up
   on the pitch, which is reading, not tracking. Only the edit and delete
   buttons behind the row go; the swipe that reveals them is disabled in
   track.js, since a gesture cannot be turned off from a stylesheet. */
body[data-role="viewer"] #recent-list .row-actions { display: none !important; }


/* ---- the Live Game tab --------------------------------------------
   Pulses only while a game is actually being tracked, so it means
   something when it moves. A dot rather than the label itself: animating
   text is hard to read, and the tab still has to be tappable. */
body[data-role="viewer"] .tab[data-view="track"] { position: relative; }

body[data-role="viewer"] .tab.tab-live::after {
  content: "";
  position: absolute;
  top: 2px;
  right: -2px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #35c37d;
  animation: gt-pulse 1.8s ease-in-out infinite;
}

@keyframes gt-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.35; transform: scale(0.72); }
}

/* A pulse is decoration; the dot is the information. Keep the dot. */
@media (prefers-reduced-motion: reduce) {
  body[data-role="viewer"] .tab.tab-live::after { animation: none; }
}

/* ---- past games list ----------------------------------------------
   Fixture, score, and a way in. Nothing else earns the width. */
.logrow.arch {
  display: flex;
  align-items: center;
  gap: 12px;
}
.gl-fixture {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.gl-side {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  line-height: 1.45;
  min-width: 0;
}
/* The name takes whatever is left after the crest and the score, and only
   ellipsises when a club genuinely outruns the width. */
.gl-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gl-sc {
  flex: none;
  font-variant-numeric: tabular-nums;
  min-width: 1.1em;
  text-align: right;
}
/* The winner in full strength, the other side muted — the result readable
   without doing the subtraction. A draw leaves both alike, which is correct. */
.gl-side:not(.won) .gl-nm, .gl-side:not(.won) .gl-sc { color: var(--muted); }
.gl-side.won .gl-nm { font-weight: 700; }

.gl-when { font-size: 12px; opacity: 0.6; margin-top: 2px; }
.gl-score { font-variant-numeric: tabular-nums; padding: 0 2px; flex: none; }

.gl-crest {
  flex: none;
  width: 20px;
  height: 20px;
  object-fit: contain;
  border-radius: 3px;
}
/* A club with no crest still takes the space, so the names stay in a column
   down the list instead of stepping in and out. */
.gl-crest-none { display: inline-block; }
.gl-report { flex: none; text-decoration: none; }

/* ---- correcting a shot in a saved report ---------------------------- */

.edit-hint { margin: 8px 0 0; }

/* Rows only look tappable for the tracker; a viewer never gets the class. */
.tbl-shots tr.editable { cursor: pointer; }
.tbl-shots tr.editable:hover td { background: rgba(61, 220, 132, .08); }
.tbl-shots tr.editable:active td { background: rgba(61, 220, 132, .16); }

.sheet-back {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .55);
  z-index: 300;
  touch-action: none;
}
/* Held inside the VISIBLE screen, not 100vh. On an iPhone 100vh is the height
   with Safari's toolbars hidden, so a card capped by it ran off the top and
   bottom, never needed to scroll itself, and a swipe scrolled the report
   behind it instead — leaving Save unreachable. Pinned between the safe-area
   insets and centred with auto margins, it scrolls within itself. */
.sheet-card {
  position: fixed;
  z-index: 301;
  top: calc(env(safe-area-inset-top) + 12px);
  bottom: calc(env(safe-area-inset-bottom) + 12px);
  left: 0;
  right: 0;
  margin: auto;
  height: fit-content;
  width: min(460px, calc(100vw - 32px));
  max-height: calc(100% - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 24px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 18px;
}
.sheet-card h3 { margin: 0 0 2px; }
.sheet-card > .muted.small { margin: 0 0 14px; }

.edit-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 10px;
}
.edit-grid label,
.edit-note {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--muted);
  min-width: 0;
}
.edit-note { margin-bottom: 14px; }
.edit-grid input,
.edit-grid select,
.edit-note input {
  width: 100%;
  /* 16px or more, or iOS zooms the page when the field takes focus. */
  font-size: 16px;
  padding: 8px 10px;
  border-radius: 8px;
}

/* The page behind stays put while the sheet is open. */
html.sheet-open, html.sheet-open body { overflow: hidden; }

/* A correction sheet has no business on a printed report. */
@media print { .sheet-back, .sheet-card, .edit-hint { display: none !important; } }

/* ---- the tracker cohort link ---------------------------------------
   Hidden for everyone but viewers, and quiet even for them: almost every
   person on this page is a parent watching a match, not a coach about to
   take over the tracking. */
.cohort-wrap { display: none; margin: 28px 0 8px; text-align: center; }
body[data-role="viewer"] .cohort-wrap { display: block; }

.cohort-link {
  font-size: 12px;
  opacity: 0.45;
  letter-spacing: 0.02em;
}
.cohort-link:hover, .cohort-link:focus-visible { opacity: 0.85; }

#signin-back { margin-top: 4px; font-size: 13px; opacity: 0.7; }

/* ---- goal scorers on the scoreboard ---------------------------------
   Minute and shirt number under the side that scored, the way a broadcast
   scoreboard reads. Quiet enough that the score itself still dominates. */
.scorers {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-top: 4px;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.scorers:empty { display: none; }
.scorer { white-space: nowrap; }

/* ---- no teams yet ---------------------------------------------------
   Where saving a game leaves the tracker. The pitch is meaningless without
   two sides, so it steps aside for the one instruction that matters. */
#no-teams { display: none; }
body.no-teams #no-teams { display: block; }
body.no-teams #view-track .scoreboard,
body.no-teams #view-track .controls,
body.no-teams #view-track #track-hint,
body.no-teams #view-track .pitch-wrap,
body.no-teams #view-track .recent { display: none !important; }

/* A viewer is never in this state — they follow whatever is live, and if
   nothing is, the badge already says so. */
body[data-role="viewer"] #no-teams { display: none !important; }

/* ---- viewer, nothing live ------------------------------------------- */
#no-live-prompt { display: none; }
body[data-role="viewer"].viewer-nolive #no-live-prompt { display: block; }
body[data-role="viewer"].viewer-nolive #view-track .scoreboard,
body[data-role="viewer"].viewer-nolive #view-track .controls,
body[data-role="viewer"].viewer-nolive #view-track .pitch-wrap,
body[data-role="viewer"].viewer-nolive #view-track .recent,
body[data-role="viewer"].viewer-nolive #track-xg-note,
body.no-teams #track-xg-note { display: none !important; }
/* The cohort link stays. Before kickoff is precisely when a coach needs to sign
   in as the tracker, and that is exactly when nothing is live. */

/* ---- goal mouth inside the correction sheet -------------------------- */
.edit-goal { margin-bottom: 14px; }
.edit-goal-label {
  display: block;
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 6px;
}
.edit-goal .goal-host { margin: 0 auto; max-width: 320px; }
.edit-goal .linkbtn { margin-top: 6px; font-size: 13px; }

/* The pitch inside the correction sheet. Capped so the sheet still scrolls
   sensibly on a phone with the keyboard up. */
.edit-goal .sheet-pitch { margin: 0 auto; max-width: 300px; }

/* ---- viewer, full time ----------------------------------------------
   The clock and the period stop meaning anything once the whistle has gone,
   so they step aside and the badge carries the column on its own. */
body[data-role="viewer"].game-final #clock,
body[data-role="viewer"].game-final .clock-controls { display: none !important; }

/* At full time the clock and the half are gone, so the badge and the button
   are all that is left in the column. */
body[data-role="viewer"].game-final .clock-block { gap: 8px; }

body[data-role="viewer"].game-final .live-status {
  font-size: 14px;
  letter-spacing: 0.1em;
  padding: 5px 16px;
  margin: 0;
}

/* Sits under FINAL once the clock and period have stepped aside. */
.final-report { display: none; text-decoration: none; }
body[data-role="viewer"].game-final .final-report {
  display: inline-block;
  margin: 0;
}

/* ===================================================================
   STATS — season totals, one module per club.
   =================================================================== */

.stat-team + .stat-team { margin-top: 14px; }

.stat-name {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
  font-size: 18px;
}
.stat-crest { width: 24px; height: 24px; object-fit: contain; flex: none; }

/* Auto-fit rather than a fixed count: eight tiles want four columns on a
   tablet and two on a phone, and the same rule gives both. */
.stat-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 8px;
  margin-bottom: 10px;
}
.stat-tile {
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 8px 10px;
  min-width: 0;
}
.stat-tile .st-k {
  display: block;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Nothing in a tile wraps. A figure that runs onto a second line makes its
   tile taller than the rest of the row, which reads as though it means more
   than the others. If a value is ever too wide it shrinks instead. */
.stat-tile b {
  display: block;
  font-size: 20px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Rates read as a separate class of number from counts, so they are marked as
   such rather than sitting in the same block looking like totals. */
.stat-rates .stat-tile { background: none; border-style: dashed; }
.stat-rates .stat-tile b { font-size: 17px; color: var(--accent2); }

.stat-h {
  margin: 16px 0 8px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.tbl-stats { font-size: 13px; }
.tbl-stats td { font-variant-numeric: tabular-nums; }
.tbl-stats .op, .tbl-stats .nm { font-variant-numeric: normal; }
.tbl-stats .at { color: var(--muted); font-size: 11px; }
.tbl-stats .res b { font-variant-numeric: normal; }
.res-W b { color: var(--home); }
.res-L b { color: var(--off, #94a3b8); }
.res-D b { color: var(--muted); }
.tbl-stats .rate { color: var(--accent2); }
/* Sits under the last module rather than inside one, so it reads as a footnote
   to the page. */
.stat-note { margin: 14px 4px 0; }

/* On a phone it scrolls inside its own wrapper rather than squeezing every
   figure to nothing. */
.tbl-squad { min-width: 400px; }

@media (max-width: 520px) {
  .stat-tile b { font-size: 18px; }
  .tbl-stats { font-size: 12px; }
}

/* The caveat at the top of the Stats page. */
.stats-note{margin:0 0 14px;padding:10px 14px;border:1px solid var(--line);
  border-left:3px solid var(--accent2);border-radius:10px;background:var(--bg2);
  font-size:13px;line-height:1.45;color:var(--muted)}
.stats-note a{color:var(--accent2);font-weight:700}

/* ---- sortable stat columns ------------------------------------------ */
.tbl-stats th.sortable {
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  /* Room for the arrow, reserved whether or not it is showing, so turning a
     column on doesn't shove the row sideways. */
  padding-right: 16px;
  position: relative;
}
.tbl-stats th.sortable:hover { color: var(--ink); }
.tbl-stats th.sorted { color: var(--accent); }
/* The arrow hangs off the end of the label, not the edge of the cell. A wide
   column put it at the far right, next to the heading of the column beside. */
.sort-lbl { position: relative; display: inline-block; }
.sort-ar {
  position: absolute;
  left: 100%;
  top: 0;
  margin-left: 3px;
  font-size: 10px;
  line-height: inherit;
}
/* A tapped heading should look tapped on a phone, where there is no hover. */
.tbl-stats th.sortable:active { color: var(--accent); }

/* Short dates line up as a column of digits rather than drifting. */
.tbl-stats .gdate { font-variant-numeric: tabular-nums; color: var(--muted); }

/* ---- the admin page --------------------------------------------------
   Hidden unless the server said this session is the owner. A guest tracker
   gets the rest of Settings and not this, and the server refuses them the
   endpoints anyway -- this only keeps the page honest about it.

   Hidden by DEFAULT, so the private single-user copy (no server, no role, and
   no password to change) doesn't show a card whose every button would fail. */
#pw-card { display: none; }
body[data-role="admin"] #pw-card { display: block; }

.pw-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 0;
  border-top: 1px solid var(--line);
}
.pw-form .signin-label { margin-bottom: -4px; }
.pw-form .pw-hint { margin: 0; }
.pw-form input {
  /* 16px or more, or iOS zooms the page when the field takes focus. */
  font-size: 16px;
  padding: 10px 12px;
  border-radius: 8px;
}
.pw-form button { align-self: flex-start; }

.pw-state {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.pw-state.is-set { color: var(--muted); }
/* Not a warning colour by accident: an unset tier means nobody can sign in
   there at all, which is worth noticing rather than reading past. */
.pw-state.is-unset { color: var(--away); text-transform: none; letter-spacing: 0; }

.pw-msg { font-size: 13px; color: var(--muted); margin: 0; min-height: 1.1em; }

/* The match date, under the fixture rather than trailing off the end of it. */
.report-date {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ---- the xG caveat --------------------------------------------------
   Shown wherever an xG figure appears. Quiet, but not so quiet it reads as
   boilerplate — the first sentence is the whole point. */
.xg-note {
  margin: 14px 4px 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 68ch;
}
.xg-note b { color: var(--ink); font-weight: 700; }
/* A printed report is read away from anyone who could explain the figure, so
   the caveat matters more there, not less. */
@media print { .xg-note { color: #444; font-size: 8pt; } }

/* ---- tap a marker on a report map -----------------------------------
   Two lines under the map it belongs to. Deliberately not an overlay: a
   popover on a phone covers the pitch you are trying to read, and the thing
   you just tapped is the thing it would hide. */
.map-anchor { position: relative; }

.shot-pop {
  position: absolute;
  z-index: 5;
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 7px 9px;
  /* Opaque, not tinted: it sits over the pitch, and grass showing through the
     text is the one thing that would make it harder to read than the table. */
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
  font-size: 12px;
  line-height: 1.35;
  max-width: min(240px, calc(100% - 12px));
  pointer-events: none;
}

/* The arrow, pointed at the dot. --arrow is set from the marker's position, so
   a bubble nudged away from an edge still points at the right shot. */
.shot-pop::after {
  content: "";
  position: absolute;
  left: var(--arrow, 50%);
  margin-left: -6px;
  border: 6px solid transparent;
}
.shot-pop:not(.below)::after { top: 100%; border-top-color: var(--line); }
.shot-pop.below::after { bottom: 100%; border-bottom-color: var(--line); }
.sp-n {
  flex: none;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 11.5px;
  /* Dark on the fill, the same way the pitch and strip numerals read. */
  color: #08110c;
}
.sp-n.oc-goal { background: var(--goal); }
.sp-n.oc-saved { background: var(--saved); }
.sp-n.oc-blocked { background: var(--blocked); }
.sp-n.oc-post { background: var(--post); }
.sp-n.oc-off { background: var(--off); }

.sp-txt { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.sp-sub { color: var(--muted); font-size: 11.5px; }
.sp-xg { color: var(--accent2); }

/* The tapped marker, ringed white the way a highlighted shot is on the
   tracker. Above .mk-hot in the file for the same specificity reason. */
.mk-sel circle { stroke: #fff !important; stroke-width: .34 !important; }
.goal-svg .mk-sel circle { stroke-width: .07 !important; }
/* While one shot is picked, the rest step back so it can be found at a glance
   on both of a team's maps. */
.sel-on .mk:not(.mk-sel) { opacity: .3; }

/* A reader's aid, not part of the report. */
@media print { .shot-pop { display: none !important; } }

/* The pencil inside a shot bubble. */
.sp-edit {
  flex: none;
  align-self: center;
  margin-left: 2px;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg2);
  color: var(--ink);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.sp-edit:active { background: var(--line); }

/* Fewer columns than the squad table, so it needs less room before it scrolls. */
.tbl-keepers { min-width: 620px; }

/* Pitch dimensions on the Setup page. */
.pitch-dims {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: 8px;
  margin: 10px 0 8px;
}
.pitch-dims label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--muted);
}
.pitch-dims input { font-size: 16px; padding: 8px 10px; border-radius: 8px; }
