/* Copyright (c) 2026 Oddball Fantasy. All rights reserved -- proprietary, see LICENSE. */

/* ODDBALL -- the base look for the whole app: cartoony, bright, chunky.
 *
 * Galaxy mixed with royalty (17 Sep). This file is the PAINT and nothing else. Every page wears it. Inside a league, the league's game mode
 * adds its own layer on top (mode-bank.css), which only swaps the colour tokens and adds that mode's
 * flourishes -- so Bank's money look appears only inside a Bank league, as he asked (17 Sep).
 *
 * To repaint the app: change the tokens in the :root block below. To repaint Bank: mode-bank.css.
 * Edit and refresh -- no restart needed.
 */

:root{color-scheme:dark;
/* Darker, with galaxy COLOURS rather than a galaxy (17 Sep): near-black and charcoal with a violet cast,
   royal violet and silver for the bright bits (silver replaced gold, 17 Sep), and two faint colour washes instead of stars. Cartoony comes
   from the chunky outlines and shadows, not from bright paper. */
--display:"Fredoka","Baloo 2",system-ui,sans-serif;
/* FIGURES THAT MATTER DO NOT WEAR THE COMIC FACE (his, 5 Oct, watching the first live night: "im not sure how
   much of a fan i am of the comic font generally speaking, but when looking at numbers that are important, its
   not great"). Fredoka is friendly and soft-edged, which is right for a heading and wrong for a score somebody is
   deciding on. Archivo is already loaded, is a proper grotesque, and its digits are unambiguous at a glance. One
   token, so the whole app's important numbers move together if he wants them somewhere else again. */
--figures:"Archivo",system-ui,sans-serif;
--paper:#07050c;--panel:#13101c;--panel-2:#1a1626;
--ink:#efeaf8;--ink-2:#bfb6d6;--ink-3:#877f9f;
--line:#2d2740;--line-soft:#201b2e;
--edge:#b8bcc8;                                  /* silver trim on cards and buttons */
--accent:#8f5cff;--accent-soft:#261a4d;
--accent-2:#d7dbe4;--accent-2-soft:#262830;
--alert:#ff5d8f;--alert-soft:#35142a;
/* A game's box: grey unplayed or unlocked, silver-trimmed while it is being played, purple once banked.
   It was WHITE while playing (19 Sep) until he watched a strip of them on a live night. --box-white is
   still what a light panel is built from; nothing uses it to shout any more. */
--box-grey:#221d30;--box-white:#efeaf8;--box-edge:#3a3350;
--shadow:#000000;
--wash-1:rgba(110,60,220,.22);--wash-2:rgba(210,60,140,.10)}
*{box-sizing:border-box}
body{margin:0;color:var(--ink);font-size:14.5px;line-height:1.55;
font-family:"IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
background-color:var(--paper);
background-image:radial-gradient(70% 55% at 0% 0%,var(--wash-1),transparent 70%),
radial-gradient(60% 50% at 100% 100%,var(--wash-2),transparent 70%);
background-repeat:no-repeat;background-attachment:fixed}
h1,h2,h3{font-family:var(--display);margin:0;font-weight:700;
letter-spacing:-.01em;text-wrap:balance}
h1{font-size:clamp(28px,4.6vw,44px);line-height:1.05}
h2{font-size:18px}
.num{font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;font-variant-numeric:tabular-nums}
a{color:var(--accent)}

/* ---------- the teller window: menu ---------- */
.topbar{position:sticky;top:0;z-index:20;background:var(--panel);
border-bottom:3px solid var(--edge);display:flex;flex-wrap:wrap;align-items:center;
gap:10px;padding:10px 18px 11px;
box-shadow:0 3px 0 var(--shadow)}
.brand{font-family:var(--display);font-weight:700;font-size:19px;letter-spacing:-.02em;
text-decoration:none;color:var(--ink);display:flex;flex-direction:column;line-height:1.1}
/* The front page: the name front and centre, a tagline beneath (19 Sep). */
.hero h1.brandname{font-size:clamp(40px,7vw,76px);line-height:1;letter-spacing:-.03em;margin:4px 0 8px}
.hero .tagline{font:700 clamp(17px,2.3vw,23px)/1.3 var(--display);color:var(--ink-2);margin:0 0 14px}
.brand em{font:600 9.5px/1.5 "IBM Plex Mono",monospace;letter-spacing:.14em;
text-transform:uppercase;color:var(--ink-3);font-style:normal}
.menu{display:flex;flex-wrap:wrap;gap:7px;margin-left:auto}

/* ---------- buttons: chunky, stamped, pressable ---------- */
.btn,.ghost,.chip{font-family:inherit;text-decoration:none;display:inline-flex;align-items:center;
gap:6px;cursor:pointer;white-space:nowrap;border-radius:10px;font-weight:600}
/* Every button looks the same until you touch it (his call, 17 Sep): dark, silver-trimmed. Hover, keyboard
   focus or a press turns it the accent colour -- purple here, the mode's own colour inside a league. */
.btn{background:var(--panel);color:var(--ink);border:2px solid var(--edge);padding:8px 15px;font-size:13.5px;
box-shadow:3px 3px 0 var(--shadow)}
.ghost{background:var(--panel);border:2px solid var(--edge);padding:7px 13px;font-size:13px;
color:var(--ink);box-shadow:2px 2px 0 var(--shadow)}
.btn:hover,.btn:focus-visible,.ghost:hover,.ghost:focus-visible,a.portal:hover .btn,a.portal:focus-visible .btn,
button.btn:active,.btn:active,.ghost:active{background:var(--accent);color:#fff;outline:none}
.btn:active{transform:translate(3px,3px);box-shadow:none}
.ghost:active{transform:translate(2px,2px);box-shadow:none}
/* Where you are, or what is selected: a silver underline, not a colour -- colour means "touching it". */
.btn[aria-current],.ghost[aria-current],.chip[aria-current]{background:var(--panel-2);
box-shadow:inset 0 -3px 0 var(--edge),2px 2px 0 var(--shadow)}
.btn[aria-current]:hover,.ghost[aria-current]:hover,.chip[aria-current]:hover{background:var(--accent);color:#fff}
.chip{border:2px solid var(--line);background:var(--panel-2);color:var(--ink);
padding:1px 9px;font-size:12.5px;border-radius:999px;font-weight:600}
.chip:hover,.chip:focus-visible{background:var(--accent);border-color:var(--accent);color:#fff;outline:none}
.chip.plain{background:none;border-color:transparent;padding:1px 4px;font-weight:500}
.chip.plain:hover{background:var(--accent-soft);border-color:var(--accent)}
.btnrow{display:flex;flex-wrap:wrap;gap:9px}

/* ---------- layout ---------- */
main{max-width:1180px;margin:0 auto;padding:26px 18px 64px;display:flex;flex-direction:column;gap:24px}
.hero{display:flex;flex-direction:column;gap:11px;padding:20px 22px;background:var(--panel);
border:3px solid var(--edge);border-radius:18px;box-shadow:6px 6px 0 var(--shadow);position:relative;
overflow:hidden}
/* THE ART ON A BANNER (his, 28 Sep): it comes in from the EDGES and the box crops it -- never a picture
   centred in a frame. It sits behind the words and is not clickable, so a heading always wins. It leaves
   below 860px, where the space stops being spare and the drawing would fight the title for the width.
   Colours are named here rather than in the drawing, so one line re-skins every banner. */
/* FULL WIDTH, bottom-anchored, and the box crops the top. His, 28 Sep: "its also using the space wrong." A
   banner is a WIDE SHORT STRIP, and the first attempt drew a small picture and pushed it into the right corner,
   so the arms he asked to come "from each side of the box" both arrived from the same side. Spanning the width
   is what lets them enter from the actual edges. Width 100% with height auto means the drawing scales with the
   banner and the viewBox's own ratio sets how tall it comes out; overflow:hidden on .hero takes the excess off
   the TOP, which is where there is nothing but sleeve.
   (An inline <svg> has a viewBox but no intrinsic SIZE -- pinning top and bottom with width:auto draws nothing
   at all, which is how this spent a round drawing an invisible handshake.) */
/* IT FILLS THE BOX. The rule used to be `width:100%;height:auto;bottom:-6px`, which was right for the inline
   SVG placeholders it was written for and wrong for a picture, in two ways that only show up on somebody
   else's screen.
   A banner's WIDTH follows the window; its HEIGHT follows the words in it and does not move. So `height:auto`
   made the drawing's height follow the width, and the two agreed at exactly one window size -- mine. He
   photographed his own, narrower: the drawing had shrunk to 112px inside a 135px banner, sat on the bottom
   edge with bare panel above it, and the `-6px` had sliced the fingers off the bottom. "something is wrong",
   and it was: a layout that is correct at one width is not a layout.
   `object-fit:cover` fills the box at every width and crops whatever does not fit, which for a wide shallow
   drawing means taking a little off the ends -- the part of a handshake that is just more sleeve.
   THIS ASSUMES AN <img>, and that is now the only thing hero_art() makes. An inline SVG would be stretched by
   height:100% rather than fitted, so a drawing that comes back as markup needs its own rule. */
.heroart{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center;
pointer-events:none;z-index:0;
--art-suit:color-mix(in srgb,var(--accent) 70%,var(--panel));
--art-cuff:#f4efe2;--art-skin:#e0a86e;--art-skin-far:#b87c45}
/* :not(.heroart) MATTERS. This rule lifts the words above the drawing -- but it also matched the drawing, and
   `position:relative` beat the `absolute` above it (same specificity, later wins). The SVG then laid out as a
   block in the flex column and stretched the banner to its own height: a 700px tall box with a giant handshake
   in it. Anything absolutely placed inside a hero must be excluded here. */
.hero>*:not(.heroart){position:relative;z-index:1}
/* THE WORDS WIN, WHATEVER THE DRAWING DOES (25 Oct). The rule above puts the heading in front of the art, which
   is not the same as being READABLE over it: the first real banner is a handshake whose sleeve runs the full
   width, and "2026 SEASON" -- small grey capitals -- all but disappeared where it crossed the green. The fix
   cannot be "ask the artist to leave the corner empty", because that was in the brief twice and came back
   filled both times; a drawing that has to behave is a drawing that will eventually misbehave on a page nobody
   is looking at.
   So the banner fades its own art out under the words: solid panel at the left edge, gone by the middle. The
   right-hand side, where there is nothing to read, is untouched.
   ::after RATHER THAN ::before, and that is not a style choice. Both are children of .hero for painting, and a
   positioned child at the same z-index paints in document order -- ::before comes before .heroart and would be
   covered by it, which is a scrim that does nothing. ::after is the last child, so it lands on top of the art
   and under the words, which carry z-index 1. */
/* HOW HARD IT FADES is a judgement, and his (25 Oct): "make the left side of the banner art a little more
   opaque". It used to go fully solid at the left edge, which read as a dead corner with nothing in it rather
   than as a drawing going quiet. Now it never quite reaches the panel colour, so the arm stays visible behind
   the words and the fade is something the art does rather than a lid over it. The heading is still the thing
   that must win: anything weaker than this and small grey capitals stop being legible on the green. */
/* AND ITS STRENGTH IS PER PAPER, which it was not, and that was a plain bug (his, 29 Oct: "the handshake
   looks bad on the light theme because of the opacity"). The scrim is made of var(--panel): on the vault
   that is near-black, so fading the art into it reads as the drawing going into shadow. On the BANKNOTE
   the same token is cream, so the identical 88% does not shade the handshake, it BLEACHES it -- the
   sleeve by the heading went to almost nothing. Dark ink still needs a light backing to stay readable, so
   the scrim must go on lightening by day; it simply has to do far less of it. The numbers below are the
   night ones kept exactly as he approved them, with a much lighter pair for the day. */
.hero:has(.heroart){
--scrim-0:light-dark(color-mix(in srgb,var(--panel) 52%,transparent),
                     color-mix(in srgb,var(--panel) 88%,transparent));
--scrim-1:light-dark(color-mix(in srgb,var(--panel) 30%,transparent),
                     color-mix(in srgb,var(--panel) 66%,transparent))}
.hero:has(.heroart)::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
background:linear-gradient(90deg,var(--scrim-0) 0%,var(--scrim-1) 32%,transparent 60%)}
/* A LITTLE MORE AIR OVER THE SEASON (his, same breath). It sat tight against the top edge, which is fine on a
   plain banner and cramped on one with a drawing behind it, because the art gives the eye something to measure
   the gap against. On the hero's own eyebrow only, so nothing else that wears the class moves.
   MEASURED, NOT GUESSED. The first attempt set 7px and moved it UP, because a paragraph here already carries
   an 11px margin and setting the property replaced it rather than adding to it. The browser was asked what it
   was actually using before this number was chosen. */
.hero>.eyebrow{margin-top:18px}
/* THE WORDS STOP WHERE THE DRAWING STARTS (his, 26 Oct: "i like it but the text is causing problems").
   The fade above is a scrim, and a scrim can only do so much: a sentence that runs the WHOLE width ends up
   on the part of the picture the fade deliberately leaves alone. In the draft room before a draft starts
   that is one long line -- the rules of the league, teams, rounds, snake order, the clock -- and it lay
   straight across the hand.
   So a banner's paragraphs are given the left of the box and the drawing keeps the right. 60% is where the
   fade has finished its work, so nothing is asked to be legible over a picture at full strength. A long line
   now wraps rather than carrying on into the art, which costs a row of height and is the right trade: the
   banner is allowed to grow, the words are not allowed to become unreadable.
   AND IT COMES OFF WITH THE ART. Below 860px the drawing is hidden, so there is nothing to keep clear of --
   `:has(.heroart)` still matches there, because the image is hidden rather than gone, so the width has to be
   given back explicitly or a plain banner would be wearing a margin for a picture nobody can see. */
.hero.draftroom:has(.heroart)>p{max-width:54%}
/* AND THE FADE HAS TO HOLD LONGER HERE, or stopping the words early achieves nothing. The first attempt
   capped them at 60% and the line still read badly -- because the gradient above is already transparent at
   60%, so "keep the words off the art" and "the art is at full strength from 60%" were the same edge. The
   two numbers only work as a pair: the words stop at 54%, and the fade is still doing real work there and
   is gone by 64%. The hand and the bundle it is lifting start at 65%, so the one thing the picture is about
   is never dimmed.
   DRAFT ROOM ONLY. The Trades banner is tuned to its own header and he has approved how it looks; this
   header is the text-heaviest in the app -- eyebrow, heading, the round, the clock, a note, the league's
   whole rule set, and a row of controls -- so it earns a stronger scrim rather than imposing one. */
/* THE DAY VALUES ARE STRONGER HERE THAN ON TRADES, and that is the same reason the night ones are: this
   header carries the league's whole rule set in one long sentence, and it lies right across the counter.
   The first pass at the banknote used Trades' own gentler numbers and the line crossed the note bundles
   plainly -- readable, but not clean. These sit between: far lighter than the 92/84/45 that was bleaching
   the drawing, firm enough that the sentence still wins. */
.hero.draftroom:has(.heroart){
--dr-0:light-dark(color-mix(in srgb,var(--panel) 78%,transparent),
                  color-mix(in srgb,var(--panel) 92%,transparent));
--dr-1:light-dark(color-mix(in srgb,var(--panel) 70%,transparent),
                  color-mix(in srgb,var(--panel) 84%,transparent));
--dr-2:light-dark(color-mix(in srgb,var(--panel) 34%,transparent),
                  color-mix(in srgb,var(--panel) 45%,transparent))}
.hero.draftroom:has(.heroart)::after{
background:linear-gradient(90deg,var(--dr-0) 0%,var(--dr-1) 38%,var(--dr-2) 54%,transparent 64%)}
@media (max-width:860px){.heroart{display:none}
/* and with no art there is nothing to fade, so the gradient goes too rather than tinting a plain banner. */
.hero:has(.heroart)::after{display:none}
.hero.draftroom:has(.heroart)>p{max-width:none}}
/* No striped band along the top of a banner: he does not want one, in any mode (17 Sep). */
.eyebrow{font:600 11px/1.4 "IBM Plex Mono",monospace;letter-spacing:.12em;text-transform:uppercase;
color:var(--ink-3)}
.eyebrow b{color:var(--accent)}
.panel{background:var(--panel);border:3px solid var(--edge);border-radius:16px;
box-shadow:5px 5px 0 var(--shadow);overflow:hidden}
.panel>h2{font-family:var(--display);font-size:14px;text-transform:uppercase;
letter-spacing:.06em;color:var(--ink);padding:12px 16px 0}
.panel .note{padding:3px 16px 11px;color:var(--ink-3);font-size:12px}
.panel .pad{padding:0 16px 16px;display:flex;flex-direction:column;gap:11px}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));gap:12px}
.stat{background:var(--panel);border:3px solid var(--edge);border-radius:14px;
box-shadow:4px 4px 0 var(--shadow);padding:12px 14px;display:flex;flex-direction:column;gap:3px}
.stat span{font:600 9.5px/1.3 "IBM Plex Mono",monospace;letter-spacing:.12em;text-transform:uppercase;
color:var(--ink-3)}
.stat b{font-family:var(--display);font-size:24px;line-height:1.1}
.stat b.small{font-size:14.5px;letter-spacing:-.01em}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(275px,1fr));gap:16px}
.two{display:grid;grid-template-columns:1.3fr .7fr;gap:20px;align-items:start}
@media (max-width:900px){.two{grid-template-columns:1fr}}

/* ---------- the ledger ---------- */
.tablewrap{overflow:auto;max-height:76vh}
table{border-collapse:collapse;width:100%}
thead th{position:sticky;top:0;background:var(--ink);color:var(--panel);z-index:5;
font:700 10.5px/1.4 "IBM Plex Mono",monospace;text-transform:uppercase;letter-spacing:.09em;
text-align:right;padding:9px 11px;white-space:nowrap}
thead th.l{text-align:left}
tbody td{padding:8px 11px;text-align:right;white-space:nowrap;font-size:13px;
border-bottom:1px solid var(--line-soft)}
tbody td.l{text-align:left;white-space:normal}
/* A COIN IS A PICTURE, not a number stuck to the right edge nor a word stuck to the left: its own column, the same
   width on every row, and the coin in the middle of it (19 Sep: "none of the coins are centered in the right spot"). */
thead th.coincell,tbody td.coincell{text-align:center;width:104px;padding-left:6px;padding-right:6px}
tbody tr:nth-child(even) td{background:var(--panel-2)}
tbody tr:hover td{background:var(--accent-2-soft)}
tbody tr:last-child td{border-bottom:none}
.pname{font-weight:600}
.meta{color:var(--ink-3);font-size:11.5px}
.rk{color:var(--ink-3);width:42px}
.fp{font-weight:700;font-family:"IBM Plex Mono",monospace}
.risk{color:var(--ink-3);font-size:12px}
.risk.mid{color:var(--accent-2);font-weight:600}
.risk.hi{color:var(--alert);font-weight:700}
.belowbar{color:var(--ink-3);opacity:.65}
.ii{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;
margin-left:6px;border-radius:50%;border:1.5px solid currentColor;font-style:normal;font-size:10px;
font-weight:700;line-height:1;vertical-align:middle}
.ii.r-low{background:var(--accent-soft);color:var(--accent)}
.ii.r-moderate{background:var(--accent-2-soft);color:var(--accent-2)}
.ii.r-high{background:var(--alert-soft);color:var(--alert)}

/* ---------- rubber stamps ---------- */
.stamp{display:inline-block;padding:1px 8px;border:2px solid currentColor;border-radius:4px;
font:700 10.5px/1.5 "IBM Plex Mono",monospace;letter-spacing:.11em;text-transform:uppercase;
transform:rotate(-4deg)}
.stamp.paid{color:var(--accent);background:var(--accent-soft)}
.stamp.void{color:var(--ink-3);border-style:dashed;background:none}
.stamp.red{color:var(--alert);background:var(--alert-soft)}
.pill{display:inline-block;border-radius:999px;padding:2px 10px;
font:700 11px/1.5 "IBM Plex Mono",monospace}
.p-good{background:var(--accent-soft);color:var(--accent)}
.p-warn{background:var(--accent-2-soft);color:var(--accent-2)}
.p-crit{background:var(--alert-soft);color:var(--alert)}
.p-none{color:var(--ink-3)}
.win{color:var(--accent);font-weight:700}
.forced{color:var(--ink-3)}
.gone{color:var(--ink-3);text-decoration:line-through}

/* ---------- a week, as a receipt ---------- */
.receipt{position:relative;padding-bottom:12px}
/* The torn edge: triangles cut out of the bottom, gradients only, no artwork. */
.receipt:after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:10px;
background:repeating-linear-gradient(135deg,var(--paper) 0 7px,transparent 7px 14px)}
.mrow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 15px;
border-bottom:2px dashed var(--line-soft)}
.mscore{font-family:"IBM Plex Mono",monospace;font-variant-numeric:tabular-nums;font-size:17px;
font-weight:700}
.mnote{padding:9px 15px;color:var(--ink-3);
font:600 10px/1.4 "IBM Plex Mono",monospace;letter-spacing:.1em;text-transform:uppercase;
display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap}
.games span{font-family:"IBM Plex Mono",monospace;font-size:12px}

/* ---------- the league menu: a sidebar in the space beside the page (19 Sep) ---------- */
.withnav{display:grid;grid-template-columns:220px minmax(0,1fr);gap:26px;max-width:1440px;margin:0 auto;padding:0 18px}
.withnav>main{max-width:none;margin:0;padding:26px 0 64px;min-width:0}
.leaguenav{position:sticky;top:78px;align-self:start;margin-top:26px;max-height:calc(100vh - 96px);overflow:auto;
display:flex;flex-direction:column;gap:10px;padding:12px 10px;background:var(--panel);border:3px solid var(--edge);
border-radius:16px;box-shadow:5px 5px 0 var(--shadow)}
.navleague{font:700 17px/1.2 var(--display);color:var(--ink);text-decoration:none;padding:0 6px}
/* ONE FLAT LIST (19 Sep: "it doesnt need to be seperated like that. make it way simpler please"). There are no
   headings, so each word gets a little more room to be a target. */
.navgroup{display:flex;flex-direction:column;gap:2px}
.leaguenav .navgroup a,.leaguenav-phone .navgroup a{padding:7px 10px;font-size:14.5px}
.navhead{margin:0 0 3px;padding:0 6px;font:600 9.5px/1.4 "IBM Plex Mono",monospace;letter-spacing:.13em;
text-transform:uppercase;color:var(--ink-3)}
.navgroup a{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:4px 8px;border-radius:8px;
color:var(--ink);text-decoration:none;font-weight:600;font-size:13.5px;line-height:1.35}
.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:-12px;font-size:13px;color:var(--ink-3)}
.crumbs a{color:var(--ink);font-weight:600;text-decoration:none;border-bottom:2px solid var(--edge)}
.crumbs a:hover{color:var(--accent);border-color:var(--accent)}
.navgroup a:hover,.navgroup a:focus-visible{background:var(--accent);color:#fff;outline:none}
/* Where you are: the same silver underline the buttons use, as a bar down the side. */
.navgroup a[aria-current]{background:var(--panel-2);box-shadow:inset 3px 0 0 var(--edge)}
.navgroup a[aria-current]:hover{background:var(--accent)}
.leaguenav-phone{display:none}
@media (max-width:900px){
  .withnav{grid-template-columns:minmax(0,1fr);gap:0;padding:0 18px}
  .leaguenav{display:none}
  .leaguenav-phone{display:block;margin-top:14px;background:var(--panel);border:3px solid var(--edge);border-radius:14px;
  box-shadow:4px 4px 0 var(--shadow)}
  .leaguenav-phone>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:10px;padding:10px 14px}
  .leaguenav-phone>summary::-webkit-details-marker{display:none}
  .leaguenav-phone .navleague{padding:0;font-size:15px}
  .navhere{color:var(--ink-3);font-size:13px;flex:1}
  .navmenu{font-weight:700;border:2px solid var(--edge);border-radius:9px;padding:3px 10px}
  .leaguenav-phone[open] .navmenu{background:var(--accent);color:#fff}
  .leaguenav-phone-groups{padding:0 12px 14px}
  .leaguenav-phone-groups .navgroup{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:2px 10px}
}
.form{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px}
.field{display:flex;flex-direction:column;gap:4px}
.field span{font:600 9.5px/1.3 "IBM Plex Mono",monospace;letter-spacing:.12em;text-transform:uppercase;
color:var(--ink-3)}
.field input{font-family:inherit;font-size:14px;padding:8px 11px;border:2px solid var(--line);
border-radius:10px;background:var(--panel);color:var(--ink)}
.field input[type=checkbox]{width:22px;height:22px;accent-color:var(--accent)}
.field input[type=number]{font-family:"IBM Plex Mono",monospace;font-weight:600;max-width:160px}
.field input:focus{outline:3px solid var(--accent);outline-offset:1px}
button[disabled],.btn[disabled]{opacity:.55;cursor:not-allowed}
/* A DRAFT BUTTON THAT IS NOT YOUR TURN YET IS GREY, NOT FADED (his, 26 Oct). The rule above is right for a
   control you may not use, and wrong for one you are about to: fading a 12px button makes its word hard to
   read, which on a draft board is the one thing it must not be. So it keeps full opacity and goes properly
   muted instead -- a button waiting its turn rather than a broken one -- and the cursor says "not yet" rather
   than "not allowed", because nothing is being refused. Its `title` names whose turn it actually is. */
.dr-pick:disabled{opacity:1;background:var(--panel-2);color:var(--ink-3);border-color:var(--line);
box-shadow:none;cursor:default}
.panel ul{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:6px}
/* THE FOOTER IS PASTED TO THE BOTTOM OF THE SCREEN (18 Sep). The body is a column at least as tall as the window; the
   page takes all the spare height and the footer comes last. On a short page that puts it on the bottom edge instead of
   floating up under the sidebar; on a long page it is at the end, where a footer goes. One row of small print, wrapping,
   instead of five stacked lines. `width:100%` matters: an item with auto side margins in a column does not stretch. */
body{min-height:100vh;display:flex;flex-direction:column}
body>.withnav,body>main{flex:1 0 auto;width:100%}
/* CENTRED, AND QUIET. His, 9 Oct: "why are the notices at the bottom of every screen not centered" and "its
   supposed to be small and only important for those looking for it." The BOX was centred all along -- 1180px
   with auto margins -- while the words inside it ran from the left edge, which is the version of centred that
   satisfies a stylesheet and nobody looking at the page. The row centres its items now and the text with them,
   and it drops a point of size, because this is the part of the page you should only find when you go looking. */
/* 760px, NOT THE PAGE'S FULL WIDTH, and that is what makes the centring visible. Centred inside 1180px the small
   print simply filled the row end to end and looked left-aligned, which is centred by the stylesheet's reckoning
   and not by his. Narrow enough to wrap into a couple of short lines, which is the shape a footer has.
   BESIDE A LEAGUE MENU IT IS THE SAME, deliberately: it used to be pushed right by the width of the menu and
   held off the right-hand edge by 120px because the coin purse floated there. The purse has lived in the top bar
   since 23 Sep and floats over nothing -- which checks/footer_bottom.py MEASURES rather than assumes, so if it
   ever floats again that check says so rather than this quietly being wrong. */
footer{width:100%;max-width:760px;margin:auto auto 0;padding:18px 18px 20px;color:var(--ink-3);font-size:11px;
display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;text-align:center;gap:4px 14px}
/* The footer's two rows are wrapped (kit.footer_note) so a mode can give each its own paper over its art.
   `display:contents` means the wrappers are not laid out at all here: the spans stay direct flex children of
   the footer and every mode that does not ask for bubbles is drawn exactly as it was. */
footer>.frow{display:contents}

/* ---------- dropdowns, and settings that do not do anything yet ---------- */
.field select{font-family:inherit;font-size:14px;padding:8px 11px;border:2px solid var(--line);
border-radius:10px;background:var(--panel);color:var(--ink);font-weight:600}
.field select:focus{outline:3px solid var(--accent);outline-offset:1px}
.field select[disabled],.field input[disabled]{opacity:.65}
/* accounts */
.problem{margin:0;color:var(--alert);font-weight:600}
.seatform{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
/* A mock draft has no queue, so the board gets the whole width (20 Sep). */
.wideboard{display:grid;grid-template-columns:minmax(0,1fr);gap:18px}
/* Your own columns on the board (20 Sep). */
.mylist{white-space:nowrap}
.board td.mylist{color:var(--ink);font-family:"IBM Plex Mono",monospace;font-size:12.5px}
.tiny{padding:1px 7px;font-size:12px;min-height:0;line-height:1.5}
/* A team's name opens a menu (20 Sep): where to go, rather than one guess. */
.tmenu{display:inline-block;position:relative}
.tmenu>summary{list-style:none;cursor:pointer}
.tmenu>summary::-webkit-details-marker{display:none}
.tmenu[open]>summary{background:var(--accent);color:#fff;border-color:var(--accent)}
/* FIXED, not absolute, and this is not a style choice. These menus sit inside .tablewrap, which scrolls
   (overflow:auto) so a wide table works on a narrow screen -- and an absolutely positioned child is CLIPPED
   by any scrolling ancestor. z-index cannot rescue it: clipping happens before stacking. The menu was
   buried inside the table box on desktop; the phone rule below already used fixed, which is why it looked
   fine there and the width checks never caught it. Fixed escapes the clip, and kit.py's NAME_MENUS script
   puts it under the name it belongs to. */
.tmenu-box{position:fixed;z-index:40;min-width:172px;display:flex;flex-direction:column;
gap:2px;padding:6px;background:var(--panel);border:3px solid var(--edge);border-radius:12px;box-shadow:5px 5px 0 var(--shadow)}
.tmenu-box a{padding:7px 9px;border-radius:8px;color:var(--ink);text-decoration:none;font-weight:600;font-size:13.5px;
white-space:nowrap}
.tmenu-box a:hover,.tmenu-box a:focus-visible{background:var(--accent);color:#fff;outline:none}
@media (max-width:560px){.tmenu-box{position:fixed;left:12px;right:12px;min-width:0}}
/* Every matchup of the year, in a row you can scroll (20 Sep). */
.weekscroll{display:flex;gap:6px;overflow-x:auto;padding:8px 0 4px;margin:6px 0 0;scrollbar-width:thin}
.weekscroll .chip{flex:0 0 auto;min-width:34px;text-align:center}
.weekscroll .chip.on{background:var(--accent);color:#fff;border-color:var(--accent)}
/* Checkbox lists: a trade's players, drops. */
.ticks{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:4px 12px;margin:0 0 10px}
.tick{display:flex;gap:7px;align-items:center;font-size:14px;margin:4px 0}
.tick input[type=checkbox],.tick input[type=radio]{width:18px;height:18px;accent-color:var(--accent)}
.seatform select{font-family:inherit;font-size:13.5px;padding:6px 9px;border:2px solid var(--line);border-radius:9px;
background:var(--panel-2);color:var(--ink)}
.field em.meta{font-style:normal;font-size:11.5px;color:var(--ink-3)}
.soon{display:inline-block;margin-left:7px;padding:0 6px;border:1.5px solid var(--accent-2);
border-radius:999px;background:var(--accent-2-soft);color:var(--accent-2);font-style:normal;
font:700 8.5px/1.6 "IBM Plex Mono",monospace;letter-spacing:.09em;text-transform:uppercase}
.groupname{font-size:13px;text-transform:uppercase;letter-spacing:.09em;color:var(--ink-3);
margin:14px 0 8px}

/* ---------- a sortable board ---------- */
thead th a.sorthead{color:inherit;text-decoration:none;display:inline-block;white-space:nowrap}
thead th a.sorthead:hover{color:var(--accent-2)}
thead th{cursor:pointer}
.searchform{display:flex;gap:7px;align-items:center}
.searchform input[type=text]{font-family:inherit;font-size:13px;padding:6px 11px;
border:2px solid var(--edge);border-radius:10px;background:var(--panel);color:var(--ink)}

/* ---------- game mode portals: the first step of creating a league ----------
 * A puddle in a box (his words, 17 Sep): each mode's world pooled in the middle of a plain card, its look
 * bleeding out into the box around it, and the mode's name written on the surface. Static -- no spin.
 * Each mode supplies its own puddle through --p-* tokens, so a new mode is a few lines, not new rules. */
.portals{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:24px}
.portal{position:relative;overflow:hidden;display:flex;flex-direction:column;align-items:center;gap:14px;
text-align:center;padding:30px 20px 22px;background:var(--panel);border:3px solid var(--edge);
border-radius:24px;box-shadow:7px 7px 0 var(--shadow);color:var(--ink);text-decoration:none;
transition:transform .15s ease,box-shadow .15s ease}
/* The bleed: the mode's colour soaking outward from the puddle into the box. */
.portal:before{content:"";position:absolute;inset:-30% -30% 10%;pointer-events:none;
background:radial-gradient(closest-side at 50% 42%,var(--p-bleed),transparent 78%)}
.portal>*{position:relative}
a.portal:hover{transform:translate(-3px,-3px);box-shadow:10px 10px 0 var(--shadow)}
a.portal:active{transform:translate(4px,4px);box-shadow:2px 2px 0 var(--shadow)}
.portal-puddle{position:relative;width:86%;aspect-ratio:2/1.15;display:grid;place-items:center;
border-radius:54% 46% 58% 42% / 46% 58% 42% 54%;border:3px solid var(--p-edge);
background:var(--p-surface);
box-shadow:0 0 0 7px var(--p-ring),0 0 34px 16px var(--p-bleed),inset 0 10px 22px rgba(0,0,0,.28)}
/* Splashes: droplets that escaped the puddle. */
.portal-puddle:before,.portal-puddle:after{content:"";position:absolute;border:3px solid var(--p-edge)}
.portal-puddle:before{width:21%;aspect-ratio:1.7;right:-9%;bottom:0;border-radius:52% 48% 60% 40% / 50% 45% 55% 50%;
background:var(--p-surface)}
.portal-puddle:after{width:10%;aspect-ratio:1.25;left:-5%;top:10%;border-radius:50%;background:var(--p-ring)}
.portal-puddle span{font-family:var(--p-font);font-size:clamp(34px,4.4vw,48px);line-height:1;
color:var(--p-ink);text-transform:uppercase;letter-spacing:.03em;transform:rotate(-5deg);
text-shadow:2px 2px 0 var(--p-ring)}
.portal p{margin:0;color:var(--ink-2);font-size:13.5px;max-width:30ch}
/* The mode's name, under its painted hole. It is the puddle's lettering moved out of the drawing, so it
   wears the same font and the same accent drop -- but the card's own ink, NOT --p-ink: those are mixed
   for text sitting ON a bright puddle (War's is near-black brown) and would all but vanish on the card. */
.portal p.portal-name{margin:-2px 0 0;max-width:none;font-family:var(--p-font);line-height:1;
font-size:clamp(26px,3.2vw,36px);color:var(--ink);text-transform:uppercase;letter-spacing:.03em;
text-shadow:2px 2px 0 var(--p-ring)}
/* The ordinary fantasy word for the mode, under its themed name. Needs `p.` to outweigh `.portal p`
   above, which is the more specific selector whichever order they are written in. */
.portal p.portal-format{margin:-6px 0 0;color:var(--ink-3);font-size:12px;letter-spacing:.1em;
text-transform:uppercase}
.portal.locked{box-shadow:4px 4px 0 var(--shadow);border-style:dashed;opacity:.7}
.portal.locked .portal-puddle{filter:grayscale(1) brightness(.8)}
.portal.locked:before{opacity:.25}

/* A mode with a drawing shows that instead of a CSS puddle: a PICTURE at art/<mode>/portal.png, else a
   hand-drawn lockin/theme/portals/<mode>.svg. The liquid and its drips are in the drawing; the card only
   adds the glow soaking out around it. Either kind sits in the same box and takes the same animation --
   the shot is fired by the card, not by what is in the hole. */
/* THE GLOW BLEEDS INTO PADDING, NOT PAST THE EDGE. It used to hang 8% out either side on negative insets,
   which looks the same and is not the same: anything sticking out to the RIGHT counts toward an element's
   scroll width, so on a phone the card reported 26px of itself cut off with no way to reach it, and
   checks/phone_width.py said so the moment all three cards had art instead of one. The box is 26px wider
   than the drawing in each direction and the glow fills it, so the picture is the size it always was and
   nothing reaches past the edge. */
.portal-art{--art-pad:26px;position:relative;width:100%;max-width:382px;padding:0 var(--art-pad);
margin:-6px auto 0}
.portal-art:before{content:"";position:absolute;inset:0 0 12%;pointer-events:none;
background:radial-gradient(closest-side at 50% 42%,var(--p-bleed),transparent 80%)}
.portal-art svg{position:relative;display:block;width:100%;height:auto;overflow:hidden}
/* THE HOLE IS A BACKGROUND so that only the paper you are actually on is ever downloaded. Its two
   addresses arrive on the element as --pic-night and --pic-day (kit.portal_art), and a browser fetches an
   image out of a custom property only when a winning rule uses it -- so the other paper's picture is
   never asked for. `aspect-ratio` comes from the file's own size, so the card cannot jump as it arrives.
   The three blocks below are the same three the mode papers use everywhere else: a pinned Light, a pinned
   Dark, and auto following the machine. A mode left out of them answers "Light" by not changing, which
   looks exactly like a broken button. */
.portal-art .portalpic{position:relative;display:block;width:100%;
background:var(--pic-night) center/contain no-repeat}
html[data-theme="light"] .portal-art .portalpic{background-image:var(--pic-day)}
@media (prefers-color-scheme:light){
  html[data-theme="auto"] .portal-art .portalpic{background-image:var(--pic-day)}}
/* THE MARK SITS IN THE HOLE, centred on it and sized against it rather than against the card -- so the
   hole and its identifier keep the same relationship at every card width. It is the only part of the pair
   that is the mode's OWN drawing; the hole behind it is one picture in three colourways. */
/* THE MIDDLE OF THE HOLE IS NOT THE MIDDLE OF THE PICTURE, which is the whole reason this number exists.
   His, 29 Oct: "they arent centered". They were -- on the picture's box, which is the drawing's alpha
   bounding box with the droplets in it, and the droplets hang 124px below the oval while touching its
   top. So the oval's middle sits 62px of 504 above the box's, and a mark at top:50% lands low every time.
   To CSS the picture is a rectangle and it cannot see this; scratch/centre.py floods the biggest blob of
   opaque pixels -- the lip and everything inside it, ignoring every separate speck -- and reports its
   middle as a percentage. One drawing serves all three modes, so one number does.
   REMEASURE IT WHENEVER THE HOLE IS REDRAWN. Horizontally it came out within a pixel of centre, and the
   padding either side is symmetric, so only the vertical needs saying. */
.portal-art{--hole-y:37.8%}
/* 64% OF THE PICTURE, not of the box around it. A percentage width on an absolutely positioned child is
   measured against the containing block's PADDING box, and this one is padded 26px either side to give
   the glow somewhere to bleed -- so a plain `width:64%` came out at 74% of the hole, and Bank's note sat
   noticeably wider in its portal than War's bullet does in his. Subtracting the padding first makes the
   two marks siblings again, and keeps a painted mark the same size as the drawn one it replaces. */
.portal-art img.portalmark{position:absolute;left:50%;top:var(--hole-y);transform:translate(-50%,-50%);
width:calc((100% - var(--art-pad) * 2) * .64);height:auto;pointer-events:none}
/* A HAND-DRAWN mark, where the mode has no mark.png yet. It is SCALED rather than re-sized: the note is
   drawn at 112px with a 3px border, 4 and 6px inset rings and 11px $ corners, and growing the box alone
   would leave all five of those at their button size on an object three times the area. One transform
   takes the whole drawing up together. The step at 620px is where the card itself stops shrinking.
   THE SCALE IS THE PAINTED RULE, ARITHMETIC NOT TASTE: a mark.png is laid out at 64% of the PICTURE
   just above, the picture is the art box less its padding (382 - 52 = 330), and the drawn marks are
   112 units wide -- so .64 * 330 / 112. A mode must read the same size before and after somebody draws
   it a mark, or the slot means two different things.
   IT WAS 1.37 UNTIL 30 OCT and that was wrong: it had been worked out against the OVAL's width rather
   than the picture's, so a drawn mark came out at 153px where a painted one gets 211 -- three quarters
   the size, in the same slot, for no reason anybody could have seen in the stylesheet. His, looking at
   War's bullet: "too small". */
.portal-art .bill,.portal-art .slug{position:absolute;left:50%;top:var(--hole-y);z-index:2;
pointer-events:none;transform:translate(-50%,-50%) rotate(-4deg) scale(1.88)}
@media (max-width:620px){.portal-art .bill,.portal-art .slug{
transform:translate(-50%,-50%) rotate(-4deg) scale(1.58)}}
.portal.drawn:before{display:none}

/* Bank: a dripping portal in Bank's own format -- money green, brass, cream paper -- with a dollar bill floating
   over it (the drawing). */
/* The tile IS the preview: it is the paper you are about to step onto, so it wears whichever of Bank's two the
   reader has -- cream under the engraving by day, the vault's deep green by night. Its colour-scheme and the pin
   that fixes it are further down, with the wipe that spreads this same colour across the screen. */
.portal-bank{--p-font:"Archivo Black",Archivo,sans-serif;
--p-ink:light-dark(#1f7a4d,#4fbb83);--p-edge:light-dark(#1d2a24,#eef3ec);
--p-ring:#d9ab52;--p-bleed:rgba(184,134,43,.30);
--p-surface:repeating-linear-gradient(45deg,rgba(31,122,77,.16) 0 2px,transparent 2px 9px),
repeating-linear-gradient(-45deg,rgba(184,134,43,.16) 0 2px,transparent 2px 9px),light-dark(#efe7d3,#101713)}
/* War: the puddle is a cartoon blast -- a yellow middle going orange at the edge, with a fat dark outline. Its
   colours are mode-war.css's, named here too because the portal stands in the LOBBY, which never loads a mode's
   stylesheet. Two places holding one palette is how they part, so if either moves, move both. */
.portal-war{--p-font:var(--display);--p-ink:#2a1206;--p-edge:#191b12;--p-ring:#ffc93c;
--p-bleed:rgba(255,122,47,.34);--p-surface:radial-gradient(circle at 38% 32%,#ffe9a8 0%,#ffc93c 34%,#ff7a2f 100%)}
.portal-subjects{--p-font:var(--display);--p-ink:#07555c;--p-edge:#1b1340;--p-ring:#cfd3dc;
--p-bleed:rgba(15,163,177,.30);--p-surface:radial-gradient(circle at 30% 30%,#e9fdff,#9fe6ee)}

/* ---------- blending between looks (17 Sep: "so its not harsh") ----------
 * Every page change cross-fades, so moving between the base look and a mode's look blends instead of snapping.
 * Browsers without view transitions simply switch as before. Anyone who asks their device for less motion
 * gets no animation at all. */
@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:.4s;animation-timing-function:ease}
@media (prefers-reduced-motion:reduce){::view-transition-old(root),::view-transition-new(root){animation:none}}

/* Stepping through a portal: the mode's paper spreads out from the portal until it fills the screen, and the
 * mode's page then fades in over the same colour. --p-wipe and --p-wipe-ring are each mode's to set. */
.portal-wipe{position:fixed;z-index:100;width:40px;height:40px;margin:-20px 0 0 -20px;border-radius:50%;
pointer-events:none;transform:scale(0);transition:transform .45s cubic-bezier(.65,0,.35,1)}
/* A PORTAL FLOODS THE SCREEN WITH THE PAPER YOU ARE ABOUT TO LAND ON, so it must know which of Bank's two
   papers that is. It stands on the lobby, which is always night, so it cannot simply inherit: it declares its
   own colour-scheme and the pin below reaches it. Cream flooding the screen ahead of the vault would be the
   exact harshness the wipe exists to smooth. */
.portal-bank{color-scheme:dark;
--p-wipe:light-dark(#efe7d3,#101713);--p-wipe-ring:light-dark(#1f7a4d,#4fbb83)}
/* War floods with the paper you are about to land on, like Bank: sandy newsprint by day, blue-black by night. */
.portal-war{color-scheme:dark;
--p-wipe:light-dark(#f1e8d5,#161a23);--p-wipe-ring:light-dark(#d9541a,#ff7a2f)}
.portal-subjects{--p-wipe:#9fe6ee;--p-wipe-ring:#07555c}

/* HIS CHOICE OF LOOK, from App Settings (kit.LOOK_FIRST puts it on <html> before the first paint). It lives
   here, in the base, rather than beside Bank's colours: a mode's stylesheet is only loaded inside that mode's
   league, and the portal into one stands in the lobby, where it is not. */
/* EVERY MODE WITH TWO PAPERS BELONGS IN THESE THREE LISTS. A mode left out answers "Light" by not changing,
   which looks exactly like a broken button -- and that is the whole reason War grew a daylight paper when the
   choice was renamed to Dark and Light (28 Oct). Add a mode's body AND its portal: the portal stands in the
   lobby, which never loads a mode's own stylesheet. */
html[data-theme="light"] :is(body.mode-bank,.portal-bank,body.mode-war,.portal-war){color-scheme:light}
html[data-theme="dark"] :is(body.mode-bank,.portal-bank,body.mode-war,.portal-war){color-scheme:dark}
/* Following the machine is a CHOICE now rather than the starting point, so it has an attribute of its own. */
html[data-theme="auto"] :is(body.mode-bank,.portal-bank,body.mode-war,.portal-war){color-scheme:light dark}

/* ---------- the draft board: the tool's board, in this app's paint ---------- */
table.board thead th{white-space:nowrap}
.ci{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;margin-left:5px;
border-radius:50%;border:1.5px solid currentColor;background:none;color:inherit;opacity:.7;padding:0;
font:700 9.5px/1 var(--display);cursor:help;vertical-align:middle}
.ci:hover{opacity:1;background:var(--accent);border-color:var(--accent);color:#fff}
button.ii{padding:0;cursor:help;font-family:var(--display)}
.colinfo{max-width:min(460px,92vw);margin:auto;padding:16px 18px;background:var(--panel);color:var(--ink);
border:3px solid var(--edge);border-radius:16px;box-shadow:6px 6px 0 var(--shadow);text-align:left;
white-space:normal;text-transform:none;letter-spacing:normal;font:400 13.5px/1.55 "IBM Plex Sans",system-ui,sans-serif}
.colinfo h3{font-size:15px;margin:0 0 8px}
.colinfo p{margin:0}
.colinfo::backdrop{background:rgba(0,0,0,.35)}
.verdict{display:block;margin-top:8px;font-weight:700}
/* A PURPLE R, the draft tool's own (his, 29 Sep: "the rookies didnt have a massive rookie, it was just a purple
   R"). Same colours the tool uses, so the two boards read the same. */
.rook{display:inline-block;margin-left:6px;padding:0 5px;border-radius:3px;background:#241c30;color:#b591e2;
font:700 10.5px/1.6 var(--display);letter-spacing:.04em;vertical-align:middle}
.badge{display:inline-block;margin-left:6px;padding:0 6px;border-radius:5px;background:var(--alert);color:#fff;
font:700 9.5px/1.6 "IBM Plex Mono",monospace;letter-spacing:.06em;vertical-align:middle}
/* A QUIET BADGE, for a state that is not a warning. It is the same shape, size and baseline as the loud one so
   the two line up in a column -- his, 27 Sep: "why are banked and out not in line with eachother". They were
   different ELEMENTS before, a badge and a run of plain text, which is why they never could. */
.badge.quiet{background:color-mix(in srgb,var(--ink) 16%,transparent);color:var(--ink-3)}
.boardpicks{align-items:center;gap:6px}
.drop{color:var(--ink-3)}
.drop.g1{opacity:.55}
.drop.g3{color:var(--accent-2);font-weight:700}
.drop.g4{color:var(--alert);font-weight:700}
.drop.tied{font-style:italic;opacity:.55;font-size:11px}
tr.tierbreak td{padding:3px 11px!important;background:transparent!important;text-align:left;
font:600 10px/1.5 "IBM Plex Mono",monospace;text-transform:uppercase;letter-spacing:.09em;color:var(--ink-3);
border-top:1px solid var(--line);border-bottom:none}
tr.tierbreak.g3 td{color:var(--accent-2);border-top:2px solid var(--accent-2)}
tr.tierbreak.g4 td{color:var(--alert);border-top:3px solid var(--alert)}

/* ---------- league status: Active or Retired (his words, 17 Sep) ---------- */
.status{display:inline-flex;align-items:center;gap:6px;padding:2px 11px;border-radius:999px;border:2px solid var(--edge);
font:700 11px/1.5 var(--display);letter-spacing:.06em;text-transform:uppercase;box-shadow:2px 2px 0 var(--shadow)}
.status.active{background:var(--accent);color:#fff}
.status.active:before{content:"";width:7px;height:7px;border-radius:50%;background:#7dffa8;box-shadow:0 0 6px #7dffa8}
.status.retired{background:var(--panel-2);color:var(--ink-3);border-style:dashed;box-shadow:none}
.leaguecard .cardtop{display:flex;align-items:center;justify-content:space-between;gap:10px}
.leaguecard.retired>.pad{opacity:.8}            /* the card dims, a crown does not */
/* A crown for each season you won, hung on the card's top-right corner, where the year was (17-18 Sep). */
.leaguecard{position:relative}
.leaguecard.crowned{overflow:visible}
.leaguecard.crowned .cardtop{padding-right:96px}
/* Room for the crown above and beside each card, so nothing overlaps and nothing scrolls sideways on a phone. */
/* ROOM FOR A CROWN ONLY WHERE THERE IS ONE. Every row used to reserve 48px above and 30px to the right in case a card
   wore a crown -- so rows with none floated in loose gaps and stopped 30px short of the header box above them. Seen
   18 Sep, the first time anybody looked at the page rather than at its CSS. */
.lobbycards{column-gap:22px;row-gap:22px;padding:22px 0 0 0}
.lobbycards:has(.crowned){column-gap:44px;row-gap:62px;padding-top:56px}
.lobbycards:has(>.crowned:last-child){padding-right:30px}
/* HUNG ON THE CORNER, under gravity (18 Sep: "imagine the corner was a hook ... make it rest on top of the corner.
   the right side ... leaning downward more against the right wall"). The crown's ring goes round the corner and
   the corner presses on the inside of the BACK rim -- the top of the dark opening, 50% across and 60% down the
   drawing. That point is pinned to the card's corner and the crown tips clockwise about it: the left of the ring
   lies on the box's top, the right swings down past the right wall, and the front band hangs below the corner. */
.crown{position:absolute;width:126px;right:calc(-63px + var(--i,0) * 84px);top:-76px;pointer-events:none;
transform:rotate(48deg);transform-origin:50% 60%;transition:transform .25s ease}
.crown svg{display:block;width:100%;height:auto;overflow:visible}
.crown{z-index:3;pointer-events:auto;filter:drop-shadow(-4px 5px 0 rgba(0,0,0,.5))}
/* Engraved lettering, both words the same colour: dark letters over a light lip. The app's own rounded display
   face, bold and a size up, because the decorative one was hard to read (18 Sep). */
.crown .crown-word{font:700 14px/1 var(--display);fill:#3d2302;letter-spacing:.05em;dominant-baseline:middle}
.crown .crown-word.crown-year{font-size:16.5px}
.crown .crown-word.crown-lip{fill:#fff3b8;opacity:.75}
.crown-glint{transform-box:fill-box;transform-origin:center;animation:crownglint 3.2s ease-in-out infinite}
@keyframes crownglint{0%,70%,100%{opacity:0;transform:scale(.3)}80%{opacity:1;transform:scale(1)}}
.leaguecard.crowned:hover .crown{transform:rotate(42deg)}          /* a small swing on its hook */
@media (prefers-reduced-motion:reduce){.crown{transition:none}.crown-glint{animation:none;opacity:.8}}
/* On a phone the crown hung ~50px past the screen's edge: a smaller crown, and more room beside the cards. */
/* BOTH selectors, on purpose. `.lobbycards:has(>.crowned:last-child)` above is more specific than a plain
   `.lobbycards:has(.crowned)`, so on a phone it WON and cut the crown's allowance from 60px back to 30px -- and the
   crown hung 25px off the side of the screen. checks/phone_width.py caught it the same hour it was written. */
@media (max-width:520px){.lobbycards:has(.crowned),.lobbycards:has(>.crowned:last-child){padding-right:60px}.crown{width:96px;right:calc(-48px + var(--i,0) * 64px);top:-58px}}
.leaguecard form,td form{display:inline}

/* ---------- a league before its draft: a different page, not an empty season ---------- */
.predraft{display:flex;flex-direction:column;gap:24px}
.predraft-hero{align-items:flex-start;padding:28px 26px}
.predraft-hero h1{font-size:clamp(36px,6vw,64px)}
.draftwhen{margin:0;font:600 17px/1.4 var(--display);color:var(--ink-2)}
.countdown{display:flex;flex-wrap:wrap;gap:12px;margin:6px 0 4px}
/* THE COUNTDOWN'S digits, and ONLY the countdown's. These were written as a bare `.tick`, which is also the name of
   every checkbox-with-a-label in the app -- so the days-to-the-draft styling (104px wide, a 52px number, a 3px border
   and a drop shadow) landed on the trade page's player checkboxes further down the file. That is what he was looking
   at on 20 Sep: "why are the trade player boxes so big, its ugly how much open space you are wasting by filling it
   with just a larger shape." Scoped to its own container; the checkbox rule at the top of the file wins again. */
.countdown .tick{min-width:104px;display:flex;flex-direction:column;align-items:center;gap:2px;padding:12px 16px 10px;
background:var(--panel-2);border:3px solid var(--edge);border-radius:16px;box-shadow:5px 5px 0 var(--shadow)}
.countdown .tick b{font:700 clamp(34px,5vw,52px)/1 var(--display);font-variant-numeric:tabular-nums;color:var(--accent)}
.countdown .tick span{font:600 10px/1.3 "IBM Plex Mono",monospace;letter-spacing:.14em;text-transform:uppercase;
color:var(--ink-3)}
.countdown .tick.live b{font-size:28px}
/* The draft order as take-a-number tickets: the bank queue, a little goofy on purpose. */
.tickets{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px}
.ticket{position:relative;display:flex;flex-direction:column;align-items:center;gap:2px;padding:14px 12px 12px;
background:var(--panel-2);border:2px dashed var(--line);border-radius:12px;text-align:center;
-webkit-mask:radial-gradient(circle 9px at 0 50%,transparent 98%,#000) left/51% 100% no-repeat,
radial-gradient(circle 9px at 100% 50%,transparent 98%,#000) right/51% 100% no-repeat;
mask:radial-gradient(circle 9px at 0 50%,transparent 98%,#000) left/51% 100% no-repeat,
radial-gradient(circle 9px at 100% 50%,transparent 98%,#000) right/51% 100% no-repeat}
.ticket .no{font:600 10px/1 "IBM Plex Mono",monospace;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3)}
.ticket b{font:700 44px/1 var(--display);color:var(--accent)}
.ticket .who{font:700 14px/1.3 var(--display);word-break:break-word}
.ticket .picks{font:500 11px/1.4 "IBM Plex Mono",monospace;color:var(--ink-3)}
/* The settled order: the same ticket, smaller. Twelve of them at full size filled half a screen to say twelve
   names, and the word "pick" was printed on every one of them (1 Oct). The panel heading says that once. */
.tickets.tight{grid-template-columns:repeat(auto-fill,minmax(108px,1fr));gap:10px}
.tickets.tight .ticket{padding:10px 8px 9px;gap:1px}
.tickets.tight .ticket b{font-size:30px}
.tickets.tight .ticket .who{font-size:13px}
/* Setting the order by hand: the rare way, folded away under the two that are not. */
.byhand{margin-top:14px;border-top:1px solid var(--line);padding-top:12px}
.byhand summary{cursor:pointer;font:700 15px/1.3 var(--display);padding:2px 0}
.handgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:8px 12px;margin:12px 0}
.handrow{display:flex;align-items:center;gap:8px}
.handno{flex:0 0 26px;text-align:right;font:700 15px/1 var(--display);color:var(--accent)}
.handrow select{flex:1 1 auto;min-width:0;font-family:inherit;font-size:13.5px;padding:6px 9px;
border:2px solid var(--line);border-radius:9px;background:var(--panel-2);color:var(--ink)}
.timeline{margin:0;padding:0;list-style:none;counter-reset:step;display:flex;flex-direction:column;gap:12px}
.timeline li{counter-increment:step;display:grid;grid-template-columns:34px 1fr;gap:2px 12px;align-items:start}
.timeline li:before{content:counter(step);grid-row:span 2;display:grid;place-items:center;width:30px;height:30px;
border-radius:50%;border:2px solid var(--edge);background:var(--accent);color:#fff;font:700 14px/1 var(--display)}
.timeline b{font:700 15px/1.3 var(--display)}
.timeline span{color:var(--ink-2);font-size:13.5px}

/* ---------- Open: a little dollar-bill portal (17 Sep) ----------
 * A bill with a basketball in its portrait oval (the blurry disc went, 17 Sep), in the league's own mode. It floods the screen on click,
 * like the game mode portals (--p-wipe comes from .portal-<mode>). */
.billportal{position:relative;display:inline-flex;flex-direction:column;align-items:center;gap:0;padding:0 0 4px;
border:0;background:none;cursor:pointer;text-decoration:none;font:inherit;color:inherit;vertical-align:middle}
/* THE NOTE'S SHAPE IS NOT SCOPED TO THE OPEN BUTTON, because the same object is also the mark in front
   of Bank's painted hole on Create a League (kit.mode_mark draws it for both). Only the hover and press
   below stay on .billportal -- those belong to the button, not to the note. */
/* HALF AGAIN AS BIG, BOTH MODES TOGETHER (his, 2 Nov: "still pretty small all things considered").
   112x50 was the size from when the mark was one object among several on a crowded card; alone above the
   OPEN pill it had no presence. Everything here is scaled by 1.4 rather than the box alone, so the note
   keeps its proportions -- a wider box with the same 11px corners and 32px ball would read as a different
   drawing, not a bigger one.
   THE HEIGHT IS WHAT THE BALL GETS. War's mark is a circle, so it can never be wider than the box is
   tall: 50 became 70, which is the whole of the increase it can take while the two modes share a
   footprint. That shared footprint is his rule from this morning and is worth more than another few
   pixels of ball. */
.bill{position:relative;display:block;width:156px;height:70px;border-radius:10px;border:4px solid #0b0714;
background:repeating-linear-gradient(45deg,rgba(31,122,77,.13) 0 3px,transparent 3px 10px),#dcefcf;
box-shadow:4px 4px 0 var(--shadow),inset 0 0 0 6px #dcefcf,inset 0 0 0 8px #2f7d4a;
transform:rotate(-4deg);transition:transform .15s ease,box-shadow .15s ease}
.bill .corner{position:absolute;font:700 15px/1 var(--display);color:#2f7d4a}
.bill .tl{top:10px;left:13px}
.bill .br{bottom:10px;right:13px}
.bill .hole{position:absolute;left:50%;top:50%;width:44px;height:44px;margin:-22px 0 0 -22px;display:block}
/* With no note above it the word has nothing to tuck under, so it stops pulling itself up into one. */
.billportal.plain .word{margin-top:0}

/* ---------- War's portal: a bullet, and the tear it leaves ----------
 * His, 29 Oct: "i would like the portal to the league a bullet that you click. the bullet shoots across
 * teh screen left to right and tears the screen in half into the new screen."
 * The bullet sits where Bank's banknote sits, so the card's shape is unchanged and only its object differs. */
/* THE SAME BOX AS .bill, 112x50 (his, 2 Nov: "the open icon should the exact same in size and aesthetic
 * no matter the league"). It was 40 tall against the banknote's 50, so the two marks were never the same
 * size and the War card read as the odd one. Both now share a footprint, a rotation, a drop-shadow and
 * the same hover and press -- the only thing that differs between leagues is the object itself. */
.slug{position:relative;display:block;width:156px;height:70px;
transform:rotate(-4deg);transition:transform .14s ease,filter .14s ease;
filter:drop-shadow(4px 4px 0 var(--shadow))}
.slug svg{display:block;width:100%;height:100%;overflow:visible}
.billportal:hover .slug,.billportal:focus-visible .slug{transform:rotate(0deg) translate(-2px,-2px);
filter:drop-shadow(5px 5px 0 var(--shadow))}
.billportal:active .slug{transform:translate(2px,2px);filter:drop-shadow(0 0 0 var(--shadow))}

/* THE SHOT ITSELF. Three parts, all fixed to the viewport and none of them clickable: the bullet, the
   white-hot streak it drags behind it, and the tear that opens along the line it flew.
   THE TEAR IS THE PAPER OF THE LEAGUE BEING OPENED -- the same `--p-wipe` the Bank flood uses -- so what
   comes through the rip is the screen you are about to land on, not a hole. It grows from nothing at the
   bullet's line and pushes outward both ways, which is the half-and-half he asked for; the jagged edges
   are what stop it reading as a shutter. */
.war-bullet{position:fixed;z-index:9999;left:0;width:92px;height:33px;pointer-events:none;
margin-top:-17px;transform:translateX(-160px);
filter:drop-shadow(0 0 12px rgba(255,186,84,.95)) drop-shadow(0 0 3px #fff)}
.war-streak{position:fixed;z-index:9998;left:0;height:5px;width:0;pointer-events:none;margin-top:-3px;
background:linear-gradient(90deg,transparent 0%,rgba(255,196,110,.25) 40%,rgba(255,226,170,.9) 85%,#fff 100%);
border-radius:3px}
/* THE PAPER OF THE LEAGUE, lying under both curtains -- what the zip opens onto. */
.war-paper{position:fixed;inset:0;z-index:9995;pointer-events:none}
/* THE TWO CURTAINS: copies of the page as it stands, clipped to a half each, which the zip cuts into and
   which then leave upward and downward. `overflow:hidden` keeps a copy inside its own frame, and the
   inner sheet carries the page's scroll offset so the copy lines up with what it is covering. */
.war-curtain{position:fixed;inset:0;z-index:9996;pointer-events:none;overflow:hidden;
will-change:transform,clip-path}
.war-curtain-page{position:absolute;left:0;width:100%}
/* Calm, and a device asking for less motion, get the paper and none of the theatre -- the same contract
   every other animation here keeps. */
@media (prefers-reduced-motion:reduce){.war-bullet,.war-streak{display:none}}
.billportal .word{margin-top:-8px;position:relative;padding:2px 10px;border-radius:7px;border:2px solid #0b0714;
background:var(--panel);color:var(--ink);font:700 11px/1.4 var(--display);letter-spacing:.08em;text-transform:uppercase;
box-shadow:2px 2px 0 var(--shadow)}
.billportal:hover .word,.billportal:focus-visible .word{background:var(--accent);color:#fff}
.billportal:hover .bill{transform:rotate(0deg) translate(-2px,-2px);box-shadow:5px 5px 0 var(--shadow),inset 0 0 0 4px #dcefcf,inset 0 0 0 6px #2f7d4a}
.billportal:active .bill{transform:translate(2px,2px);box-shadow:inset 0 0 0 4px #dcefcf,inset 0 0 0 6px #2f7d4a}
.billportal:focus-visible{outline:3px solid var(--accent);outline-offset:4px;border-radius:8px}

/* ---------- Live: a week as it unfolds ---------- */
/* ---------- messages ---------- */
.bell{display:inline-flex;align-items:center;gap:5px}
.bell svg{display:block}
.unread{display:inline-flex;min-width:18px;justify-content:center;padding:0 5px;border-radius:999px;background:var(--alert);
color:#fff;font:700 11px/18px var(--display)}
.msgs{display:flex;flex-direction:column;gap:8px;max-height:60vh;overflow:auto}
.msg{border:2px solid var(--line);border-radius:12px;padding:7px 11px;background:var(--panel-2);max-width:min(640px,100%)}
.msg.mine{align-self:flex-end;border-color:var(--accent);background:var(--accent-soft)}
.msg-text{white-space:pre-wrap;overflow-wrap:anywhere}
.msg-send textarea{width:100%;font-family:inherit;font-size:14px;padding:8px 11px;border:2px solid var(--line);border-radius:10px;
background:var(--panel-2);color:var(--ink);resize:vertical}

.edited{display:block;color:var(--ink-3);font-size:10.5px;line-height:1.2;margin-top:2px}
/* polls */
.poll{display:flex;flex-direction:column;gap:6px;padding:4px 0}
.poll-q{font:700 16px/1.3 var(--display)}
.poll-form{display:flex;flex-direction:column;gap:4px}
.poll-opt{display:flex;gap:8px;align-items:center;padding:5px 8px;border:2px solid var(--line);border-radius:9px;cursor:pointer}
.poll-opt:hover{border-color:var(--accent)}
.poll-bar{position:relative;display:flex;justify-content:space-between;gap:8px;padding:5px 9px;border:2px solid var(--line);
border-radius:9px;overflow:hidden}
.poll-bar.mine{border-color:var(--accent)}
.poll-bar span,.poll-bar b,.poll-bar .pmenu{position:relative}
.poll-fill{position:absolute;inset:0 auto 0 0;background:var(--accent-soft)}
/* THE COUNT IS THE BUTTON (his, 28 Sep). Every name under every bar made the results twice as tall as the
   question; press the number instead and the names come up beside it. The box is FIXED and kit.NAME_MENUS
   moves it to <body> when it opens -- the bar it lives in is overflow:hidden, and the panel it lives in is a
   spill, either of which would clip it where it stands. Above the spill (60) and the poll overlay (100). */
.poll-bar .pcount{list-style:none;cursor:pointer;font:800 13px/1.5 var(--display);padding:0 9px;
border-radius:999px;border:2px solid var(--line);background:var(--panel-2);color:var(--ink)}
.poll-bar .pcount::-webkit-details-marker{display:none}
.poll-bar .pcount:hover,.pmenu[open]>.pcount{background:var(--accent);border-color:var(--accent);color:#fff}
.pvoters{position:fixed;z-index:110;min-width:132px;max-width:min(260px,80vw);display:flex;flex-direction:column;
gap:3px;padding:8px 10px;background:var(--panel);border:3px solid var(--edge);border-radius:12px;
box-shadow:5px 5px 0 var(--shadow);font:600 13px/1.45 var(--display);color:var(--ink)}
.pvoters span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media (max-width:560px){.pvoters{left:12px;right:12px;max-width:none}}
.poll-msg{max-width:min(640px,100%)}
.poll-new{margin-top:12px}
.poll-new>summary{cursor:pointer;font-weight:600}
.checkline{display:flex;gap:8px;align-items:center;font-size:13.5px}
.poll-overlay{position:fixed;inset:0;z-index:100;background:rgba(5,4,10,.78);display:flex;align-items:center;
justify-content:center;padding:16px;overflow:auto;animation:pollarrive .3s ease-out both}
.poll-overlay-card{background:var(--panel);border:3px solid var(--edge);border-radius:18px;box-shadow:6px 6px 0 var(--shadow);
padding:18px 20px;width:min(520px,100%);max-height:100%;overflow:auto;
animation:pollfly .62s cubic-bezier(.2,1.15,.3,1) both}
/* IT FLIES OUT OF THE MESSAGE BUBBLE in the corner (his, 28 Sep: "like an animation when you enter a league that
   open from the messages menu and you have to do the poll"). The distance is the real one: the bubble is 96px
   wide, 18px from the right and 16px from the bottom, so its middle sits 66px in and 64px up -- which, from the
   middle of the screen, is 50vw and 50vh less those. So the card starts exactly where the bubble is, the size of
   a full stop, and springs open into the page. */
@keyframes pollarrive{from{opacity:0}to{opacity:1}}
@keyframes pollfly{
0%{opacity:0;transform:translate(calc(50vw - 66px),calc(50vh - 64px)) scale(.07) rotate(-12deg)}
55%{opacity:1}
100%{opacity:1;transform:translate(0,0) scale(1) rotate(0)}}
/* The bubble itself, on the card: it says where this came from without a word. */
.poll-from{display:flex;align-items:center;gap:11px;margin:0 0 4px}
.poll-from .msgart{width:40px;height:auto;display:block;flex:0 0 auto;animation:pollwake .5s ease .5s both}
.poll-from .eyebrow{margin:0}
@keyframes pollwake{0%{transform:scale(.4) rotate(-16deg)}65%{transform:scale(1.12) rotate(5deg)}100%{transform:scale(1) rotate(-3deg)}}

/* ---------- alerts: what needs the manager now ---------- */
.alerts{border:2px solid var(--alert);background:var(--alert-soft);border-radius:12px;padding:8px 14px}
.alerts ul{margin:4px 0 0;padding-left:18px;display:flex;flex-direction:column;gap:4px}
.alerts .eyebrow{color:var(--alert)}

/* ---------- the draft room ---------- */
.inline-field{flex-direction:row;align-items:center;gap:8px}
/* The room IS the board (21 Sep): drafted players are hidden rows, and each row's last cell holds the buttons. */
table.board tr[hidden]{display:none}
/* EVERY ROW THE SAME HEIGHT (his, 29 Sep: "all the rows need to be the same size first of all"). A row grew
   whenever something in it wrapped -- a long name, a news flag, the YOUR PICK tag -- so the board rippled as
   you scrolled it. Nothing in a player's row wraps now, and the height is stated rather than left to whatever
   the tallest cell happened to be. */
table.board tbody tr[data-id]>td{height:36px;padding-top:0;padding-bottom:0;vertical-align:middle}
/* EVERY COLUMN TAKES WHAT IT NEEDS AND NO MORE, so the slack goes to the names. `width:1%` on a table cell is
   the shrink-to-fit idiom: the browser gives it its content's width and hands the rest to the cell that asked
   for 100%, which is the Player column. */
table.board tbody td:not(.c-name),table.board thead th:not(.c-name){width:1%}

/* ---------- ON A PHONE THE NAME STAYS PUT (22 Oct) ----------------------------------------------------
   The board is 1092px wide inside a 390px screen, so reading any column but the first few means scrolling
   sideways -- and MEASURED, scrolling 400px took the first cell of a row from 21px to MINUS 379. The name
   left the screen entirely, so every number you had scrolled to see belonged to nobody.

   The three cells before the numbers travel together: the star, the rank and the name. Freezing the name
   alone would leave the star and the rank sliding out from under it, and the star is a control you have to
   be able to reach. Their offsets are stated here because a sticky cell needs to know where its left edge
   is, and they are the widths the board actually renders: 34 and 36 at this size.

   AND THE NAME IS CAPPED HERE AND ONLY HERE. At its desktop width of 191px the three of them would take
   266px of a 390px screen and leave 124px of board to look at, which is not a fix. At 112px the frozen
   block is 182px and rather more than half the screen is still the thing you came to read. */
@media (max-width:620px){
  /* THE WIDTHS ARE STATED, because the offsets depend on them. A sticky cell is told where its left edge
     goes as a number, so if the two columns in front of it are any other width the name lands beside its own
     place rather than on it -- measured on the draft board, which has its own column widths: the name
     slipped 96px to 91px, almost frozen and visibly wrong. Fixed widths here make the three offsets exact on
     every board rather than on the one I happened to measure. */
  table.board tbody td.c-fav,table.board thead th.c-fav{position:sticky;left:0;z-index:3;
  width:34px;min-width:34px;max-width:34px}
  table.board tbody td.c-rank,table.board thead th.c-rank{position:sticky;left:34px;z-index:3;
  width:36px;min-width:36px;max-width:36px}
  table.board tbody td.c-name,table.board thead th.c-name{position:sticky;left:70px;z-index:3;
  width:112px;min-width:112px;max-width:112px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* OPAQUE, OR THE COLUMNS SCROLL THROUGH THEM. A sticky cell keeps its place and nothing else; without a
     background of its own the rows passing underneath are drawn straight through the name. */
  table.board tbody td.c-fav,table.board tbody td.c-rank,table.board tbody td.c-name{background:var(--panel)}
  /* The heads are already sticky to the TOP, so the three corner cells are sticky both ways and have to sit
     above the rest of the head as well as above the body. */
  table.board thead th.c-fav,table.board thead th.c-rank,table.board thead th.c-name{z-index:6;
  background:var(--ink)}
  /* An edge where the frozen block ends, so it reads as a held column rather than as text overlapping. */
  table.board tbody td.c-name,table.board thead th.c-name{
  box-shadow:1px 0 0 color-mix(in srgb,var(--ink) 22%,transparent)}
}
table.board td.dr-act{white-space:nowrap;text-align:right;width:1%}
table.board td.dr-act .btn,table.board td.dr-act .ghost{margin-left:4px}
/* The draft board (21 Sep): teams across, rounds down, each square tinted by the player's position. */
table.draftgrid{border-collapse:separate;border-spacing:4px;table-layout:fixed;width:100%;min-width:980px}
table.draftgrid th{font:700 11px/1.2 var(--mono,monospace);color:var(--ink-2);text-align:left;padding:4px 6px;
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:transparent}
table.draftgrid th.dbr{width:26px;text-align:center;color:var(--ink-3)}
/* THE WAY THE ROUND RUNS, and nothing louder (his, 3 Oct: "not invasive at all, just for directional help when
   looking back"). Under the round's own number, half its weight, and it never takes a column of its own. */
table.draftgrid th.dbr .dbdir{display:block;font-style:normal;font-size:11px;line-height:1.1;opacity:.6}
table.draftgrid th.mine{color:var(--ink);background:var(--accent-soft);border-radius:8px}
/* EVERY SQUARE IS THE SAME SQUARE, taken or not (his, 30 Sep: "they should always the same size that a player
   takes up once they have been drafted"). Two things made them differ: a taken square had ONE border (4px on the
   left) where an empty one had four (2px all round), so the boxes were different sizes; and a taken square was
   sized by its contents, so a long name with a club and a "made by" line pushed its whole row taller than the
   empty rows beside it. Now the borders match on every edge, the height is stated, and the contents are clipped
   rather than allowed to push -- a grid of picks is a shape you read at a glance, and a row that grows when
   somebody is taken makes the shape move under you. */
table.draftgrid td.dbcell{background:color-mix(in srgb,var(--pos) 16%,var(--panel));
border:2px solid transparent;border-left:4px solid var(--pos);height:58px;overflow:hidden;
border-radius:8px;padding:5px 6px;vertical-align:top;font-size:11.5px;line-height:1.3;text-align:left}
table.draftgrid td.dbcell.empty{background:transparent;border:2px dashed var(--line-soft);
border-left:4px dashed var(--line-soft)}
table.draftgrid td.dbcell.mine{box-shadow:inset 0 0 0 2px var(--ink)}
/* An EMPTY square of yours is a square you have not used yet, not a pick to look at. The white ring belongs to a
   name; on an empty grid it drew a column of fourteen loud pills saying nothing, over a header already lit. */
table.draftgrid td.dbcell.mine.empty{box-shadow:none;border-color:var(--accent)}
/* A long name wraps rather than being cut off: "S. Gilgeou..." told nobody who went 1.05. */
table.draftgrid td.dbcell{white-space:normal}  /* every table cell is nowrap (tbody td); a square must wrap */
table.draftgrid td.dbcell b{display:block;hyphens:auto;overflow-wrap:break-word;color:var(--ink)}
/* A long surname shrinks rather than snapping in half: "Antetokou-/nmpo" read as somebody else entirely. */
table.draftgrid td.dbcell b.n11{font-size:10px}
table.draftgrid td.dbcell b.n13{font-size:8.8px;letter-spacing:-.2px}
table.draftgrid .dbn,table.draftgrid .dbm{display:block;font-size:10.5px;color:var(--ink-2);white-space:nowrap;
overflow:hidden;text-overflow:ellipsis}

/* ---------- practice: a week played by hand ---------- */
form.inline{display:inline-flex;margin:0}
.prow{display:flex;flex-direction:column;gap:6px;padding:4px 0;border-bottom:1px solid var(--line-soft)}
.prow .lrow{border-bottom:none}
.pacts{display:flex;flex-wrap:wrap;gap:6px;padding-left:80px}     /* under the name, clear of the slot chip */
.pacts .btn,.pacts .ghost{padding:4px 10px;font-size:12.5px}
.stack{display:flex;flex-direction:column;gap:18px}
.coinpick{display:inline-block}
.coinpick>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;padding:4px 10px;font-size:12.5px;
font-weight:600;border:2px solid var(--edge);border-radius:10px;background:var(--panel);color:var(--ink)}
.coinpick>summary::-webkit-details-marker{display:none}
.coinpick>summary:hover,.coinpick[open]>summary{background:var(--accent);color:#fff}
.coinpick[open]{display:flex;flex-wrap:wrap;gap:6px;width:100%}
.bid{width:64px;font-family:"IBM Plex Mono",monospace;padding:5px 7px;border:2px solid var(--line);border-radius:8px;
background:var(--panel-2);color:var(--ink)}
@media (max-width:520px){.pacts{padding-left:0}}

/* ---------- my matchup: you and your opponent, each player with his week of games ---------- */
.mmgrid{display:grid;grid-template-columns:1fr 1fr;gap:18px;align-items:start}
/* THE TWO COLUMNS STACK EARLIER THAN THEY USED TO. Each one now carries a seven-day calendar, so half of a
   1000px window is not enough for it -- the day columns squeeze until the clubs and scores sit on top of each
   other. Below this width the matchup is one column above the other, each with room for its week. */
@media (max-width:1180px){.mmgrid{grid-template-columns:1fr}}
/* ONE COLUMN PER TEAM, each a box with sections inside it -- coins, starting, bench (19 Sep: "there should be to
   columns, one for each team, where you can move players, the columns can have different sections"). Four tall
   panels down the page became two beside each other, which is the vertical room he was objecting to. */
/* THE SAME HEIGHT ON BOTH SIDES, unless one bench is longer than the other -- his rule, 19 Sep. That only holds if
   everything above the bench is identical in shape: one head line of a fixed height (the Play a Coin link lives in
   it, so the coin strip below is the same with or without it), a one-line note, and rows that do not grow when they
   carry a button. Anything that wraps on one side and not the other breaks it. */
.panel.side>h2{display:flex;align-items:center;gap:10px;min-height:34px}
.sidetotal{margin-left:auto;font:700 17px/1 var(--display);font-style:normal;color:var(--accent)}
.sidehead{margin:0;padding:5px 14px 1px;font:600 9.5px/1.4 "IBM Plex Mono",monospace;letter-spacing:.13em;
text-transform:uppercase;color:var(--ink-3);border-top:2px dashed var(--line-soft)}
.panel.side>.note{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.panel.side .pursebar{border-bottom:none;padding:6px 14px;justify-content:center}
/* NO ENTRANCE HERE. The coins drop in one by one on the Coin Purse, where they are the subject. On the matchup page
   -- "this is the page people look at the most" -- that meant five ghosts fading in on every load and every live
   refresh, which is what he photographed and could not name. They are simply there. */
.panel.side .coinrow .coin{animation:none}
.panel.side .lside{padding-top:2px;padding-bottom:6px;gap:0}
.panel.side .prow{padding:1px 0}
/* One word, and small with it: the score it used to carry is in the chip beside it (19 Sep, "the bank boxes make
   them to big"). */
.btn.tiny,.ghost.tiny{padding:2px 10px;font-size:12px;line-height:1.35;border-width:2px}
/* Shorter than the heading's own text, so a column with this link in its head is not one line taller than a column
   without it -- which is how "the same size unless a bench is longer" quietly stops being true. */
.ghost.tiny{padding:1px 9px;font-size:11px;font-weight:600}
/* The hero above them says the date and the clock and nothing a column says again -- so it is a strip, not a page.
   Every one of these numbers is room that used to be spent saying the pairing a second and third time. */
.hero.tight{padding:12px 18px 10px;gap:4px;width:100%;max-width:1120px;margin-left:auto;margin-right:auto}
.hero.tight .pairstrip{margin:4px 0 0;padding-bottom:4px}
.hero.tight .meta{margin:0}
.hero.tight .stepper{margin:0}
/* THE GAMES HEADER, TIGHT (his, 7 Oct): "tighten the header. its way too much open space." The sport and the
   season were a line of their own above a centred slider, in a box wide enough for a page of writing -- three
   rows of air for two short facts. They share one row now: the words to the left, the slider in the middle of
   the panel rather than of the text beside it, and the panel no wider than the list it heads. */
.gamehead{max-width:720px;padding:8px 14px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
gap:10px}
.gamehead .eyebrow{margin:0;justify-self:start}
.gamehead .stepper{grid-column:2;justify-self:center}

/* ---------- ONE GAME: every player in it, counting live (7 Oct) ---------- */
/* THE STATE GOES UNDER THE SCORES, CENTRED ON THEM. His, 12 Oct, with a photograph of "WAS 72 vs BKN 73
   Halftime": "lets move the tyime left or text next to the score right under it and center it. this way, they
   are centered with the scores below."
   Beside the score it was a fourth thing on a line of three, which pushed the pair of scores off the middle of
   the panel -- so the header's centre and the card's centre underneath disagreed by the width of the word, and
   "Halftime" is a good deal wider than "Q1". On its own line the scores sit centred whatever the state happens
   to say, and the state sits centred under them. `flex-basis:100%` is what breaks the line: the row is allowed
   to wrap, and this item is told to take all of it. */
.gamehead .gtop{grid-column:2;justify-self:center;display:flex;align-items:center;gap:14px;
flex-wrap:wrap;justify-content:center}
.gamehead .gtop .gwhen{flex-basis:100%;text-align:center;margin-top:1px}
.gamehead .gback{grid-column:3;justify-self:end}
/* THE HEADER STACKS ON A PHONE. Three columns of [words][score][back] want about 420px between them; a phone
   gives the panel 305, and a grid does not wrap -- it overflows, and `.hero` is `overflow:hidden` so that it can
   crop the banner art, so the Back link was sitting 41px past the edge where nothing could press it. Measured at
   390px: the link's right edge reached 374 in a 333px box. Stacked, each part gets the full width and the
   question stops existing -- the same answer as the two sides of the card below. */
@media (max-width:620px){
  .gamehead{grid-template-columns:minmax(0,1fr);row-gap:8px;justify-items:center}
  .gamehead .eyebrow,.gamehead .stepper,.gamehead .gtop,.gamehead .gback{grid-column:1;justify-self:center}
}

/* ONE LINE UNDER THE NAME, ACROSS. His, 7 Oct: "make it one line that goes from under the name across ... we
   can compact this a vertically." The matchup card's box is a small square because a strip of the player's
   OTHER games sits beside it -- that is where a game is chosen to bank. Nothing is chosen here, so there is no
   strip, the figures get the whole width, and the row drops from about 100px to about 50. Forty men on a card
   you scroll for a minute is not a card you watch. The matchup card is deliberately untouched: it is cramped
   because it has to be. */
.grow2{display:grid;grid-template-columns:minmax(0,1fr) 32px minmax(0,1fr);align-items:center;gap:10px;
padding:9px 12px;border-bottom:1px solid var(--line-soft)}
/* THE GAME CARD, AND ONLY THE GAME CARD, IS WIDER. His, 9 Oct: "the stats need slightly more seperation. its
   tight." Every column on the row is a fixed width now, so width added to the card cannot go anywhere except
   the eleven gaps -- 160px more comes out at about 7px a gap, which roughly doubles the air between figures and
   moves no column.
   TWO THINGS IT IS CAREFUL NOT TO BREAK, both his. The header stays exactly as long as the card (6 Oct: "the
   header should be the same length as the matchup box takes up"), so both are raised together rather than one.
   And `.mwrap` is shared with the MATCHUP card, which he cut down on 20 Sep -- "its way too wide ... you dont
   have to take up the whole screen every single time you make a box" -- so this is scoped to a page that
   actually holds a game card and the matchup page is untouched. */
main:has(.gcard){max-width:1316px}
main:has(.gcard) .mwrap,main:has(.gcard) .hero.tight{max-width:1280px}
.grow2:last-child{border-bottom:none}
.gnum{justify-self:center;font:700 10px/1 "IBM Plex Mono",monospace;color:var(--ink-3)}
/* ONE GRID PER SIDE: the name, the score, then the line. Thirteen columns, the same ones named once in the
   header band at the top -- which is what lets every label be said once instead of forty times, and what makes
   the club chip beside each name redundant: every man on the left plays for the left club by definition.
   The eight counting stats are one or two digits; the three shooting ones are "3-14", so those get half as much
   again and nothing is allowed to wrap. */
/* FIXED widths for the figures, not fractions. Each row is its own grid, so proportional columns are worked out
   per row and nothing lines up with the header or with the row above; worse, .58fr was narrower than "22.7" and
   the figures ran into each other. Fixed columns are the same on every row by definition, which is the whole
   point of putting the categories at the top. The name takes whatever is left. */
/* ROOM FOR EVERY SINGLE NAME (his, 9 Oct). MEASURED, not estimated: at a 1280px window the name had 75px and
   "F. VanVleet" -- about the shortest a name gets -- needed 89, so even the easy ones were being cut to an
   ellipsis and the long ones showed nothing but the dots. "S. Gilgeous-Alexander" wants about 175.
   Two things buy it: the figures give back a few pixels each (they are sized for two digits and "5-12", and a
   totals row's three digits still fit), and below 1200px the two sides stack rather than squeeze, which deletes
   the awkward band instead of surviving it. The 130px floor is the guarantee rather than the mechanism -- if a
   width nobody thought of turns up, a name is what survives it.
   160px, AND THE 135 IT WAS IS A LESSON. On 9 Oct I "proved" 135 was enough by measuring how wide the name's
   TEXT paints -- 125px for the longest in the league -- with a hidden probe span. The element it sits in is
   wider than its text: it carries padding and, during a game, the live dot. Measured properly, the same name
   needs 153px, and it was being clipped on any card where he happened to be playing. A measurement of the
   thing beside the thing is not a measurement.
   PROVED, not reasoned: the longest name a roster holds, "S. Gilgeous-Alexander", renders at 125px against 137px
   of room, and nothing is cut at 1920, 1440, 1280, 1201 or 1100. The figures are measured the same way rather
   than eyeballed -- see checks/game_names.py, which fails if any name on the card is ever clipped again. */
/* MINUTES GETS ITS OWN WIDTH, and that was learned by measuring rather than by looking. Trimming all eight
   counting columns alike read fine in a photograph and had quietly cut twenty-one figures: minutes is the one
   column carrying a decimal, so "24.4" needs four characters where "17" needs two. Everything else on this row
   is a whole number. */
/* THE NAME TAKES WHAT A NAME NEEDS AND NOT A PIXEL MORE. It was the one flexible column, so it swallowed every
   spare pixel on the row -- his, 9 Oct: "the names are know taking up too much space ... it seems to be
   compressing the stats together a good bit." On a wide window that left about 95px of nothing between the name
   and the first figure while the figures themselves sat on top of each other, which is the layout spending its
   slack on the one column that had already finished. Fixed at 135px (the longest name a roster holds measures
   125), and `space-between` hands the leftover to the ELEVEN GAPS instead, so the figures breathe and the wider
   the window the more they breathe. Every row is the same width with the same fixed columns, so the gaps come
   out identical and the columns still line up -- which is the whole point of a box score. */
/* THE SHOOTING COLUMNS HOLD SIX CHARACTERS, not five. At 34px they fitted a club's "41-88" and cut "43-100"
   the moment a side attempted a hundred shots -- checks/game_names caught exactly one cell on the card, at
   every width it measures. IBM Plex Mono at 11px is 0.6em an advance, so six figures need 39.6px. 40px.
   The realistic worst case really is six: a club makes at most about sixty and attempts at most about a
   hundred and twenty, so "60-120" is the widest line a real game produces. Same reasoning the fantasy column
   already carries above -- it is sized for the TOTALS row, because that is the widest thing in it. */
.gteam{display:grid;
grid-template-columns:160px 54px 28px repeat(7,22px) repeat(3,40px);align-items:center;
justify-content:space-between;
gap:2px;background:var(--box-grey);border:1.5px solid var(--box-edge);border-radius:8px;padding:7px 8px;
box-sizing:border-box}
/* The right-hand side is a TRUE mirror: its cells are emitted in reverse, so the name lands on the outer edge
   and the two sets of figures face each other down the middle. The widths are reversed with them, or the name
   would sit in a column sized for a shooting line. */
/* The fantasy column keeps its full width because the TOTALS row is the widest thing in it -- a club comes to
   "317.50", six characters, where a player's "38.75" is five. Narrowing it cut the one figure the row exists for. */
/* THE AWAY SIDE IS NOT MIRRORED ANY MORE (his, 11 Oct: "i feel like it should always read left to right").
   Its columns are the same widths in the same order as the home side's, because its cells are no longer
   emitted backwards -- see side_of in week_pages for why he changed his mind and why he was right. */
/* BOTH SIDES, THE SAME WIDTHS. They are two separate grids, so sizing these columns to their content would let
   one side grow when its club happens to attempt a hundred shots and leave the other where it was -- a card
   that goes lopsided on the strength of one game. A fixed width keeps the two halves facing each other. */
.gteam.right{grid-template-columns:160px 54px 28px repeat(7,22px) repeat(3,40px)}
/* A BUBBLE ON EACH SHOOTING FIGURE. They are the only cells on the row holding two facts -- "5-12" is makes AND
   attempts -- so without a mark they read as another count, and a column of them reads as numbers that happen to
   have a dash in. His, 9 Oct: "shooting stats should have a little bubble around them so we can recognize teh
   division."
   Three bubbles rather than one plate behind all three, and that is forced rather than chosen: the row hands its
   spare width to the gaps now, so the space between these columns is wide and not a fixed number, and nothing
   can bridge it. Separately, a lighter wash rather than one of the panel colours -- #1a1626 on #221d30 was
   technically a different colour and invisible at arm's length, which is the same as not being there. */
.gteam .gshot{background:rgba(255,255,255,.08);border-radius:5px;padding:2px 0;
box-shadow:0 0 0 1px rgba(255,255,255,.05)}
/* NOT ON THE HEADINGS. They carried a fainter version so the group would be marked where it is named as well as
   where it is read, and he was unsure of it on sight -- "i also dont know how to feel about the bubbles on teh
   category names." A heading is a label, not a figure, and the thing being marked is the figure. */
.ghead .gshot{background:none;box-shadow:none;padding:0}
/* HOME OR AWAY, said rather than implied. The side carries it now, but a word costs one line and removes the
   need to have learned the convention. */
.gside{font:700 8.5px/1 "IBM Plex Mono",monospace;letter-spacing:.1em;text-transform:uppercase;
color:var(--ink-3);margin-right:7px}
.gteam>*{text-align:center;white-space:nowrap;min-width:0;overflow:hidden}
.gteam b{font:700 11px/1 "IBM Plex Mono",monospace;color:var(--ink);font-variant-numeric:tabular-nums}
.gteam .gname{text-align:left;text-overflow:ellipsis;font:700 12.5px/1 var(--display),system-ui;color:var(--ink);
padding-right:6px}
/* The name leads on both sides now, so it is aligned the same way on both. */
.gteam.right .gname{text-align:left;padding-right:6px;padding-left:0}
.gteam .gpts{font-size:13.5px;font-weight:800}
/* The header band: the same grid and the same widths, labels where the figures go, so a column's name is always
   directly above what is in it -- on both sides, since the header mirrors with the rows. */
/* The group's heading row: no plate behind it, so it reads as a rule across the card rather than as another
   player. The group's word sits where a name would, in the slot freed by dropping the "Name" heading.
   FROZEN (his, 7 Oct): "the whole header needs its panes frozen so that we scroll down to totals." A box score
   is read by comparing a figure against its column, and a column you cannot see the name of is a column you are
   counting across to. It sticks under the app's own bar (56px of it) and under the card's scoreline, so three
   things are on screen at once: who is playing, what the score is, and what each column holds. Two of these
   rows exist, Starters and Bench, and each takes over as its own group comes up -- which is why the categories
   being repeated on the Bench row matters. */
.ghead{background:var(--panel-2);border-bottom:1px solid var(--line);padding-top:2px;padding-bottom:2px;
position:sticky;top:104px;z-index:3}
.gcard .mheads{position:sticky;top:56px;z-index:4;background:var(--panel)}
/* `clip`, NOT the `hidden` every other panel uses. Both clip the rounded corners, but `hidden` makes the panel a
   scroll box, and a sticky child sticks to the nearest scroll box -- so the headers above were pinned to a panel
   that never scrolls, which looks exactly like sticky being ignored. Measured, not guessed: scrolled 664px, the
   header sat at -379 with `position: sticky` applied to it the whole time. `clip` does not create one. */
.gcard{overflow:clip}
.ghead .gteam{background:none;border-color:transparent;padding-top:3px;padding-bottom:3px}
.ghead .gbandword{font:700 10px/1 "IBM Plex Mono",monospace;letter-spacing:.12em;text-transform:uppercase;
color:var(--ink-2)}
/* The totals close the card. Same thirteen columns as everything above, so each sum sits under what it is the
   sum of -- which is the only reason a totals row is worth having. Marked off by a rule rather than a colour,
   because it is the end of the list, not a different kind of thing. */
.gtotals{border-top:2px solid var(--line);border-bottom:none}
.gtotals .gteam{background:none;border-color:transparent}
.gtotals .gbandword{font:700 10px/1 "IBM Plex Mono",monospace;letter-spacing:.12em;text-transform:uppercase;
color:var(--ink-3)}
.gtotals b{color:var(--ink);font-weight:800}
/* The FPTS heading when it is a menu: it has to look pressable without growing the row it sits in. */
.scorepick>summary{list-style:none;cursor:pointer;font:700 8.5px/1 "IBM Plex Mono",monospace;letter-spacing:.07em;
color:var(--ink-2);text-decoration:underline dotted;text-underline-offset:3px}
.scorepick>summary::-webkit-details-marker{display:none}
.scorepick[open]>summary{color:var(--accent)}
.tmenu-box a[aria-current]{color:var(--accent);font-weight:700}
.ghead i{font:700 8.5px/1 "IBM Plex Mono",monospace;font-style:normal;color:var(--ink-3);letter-spacing:.07em;text-transform:uppercase}
.gband{padding:7px 14px;background:var(--panel-2);color:var(--ink-3);font:700 10px/1 "IBM Plex Mono",monospace;
letter-spacing:.12em;text-transform:uppercase}
/* STACK AT 1200, NOT 900. Two sides of thirteen columns each need about 1160px between them before a name has
   its full room; squeezing them into less is where the ellipsis came from. Stacked, each side has the whole
   width and the question never arises -- so the awkward band simply stops existing. */
@media (max-width:1200px){
  .grow2{grid-template-columns:minmax(0,1fr);row-gap:8px}
  .gnum{display:none}
}
/* ON A PHONE THE CARD SCROLLS SIDEWAYS, AND THE FROZEN HEADER IS WHAT PAYS FOR IT. His, 11 Oct: "lets just make
   sure everything that doesnt fit fully on the phone browser has a scroll wheel ... it might be a bug but it cuts
   off." It was not a bug, it was this file: one side of this card needs 530px and a phone gives it about 333, so
   200px of every one of the forty rows -- the three shooting columns and half the counting ones -- was simply
   absent, and `clip` above cuts content off WITHOUT making a scroll box, which is the exact property it was
   chosen for. Stacking at 1200 gave each side the whole width; on a phone the whole width is not enough, and
   there is nothing left to take.
   `auto` makes the card a scroll box, and a sticky child sticks to its nearest scroll box -- so the two header
   rows stop freezing at this width. They are set `static` rather than left to half-work: a header pinned to the
   top of a box that only scrolls sideways reads as a bug, where a header that scrolls with its figures reads as
   a table. The trade is deliberate and it is the right way round -- a figure you cannot reach at all beats a
   label that stays put -- and both header rows are repeated per group (Starters, Bench) anyway, so a phone is
   never more than twenty rows from one. */
@media (max-width:620px){
  .gcard{overflow-x:auto}
  .gcard .mheads,.gcard .ghead{position:static}
}
/* ONE TEAM, THEN THE OTHER TEAM -- the phone reading of this card (his, 12 Oct). Stacking at 1200 turns a row
   built as a PAIR into two clubs interleaved all the way down: "it'll be like the point guard from one team on
   top of the point guard for the other ... it should just be one team on top of another team."
   `display:contents` dissolves each pair so its two sides become children of `.mrows` directly, and `order`
   then gathers every home side ahead of every away side -- items sharing an order value keep their document
   order among themselves, so within each club the Starters band, its five, the Bench band, its rest and the
   totals all stay in the right sequence. ONE copy of the markup, regrouped, rather than a phone copy and a
   desktop copy drifting apart the way the browser's board did.
   The headings take 1 and 3 so each lands at the top of its own block. `.gnum` is already gone at 1200, which
   is why the pair dissolving into two parts rather than three is safe.
   What `.grow2` was carrying has to be handed over, because an element set to `display:contents` draws nothing:
   its padding, its dividing line and the band behind a header all belonged to it. The gap replaces the padding,
   and the two rules below put the band and the totals rule on the sides themselves. */
/* Above a phone the two clubs are side by side and each column is already headed by its badge, its record and
   the word HOME or AWAY, so a second label would be the same fact twice on one screen.
   THIS LINE COMES FIRST, and that is not tidiness. It ties with the `display:flex` below on specificity, so
   whichever is written later wins -- and written later, `none` would hide the headings on the one screen that
   needs them, inside the media query that was put there to show them. */
.gblock{display:none}
@media (max-width:620px){
  .mrows{gap:7px}
  .grow2{display:contents}
  .gblock{display:flex;align-items:center;gap:9px;order:1;margin:2px 0 0;padding:7px 9px;
  border-radius:8px;background:var(--panel-2);border:1.5px solid var(--box-edge)}
  .gblock.right{order:3}
  .gteam{order:2}
  .gteam.right{order:4}
  .ghead .gteam{background:var(--panel-2);border-color:var(--line)}
  .gtotals .gteam{border-top:2px solid var(--line)}
  /* THE PADDING ROWS GO. A club that used eleven men against nine leaves the shorter side with blank rows, and
     side by side those are load-bearing -- they are what keeps row seven opposite row seven. Stacked under one
     club they are rows of nothing in the middle of a team's own list, which reads as players whose names failed
     to load. A placeholder is the only .gteam holding a single dash and nothing else. */
  .gteam:has(>.gname.forced:only-child),.gteam:empty{display:none}
}
/* ON THE FLOOR RIGHT NOW: a live dot beside the name. Only ever drawn while a game is being played. */
.onfloor{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent);margin-right:7px;
animation:livedot 1.6s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){.onfloor{animation:none}}
.calm .onfloor{animation:none}
.mmtop{padding:12px 16px 10px;display:flex;flex-direction:column;gap:4px;border-bottom:2px dashed var(--line-soft)}
.mmteam{display:flex;align-items:center;justify-content:space-between;gap:10px}
.mmtotal{font:700 28px/1 var(--display);color:var(--ink)}
.mmtotal.won{color:var(--accent)}
.mmside .pad{padding-top:6px;gap:0}
.mmrow{padding:9px 0;border-bottom:1px solid var(--line-soft);display:flex;flex-direction:column;gap:6px}
.mmrow:last-child{border-bottom:none}
.mmhead{display:grid;grid-template-columns:44px 1fr auto;align-items:center;gap:8px}
.mmslot{font:700 11px/1 "IBM Plex Mono",monospace;color:var(--ink-3);letter-spacing:.06em}
.mmname{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mmbank{font:700 16px/1 var(--display);color:var(--accent);background:var(--accent-soft);border-radius:8px;padding:3px 8px}
.mmbank i{font:600 10px/1 "IBM Plex Mono",monospace;font-style:normal;opacity:.8}
.mmbank.auto{color:var(--ink-2);background:var(--panel-2)}
.mmgames{display:flex;flex-wrap:wrap;gap:6px;padding-left:52px}
.mmgame{display:inline-flex;flex-direction:column;gap:1px;min-width:92px;padding:5px 8px;border-radius:9px;
border:2px solid var(--line);background:var(--panel-2);font-size:11.5px;line-height:1.25}
.mmwhen{font:700 11px/1.2 "IBM Plex Mono",monospace;color:var(--ink)}
.mmopp{color:var(--ink-3)}
.mmres i{font-style:normal;color:var(--ink-3);font-size:10.5px}
.mmres b{font-family:"IBM Plex Mono",monospace}
.mmgame.banked{border-color:var(--accent);background:var(--accent-soft)}
.mmgame.banked .mmres b,.mmgame.banked .mmres i{color:var(--accent)}
.mmgame.dnp{border-style:dashed;background:none;opacity:.75}
@media (max-width:520px){.mmgames{padding-left:0}}
.lcards{display:grid;grid-template-columns:repeat(auto-fit,minmax(520px,1fr));gap:18px}
@media (max-width:620px){.lcards{grid-template-columns:1fr}}
.lcard{display:grid;grid-template-columns:1fr 1fr}
.lside{padding:12px 14px;display:flex;flex-direction:column;gap:4px}
.lside+.lside{border-left:2px dashed var(--line-soft)}
/* A phone stacks the two teams of a live matchup: side by side, the right one was cut off (18 Sep, phone check). */
@media (max-width:620px){.lcard{grid-template-columns:1fr}.lside+.lside{border-left:none;border-top:2px dashed var(--line-soft)}}
.lhead{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}
.ltotal{font:600 13px/1.2 "IBM Plex Mono",monospace}
.ltotal b{font:700 20px/1 var(--display);color:var(--accent)}
/* 58px, not 42: slot labels grew names of their own -- "UTIL 1", and a Savings Account slot is called SAVINGS, which
   overflowed the column and sat against the player's name ("SAVINGSempty"). Every row shares this, so they stay aligned. */
/* 74px, because the slot names are not all two letters. A league with three UTIL spots numbers them (UTIL 1, UTIL 2,
   UTIL 3), the chip you press to fill one carries an arrow as well, and at 58px that chip overflowed its column and
   sat on top of the first letters of the name beside it -- "Nikola Jokic" read "ikola Jokic". */
/* HIS WEEK, GAME BY GAME, in the width the columns were wasting (19 Sep: "each game needs its own bank slot. people
   want to be able to see their options"). One cell per game: the day, the club he plays in the CLUB'S OWN COLOUR
   (kit.club_chip -- a colour, never a logo, which would be somebody's property), and what it is worth. A game that
   has gone is struck through; the one being held carries the score and the Bank button beside the strip; the ones
   ahead are just a day and a club. */
/* ---------- THE MATCHUP: one slot a row, the two teams mirrored around the position ----------
   His, 20 Sep, with two Sleeper screenshots: "symmetry is incredibly important in a matchup page." Two lists side by
   side were not a matchup, because nothing lined up AGAINST anything. Every half is drawn by one function and painted
   by one rule set, mirrored -- so the two sides cannot drift apart, and a row is as tall as its taller half. */
.panel.match{padding-bottom:6px}
/* A BOX DOES NOT HAVE TO BE THE WHOLE SCREEN (his, 20 Sep: "its way too wide ... you dont have to take up the whole
   screen every single time you make a box"). The matchup sits at a readable width with its two coin strips beside
   it, and the row inside it tightens with it rather than spreading. */
/* WIDTH:100% MATTERS HERE. `main` is a column flex, and `margin:0 auto` on a flex item centres it AND shrinks
   it to its content -- so a matchup whose rows were narrow (short names, a quiet night) drew a 741px card under a
   1120px header. His, 6 Oct: "the header should be the same length as the matchup box takes up." Both are capped
   at 1120 now and both fill it, so they line up whatever is in them. */
.mwrap{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;align-items:start;
width:100%;max-width:1120px;margin:0 auto}
.mcoins{display:flex;flex-direction:column;align-items:center;gap:8px;padding:10px 4px;border:3px solid var(--edge);
border-radius:16px;background:var(--panel);box-shadow:4px 5px 0 var(--shadow);min-height:64px}
.mcoins:empty{border:none;background:none;box-shadow:none}
.coinstack{display:flex;flex-direction:column;align-items:center;gap:9px;text-decoration:none}
.mcoins .coin.small{flex-direction:column;gap:0}
/* Big enough to SEE. At 30px the count badge covered a third of the coin and the whole strip read as dark blobs. */
/* `.coin.small .coin-disc` is two classes deep, so a `.mcoins .coin-disc` rule loses to it however late it comes --
   which is why the first attempt at this left the strip as dark blobs. Match its depth. */
.mcoins .coin.small .coin-disc{width:42px;height:42px;border-width:3px;
box-shadow:inset 0 0 0 2.5px #ffe680,2px 3px 0 var(--shadow)}
.mcoins .coin.small .coin-face{font-size:19px}
.mcoins .coin.small .coin-n{right:-5px;bottom:-2px;font-size:10px;min-width:18px;border-width:1.5px}
/* THE HEADER IS TWO NAMES FACING A SCORE. Whatever is on one side is on the other, at the same height: his
   "0 of 10 banked" and "4 of 10 banked" were in different places, and the totals ran on a diagonal. */
.mheads{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:10px;padding:10px 16px 12px;
border-bottom:2px dashed var(--line-soft)}
/* A side of the header is the name and count TOGETHER (.mteamwho -- NOT .mwho, which is the player row's name
   block further down this file), plus anything that side carries beside them: the purse, on yours, between your
   name and the score (24 Sep). In flow, so nothing overlaps. */
/* The purse sits BESIDE YOUR NAME, not out at the edge of the cell (his, 24 Sep: "move the coin purse closer to
   the name and farther from the score"). space-between pushed it as far right as the cell allowed, which put it
   against the score; flex-start keeps it with the name it belongs to and leaves the gap before the score. */
.mteam{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:12px;min-width:0}
.mteamwho{display:flex;flex-direction:column;gap:1px;min-width:0}
.mteam.right{justify-content:flex-end}
.mteam.right .mteamwho{align-items:flex-end;text-align:right}
.mteamname{font:700 16px/1.2 var(--display)}
.mscoreline{display:flex;align-items:baseline;gap:10px}
/* THE WIN BAR (21 Sep): "there needs to be a win percentage bar." Your share on the left in the accent colour,
   theirs on the right, under the two totals it belongs to. */
.mscorecol{display:flex;flex-direction:column;align-items:center;gap:6px}
/* A COIN THEY USED THIS WEEK, under the team's name (21 Sep): "you friend has used a coin". Gold, because coins are. */
.mcoinused{display:inline-flex;align-items:center;gap:5px;margin-top:3px;padding:1px 8px 1px 4px;border-radius:999px;
font:800 9px/1.6 "IBM Plex Mono",monospace;letter-spacing:.06em;text-transform:uppercase;color:#6b4a00;
background:linear-gradient(180deg,#ffe28a,#f0b322)}
.mcoinused::before{content:"";width:10px;height:10px;border-radius:50%;background:radial-gradient(circle at 35% 35%,#fff6c8,#e0a318);
box-shadow:0 0 0 1px #b07d0a}
.mteam.right .mcoinused{align-self:flex-end}
/* The newspaper opens its story when there is one to tell. */
details.newsmenu>summary.news{cursor:pointer}
/* NO UNDERLINE ON HOVER (his, 29 Sep: "i dont want there status to function like a hyperlink"). A status is a
   thing you press, not a link you follow, so it brightens rather than pretending to be text with an address. */
details.newsmenu>summary.news:hover{filter:brightness(1.25)}
.winbar{display:flex;justify-content:space-between;width:180px;height:16px;border-radius:999px;overflow:hidden;
border:1.5px solid var(--box-edge);background:linear-gradient(90deg,var(--accent) var(--w),var(--box-grey) var(--w));
font:800 9.5px/16px "IBM Plex Mono",monospace;font-variant-numeric:tabular-nums}
.winbar .wbl{padding-left:7px;color:#fff;text-shadow:0 1px 0 rgba(0,0,0,.25)}
.winbar .wbr{padding-right:7px;color:var(--ink-2)}
.mscoreline b{font:700 26px/1 var(--figures);font-variant-numeric:tabular-nums;color:var(--accent)}
.mvs{font:700 10px/1 "IBM Plex Mono",monospace;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3)}

.mrows{display:flex;flex-direction:column}
.mrow{display:grid;grid-template-columns:1fr 158px 104px 158px 1fr;align-items:center;gap:6px;padding:7px 12px;
border-bottom:1px solid var(--line-soft)}
/* The two scores flank the position, the way both of his screenshots do it: you compare down the middle of the page
   rather than across the width of it. */
/* THE MIDDLE OF A ROW, TOP DOWN (20 Sep): the score, the fixture it came from, and the box score of that game --
   "move the score in the middle up and have the box under it". The position badge rises with the scores, so the
   three read across as one line: 48.3 · PG · 27.9. */
.mscorecell{display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-start;gap:3px;
align-self:start;padding-top:2px}
.mscorecell.right{align-items:flex-start}
.mscorecell .mbox{margin-top:3px}
/* The game on show in the middle: .mcur is the one his score comes from, .mpicked whichever other game was
   pressed (21 Sep). They lay out exactly as the cell did. A display rule beats the hidden attribute, so hidden
   has to be said again here or both would show. */
.mscorecell>.mcur,.mscorecell>.mpicked{display:flex;flex-direction:column;gap:3px;align-items:inherit}
.mscorecell>.mcur[hidden],.mscorecell>.mpicked[hidden]{display:none}
.mscorecell .mscore.proj{color:var(--ink-3)}
/* The game whose numbers the middle is showing. */
.gsm.picked{outline:2px solid var(--ink);outline-offset:1px}
/* A game box is a plain button since 21 Sep -- pressing it only chooses which game the middle shows -- so it wears
   the box's own look, not a browser button's. The held game stays a <summary>, because it opens the confirm. */
button.gsm{font:inherit;line-height:inherit;color:inherit;cursor:pointer;-webkit-appearance:none;appearance:none}
/* THE BOX SCORE OPENS THE POINT BREAKDOWN ("make it pop up by clicking on the box score", 21 Sep). */
details.gmenu.bbox{display:block}
.mboxsum{display:block}
.mboxsum:hover .mbox{border-color:var(--ink)}
details.gmenu.bbox[open]>.mboxsum .mbox{border-color:var(--ink);box-shadow:0 0 0 2px var(--box-white)}
/* The fixture the score came from, under the score: both clubs, written the same way. */
.mfix{font:700 8.5px/1 "IBM Plex Mono",monospace;letter-spacing:.04em;color:var(--ink-3);font-style:normal;
white-space:nowrap}
/* A fixture names two clubs in a line eight pixels tall, so its plates shrink to match rather than shove the
   row open. The word between them -- vs, at -- stays the quiet grey it was. */
/* NINE PIXELS IS BELOW WHAT A BORDERED LETTER SURVIVES (22 Oct). The pills carry a stroke round the letters
   because he asked for one -- "i think we need to give all text a border. the ones that have it look 3d while
   the ones that dont look 2d and its a weird discrpenency" (27 Sep) -- and a stroke is a fraction of the
   letter, so at 9px it eats the counter in a D until DAL reads as BAL. He reported exactly that.
   FIVE WAYS WERE COMPARED AT ACTUAL SIZE and photographed: as it was; a thinner stroke; no stroke; a shadow
   outline instead; and one size up. Only the last fixes it without taking something away -- dropping the
   stroke clears DAL and makes Detroit's red on royal blue unreadable, which is the pair the stroke exists
   for. So the three smallest pills go to 10px. Nothing else moves: the stroke, the colours and the rule that
   every club gets a border are all his and all untouched. */
.mfix b.club.pill,.gsub b.club.pill{font-size:10px;padding:0 4px;letter-spacing:.03em;vertical-align:1px}
.mrow:last-child{border-bottom:none}
.mpos{display:flex;align-items:center;justify-content:center;align-self:start;padding-top:2px}
.posbadge{display:inline-flex;align-items:center;justify-content:center;min-width:52px;padding:5px 9px;border-radius:10px;
background:var(--pos);color:#fff;font:800 11.5px/1 "IBM Plex Mono",monospace;letter-spacing:.06em;
box-shadow:2px 2px 0 var(--shadow)}
.mpos .slotname{min-width:52px;justify-content:center;background:var(--pos);color:#fff;border-style:solid;
border-color:transparent}
/* NO ARROW ON THE MIDDLE BADGES (21 Sep): "i do not like how the arrow on the positions in the middle of the screen
   makes the box look bigger." A pressable badge was a character wider than a plain one, so the column wobbled.
   The pointer changing on hover says it can be pressed. */
.mpos .slotname::after,.mpos .movemenu[open]>summary .slotname::after{content:none}
.mpos .movemenu[open]>summary .slotname{background:var(--accent)}

/* A HALF. The right one is the same markup, laid out backwards -- never a second copy of it. */
/* A HALF IS TWO THINGS SIDE BY SIDE since 20 Sep: the man and his week (.mmain), and the box score of the game his
   score came from, on the side nearest that score -- "squezzing it in between the score of the current game and
   all the game information". On the mirrored half the order reverses, so both box scores face the middle. */
.mhalf{display:flex;flex-direction:row;align-items:center;gap:12px;min-width:0}
.mhalf.right{flex-direction:row-reverse;text-align:right}
.mmain{display:flex;flex-direction:column;gap:5px;min-width:0;flex:1 1 auto}
.mhalf.right .mmain{align-items:flex-end}
/* The box score: seven numbers under seven labels, and the shooting under them. A projection is grey, like every
   other projection on the page. */
/* ONE WIDTH FOR EVERY BOX SCORE, so a starter's and a bench man's line up down the page and a big number never
   shifts a row. */
.mbox{flex:0 0 auto;display:flex;flex-direction:column;gap:3px;padding:5px 7px;border-radius:8px;width:136px;
box-sizing:border-box;background:var(--box-grey);border:1.5px solid var(--box-edge)}
.mbox.none{background:none;border-color:transparent;align-items:center;justify-content:center}
.mbox.none i{font:600 9px/1.3 "IBM Plex Mono",monospace;color:var(--ink-3);font-style:normal}
/* Two rows of four (20 Sep): MIN PTS REB AST over STL BLK TO PF, each label above its number, smaller type. */
/* Twelve narrow columns so both rows fill the same width: four cells of three columns on top, six cells of two
   underneath, once technicals and flagrants joined the foul row. The box stays 136px wide either way. */
.mboxgrid{display:grid;grid-template-columns:repeat(12,1fr);column-gap:2px;row-gap:0;text-align:center}
.mboxgrid .w3{grid-column:span 3}
.mboxgrid .w2{grid-column:span 2}
.mboxgrid b.hot{color:#d23b3b}
.mboxgrid i{font:700 6.5px/1.2 "IBM Plex Mono",monospace;letter-spacing:.06em;color:var(--ink-3);font-style:normal}
.mboxgrid b{font:700 9.5px/1.15 "IBM Plex Mono",monospace;font-variant-numeric:tabular-nums;color:var(--ink);
margin-bottom:2px}
.mboxshoot{font:600 7.5px/1 "IBM Plex Mono",monospace;color:var(--ink-2);text-align:center;white-space:nowrap}
.mbox.proj .mboxgrid b,.mbox.proj .mboxshoot{color:var(--ink-3);font-weight:600}

/* THE BOOSTS (20 Sep): "can we make it kind of fun ... some little colorful graphic for a double double, or a
   triply double or any of the boosts." A rosette per milestone this league scores, rarer is shinier: the double-
   double green, the triple-double gold with a glint, the big nights hot, the glass blue, the dishes purple.
   Penalties are red, and only appear when one happened. Every colour is one rule -- restyle freely. */
.miles{display:flex;flex-direction:column;align-items:stretch;gap:2px;margin-top:2px}
.mile{display:flex;align-items:center;gap:4px;padding:2px 6px 2px 2px;border-radius:999px;
font:800 6.5px/1 "IBM Plex Mono",monospace;letter-spacing:.08em;text-transform:uppercase;color:#fff;
box-shadow:0 1px 0 rgba(0,0,0,.25),inset 0 1px 0 rgba(255,255,255,.35)}
.mile i{display:inline-grid;place-items:center;width:12px;height:12px;border-radius:50%;font-style:normal;
font-size:8px;background:rgba(255,255,255,.28);box-shadow:0 0 0 1.5px rgba(255,255,255,.55)}
.mile b{margin-left:auto;font-weight:800;letter-spacing:0}
.mile.dd{background:linear-gradient(180deg,#46b86f,#237d45)}
.mile.td{background:linear-gradient(180deg,#ffd76a 0%,#f0b322 45%,#c48a0c 100%);color:#4a3200;
box-shadow:0 1px 0 rgba(0,0,0,.25),inset 0 1px 0 rgba(255,255,255,.7),0 0 6px rgba(255,200,60,.6)}
.mile.td i{background:#fff6d0;color:#c48a0c}
.mile.fire{background:linear-gradient(180deg,#ff8a3d,#d8401f)}
.mile.glass{background:linear-gradient(180deg,#4aa8ff,#1f64c8)}
.mile.dish{background:linear-gradient(180deg,#a47bff,#6c3fd1)}
.mile.tech{background:none;color:#d23b3b;box-shadow:inset 0 0 0 1.5px #d23b3b}
.mile.tech i{background:#d23b3b;color:#fff;box-shadow:none}
.mile.flag{background:linear-gradient(180deg,#e0453f,#8f1d1a)}
/* The same colours as a dot beside the line in the breakdown card, and as a star in the corner of a game box. */
.mdot{display:inline-grid;place-items:center;width:11px;height:11px;margin-right:4px;border-radius:50%;
font:800 7px/1 "IBM Plex Mono",monospace;font-style:normal;color:#fff;vertical-align:-1px;background:var(--ink-3)}
.gstar{position:absolute;top:-5px;right:-5px;z-index:2;display:grid;place-items:center;width:13px;height:13px;
border-radius:50%;font:800 8px/1 "IBM Plex Mono",monospace;color:#fff;box-shadow:0 0 0 1.5px var(--panel)}
.mdot.dd,.gstar.dd{background:#2f9e5b}
.mdot.td,.gstar.td{background:#f0b322;color:#4a3200;box-shadow:0 0 0 1.5px var(--panel),0 0 5px rgba(255,200,60,.8)}
.mdot.fire,.gstar.fire{background:#e8582a}
.mdot.glass,.gstar.glass{background:#2f7fe0}
.mdot.dish,.gstar.dish{background:#8455e8}
.mdot.tech,.gstar.tech{background:#d23b3b}
.mdot.flag,.gstar.flag{background:#8f1d1a}
.mhalf.empty{justify-content:center;color:var(--ink-3)}
/* HARD AGAINST THE FACE (20 Sep): "we should push the name, positions, and team closer to the left where the
   profile picture will be." Spread across the half, the name floated away from the portrait it belongs to. */
.mwho{display:flex;align-items:center;gap:8px;min-width:0;width:100%;justify-content:flex-start}
/* THE MIRRORED HALF IS row-reverse, SO ITS START IS THE RIGHT EDGE. flex-end there points at the middle of the
   page, which left every face slot on his opponent's side floating at a different distance from the edge --
   "some names and photo slots are moved over" (20 Sep). flex-start packs them against the edge, which is the
   mirror image of what his own side does. */
.mhalf.right .mwho{justify-content:flex-start}
.mhalf.right .mwho{flex-direction:row-reverse}
.face{flex:none;width:34px;height:34px;border-radius:50%;display:grid;place-items:center;background:var(--panel-2);
border:2px solid var(--line);font:800 14px/1 var(--display);color:var(--ink-3)}
.mname{display:flex;flex-direction:column;gap:1px;min-width:0}
/* The news flag rides WITH THE NAME (20 Sep: "put the news icon next to the name, not the data") -- it is news
   about the man, not another fact about what he is. On the mirrored side it sits on the far side of the name,
   so the two rows read outward from the middle the same way. */
.mnameline{display:flex;align-items:center;gap:6px;min-width:0}
.mhalf.right .mnameline{flex-direction:row-reverse}
.mname .chip.plain{font:700 15px/1.15 var(--display);border:none;background:none;padding:0;color:var(--ink)}
.mname .chip.plain:hover{color:var(--accent);background:none}
.mmeta{display:flex;align-items:center;gap:5px;flex-wrap:wrap;font:600 10.5px/1.2 "IBM Plex Mono",monospace}
.mhalf.right .mmeta,.mhalf.right .mname{align-items:flex-end}
.mmeta i{font-style:normal;color:var(--pos);font-weight:800}
/* A club under a player's name is a plate like every other club in the app (his, 26 Sep: "wherever there is a
   team name replace it with the pill"), and at this size it wants to be smaller than one standing alone. */
.mmeta b.club.pill{font-size:10px;padding:0 4px;letter-spacing:.04em}
.news{display:inline-flex;align-items:center;gap:3px;color:var(--alert)}
.news .newsy{width:14px;height:12px;flex:none}
.news i{font-style:normal;font-size:9.5px;letter-spacing:.04em;text-transform:uppercase}
/* THE STATUS IS A BUTTON, and it sits level with the name beside it (his, 29 Sep: "the status and newspaper icon
   are not level with the player name"). A button carries the browser's own font, padding and border, none of
   which belong here; stripping them and letting the row's flex do the centring is what makes it line up. */
.newsbtn{appearance:none;background:none;border:0;padding:0;margin:0;font:inherit;cursor:pointer;
line-height:1;color:var(--alert)}
.newsbtn:hover{filter:brightness(1.25)}
.newsbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
/* THE NAME AND ITS FLAGS SHARE ONE MIDDLE LINE, rather than sitting on the text baseline at three different
   heights. The flex line is a SPAN INSIDE the cell, never the cell itself: `display:flex` on a <td> takes the cell
   out of the table, so it stopped sharing the row's height and background and the name came away from its own row
   -- which is the glitch he photographed on 29 Sep.
   AND IT NEVER WRAPS (his, 29 Sep: "all the rows need to be the same size first of all. this means that we need
   to use the horizontal space better"). A wrapped name, or a news flag pushed onto a second line, made that one
   row taller than its neighbours and the whole board uneven. The name column takes the slack the other columns
   do not need, so there is room for the longest name in the league on one line. */
/* HIS PROJECTED LINE: narrow, mono, and quieter than the numbers that price him. They are the detail behind
   Fpts/Gm rather than a second opinion to weigh against it, so they must not pull the eye the way Value does.
   `pj` on purpose -- "pts" belongs to Fpts/Gm in this stylesheet and the two must never collide. */
.board td.c-pjmin,.board td.c-pjpts,.board td.c-pjreb,.board td.c-pjast,
.board td.c-pjstl,.board td.c-pjblk,.board td.c-pjto{font-size:12px;color:var(--ink-2)}
/* SLEEPER FTPS IS A REFERENCE, NOT A RIVAL. It is the same arithmetic as Fpts/Gm with our own corrections
   left out, so it reads quieter than the column it is there to be checked against -- otherwise a board with
   both on it shows two numbers of equal weight and no reader can tell which one the model believes. */
.board td.c-sleeperpts{color:var(--ink-2)}
.board td.c-name{white-space:nowrap;width:100%}
/* POSITIONS SIT SIDE BY SIDE, NEVER STACKED. Every other column in this table is one line tall, so a man with
   three of them -- DeRozan PF/SF/SG, LeBron PF/PG/SF -- made his own row three times as tall as its neighbours
   and the table went ragged. It is the stacking he caught on the board's own name cell on 13 Oct, one column
   over. The table already scrolls sideways, so there is nothing to be won by wrapping here. */
.board td.c-pos{white-space:nowrap}
.board td.c-name .nameline{display:flex;align-items:center;gap:7px;flex-wrap:nowrap}
.board td.c-name .rook,.board td.c-name .newsbtn{align-self:center;margin-left:0}
.board td.c-name .tookby{margin-left:0}

.mline{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-height:28px;width:100%;
justify-content:space-between}
.mhalf.right .mline{flex-direction:row-reverse}
/* THE SCORE IS THE POINT OF THE ROW (20 Sep): "the scores that each player is giving should be significantly
   bigger." Everything else on a row tells you what to do about this number. Tabular figures so a column of them
   lines up on the decimal rather than wobbling.
   A row's score also carries `lstate`, so the live updater can find it -- and `.lstate` is one class, defined
   LATER in this file, which means it was quietly winning: the row score had been drawn at 11px since the rebuild,
   and the 21px rule that was meant to size it never applied at all. Two classes to outrank it. */
.mscorecell .mscore{font:700 20px/1 var(--figures);font-variant-numeric:tabular-nums;color:var(--ink);
letter-spacing:-.01em;padding:0;border-radius:0}
.mscore.hold{color:var(--accent-2)}
.mscore.live{color:var(--ink)}
.mwhen{font:600 11px/1 "IBM Plex Mono",monospace;color:var(--ink-3)}
.locked{font:800 10px/1 "IBM Plex Mono",monospace;letter-spacing:.12em;text-transform:uppercase;color:var(--accent);
border:1.5px solid var(--accent);border-radius:6px;padding:3px 6px;transform:rotate(-4deg)}
.btn.lockin{padding:4px 12px;font-size:12.5px}
.mnote{font:600 10px/1.3 "IBM Plex Mono",monospace;color:var(--accent-2)}

/* THIS PERIOD, AS CRESTS. The schedule with no words in it -- played dimmed, the one in hand lit, the rest plain. */
.gstrip{display:flex;gap:5px;flex-wrap:wrap;width:100%}
.mhalf.right .gstrip{justify-content:flex-end}
.gsm{position:relative;display:flex;flex-direction:column;align-items:center;gap:1px;padding:2px 3px;border-radius:8px;
border:1.5px solid transparent;background:var(--box-grey);border-color:var(--box-edge)}
.gsm i{font-style:normal;font:700 8px/1 "IBM Plex Mono",monospace;letter-spacing:.06em;color:var(--ink-3)}
/* The tip time, on the box itself (20 Sep: "the time is always visually available"). */
.gsm i.gtime{font-weight:600;letter-spacing:0;color:var(--ink-2)}
/* What the game is worth: the score once it has been played, the PROJECTION before -- grey and lighter, so it is
   never mistaken for something that happened (his, 20 Sep: "before shows projections, after shows the unlocked
   score"). It carried a dotted underline until he saw it: "this little line under a projected game is awful."
   Grey against black is difference enough. */
.gsm b{font:800 9.5px/1.1 "IBM Plex Mono",monospace;color:var(--ink)}
.gsm u{font:600 9.5px/1.1 "IBM Plex Mono",monospace;color:var(--ink-3);text-decoration:none}
.gsm.none{background:none;border-color:transparent}
.gsm.none i{font-style:italic;letter-spacing:0}
/* The bench carries the same strip, so the row needs the room for it. */
/* Greyed out AND struck through was the same fact twice (his rule, and he caught it here). */
/* Greyed, but not its milestone star: a filter on the box would grey everything inside it, so it goes on each
   part except the star -- the one thing on a played game that should still shout. */
.gsm.gone{text-decoration:none}
.gsm.gone>:not(.gstar){filter:grayscale(1);opacity:.85}
.gsm.gone i,.gsm.gone b{text-decoration:none}
.gsm.holding{background:var(--accent-2-soft);border-color:var(--accent-2)}
/* EVERY GAME OPENS (20 Sep): "the games arent individually clicable, but they should be for information on them."
   So each box is a <details> summary -- told not to draw a triangle, and to sit in the strip exactly as a plain
   box does. The one you are holding keeps the word `bank` and a lift under the pointer, because it is the one
   that leads anywhere. */
details.gmenu{display:inline-flex;position:relative}
details.gmenu>summary{list-style:none;cursor:pointer}
details.gmenu>summary::-webkit-details-marker{display:none}
summary.gsm.press{border-width:2px;box-shadow:0 1px 0 var(--accent-2)}
summary.gsm.press:hover{background:var(--accent-2);transform:translateY(-1px)}
details.gmenu[open]>summary.gsm{border-color:var(--ink);box-shadow:0 0 0 2px var(--box-white)}
/* The card itself: parked on <body> by kit.NAME_MENUS, so no panel can clip it. */
/* WHY THE GAME IS WORTH WHAT IT IS: stat, times its weight, equals points -- grouped, each group totalled, the
   whole adding up to the number on the box. */
.gbreak{width:100%;border-collapse:collapse;margin-top:6px;font:600 10.5px/1.35 "IBM Plex Mono",monospace;
font-variant-numeric:tabular-nums;color:var(--ink-2)}
.gbreak caption{caption-side:top;text-align:left;font:800 12px/1.3 var(--display);color:var(--ink);padding-bottom:3px}
.gbreak.proj caption{color:var(--ink-3)}
/* The site's table rules (zebra rows, padded, right-set cells) are for big tables; this one is four columns in a
   card, and inherited them all. Reset here rather than fight each one. */
.gbreak tr,.gbreak td,.gbreak th{background:none !important;border-left:none;border-right:none}
.gbreak td{padding:1px 0;text-align:left}
.gbreak td:first-child{text-align:left;padding-right:10px;white-space:nowrap}
.gbreak td:nth-child(2),.gbreak td:nth-child(3),.gbreak td:nth-child(4),.gbreak th:last-child{text-align:right}
.gbreak td:nth-child(3){color:var(--ink-3);padding-left:8px}
.gbreak td:nth-child(4){padding-left:10px;color:var(--ink)}
.gbreak tr.bgroup th{padding-top:6px;font:800 8.5px/1.3 "IBM Plex Mono",monospace;letter-spacing:.12em;
text-transform:uppercase;color:var(--ink-3);text-align:left;border-bottom:1px solid var(--box-edge)}
.gbreak tr.bgroup th:last-child{color:var(--ink);text-align:right}
.gamebox{position:fixed;z-index:40;min-width:240px;max-width:min(320px,86vw);padding:10px 12px;border-radius:12px;
border:2px solid var(--edge);background:var(--panel);box-shadow:5px 6px 0 var(--shadow)}
.gamebox p{margin:0}
.ghead{font:800 12.5px/1.3 var(--display);color:var(--ink)}
.gsub{font:700 10px/1.5 "IBM Plex Mono",monospace;letter-spacing:.08em;color:var(--ink-3)}
.gworth{margin-top:4px !important;font:600 12px/1.35 "IBM Plex Mono",monospace;color:var(--ink-2)}
/* BANKING CANNOT BE UNDONE, so it says so and asks. "theres needs to be a 'are you sure you want to bank this
   game' type of system so that we dont accidentally bank people." */
.gwarn{margin:7px 0 8px !important;padding-top:7px;border-top:1.5px dashed var(--box-edge);
font:600 10.5px/1.45 "IBM Plex Mono",monospace;color:var(--ink-2)}
.gsm .takeit{font:800 7px/1 "IBM Plex Mono",monospace;letter-spacing:.12em;text-transform:uppercase;
color:var(--accent-2-ink,var(--ink));font-style:normal}
.gsm.live{background:var(--box-grey);border-color:var(--edge);box-shadow:0 0 0 1px var(--edge) inset}
.gsm.banked{background:var(--accent-soft);border-color:var(--accent)}
/* The stamp is OVER the crest, not beside it: "would put a bank stamp over the logo as soon as its banked". */
.ostamp{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) rotate(-11deg);z-index:2;pointer-events:none;
font:800 6.5px/1 "IBM Plex Mono",monospace;letter-spacing:.1em;text-transform:uppercase;color:var(--accent);
border:1.5px solid var(--accent);border-radius:3px;padding:1px 3px;background:var(--panel);opacity:.96}
.crest{display:block;width:28px;height:28px;object-fit:contain}
.crest.sm{width:22px;height:22px}

/* The bench is the same row as the lineup, with BN or IR where the position would be. */
.benchhead{border-top:2px dashed var(--line-soft);padding-top:8px}
.posbadge.bench{background:var(--ink-3);min-width:44px}
.mrow.bench{padding-top:6px;padding-bottom:6px}
/* NARROW SCREENS: THE SAME MATCHUP, STACKED (21 Sep). His: "build the phone version however you see fit in order to
   mimic what we are doing here on the laptop/destop version." Nothing is dropped and nothing is reordered: every
   row keeps you on the left and them on the right, and only folds into lines -- the position on top, then the two
   men, then their scores and box scores meeting in the middle, then each man's week. The halves and their inner
   column step aside (display:contents) so their parts can sit on those lines. Coins go in a row above: yours left,
   theirs right. It takes over wherever the side-by-side row no longer fits -- the old 820px rule squeezed the score
   columns to 46px, which the box scores (136px) cannot live in.
   Desktop first, per his priority; this is a first pass to be judged once the desktop page is settled. */
@media (max-width:1100px){
  .hero.tight,.mwrap{width:100%;max-width:100%;min-width:0;box-sizing:border-box}
  .pairstrip{max-width:100%}
  .mwrap{grid-template-columns:1fr 1fr;grid-template-areas:"lc rc" "m m";gap:10px}
  .mwrap>.mcoins:first-child{grid-area:lc}
  .mwrap>.mcoins:last-child{grid-area:rc}
  .mwrap>.panel.match{grid-area:m;min-width:0}
  .mcoins{flex-direction:row;flex-wrap:wrap;justify-content:center;padding:6px;min-height:0}
  .mheads{grid-template-columns:1fr 1fr;grid-template-areas:"lt rt" "sc sc";row-gap:8px}
  .mheads>.mteam:not(.right){grid-area:lt}
  .mheads>.mteam.right{grid-area:rt}
  .mheads>.mscorecol{grid-area:sc}
  .mrow{grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    grid-template-areas:"pos pos" "lw rw" "ll rl" "ls rs" "lg rg" "ln rn";gap:6px 12px;padding:10px 10px 12px}
  .mrow>.mpos{grid-area:pos;justify-self:center;padding-top:0}
  .mrow>.mhalf:not(.empty),.mrow>.mhalf:not(.empty)>.mmain{display:contents}
  .mrow>.mhalf:not(.right)>.mmain>.mwho{grid-area:lw}
  .mrow>.mhalf.right>.mmain>.mwho{grid-area:rw;justify-self:end}
  .mrow>.mhalf:not(.right)>.mmain>.mline{grid-area:ll}
  .mrow>.mhalf.right>.mmain>.mline{grid-area:rl;justify-self:end}
  .mrow>.mhalf:not(.right)>.mmain>.gstrip{grid-area:lg}
  .mrow>.mhalf.right>.mmain>.gstrip{grid-area:rg}
  .mrow>.mhalf:not(.right)>.mmain>.mnote{grid-area:ln}
  .mrow>.mhalf.right>.mmain>.mnote{grid-area:rn;justify-self:end}
  .mrow>.mhalf.empty:not(.right){grid-area:lw}
  .mrow>.mhalf.empty.right{grid-area:rw;justify-self:end}
  .mrow>.mscorecell:not(.right){grid-area:ls}
  .mrow>.mscorecell.right{grid-area:rs}
  .mbenches{grid-template-columns:1fr}
}
/* The coins lie in a row on narrow screens, not a tall stack: yours left, theirs right. */
@media (max-width:1100px){.mcoins .coinstack{flex-direction:row;flex-wrap:wrap;justify-content:center;gap:6px}}
/* Phones: the portrait slot gives its room to the name, and both arrows stay on the matchup's own line. */
@media (max-width:560px){.face{display:none}.mrow{padding:10px 6px 12px;column-gap:8px}
  /* `.hero .stepper`, not `.stepper`: the stepper's own rule sits later in this file and would win a tie. */
  .hero .stepper{flex-wrap:nowrap;gap:6px}.hero .stepper .step{width:40px;height:40px}}

/* Four cells now, the last one the action: Bank sits ON the row. It used to hang under it in .pacts, which gave
   every holding player two lines and made the page twice as tall as it needed to be (19 Sep). */
.lrow>form.inline{justify-self:end}
.lrow{display:grid;grid-template-columns:74px minmax(0,1fr) auto auto;align-items:center;gap:6px;padding:2px 0;
min-height:30px;border-bottom:1px solid var(--line-soft);font-size:12.5px}
/* Inside a matchup column the row is the calendar: slot, name, then one column per day of the matchup. Every row
   shares the template, so the days line up down the whole lineup without a table. */
.lside.week .lrow{grid-template-columns:74px minmax(86px,1.5fr) repeat(7,minmax(0,1fr));gap:3px}
.lside.week .lname{font-size:12px}
.lname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lstate{white-space:nowrap;font:600 11px/1.4 "IBM Plex Mono",monospace;padding:1px 7px;border-radius:999px}
.lstate b{font-size:12.5px}
/* A score that just moved. Deliberately quiet: on a busy night several of these change at once, and something
   that flashes hard nine times a minute stops being information and becomes weather. Restyle freely. */
.lstate.ticked b{animation:ticked 1.1s ease-out}
@keyframes ticked{0%{transform:scale(1.18);opacity:.55}60%{transform:scale(1)}100%{opacity:1}}

/* THE SCORE SAYS IT IS LIVE, AND HOW IT MOVED (his, 5 Oct, watching the first live night): "we also do need some
   colors on the score to show when the game is live. maybe we put a little live tag next to the score. we could
   also have an arrow and number flip up or downwards as an animation ... like you are working at a stock
   exchange."

   GREEN UP, RED DOWN, which is the one convention nobody has to be taught -- and in Bank mode --accent IS the
   green and --alert the muted red, so this borrows the money colours rather than inventing any. It is also why
   the red he objected to is not back: red here means a number went DOWN, which is a thing going wrong, not a
   game being played. A score falls only when a stat is corrected, so almost every tick is green. */
/* The fixture and the LIVE tag share one line, because the tag must cost no height: the fixture is eight pixels
   tall and half the width of its column, so the room was already there going unused. It follows the cell's own
   alignment, so on the right-hand half of a matchup the pair sits against the other edge like everything else. */
/* AND IT MIRRORS (his, 6 Oct): "the live tag isnt symmetrical." Aligning the pair to each half's own edge was
   only half of it -- the ORDER stayed the same both sides, so on the left the tag sat nearest the middle of the
   row and on the right it sat furthest from it. The right half reverses its row, so the tag is the inner word on
   both sides and the two halves fold onto each other. (Reversing a row also flips what flex-end means, hence the
   opposite word below: both lines pack their content toward the middle of the card.) */
.mfixline{display:flex;align-items:center;gap:7px;justify-content:inherit}
.mscorecell .mfixline{justify-content:flex-end}
.mscorecell.right .mfixline{flex-direction:row-reverse;justify-content:flex-end}
.livetag{display:inline-flex;align-items:center;gap:4px;padding:2px 6px;border-radius:999px;
font:800 8.5px/1 "IBM Plex Mono",monospace;letter-spacing:.14em;font-style:normal;vertical-align:middle;
color:var(--accent);background:color-mix(in srgb,var(--accent) 14%,transparent);
border:1px solid color-mix(in srgb,var(--accent) 40%,transparent)}
.livetag s{width:5px;height:5px;border-radius:50%;background:var(--accent);text-decoration:none;
animation:livedot 1.6s ease-in-out infinite}
@keyframes livedot{0%,100%{opacity:1}50%{opacity:.25}}

/* The number itself rises into place when it goes up and drops when it goes down -- the old one is already gone,
   so what the eye reads is the new figure arriving from the direction it moved. The arrow rides beside it and
   fades, because a permanent arrow is furniture: it is the CHANGE that is worth marking, not the state. */
/* IT FLIPS LIKE A SCOREBOARD, and the ARROW carries the colour (his, 6 Oct): "its supposed to be a red or green
   arrow to signify direction. then the numbers are supposed to flip up or down like a scorecard for baseball."
   The first attempt slid the number in from below, which reads as arriving rather than turning over.
   A split-flap turns on its horizontal axis, so the number rotates away edge-on and the new one rotates in
   behind it -- the swap itself is hidden at the halfway point, where the glyph is side-on and invisible. Up and
   down are the same turn in opposite directions. The number stays in the ink colour: the arrow says which way,
   so colouring the figure too would be the same fact twice. */
/* THE ARROW SITS BESIDE THE MAN IT BELONGS TO (his, 6 Oct): "the arrows should be on the side closest to the
   players name." It was always to the right of the number, so on the left-hand half it pointed away from the man
   who scored and across at his opponent -- the one place the eye should not be sent. The name is on the OUTSIDE
   of each half, so the arrow goes left of the number on the left side and right of it on the right side, and a
   glance from a name lands on its own arrow. */
.mscore[data-dir]{display:inline-block;position:relative;transform-style:preserve-3d}
.mscore[data-dir]::after{position:absolute;right:100%;top:50%;margin-right:4px;transform:translateY(-50%);
font-size:.46em;line-height:1;opacity:0;pointer-events:none;font-weight:900}
.mscorecell.right .mscore[data-dir]::after{right:auto;left:100%;margin-right:0;margin-left:4px}
/* IT TAKES ITS TIME (his, 6 Oct): "a delicate flip that takes its time." Half a second was a snap -- by the time
   the eye had found the number the turn was over, which is the opposite of what a scoreboard flap does. It is
   1.15s now, it leans into the turn rather than starting at speed, and it lands with one small rock back the way
   a real flap settles against its stop instead of stopping dead. */
.lstate.ticked.mscore[data-dir="up"]{animation:flipup 1.15s cubic-bezier(.4,0,.2,1)}
.lstate.ticked.mscore[data-dir="down"]{animation:flipdown 1.15s cubic-bezier(.4,0,.2,1)}
.lstate.ticked.mscore[data-dir="up"]::after{content:"\25B2";color:var(--accent);animation:arrowshow 2.6s ease-out}
.lstate.ticked.mscore[data-dir="down"]::after{content:"\25BC";color:var(--alert);animation:arrowshow 2.6s ease-out}
/* 46/54 rather than a clean 50: at exactly side-on some browsers drop the glyph for a frame and it reads as a
   flicker rather than a turn. The step at 82% is the settle -- a few degrees past level and back. */
@keyframes flipup{0%{transform:perspective(260px) rotateX(0)}
46%{transform:perspective(260px) rotateX(-88deg)}
54%{transform:perspective(260px) rotateX(88deg)}
82%{transform:perspective(260px) rotateX(-7deg)}
100%{transform:perspective(260px) rotateX(0)}}
@keyframes flipdown{0%{transform:perspective(260px) rotateX(0)}
46%{transform:perspective(260px) rotateX(88deg)}
54%{transform:perspective(260px) rotateX(-88deg)}
82%{transform:perspective(260px) rotateX(7deg)}
100%{transform:perspective(260px) rotateX(0)}}
@keyframes arrowshow{0%{opacity:0}14%{opacity:1}72%{opacity:1}100%{opacity:0}}

/* CALM TURNS IT OFF, like everything else that moves (App Settings), and so does the device's own setting. The
   number still changes; it simply arrives instead of travelling. */
.calm .lstate.ticked.mscore[data-dir],
.calm .livetag s{animation:none}
@media (prefers-reduced-motion:reduce){
  .lstate.ticked.mscore[data-dir],.livetag s,.lstate.ticked b{animation:none}
}
.lstate.banked{background:var(--accent-soft);color:var(--accent)}
.lstate.live{background:var(--panel-2);color:var(--ink)}
.mscore.lstate{background:none}
.lstate.live i{display:inline-block;width:7px;height:7px;margin-right:5px;border-radius:50%;background:var(--alert);
animation:livepulse 1.2s ease-in-out infinite}
@keyframes livepulse{50%{opacity:.25}}
@media (prefers-reduced-motion:reduce){.lstate.live i{animation:none}}
.lstate.holding{background:var(--accent-2-soft);color:var(--ink)}
.lstate.delayed{background:var(--alert);color:#fff}
.lstate.upcoming,.lstate.idle{color:var(--ink-3)}

/* ---------- drawing the draft order: the reverse wheel and the horse race (18 Sep) ----------
   His brief: "really lean in to cartoony and whimsical." So: chunky outlines, six loud slice colours, a wheel that
   WOBBLES when it lands, horses that gallop in place and fidget at the gate, a hat each. All paint, no logic --
   the page decides nothing, it replays lockin/order_draw.py. The horse is an emoji by default; to use his own art,
   one rule per lane does it:   .horse[data-lane="0"] .horse-img::before{content:"";background:url(...) ...}   */
.order-stage{margin:0;padding:18px 16px;background:var(--panel);border:3px solid var(--edge);border-radius:18px;
box-shadow:6px 6px 0 var(--shadow);display:grid;grid-template-columns:minmax(0,520px) 1fr;gap:18px;align-items:start}
.order-stage[data-kind="race"],.order-stage.skipped{grid-template-columns:1fr}
/* THE WHEEL GOES WHEN ITS LAST NAME COMES OFF IT (his, 2 Oct), and so does the column it stood in -- otherwise
   the finished order sits beside 520px of nothing where a wheel used to be. */
.order-stage.spent{grid-template-columns:1fr}
.wheel-wrap.spent{display:none}
@media (max-width:900px){.order-stage{grid-template-columns:1fr}}
/* THE SHOW IS A SPILL (his, 1 Oct). A spill's innards are a two-column grid, and the room is one thing: it takes
   the whole width, and the wheel and its tickets lay themselves out inside it exactly as they did on the page. */
.spillcols>.order-stage,.spillcols>.orderroom{grid-column:1/-1;width:100%}
/* THE ROOM IS ONE SHEET. Every other spill pours panels into itself, which bring their own paper; this one is a
   list of names and a button, and without this they floated loose over the blurred page with nothing behind them. */
.orderroom{display:flex;flex-direction:column;gap:14px;align-items:center;padding:18px 16px;background:var(--panel);
border:3px solid var(--edge);border-radius:18px;box-shadow:6px 6px 0 var(--shadow)}
.orderroom .btnrow{justify-content:center}
.orderroom .order-stage{width:100%;border:0;padding:0;background:none;box-shadow:none;border-radius:0}
/* Nothing is drawn on the stage until the draw starts, and an empty stage is an empty bar across the room. */
.order-stage:empty{display:none}
/* WHO IS IN THE ROOM (his, 1 Oct: "it should tell me who is there so i know when to start it. maybe there name
   lights up a bit"). EVERY seat is listed, because the question a commissioner is really asking is who is still
   missing -- a list of only the people present cannot answer that. */
.roomwho{display:flex;flex-direction:column;gap:8px;align-items:center;text-align:center}
.seatlights{display:flex;flex-wrap:wrap;gap:6px;justify-content:center}
.seathere{padding:3px 10px;border:2px solid var(--line);border-radius:999px;background:var(--panel-2);
font:700 12px/1.5 var(--display);color:var(--ink-3);transition:color .25s,border-color .25s,box-shadow .25s}
.seathere.lit{color:var(--ink);border-color:var(--accent);box-shadow:0 0 0 3px rgba(94,201,126,.22)}
.roomwho .herecount{color:var(--accent)}
/* THE TEAMS, IN NO ORDER, before anything has settled one. Deliberately NOT the numbered tickets the order is
   drawn as: a number beside a name is a pick, and nobody has a pick yet (his, 1 Oct). */
.teamlist{display:flex;flex-wrap:wrap;gap:8px}
.teamname{display:inline-flex;align-items:center;padding:6px 12px;background:var(--panel-2);
border:2px dashed var(--line);border-radius:999px;font:700 14px/1.4 var(--display)}
.byhandroom{align-items:stretch}
.byhandroom .handgrid{margin:4px 0 0}
.byhandroom .btnrow{justify-content:center}
.order-callout{font:700 clamp(20px,3.4vw,30px)/1.15 var(--display);margin:0 0 12px;text-align:center;text-wrap:balance}
.order-callout b{color:var(--accent)}
.order-over{font:700 22px/1.2 var(--display);text-align:center;margin:6px 0}
/* the wheel */
/* Bigger, so a long name has room (22 Sep). The wheel is one SVG, so everything on it grows with it. */
.wheel-wrap{width:520px;max-width:100%;margin:0 auto}
.wheel{width:100%;height:auto;display:block;overflow:visible}
.wheel-turn{transform-origin:160px 160px;transform-box:view-box}
.slice{stroke:var(--shadow);stroke-width:4;stroke-linejoin:round}
.slice.s0{fill:#8f5cff}.slice.s1{fill:#ff5d8f}.slice.s2{fill:#ffd166}.slice.s3{fill:#06d6a0}.slice.s4{fill:#4cc9f0}.slice.s5{fill:#f8961e}
.slice-name{font:700 13px var(--display);fill:#fff;text-anchor:middle;dominant-baseline:middle;
paint-order:stroke;stroke:#000;stroke-width:3px;stroke-linejoin:round;pointer-events:none}
.hub{fill:var(--edge);stroke:var(--shadow);stroke-width:4}
.pointer{fill:var(--alert);stroke:var(--shadow);stroke-width:3;stroke-linejoin:round;filter:drop-shadow(2px 3px 0 var(--shadow))}
.wheel-wrap.landed .wheel{animation:wheelwobble .55s cubic-bezier(.36,.07,.19,.97)}
@keyframes wheelwobble{0%{transform:scale(1)}25%{transform:scale(1.06,.94) rotate(-2deg)}55%{transform:scale(.96,1.05) rotate(2deg)}100%{transform:scale(1)}}
.wheel-tickets .ticket{animation:ticketpop .5s cubic-bezier(.2,1.6,.4,1)}
.wheel-tickets .ticket.first-pick{border-color:#ffd166;box-shadow:0 0 0 3px rgba(255,209,102,.35),6px 6px 0 var(--shadow)}
.wheel-tickets .ticket.first-pick b{color:#ffd166}
@keyframes ticketpop{0%{transform:scale(.4) rotate(-8deg);opacity:0}70%{transform:scale(1.08) rotate(2deg)}100%{transform:scale(1);opacity:1}}
/* the race -- "could get a bit cartoonier" (21 Sep): a striped turf with white rails, a candy-striped gate, a
   chequered post with its flag, a coat of its own for every horse, dust kicked up behind, a squash-and-stretch
   gallop, and a hop as each one crosses. Paint only: lockin/order_draw.py still decides everything. */
.track{position:relative;padding:8px 52px 8px 112px;border:3px solid var(--edge);border-radius:14px;overflow:hidden;
background:linear-gradient(90deg,#f6e7c8 0 104px,transparent 104px),
repeating-linear-gradient(180deg,#63bf5c 0 44px,#55ae50 44px 88px) 0 8px}
.track::before{content:"";position:absolute;left:103px;top:0;bottom:0;width:9px;border:2px solid var(--shadow);
background:repeating-linear-gradient(180deg,#e63946 0 11px,#fff 11px 22px)}
.lane{position:relative;height:44px}
.lane::after{content:"";position:absolute;left:0;right:-52px;bottom:-1px;height:2px;pointer-events:none;
background:repeating-linear-gradient(90deg,rgba(255,255,255,.8) 0 16px,transparent 16px 26px)}
/* A LITERAL DARK, because the strip it sits on is a literal cream (#f6e7c8, above). It used to be var(--ink) --
   which is the PAGE's text colour, near-white on this paper -- so every rider's name was white on cream and could
   not be read. Invisible until 1 Oct because the race had never been looked at on a dark screen at full width;
   same shape as the shirt cuffs that came out as dark blobs for taking var(--paper) instead of white. */
.lane-name{position:absolute;left:-104px;top:50%;transform:translateY(-50%);width:92px;overflow:hidden;text-overflow:ellipsis;
white-space:nowrap;font:700 12px/1 var(--display);color:#3a2d16;text-align:right}
.horse{position:absolute;top:50%;transform:translate(-50%,-50%);width:48px;height:42px;will-change:left;z-index:1}
.horse-img{display:block;font-size:38px;line-height:1;filter:drop-shadow(2px 3px 0 var(--shadow));transform-origin:50% 90%}
.horse-img::before{content:"\1F40E";display:block;transform:scaleX(-1)}
.horse[data-lane="0"] .horse-img{filter:hue-rotate(0deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="1"] .horse-img{filter:hue-rotate(35deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="2"] .horse-img{filter:hue-rotate(170deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="3"] .horse-img{filter:hue-rotate(260deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="4"] .horse-img{filter:hue-rotate(300deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="5"] .horse-img{filter:hue-rotate(95deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="6"] .horse-img{filter:hue-rotate(0deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="7"] .horse-img{filter:hue-rotate(35deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="8"] .horse-img{filter:hue-rotate(170deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="9"] .horse-img{filter:hue-rotate(260deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="10"] .horse-img{filter:hue-rotate(300deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="11"] .horse-img{filter:hue-rotate(95deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="12"] .horse-img{filter:hue-rotate(0deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="13"] .horse-img{filter:hue-rotate(35deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="14"] .horse-img{filter:hue-rotate(170deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="15"] .horse-img{filter:hue-rotate(260deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="16"] .horse-img{filter:hue-rotate(300deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="17"] .horse-img{filter:hue-rotate(95deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="18"] .horse-img{filter:hue-rotate(0deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.horse[data-lane="19"] .horse-img{filter:hue-rotate(35deg) saturate(1.5) drop-shadow(2px 3px 0 var(--shadow))}
.hat{position:absolute;left:16px;top:-13px;font-size:16px;line-height:1;transform:rotate(-12deg);pointer-events:none;z-index:2}
.dust{position:absolute;left:-18px;bottom:2px;width:24px;height:14px;opacity:0;pointer-events:none;
background:radial-gradient(circle at 30% 60%,#f3e6c6 0 5px,transparent 6px),radial-gradient(circle at 72% 40%,#fbf3de 0 4px,transparent 5px)}
.horse.running .horse-img{animation:gallop .32s ease-in-out infinite}
.horse.running .hat{animation:hatbob .32s ease-in-out infinite}
.horse.running .dust{animation:dust .32s linear infinite}
@keyframes gallop{0%,100%{transform:translateY(0) rotate(0) scale(1)}25%{transform:translateY(-8px) rotate(-8deg) scale(1.06,.94)}
50%{transform:translateY(-10px) rotate(-3deg) scale(1)}75%{transform:translateY(-2px) rotate(4deg) scale(.95,1.06)}}
@keyframes hatbob{0%,100%{transform:translateY(0) rotate(-12deg)}40%{transform:translateY(-6px) rotate(-26deg)}}
@keyframes dust{0%{opacity:.95;transform:translateX(0) scale(.6)}100%{opacity:0;transform:translateX(-16px) scale(1.3)}}
.track.waiting .horse{animation:fidget .9s ease-in-out infinite}
@keyframes fidget{0%,100%{transform:translate(-50%,-50%)}30%{transform:translate(-48%,-53%) rotate(3deg)}60%{transform:translate(-52%,-49%) rotate(-3deg)}}
.horse.home .horse-img{animation:homehop .6s cubic-bezier(.2,1.6,.4,1)}
@keyframes homehop{0%{transform:translateY(0) scale(1)}40%{transform:translateY(-14px) scale(1.12,.9)}100%{transform:translateY(0) scale(1)}}
.place{position:absolute;right:-16px;top:-8px;min-width:20px;padding:2px 5px;border-radius:999px;background:var(--alert);color:#fff;
font:700 12px/1 var(--display);border:2px solid var(--shadow);animation:ticketpop .5s cubic-bezier(.2,1.6,.4,1);z-index:3}
.finish-line{position:absolute;right:38px;top:0;bottom:0;width:12px;
background:repeating-conic-gradient(#000 0 25%,#fff 0 50%) 0 0/12px 12px;border-left:2px solid var(--shadow);border-right:2px solid var(--shadow)}
.finish-line::before{content:"\1F3C1";position:absolute;top:2px;left:-8px;font-size:20px;line-height:1}
@media (max-width:520px){.track{padding-left:84px;padding-right:34px;background:linear-gradient(90deg,#f6e7c8 0 78px,transparent 78px),
repeating-linear-gradient(180deg,#63bf5c 0 44px,#55ae50 44px 88px) 0 8px}.track::before{left:77px}
.lane-name{left:-78px;width:70px;font-size:10px}.finish-line{right:26px}}
@media (prefers-reduced-motion:reduce){.wheel-wrap.landed .wheel,.wheel-tickets .ticket,.horse.running .horse-img,.horse.running .hat,.horse.running .dust,.horse.home .horse-img,
.track.waiting .horse,.place{animation:none}}

/* ---------- the coins: leprechaun gold (18 Sep) ----------
   His: "make the coins leprechauny. i think each coin should have a symbol on them with a title under it." So every
   coin is a fat gold piece with a rim, a symbol STAMPED into it (the sepia filter turns any emoji into struck gold, so
   a face can be swapped for his own art without touching this), its name beneath, and a glint that wanders across.
   Held coins shine; played ones are dulled and stamped PLAYED; ones that became FAAB go to pewter.
   All decoration: a coin is spent on the server before any of this moves. */
.coin{display:inline-flex;flex-direction:column;align-items:center;gap:4px;vertical-align:middle;--i:0}
.coin-disc{position:relative;display:grid;place-items:center;width:46px;height:46px;border-radius:50%;flex:none;
background:radial-gradient(circle at 32% 28%,#fff6c2 0 10%,#ffd84a 30%,#f2b705 62%,#b47a00 100%);
border:3px solid #5e3c00;box-shadow:inset 0 0 0 3px #ffe680,inset 0 0 0 5px #b47a00,3px 4px 0 var(--shadow)}
.coin-disc::after{content:"";position:absolute;inset:0;border-radius:50%;pointer-events:none;
background:linear-gradient(115deg,transparent 42%,rgba(255,255,255,.8) 50%,transparent 58%) no-repeat;background-size:260% 100%;
background-position:130% 0;animation:coinglint 3.8s ease-in-out infinite;animation-delay:calc(var(--i)*.23s)}
@keyframes coinglint{0%,55%{background-position:130% 0}85%,100%{background-position:-130% 0}}
/* THE SYMBOL. Chosen by looking, not by reasoning: three versions were rendered side by side on the paper coins
   actually live on (Bank's cream). The first stamp was a muddy orange blur; FULL COLOUR sounded more cartoony and
   looked wrong -- Withdraw and Options turned into blue app tiles pasted on a coin, and the Collateral scales all
   but vanished, pale gold on gold. This darker strike is the one that reads as a coin. */
.coin-face{font-size:21px;line-height:1;filter:grayscale(1) contrast(1.8) brightness(.38) sepia(1) saturate(2.4) hue-rotate(-18deg);
text-shadow:0 1px 0 rgba(255,244,180,.9)}
/* ...AND THEN HE LOOKED AT IT (19 Sep): "i think the images in the coins need to be colored so they pop more." One
   rule per coin, each one his instruction word for word. The single dark strike above is what is left for anything
   without a rule here. The raccoon and the pig keep their own colours -- the filter was painting a pink pig brown.
   The two that were emoji TILES (a white arrow in a blue box) are plain arrows now, in kit.COIN_FACE, so a colour
   applies to them at all; `filter:none` is what lets the colour through, since the strike is a greyscale filter. */
.coin[data-coin="robber"] .coin-face,
.coin[data-coin="savings"] .coin-face{filter:drop-shadow(0 1px 0 rgba(94,60,0,.45)) saturate(1.15)}
.coin[data-coin="collateral"] .coin-face{filter:grayscale(1) contrast(1.05) brightness(.92)     /* steel scales */
drop-shadow(0 1px 0 rgba(94,60,0,.5))}
.coin[data-coin="withdraw"] .coin-face,
.coin[data-coin="options"] .coin-face{filter:none;color:#1a7a34;font-weight:700;
text-shadow:0 1px 0 rgba(255,244,180,.95)}
.coin[data-coin="withdraw"] .coin-face{font-size:25px}
.coin[data-coin="options"] .coin-face{font-size:24px}
.coin.small[data-coin="withdraw"] .coin-face{font-size:13px}
.coin.small[data-coin="options"] .coin-face{font-size:12.5px}
.coin.big[data-coin="withdraw"] .coin-face,.coin.big[data-coin="options"] .coin-face{font-size:64px}
/* INK, not gold. The gold-with-an-outline title was designed against the dark base -- and coins only ever appear
   inside a Bank league, which is CREAM, where it was a low-contrast smudge. --ink is dark on cream and light on
   dark, so this is right wherever a coin turns up. */
.coin-title{font:700 10.5px/1.1 var(--display);letter-spacing:.05em;text-transform:uppercase;color:var(--ink);text-align:center;
max-width:78px}
.coin-n{position:absolute;right:-7px;bottom:-1px;min-width:20px;padding:1px 4px;border-radius:999px;background:#1d6b2c;color:#fff;
font:700 10.5px/1.3 var(--display);font-style:normal;border:2px solid var(--shadow);text-align:center}
.coin.played .coin-disc{filter:saturate(.5) brightness(.78)}
.coin.cashed .coin-disc{filter:grayscale(1) brightness(.72)}
.coin.played .coin-disc::after,.coin.cashed .coin-disc::after{animation:none;background:none}
/* No "PLAYED" / "FAAB" micro-stamps across the coin: at this size they could not be read, and they covered the
   symbol -- the one thing that says WHICH coin it is. A row of coins says its state once, as a word (kit.coin_row). */
.coingroup{display:inline-flex;align-items:flex-start;gap:10px 12px;flex-wrap:wrap}
.coingroup+.coingroup{margin-left:6px;padding-left:16px;border-left:2px dashed var(--line-soft)}
.coinstate{align-self:center;font:600 10px/1.2 "IBM Plex Mono",monospace;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3)}
.coin.played .coin-title,.coin.cashed .coin-title{color:var(--ink-3)}
.coinrow{display:inline-flex;flex-wrap:wrap;gap:10px 12px;align-items:flex-start}
.coinrow .coin{animation:coindrop .62s cubic-bezier(.2,1.5,.4,1) both;animation-delay:calc(var(--i)*75ms)}
@keyframes coindrop{0%{transform:translateY(-46px) scale(.5) rotate(-40deg);opacity:0}60%{transform:translateY(0) scale(1.14,.86);opacity:1}
80%{transform:translateY(-7px) scale(.96,1.05)}100%{transform:translateY(0) scale(1)}}
.coin:hover .coin-disc,.coinbtn:hover .coin-disc{animation:coinflip .75s ease}
@keyframes coinflip{0%{transform:translateY(0) rotateY(0)}45%{transform:translateY(-9px) rotateY(180deg)}100%{transform:translateY(0) rotateY(360deg)}}
.coin.small{flex-direction:row}
.coin.small .coin-disc{width:22px;height:22px;border-width:2px;box-shadow:inset 0 0 0 1.5px #ffe680,1px 2px 0 var(--shadow)}
.coin.small .coin-face{font-size:11px}
.coinbtn{display:inline-flex;align-items:center;gap:7px}
.coinbtn:active .coin-disc{transform:scale(.82)}
.coinpick>summary.coinbtn{display:inline-flex}
.coin.big .coin-disc{width:124px;height:124px;border-width:6px;box-shadow:inset 0 0 0 6px #ffe680,inset 0 0 0 10px #b47a00,6px 8px 0 var(--shadow)}
.coin.big .coin-face{font-size:58px}
.coin.big .coin-title{display:none}
.pursebar{display:flex;flex-wrap:wrap;align-items:center;gap:10px 18px;border-bottom:2px dashed var(--line-soft)}
.pursebar .btnrow{margin:0}
/* the purse itself, on the Coin Purse page: a leprechaun's green pouch that jiggles, with gold bobbing out of the top */
.hero.with-art{position:relative;padding-right:214px;min-height:168px}
.purse-art{position:absolute;right:18px;top:50%;width:178px;transform:translateY(-50%);pointer-events:none}
.purse-svg{width:100%;height:auto;display:block;overflow:visible;animation:pursejiggle 3.1s ease-in-out infinite;transform-origin:50% 100%}
.purse-bag{fill:#2f8a3e;stroke:var(--shadow);stroke-width:4;stroke-linejoin:round}
.purse-neck{fill:#226b2e;stroke:var(--shadow);stroke-width:4;stroke-linejoin:round}
.purse-string{fill:none;stroke:#ffd84a;stroke-width:4;stroke-linecap:round}
.purse-clover{font-size:34px;fill:#9be8a6;text-anchor:middle;dominant-baseline:middle}
.pc{fill:#ffd84a;stroke:#5e3c00;stroke-width:3;transform-box:fill-box;transform-origin:center;animation:pcbob 2.2s ease-in-out infinite}
.pc2{animation-delay:.35s}.pc3{animation-delay:.7s}
.pc-shine{fill:none;stroke:#fff6c2;stroke-width:2.5;stroke-linecap:round}
.purse-spark{font-size:18px;fill:#fff6c2;animation:sparkle 1.9s ease-in-out infinite;transform-box:fill-box;transform-origin:center}
.purse-spark.s2{animation-delay:.6s}.purse-spark.s3{animation-delay:1.2s}
@keyframes pursejiggle{0%,100%{transform:rotate(0) scale(1)}8%{transform:rotate(-3deg) scale(1.03,.97)}16%{transform:rotate(3deg) scale(.98,1.03)}24%{transform:rotate(-1.5deg)}32%{transform:rotate(0)}}
@keyframes pcbob{0%,100%{transform:translateY(0)}50%{transform:translateY(-9px)}}
@keyframes sparkle{0%,100%{opacity:0;transform:scale(.3) rotate(0)}50%{opacity:1;transform:scale(1) rotate(40deg)}}
@media (max-width:620px){.hero.with-art{padding-right:22px;min-height:0}.purse-art{position:static;transform:none;width:132px;margin:4px auto 0}}
/* ---------- THE HOME SCREEN, his layout of 18 Sep ----------
   "i like how sleek it is, lets keep it sleek, but lets make the active leagues more interesting looking, their boxes
   can be bigger, and the retired leagues can be the same size they are now, but less important with no highlights."
   An ACTIVE league's card is two columns: who it is on the left, what is happening in it on the right -- the space that
   used to be empty. A retired card is exactly what it was. On a phone the two columns stack. */
/* HIS WIDTHS, and they are fractions of the row, NOT "share out whatever is left": an active league is a HALF (it has
   the highlights to fill it), a retired one is a THIRD. `repeat(N, 1fr)` rather than auto-fit is the whole point --
   auto-fit let two retired leagues stretch to half the screen each, empty. A third stays a third with two of them. */
.lobbycards.halves{grid-template-columns:repeat(2,minmax(0,1fr))}
.lobbycards.thirds{grid-template-columns:repeat(3,minmax(0,1fr))}
.lobbycards.halves+.lobbycards.thirds{margin-top:6px}
@media (max-width:980px){.lobbycards.halves{grid-template-columns:minmax(0,1fr)}
.lobbycards.thirds{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:620px){.lobbycards.thirds{grid-template-columns:minmax(0,1fr)}}
/* The left side holds a name, one line and a button; the right holds the highlights. Split nearly evenly (1 : 1.15) it
   squeezed a two-column draft order until thirteen-letter names were cut off. The first fix went too far the other way
   (.62 : 1.38) and the LEFT side wrapped instead -- "Pre-Alpha / Testing", "BANK - / 2026". Both were only visible by
   photographing the page. This is the split where neither side wraps at the width the page actually has. */
.leaguecard.big{display:grid;grid-template-columns:minmax(224px,.82fr) minmax(0,1.18fr);align-items:stretch}
.leaguecard.big .cardmain{padding-top:15px;display:flex;flex-direction:column;gap:2px}
.leaguecard.big .cardmain h2{font-size:24px;margin-top:4px}
.leaguecard.big .cardmain .btnrow{margin-top:auto;padding-top:12px}
.leaguecard .cardmain{padding-top:15px}
/* MANAGE LEAGUES is a workbench, not a shop window (his, 18 Sep: "a more technical non presentation based page"):
   one league to a row, dense, plain buttons. A league hidden from the home screen is dimmed, not moved -- it keeps its
   place in the list, and the Home Screen column says why it looks different. */
.managetable td{vertical-align:middle}
.managetable .pname{font-weight:700}
.managetable .meta.num{font-size:11.5px}
/* A name and an id are ONE LINE EACH. Left to the browser they wrapped at every space and every hyphen --
   "pre-/alpha-/testing" down three lines -- which is the opposite of a list you can scan. */
.managetable .leaguecell,.managetable .nowrap{white-space:nowrap}
.hiddentag{margin-left:6px;padding:0 6px;border:1.5px dashed var(--ink-3);border-radius:999px;
font:600 9.5px/1.5 "IBM Plex Mono",monospace;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.managetable tr.hiddenrow td:not(:last-child){opacity:.55}
.btnrow.tight{gap:6px;flex-wrap:nowrap;margin:0}
.btnrow.tight .ghost{padding:4px 10px;font-size:12.5px}
.hl{padding:14px 16px 14px 18px;border-left:2px dashed var(--line-soft);display:flex;flex-direction:column;gap:5px;min-width:0}
.hl-head{margin:0;font:700 12px/1.4 var(--display);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2);
display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px}
.hl-sub{margin:6px 0 0;font:600 10.5px/1.3 "IBM Plex Mono",monospace;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3)}
.hl-line{margin:0;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hl-line .meta{font:600 10.5px/1.3 "IBM Plex Mono",monospace;letter-spacing:.08em;text-transform:uppercase;margin-right:6px}
.hl-score{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:8px;margin-top:2px}
.hl-side{display:flex;align-items:baseline;gap:8px;min-width:0}
.hl-side+.hl-vs+.hl-side{justify-content:flex-end}
.hl-side b{font:700 26px/1 var(--display);color:var(--ink-2)}
.hl-side.up b{color:var(--accent)}
.hl-team{font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.hl-vs{font:600 10px/1 "IBM Plex Mono",monospace;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.hl-best{list-style:none;margin:2px 0 0;padding:0;display:flex;flex-direction:column;gap:2px;counter-reset:best}
.hl-best li{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:baseline;gap:8px;font-size:13px;
padding:2px 0;border-bottom:1px solid var(--line-soft)}
.hl-best li:last-child{border-bottom:none}
.hl-who{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hl-best .meta{font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:9em}
.hl-best b{font:700 14px/1 var(--display);color:var(--accent)}
/* Before anything has happened there is no score, only who plays whom: the two names, centred on the "vs". */
.hl-score.quiet .hl-team{font:700 15px/1.2 var(--display)}
.hl-score.quiet .hl-side:first-child{justify-content:flex-end}
.hl-score.quiet .hl-side:last-child{justify-content:flex-start}
/* BETWEEN SEASONS: the draft order, two columns of it so ten or twelve names fit the card without a scrollbar. The
   pick number is the point, so it gets the loud face; YOUR line is marked, because it is the one you came for. */
.hl-order{list-style:none;margin:2px 0 6px;padding:0;columns:2;column-gap:16px}
.hl-order li{display:flex;align-items:baseline;gap:8px;padding:2px 0;font-size:13px;break-inside:avoid;min-width:0}
.hl-pick{flex:none;width:1.6em;text-align:right;font:700 14px/1 var(--display);color:var(--ink-3)}
.hl-order .hl-who{font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hl-order li.you .hl-pick,.hl-order li.you .hl-who{color:var(--accent)}
/* Its own element, and it never shrinks: if the line is too long the NAME gives way, not the tag. (As an ::after on
   the name it sat inside the part that truncates, and the real card read "maxsjam <-...".) */
.hl-youtag{flex:none;padding:0 6px;border-radius:999px;background:var(--accent);color:#fff;
font:700 9.5px/1.55 var(--display);letter-spacing:.08em;text-transform:uppercase}
.hl-head .meta{font:600 10px/1.3 "IBM Plex Mono",monospace;letter-spacing:.08em;text-transform:none;color:var(--ink-3)}
.hl-live{display:inline-flex;align-items:center;gap:5px;color:var(--alert);font:700 10.5px/1 var(--display);letter-spacing:.08em}
.hl-live i{width:7px;height:7px;border-radius:50%;background:var(--alert);animation:livepulse 1.2s ease-in-out infinite}
@media (max-width:620px){.leaguecard.big{grid-template-columns:1fr}
.hl{border-left:none;border-top:2px dashed var(--line-soft);padding:12px 16px 14px}}

/* ---------- THE TOP BAR: Home, a gear, your picture (18 Sep) ---------- */
.iconbtn{padding:7px 9px;line-height:0}
.gear{display:block;transition:transform .5s cubic-bezier(.3,1.4,.5,1)}
.iconbtn:hover .gear,.iconbtn:focus-visible .gear{transform:rotate(120deg)}
.profilebtn{display:inline-flex;align-items:center;border-radius:50%;line-height:0;text-decoration:none;
transition:transform .15s cubic-bezier(.2,1.6,.4,1)}
.profilebtn:hover,.profilebtn:focus-visible{transform:scale(1.1) rotate(-4deg)}
.profilebtn[aria-current="page"] .avatar{border-color:var(--accent)}
.avatar{--c:#8f5cff;display:inline-grid;place-items:center;flex:none;width:64px;height:64px;border-radius:50%;overflow:hidden;
background:var(--c);border:3px solid var(--edge);box-shadow:3px 3px 0 var(--shadow);font-size:32px;line-height:1}
.avatar img{width:100%;height:100%;object-fit:cover;display:block}
.avatar.small{width:36px;height:36px;border-width:2px;box-shadow:2px 2px 0 var(--shadow);font-size:18px}
.avatar.big{width:96px;height:96px;font-size:48px}
.avatar.huge{width:112px;height:112px;font-size:58px;border-width:4px}
.profilehero{flex-direction:row;align-items:center;gap:20px;flex-wrap:wrap}
.picrow{display:flex;flex-wrap:wrap;gap:22px;align-items:flex-start}
.picforms{display:flex;flex-direction:column;gap:16px;flex:1 1 300px;min-width:0}
.facegrid,.colourgrid{display:flex;flex-wrap:wrap;gap:7px;margin:4px 0 8px}
.facepick input,.colourpick input{position:absolute;opacity:0;pointer-events:none}
.facepick span{display:grid;place-items:center;width:44px;height:44px;border-radius:12px;font-size:24px;cursor:pointer;
background:var(--panel-2);border:2px solid var(--line);transition:transform .12s}
.colourpick span{display:block;width:34px;height:34px;border-radius:50%;background:var(--c);cursor:pointer;
border:3px solid var(--line);transition:transform .12s}
.facepick span:hover,.colourpick span:hover{transform:scale(1.12)}
.facepick input:checked+span,.colourpick input:checked+span{border-color:var(--edge);box-shadow:0 0 0 3px var(--accent);transform:scale(1.08)}
.facepick input:focus-visible+span,.colourpick input:focus-visible+span{outline:3px solid var(--accent);outline-offset:3px}
.filebtn{cursor:pointer}
.ghost.on,.ghost[aria-pressed="true"]{background:var(--accent);color:#fff;border-color:var(--accent)}
/* CALM, chosen in App Settings. One blunt rule on purpose: whatever animates now, and whatever is added next year,
   stops -- without anybody having to remember to write a second rule for it. Scripts ask for the same class. */
.calm *,.calm *::before,.calm *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
/* Two things that LEAVE by animating, so with animation off they would never leave. The played-coin toast takes itself
   away with a fade; stopped, it would sit over the page for good. The coin is already spent and the page already says
   so -- the toast was only ever the fuss -- so in Calm there is simply no toast. Same for the burst's layer. */
.calm .coin-toast,.calm .coinburst{display:none}
.calm::view-transition-old(root),.calm::view-transition-new(root){animation:none}
@media (prefers-reduced-motion:reduce){.gear,.profilebtn,.facepick span,.colourpick span{transition:none}
.iconbtn:hover .gear,.profilebtn:hover{transform:none}}

/* THE WAY IN (18 Sep): the purse sits in the bottom right corner of every league page and fidgets. "moving a little
   so you know its an interaction" -- a still picture in a corner reads as decoration; one that shuffles its feet
   reads as something to press. It hops when you point at it. z-index 30: over the page, under every menu (40+). */
.pursefab{position:fixed;right:18px;bottom:16px;z-index:30;width:96px;display:block;line-height:0;text-decoration:none;
filter:drop-shadow(4px 5px 0 var(--shadow));transition:transform .18s cubic-bezier(.2,1.6,.4,1)}
/* THE PURSE READS THE SAME SIZE AS THE MESSAGE BUBBLE, which it did not. His, 10 Oct: "the coin purse message
   icon needs to the be the same size as the message icon when they are on the same page on the bottom right."
   BOTH LINKS WERE ALREADY 96px AND THAT WAS THE TRAP -- measuring the boxes said they matched, and looking at
   the page said otherwise. The bubble's drawing fills its box edge to edge; the purse's sits inside its own
   with a margin all round, so the same 96px box gave a glyph about a third smaller. What has to match is the
   INK, not the box, so the purse's box is grown until the two drawings read alike. */
/* AND IT SITS THE SAME DISTANCE FROM THE WALL, which it did not -- his, 10 Oct: "the sizes seem fine but they
   arent teh same distance from the wall probably because of the sparkle effects of the coin purse." He had it
   right. The purse's drawing is 200 units wide and the BAG ends at 172 of them; the rest is sparkle and air. So
   a box pinned 18px off the edge put the bag 18px further in again, while the bubble's art fills its box and
   lands where it is told.
   The offset is worked out FROM the drawing rather than typed as a number that happens to look right: 28 of 200
   units is 14% of whatever width the box is given, so changing the size below keeps the two lined up. */
/* AND THE SAME SIZE AS THE BUBBLE IT SITS ABOVE (his, 10 Oct: "the coin purse message icon should be the same
   size as the message icon when both sit bottom right"). It was 128px wide against the bubble's 96, and its
   drawing is 200x172, so it stood 110px tall against the bubble's 84 -- a third bigger in the one place the
   eye has something to compare it with. MEASURED IN THE BROWSER rather than read off the declarations, which
   both say 96px: the override on this line is what made the difference, and the bag inside it renders at 62%
   of the box and not the 86% the note above assumes.
   HEIGHT IS WHAT MATCHES, because the two are stacked one above the other and that is the dimension the eye
   lines up. 84px tall at 200/172 is 98px wide, which is also within two pixels of the bubble's width. */
.pursefab{--purse-w:98px;width:var(--purse-w);right:calc(18px - var(--purse-w) * 0.14)}
.pursefab .purse-svg{animation:pursejiggle 3.4s ease-in-out infinite}
.pursefab:hover,.pursefab:focus-visible{transform:translateY(-7px) scale(1.09) rotate(-3deg)}
.pursefab:hover .purse-svg{animation-duration:.9s}
.pursefab:active{transform:translateY(0) scale(.94)}
.pursefab:focus-visible{outline:3px solid var(--accent);outline-offset:4px;border-radius:18px}
/* ---------- MESSAGES IN THE CORNER (his, 28 Sep) ----------
   "i think messages shouyld be in the bottom right, and it needs to be more poppy." Built like the purse: a
   thing sitting on the page with weight and a hard shadow, not a word in a list. It opens as a SPILL over the
   page you are on, because a conversation is something you want open WHILE doing something else.
   THE PURSE MOVES UP where both appear -- only the matchup page and the trade desk, since the purse was cut to
   those two. Two things in one corner is the mess he opened the conversation complaining about. */
.msgfab{position:fixed;right:18px;bottom:16px;z-index:30;width:96px;display:block;line-height:0;
text-decoration:none;--msg-paper:#f4efe2;
filter:drop-shadow(4px 5px 0 var(--shadow));transition:transform .18s cubic-bezier(.2,1.6,.4,1)}
.msgfab .msgart{width:100%;height:auto;display:block;animation:msgtilt 4.2s ease-in-out infinite}
.msgfab:hover .msgart{animation-duration:1s}
@keyframes msgtilt{0%,88%,100%{transform:rotate(0deg)}92%{transform:rotate(-5deg)}96%{transform:rotate(4deg)}}
@media (prefers-reduced-motion:reduce){.msgfab .msgart{animation:none}}
.msgfab:hover,.msgfab:focus-visible{transform:translateY(-7px) scale(1.09) rotate(-3deg)}
.msgfab:active{transform:translateY(0) scale(.94)}
.msgfab:focus-visible{outline:3px solid var(--accent);outline-offset:4px;border-radius:14px}
/* The count: a red disc, the same shape as the green one on the coin sack. ONE bounce when the page loads, then
   still -- a badge that flashes for ever is a badge nobody looks at. */
.msgcount{position:absolute;top:-10px;right:-8px;min-width:26px;height:26px;padding:0 7px;border-radius:999px;
background:#d7263d;color:#fff;border:3px solid var(--shadow);box-sizing:border-box;
font:800 13px/20px var(--display);text-align:center;font-style:normal;
animation:msgpop .7s cubic-bezier(.2,1.7,.4,1) 1}
@keyframes msgpop{0%{transform:scale(.2)}60%{transform:scale(1.25)}100%{transform:scale(1)}}
.pursefab.raised{bottom:126px}
@media (prefers-reduced-motion:reduce){.msgcount{animation:none}}
.purse-word{font:800 25px var(--display);letter-spacing:.07em;fill:#ffd84a;text-anchor:middle;dominant-baseline:middle;
paint-order:stroke;stroke:var(--shadow);stroke-width:5px;stroke-linejoin:round}
/* Room at the foot of the page, so the purse never sits on top of the last button of a long one, comes from the page's
   own bottom padding plus the small print under it (about 130px; the purse is 95). It USED to come from 132px of padding
   under the small print -- which is exactly what stopped the small print reaching the bottom of the screen: the box was
   pasted to the bottom and the words floated at the top of it. The small print keeps clear of the purse sideways
   instead (footer.beside's right padding). */
/* On a phone BOTH shrink, or the corner holds a 72px purse above a 96px envelope. Only the purse was
   named here, which is the same fault as above wearing a different hat. */
@media (max-width:620px){.pursefab,.msgfab{width:72px;bottom:12px}
.msgfab{right:12px}
.pursefab{--purse-w:74px;right:calc(12px - var(--purse-w) * 0.14)}   /* 72px tall, as the bubble is here */
.pursefab.raised{bottom:98px}}
@media print{.pursefab{display:none}}

/* THE BURST (18 Sep): "the coin purse gets bigger and coins go everywhere, changing the screen to that other menu."
   The motion is in kit.COIN_BURST, because where a coin flies depends on the size of the screen it is flying across.
   What the coins, the swollen purse and the gold flash LOOK like is here, and is his to repaint. Over everything
   (z-index 90) because for that second it IS the screen -- and it never takes a click: pointer-events none. */
.coinburst{position:fixed;inset:0;z-index:90;pointer-events:none;overflow:hidden;contain:strict}
.burstpurse{position:fixed;height:auto;display:block;overflow:visible;transform-origin:50% 62%;animation:none;
filter:drop-shadow(6px 9px 0 var(--shadow))}
.burstcoin{position:fixed;left:0;top:0;display:block;border-radius:50%;will-change:transform;
background:radial-gradient(circle at 32% 28%,#fff6c2 0 10%,#ffd84a 30%,#f2b705 62%,#b47a00 100%);
border:3px solid #5e3c00;box-shadow:inset 0 0 0 3px #ffe680,inset 0 0 0 5px #b47a00}
.burstflash{position:fixed;width:36px;height:36px;margin:-18px 0 0 -18px;border-radius:50%;transform:scale(0);
background:radial-gradient(circle,#fff6c2 0,#ffd84a 42%,#f2b705 100%)}
.burstflash.full{inset:0;width:auto;height:auto;margin:0;border-radius:0;transform:none;
background:radial-gradient(circle at 50% 40%,#fff6c2 0,#ffd84a 45%,#f2b705 100%)}
.pursefab.bursting{pointer-events:none}
@media (prefers-reduced-motion:reduce){.coinburst{display:none}}

/* EVERY MATCHUP OF THE WEEK, IN A ROW UNDER THE ARROWS (18 Sep: "change people and date all at once"). The arrows are the
   date; this is the people. Each pair is one press: both names, both totals, the winner in bold, the one you are
   reading lit. It is also the week's scoreboard, so nothing else on the page repeats it. Scrolls sideways on a phone. */
.pairstrip{display:flex;gap:8px;flex-wrap:nowrap;overflow-x:auto;justify-content:safe center;margin:6px 0 14px;
padding:2px 2px 8px;scrollbar-width:thin}
.pair{flex:none;display:flex;flex-direction:column;gap:1px;min-width:132px;padding:6px 11px;border-radius:12px;
border:2px solid var(--line);background:var(--panel);color:var(--ink-2);text-decoration:none;transition:transform .12s}
.pair:hover{border-color:var(--edge);transform:translateY(-1px)}
.pair.on{border-color:var(--accent);background:var(--panel-2);box-shadow:2px 3px 0 var(--shadow);color:var(--ink)}
.pairside{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-size:12.5px;line-height:1.45}
.pairside b{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px}
.pairside i{font:600 12px/1.4 "IBM Plex Mono",monospace;font-style:normal;font-variant-numeric:tabular-nums}
.pairside.won b,.pairside.won i{font-weight:700;color:var(--ink)}
.pair.alone{justify-content:center;min-width:0}      /* nobody to play that week: one line, no pairing */

/* TABS (18 Sep): "why does draft room, draw the order, and mock draft, and draft log all need their own page." They
   do not need their own MENU entries: one entry, Draft, and these across the top of each other. The lit one is where
   you are, which is why these pages carry no "Section > Page" crumb as well. Scrolls sideways on a phone. */
.tabs{display:flex;gap:6px;flex-wrap:nowrap;overflow-x:auto;margin:0 0 -6px;padding:2px 2px 8px;scrollbar-width:thin}
.tab{flex:none;padding:7px 14px;border-radius:999px;border:2px solid var(--line);background:var(--panel);color:var(--ink-2);
font:600 13px/1.2 var(--display);text-decoration:none;white-space:nowrap;transition:transform .12s}
.tab:hover{border-color:var(--edge);color:var(--ink);transform:translateY(-1px)}
.tab.on{background:var(--accent);border-color:var(--accent);color:#fff;box-shadow:2px 3px 0 var(--shadow)}
/* starting the draft, in the Draft Room's own heading: one number and one button on a line, the button level with the box */
.startdraft{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;margin-top:14px}
/* THE BOARD'S CONTROLS, in three lines that breathe (his, 29 Sep: "some of the spacing is crammed"). Positions,
   then the filters, then your own columns. Every line is the same height whatever is in it, and the boxes carry
   their label above them rather than a word jammed against the control.
   THE FIRST LINE NEEDS ROOM ABOVE IT: "the position boxes are touching the ceiling" (29 Sep). The rule that took
   the margin off the first row left the chips against the panel's own edge, and a chip's drop shadow made it
   look like it had been cut off. The panel's padding does the work now. */
.boardcontrols>.pad{padding:16px 16px 18px}
.boardcontrols .btnrow:first-child{margin-top:0}
.boardcontrols .btnrow{margin:0;row-gap:8px}
.boardpos{gap:7px}
.boardfilters{display:flex;flex-wrap:wrap;align-items:flex-end;gap:10px;margin:12px 0 0}
.boardfilters input[type=text]{font-family:inherit;font-size:13.5px;padding:8px 12px;min-width:190px;flex:1 1 190px;
border:2px solid var(--line);border-radius:10px;background:var(--panel-2);color:var(--ink)}
.boardfilters input[type=text]:focus{outline:none;border-color:var(--accent)}
.boardfilters .chip,.boardfilters button{align-self:flex-end}
.boardbox{display:flex;flex-direction:column;gap:3px}
.boardbox .eyebrow{margin:0;font-size:10px}
.boardbox select{font-family:inherit;font-size:13.5px;padding:7px 10px;border:2px solid var(--line);
border-radius:10px;background:var(--panel-2);color:var(--ink);cursor:pointer}
.boardbox select:focus{outline:none;border-color:var(--accent)}
.boardcontrols .btnrow:last-child{margin-top:12px}
/* THE BOARD IN A PANEL (29 Sep): it fills the sheet, its controls at the top, and it scrolls inside itself so the
   controls stay put while you read down it. The button that opens it is the biggest thing on the Draft page. */
/* A MAN WHO HAS GONE stays on the board, struck through and greyed, with the team that took him and the pick
   beside his name -- the draft tool's own way of showing who took who (his, 29 Sep). Your own picks are marked
   in the accent rather than greyed: they are the ones you are building. */
.board tr.drafted td{color:var(--ink-3)}
.board tr.drafted .pname{text-decoration:line-through}
.board tr.drafted .rook,.board tr.drafted .news{opacity:.45}
.board tr.mine td{color:var(--ink-2)}
.board tr.mine .pname{text-decoration:none;color:var(--accent)}
/* WHICH OF YOUR PICKS HE FALLS TO -- the draft tool's own tag, and the thing that makes the board a plan rather
   than a ranking. The one you are choosing at right now shouts; the rest state the round quietly. */
/* THE STAR (his, 29 Sep: "a favorites system, where people press their favorites and sort by them"). A button
   with the browser's own chrome stripped, so it is the star and nothing else; gold when it is one of yours. */
.starbtn{appearance:none;background:none;border:0;padding:0 2px;margin:0;cursor:pointer;line-height:1;
font-size:15px;color:var(--ink-3)}
.starbtn:hover{color:#ffd24a}
.starbtn.on{color:#ffd24a}
.starbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
.board th.c-fav,.board td.c-fav{width:30px;padding-left:6px;padding-right:0}
.landtag{margin-left:8px;padding:1px 7px;border-radius:999px;white-space:nowrap;
font:700 10px/1.6 "IBM Plex Mono",monospace;letter-spacing:.04em;
border:1.5px solid var(--line);color:var(--ink-3)}
.landtag.now{background:var(--accent);border-color:var(--accent);color:#fff}
/* Your roster, filling up: slot on the left, who is in it on the right, the holes stated rather than blank. */
.slotrow{display:flex;align-items:center;gap:10px;padding:5px 2px;border-bottom:1px solid var(--line-soft)}
.slotrow:last-child{border-bottom:none}
.slotname{flex:0 0 46px;font:700 11px/1.4 "IBM Plex Mono",monospace;letter-spacing:.06em;color:var(--ink-3)}
.slotwho{font-weight:600}
.slotrow.empty .slotwho{color:var(--ink-3);font-weight:400;font-style:italic}
.slotrow.bench .slotname{color:var(--ink-3);opacity:.7}
/* FILLS -- what he plugs for YOU. Three weights, as the draft tool draws them: a dedicated slot is lit, the G/F
   flex is plainer, UTIL and the bench are quiet, and a dash means there is no room for him at all. */
/* YOUR PICKS and TARGETS: a row is the round, the man, and how sure it is. The share is the point, so it is the
   thing that changes colour -- a name at 80% is a plan, the same name at 30% is one of four things. */
.projrow .slotname{flex:0 0 38px}
.projrow{display:flex;align-items:baseline;gap:10px;padding:5px 0;border-bottom:1px solid var(--line-soft)}
.projrow:last-child{border-bottom:0}
.projwho{flex:1;min-width:0}
.projalts{display:block;font:500 10.5px/1.4 "IBM Plex Mono",monospace;color:var(--ink-3)}
.projshare{font:700 12px/1 "IBM Plex Mono",monospace;color:var(--ink-2);font-variant-numeric:tabular-nums}
.projrow.sure .projshare{color:var(--accent)}
.projrow.open .projshare{color:var(--ink-3)}
/* COST: what passing on him costs. Nearly everyone costs nothing, which is the finding -- so only the ones that
   do are coloured, and the rest stay out of the way. */
.costv{font-variant-numeric:tabular-nums;color:var(--ink-3)}
.costv.warm{color:var(--ink)}
.costv.hot{color:var(--alert);font-weight:700}
/* THE ROOM: one card per seat, and a chip per fitted trait. */
.mgrs{display:grid;grid-template-columns:repeat(auto-fill,minmax(215px,1fr));gap:10px}
.mgr{border:1.5px solid var(--line-soft);border-radius:12px;padding:9px 10px;min-width:0}
.mgr.you{border-color:var(--accent);background:var(--accent-soft)}
.mgrhead{display:flex;align-items:center;gap:7px;margin-bottom:6px}
.mgrhead .slotname{flex:0 0 auto}
.mgrhead b{font:700 13px/1.2 var(--display)}
.mgr .chips{display:flex;flex-wrap:wrap;gap:4px}
.mgrchip{font:600 10px/1.5 "IBM Plex Mono",monospace;letter-spacing:.03em;padding:1px 6px;border-radius:999px;
border:1px solid var(--line-soft);color:var(--ink-2);cursor:help}
.mgrchip.t-club{border-color:var(--accent);color:var(--accent)}
.mgrchip.t-risk{border-color:var(--accent-2);color:var(--accent-2)}
.mgrchip.t-draft,.mgrchip.t-new{border-color:var(--alert);color:var(--alert)}
.mgr .sample{margin-top:6px;font:500 10px/1.4 "IBM Plex Mono",monospace;color:var(--ink-3)}
.fills{font:600 11px/1.4 "IBM Plex Mono",monospace;letter-spacing:.04em;color:var(--ink-3)}
.fills.f0{color:var(--accent);font-weight:700}
.fills.f1{color:var(--ink-2)}
.fills.none{color:var(--ink-3);opacity:.55}
.tookby{margin-left:8px;font:600 11px/1.4 "IBM Plex Mono",monospace;color:var(--ink-3);white-space:nowrap}
.board tr.mine .tookby{color:var(--accent)}
.boardspill{grid-column:1/-1;display:flex;flex-direction:column;gap:12px;min-width:0}
/* THE GRID TAKES WHAT IT NEEDS, and the PAGE scrolls (his, 30 Sep: "extend the draft grtid to go a bit lower so
   we can see more picks"). It was capped at 62vh, so fourteen rounds lived in a box of about eight and you
   scrolled inside a panel inside a page -- two scrollbars for one list. With the log box gone from under it
   there is room, and one scrollbar is the right number. The PANEL keeps its own sizing further down, where the
   grid has a sheet to fill rather than a page to run down. */
.boardspill .tablewrap{max-height:none}
/* THE BOARD AND THE RAIL BESIDE IT, which is how the draft tool lays the room out and where he asked these to
   live (29 Sep): "the targets and roster need to be on the draft room overlay". The board takes the room it
   needs and the rail is a fixed column, so the two are read together. Under 1100px it stacks, because a rail
   squeezed to nothing is worse than a rail underneath. */
.roomcols{display:flex;gap:12px;align-items:stretch;min-width:0}
.roomboard{flex:1 1 auto;min-width:0;display:flex;flex-direction:column}
/* EVERY BOX IN THE RAIL KEEPS ITS OWN BOTTOM EDGE, exactly as the board's box does. His, 30 Sep: "all you have
   to do is put a border at the bottom that always shows for each box, like you did for the actual player box on
   the draft board."
   Before this the RAIL scrolled, which meant it scrolled the boxes themselves -- so whichever one happened to
   lie across the bottom edge was sliced through the middle, and its border was off-screen below the cut. There
   was nothing to draw an edge on. Now the rail does not scroll at all: the boxes share its height, and each one
   scrolls its own contents inside a frame that stays put. Same arrangement as the board beside it.
   (An earlier note here said sharing the height clipped every box -- it did, because the bodies could not
   scroll. That is what the last rule below fixes, so the share is safe now.) */
.roomrail{flex:0 0 300px;display:flex;flex-direction:column;gap:10px;min-width:0;overflow:hidden}
/* A BOX IS AS TALL AS WHAT IS IN IT (his, 30 Sep: "why does the roster box extend past the amount of positions
   there are. it should expand or decrease in size depending on the positions"). `flex:1 1 0` gave every box an
   equal SHARE of the rail, so a roster of fourteen slots and a roster of four drew the same box with empty space
   under the short one. `0 1 auto` is its own height -- it grows with the slots and shrinks with them.
   It may still SHRINK, which is what keeps the edge he asked for: when there is more than the rail can hold the
   boxes give way in proportion and each scrolls its own contents inside a frame that stays put. Content height
   normally, a framed window when there is too much -- never a box sliced by the bottom of the screen. */
.roomrail>.panel{flex:0 1 auto;min-height:0;display:flex;flex-direction:column}
.roomrail>.panel>h2,.roomrail>.panel>.note{flex:0 0 auto}
.roomrail>.panel>.pad,.roomrail>.panel>.tablewrap{flex:1 1 auto;min-height:0;overflow:auto;max-height:none}
/* BOTH RUN TO THE BOTTOM OF THE SCREEN (his, 29 Sep: "the roster and targets doesnt seem to be going all the
   way to the bottom of the screen. i dont know why"). They were capped at 62vh inside a sheet that could be
   86vh tall, so a band of blurred page showed under both of them. The sheet takes a real height now and the
   board and the rail divide what is left after the bar and the controls -- no arithmetic, just flex. */
/* BOTH FACES OF THE DRAFT ROOM, not just the players. Keyed on the switch, which is the one thing the player
   list and the picks grid have in common -- the grid was left at the 1120px every other panel uses while the
   players had 1760, so switching to the Picks made the room shrink (his, 29 Sep: "increase the size of the
   draftboard"). One overlay, one size. */
.spillwrap.tall:has(.spillswitch){align-items:stretch;padding-bottom:20px}
.spillwrap.tall:has(.spillswitch) .spillsheet{max-height:none;display:flex;flex-direction:column;min-height:0}
/* A COLUMN OF FLEX ALL THE WAY DOWN, not a percentage through a grid. `.spillcols` is a two-column grid for
   every other panel, and `height:100%` on a grid item whose row is sized by that item is circular: the board
   measured 452px tall with an 798px child hanging out of it. Here it is a flex column, so "the rest of the
   height" is a thing the browser can answer. */
/* AND align-items MUST BE RESET WITH IT. `.spillcols` carries `align-items:start` for the grid, where it means
   "two coin panels, top-aligned". Turning the box into a flex COLUMN turns that into a cross-axis rule, so it
   meant "shrink to your content and sit on the left" instead: the sheet was correctly centred at 1760 wide while
   the room inside it measured 1446 and hugged the left edge, leaving 314px of blurred page down the right. His,
   30 Sep: "why is this page not centered. it seems to center to the left." Measured, not guessed. */
.spillwrap.tall:has(.spillswitch) .spillcols{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;
align-items:stretch}
.boardspill:has(.spillswitch){display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
/* The switch takes what it needs; only the two columns stretch. Without this the controls were squeezed to 59px
   of their 211 and everything but the position chips was cut off. */
.boardspill>.btnrow{flex:0 0 auto}
.boardspill>.roomcols{flex:1 1 auto;min-height:0}
/* THE PANEL IS BETWEEN THEM. The board column holds a <section class="panel">, and the scrolling table is inside
   that -- so `.roomboard>.tablewrap` matched nothing at all and the table was sized by something else: measured
   30 Sep at 387..1008 inside a sheet that ends at 982, hanging 26px past the bottom of the room with its last
   row cut off. The panel has to become a column too, or there is nothing for the table to take the rest of. */
.roomboard>.panel{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
.roomboard>.panel>.note{flex:0 0 auto}
.roomboard>.panel>.tablewrap{flex:1 1 auto;min-height:0;max-height:none}
/* THE CONTROLS SIT INSIDE THE BOARD'S COLUMN NOW (his, 30 Sep: "make the roster thing extend upward where
   favorites and rookies were so the whole box is being shown"). They were a full-width band across the top,
   which pinned the rail to start underneath them -- so the rail was only ever as tall as the board and Your
   Roster was cut off at the bottom. The search belongs to the board it sits over, so it moved into that column
   and the rail now runs the whole height beside both.
   AFTER the rule above, deliberately: both selectors are two classes deep, so whichever is written last wins,
   and the controls must NOT be the thing that stretches. The board takes the rest. */
.roomboard>.boardcontrols{flex:0 0 auto;display:block;margin-bottom:10px}
/* THE PICKS FACE FILLS THE SAME ROOM. Its panel is the only thing under the switch, so it takes the height and
   the grid grows into it -- 156 squares across the whole overlay rather than a strip down the middle. */
.boardspill.pickboard:has(.spillswitch)>.panel{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
.boardspill.pickboard:has(.spillswitch)>.panel>.tablewrap{flex:1 1 auto;min-height:0;max-height:none}
/* And the SQUARES grow, not the table. Stretching the table to 100% shares the spare height out in proportion
   to what is already in each row, so the round that had picks in it came out three times the height of the
   empty ones -- the same unevenness he had just had taken off the player board. A stated square height gives
   every round the same one. */
.boardspill.pickboard:has(.spillswitch) table.draftgrid td.dbcell{height:58px}
.boardspill.pickboard:has(.spillswitch) table.draftgrid th{padding-bottom:6px}
@media (max-width:1100px){
  .roomcols{flex-direction:column}
  /* Stacked under the board on a narrow screen, the rail has the whole page to run down, so the boxes take
     their own height again and nothing shares or scrolls inside. */
  .roomrail{flex:1 1 auto;width:100%;overflow:visible}
  .roomrail>.panel{flex:0 0 auto;display:block}
  .roomrail>.panel>.pad,.roomrail>.panel>.tablewrap{overflow:visible}
  .spillwrap.tall:has(.spillswitch){align-items:flex-start}
  .spillwrap.tall:has(.spillswitch) .spillsheet{display:block}
  .roomboard>.panel>.tablewrap{max-height:62vh}      /* same miss as above: the panel sits between them */
}
/* ONE ROW OF CONTROLS at the top of the draft room (29 Sep: "compact it. put the options next to each other
   instead of layering them vertically"). Everything you can do from here on one line, wrapping on a phone.
   And the block itself is tight: the draft room is a place you WORK, not a shop window, so the headline, the
   state, the rules and the buttons sit close together instead of five paragraphs' worth of air apart. */
.hero.draftroom{gap:6px;padding:16px 20px 14px}
/* WHICH ROUND IT IS, in the corner that was empty (his, 3 Oct). Out of the stack entirely so it cannot push the
   heading about, and it draws nothing when it says nothing. The heading keeps room for it on a narrow screen. */
.hero.draftroom .drround{position:absolute;top:14px;right:20px;margin:0;
font:800 clamp(15px,1.6vw,20px)/1 var(--display);color:var(--ink-2);letter-spacing:-.01em}
.hero.draftroom h1{padding-right:120px}
/* AND IN THE ROOM'S OWN BANNER, at the right-hand end of the row that carries The Draft Room and the positions
   -- the box he photographed. It was one row higher, among Players, Picks and Close, which is the panel's
   furniture rather than the room's header. Pushed right with a margin so it sits at the end of the line whatever
   is in front of it, and it draws nothing at all until the room's script writes a number in. */
/* PINNED TO THE CORNER, not merely pushed there. Flowing to the end of the row is right until the row wraps:
   measured at 1180px wide, the chips took two lines and the round went with them, so it was no longer in the
   corner he asked for. Out of the flow entirely, with the row keeping room for it, so it is top right at every
   width. */
.boardpos{position:relative;padding-right:112px}
.boardpos .boardround{position:absolute;top:50%;right:0;transform:translateY(-50%);white-space:nowrap;
font:800 clamp(15px,1.5vw,19px)/1 var(--display);color:var(--ink-2);letter-spacing:-.01em}
.boardpos:has(.boardround:empty){padding-right:0}
.boardpos .boardround:empty{display:none}
@media (max-width:620px){.hero.draftroom .drround{position:static;order:-1}.hero.draftroom h1{padding-right:0}}
/* AND THE NOTE DRAWS NOTHING WHEN IT SAYS NOTHING. Since the count of players left went (27 Oct) this line
   is empty whenever there is no pick for the odds to be measured to -- before a draft starts, or for anyone
   without a seat in it. An empty paragraph still takes a row of height, which is a blank gap under the
   heading that looks like something failed to load. Same rule the Round in the corner already follows. */
.hero.draftroom #dr-note:empty{display:none}
.hero.draftroom h1{margin:0}
.hero.draftroom>p{margin:0}
.draftbar{align-items:center;gap:10px;flex-wrap:wrap;margin-top:6px}
.draftbar .field.inline-field{margin:0}
.draftbar .tick{white-space:nowrap}
/* AND THE TICK GETS A BACKING, like the buttons it stands next to. It is the one control on this row that
   was bare text, so on a banner it was the only one landing straight on the drawing -- at a small window
   "Autodraft from my queue" came out green on the green sleeve. The three buttons beside it never had the
   problem because they carry their own panel. Giving it the same reads as deliberate rather than patched,
   and it fixes every width at once instead of asking the picture to move. */
.hero:has(.heroart) .draftbar .tick{background:var(--panel);border:2px solid var(--line);
border-radius:999px;padding:5px 12px}
/* AND A SENTENCE ON THE CONTROL ROW KEEPS TO THE LEFT TOO. Before an order is drawn there is no Start
   button, and its place is taken by a line of explanation -- which sits on the same flex row as Open the
   Room, so a long sentence pushes that button out to the middle of the banner and strands it at 55%, the
   one band where the fade has stopped working and the picture has not. The sentence wraps instead, and the
   button comes back to where the scrim can carry it.
   This is a CHILD of the row, so the `>p` cap above never reached it -- that rule only matches paragraphs
   the hero owns directly. */
/* It takes the WHOLE line so the button is pushed below it, and keeps its words to the left 54% with
   padding rather than width. `flex-basis:100%` with a `max-width` cap does neither: the cap wins, the item
   no longer fills the line, nothing wraps, and the button simply moves along to 55% -- further onto the
   picture than where it started. Everything here is `box-sizing:border-box` (line 35), so the padding comes
   out of the 100%, which is what makes this work. */
.hero.draftroom:has(.heroart) .draftbar .meta{flex:0 0 100%;padding-right:46%}
/* AND IT COMES OFF ON A PHONE, WHERE THE DRAWING IS HIDDEN -- undone HERE rather than beside the banner's
   other phone rules, which is where it was first written and did nothing. `:has(.heroart)` still matches
   below 860px, because the image is hidden rather than removed, so the reset ties with the rule above on
   specificity and the only thing that decides is which comes later. The banner's media query is ~2000 lines
   up this file, so it lost, and a phone kept half its width reserved for a picture it cannot see. Measured:
   at a 430px window the padding was still 181px. */
@media (max-width:860px){.hero.draftroom:has(.heroart) .draftbar .meta{flex:0 1 auto;padding-right:0}}
/* THE TEAM BUILDER'S CONTROL ROW KEEPS TO THE TEXT COLUMN TOO. Its row leads with the spots panel -- nine
   position boxes, a Set button and a total -- which is wide enough on its own to push Seats, Pick and Enter
   Builder out onto the vault door's face. The row wraps inside 54% instead, so the controls fall to a second
   line at the margin and the drawing keeps its half.
   NOT the same as giving the panel the whole line, which is what the draft room's sentence does: this panel
   carries its own background, so stretching it would lay a solid box straight across the picture.
   `:has(.shapebar)` is the hook because the Team Builder shares the draft room's hero class exactly, and the
   draft room's own row of buttons fits on one line and should stay there. */
.hero.draftroom:has(.heroart) .draftbar:has(.shapebar){max-width:54%}
@media (max-width:860px){.hero.draftroom:has(.heroart) .draftbar:has(.shapebar){max-width:none}}
/* ONE SIZE FOR EVERY BUTTON ON THIS BAR (his, 30 Sep: "all the buttons need to be the same size. this include
   the box to change where you are drafting, its too big relative to the other buttons ... show drafted needs to
   be the same size"). They came from three different places -- a .btn, a .ghost, a <select> inside a label --
   and each brought its own padding, so the bar read as four unrelated controls rather than one row. The height
   is stated rather than left to the tallest, which is what the select was. */
.draftbar .btn,.draftbar .ghost,.draftbar button,.draftbar select{height:40px;padding:0 16px;font-size:14px;
display:inline-flex;align-items:center;justify-content:center}
.draftbar .field.inline-field{display:inline-flex;align-items:center;gap:8px;height:40px}
.draftbar .field.inline-field>span{font-size:12px;letter-spacing:.04em;text-transform:uppercase;
color:var(--ink-2);white-space:nowrap}
.draftbar select{min-width:0;max-width:200px}
.boardopen{align-items:center;gap:14px;margin-top:14px}
/* PLAYERS OR PICKS, at the top of the panel. The side you are on is lit and is not a link, because a link that
   takes you where you already are is the one thing this switch must not offer. */
.spillswitch{gap:8px;margin:0 0 2px}
/* THE TWO SIDES ARE THE SAME SIZE (his, 29 Sep: "the players and picks tabs need to be the same size"). One is
   a lit span and the other a link, and a .btn and a .ghost do not carry the same padding -- so the switch had a
   wide half and a narrow one and read as a button beside a smaller button rather than as two faces of a thing. */
.spillswitch>*{min-width:104px;justify-content:center;text-align:center;padding:9px 16px;font-size:14px}
.spillswitch .btn.on{cursor:default;background:var(--accent-soft);border-color:var(--accent);color:var(--ink)}
/* CLOSE JOINS THEM, at the far end of the same line (his, 30 Sep: "move the close button to be level with players
   and picks. make close the same size"). The script moves the panel's one Close button in here, so it inherits
   the rule above and is the size of the other two without being told; `margin-left:auto` is the whole of the
   rest. A panel with a switch then has nothing left in its bar, so the bar goes. */
.spillswitch .spillshut{margin-left:auto}
/* AND IT IS NOT THE PANEL'S MAIN BUTTON. The panel focuses Close as it opens, so a keyboard has somewhere to
   start -- and `.ghost:focus-visible` fills with the accent, which left Close sitting there in solid green next
   to the switch, reading as the thing you came to press. In the bar this never showed, because the bar had its
   own background rule; docking the button lost it. The focus is kept and made an OUTLINE instead: this design
   says it itself three rules up -- "colour means touching it" -- and opening a panel is not touching anything. */
.spillswitch .spillshut:focus-visible{background:var(--panel);color:var(--ink);
outline:2px solid var(--accent);outline-offset:2px}
.spillswitch .spillshut:hover{background:var(--accent);color:#fff}
.spillwrap.switched .spillbar{display:none}
/* THE PANEL'S NAME, leading the row that narrows the board. Set in the display face so it reads as a title and
   not as another chip, and allowed to shrink before the chips do -- the chips are what you press. */
.boardpos .roomname{font:800 19px/1 var(--display);margin-right:4px;align-self:center;white-space:nowrap}
/* WHAT IS CHOSEN IS LOUD, on this row only. His, 30 Sep: "i think it needs to be a bit more obvious when you
   have it selected, maybe using that brighter green color you use for the tab selector. if not, just make it
   more obvious for sleection so people know what they have highlighted."
   This row is a deliberate exception to the rule three hundred lines up -- "a silver underline, not a colour --
   colour means touching it". That rule is right for a page you are reading; this is a row of filters you are
   working with, where what is ON changes what every row below it means, and a 3px underline was not enough to
   say so. Same green and same weight as the lit tab, so a chosen position reads like a chosen tab. */
.boardpos .ghost[aria-current],.boardpos .chip.on,.boardpos .chip[aria-pressed="true"]{
background:var(--accent);border-color:var(--accent);color:#fff;
box-shadow:2px 3px 0 var(--shadow)}
.boardpos .ghost[aria-current]:hover,.boardpos .chip.on:hover,.boardpos .chip[aria-pressed="true"]:hover{
background:var(--accent);color:#fff}

/* THE LEAGUE OVERVIEW (19 Sep: "the standings should be on it. a lot of unecessary data being shown"): this week's
   matchups in a row, then the whole table. The playoff line is a rule across the table with a word on it, not a
   column of ticks: it is ONE fact about the table, so it is said once. */
/* MOVE TO (19 Sep): the player's NAME is the button, and the menu under it is every place he can go. A place he
   cannot go is shown greyed with its reason, so "why not there" is answered where it is asked. */
.movemenu>summary{list-style:none;cursor:pointer;display:inline-block}
.movemenu>summary::-webkit-details-marker{display:none}
/* The POSITION is what you press (20 Sep), so it has to look pressable; the player's name is a link to his page.
   SCOPED TO `.chip.slotname`, which is what the Move To menu draws. A bare `.slotname` here outranked the plain
   slot label two hundred lines above it, so every slot name in Your Roster wore a dashed box and a drop-down
   arrow and opened nothing. Two classes, two reasons -- and the marker class was winning. */
.chip.slotname{padding:3px 9px;border-style:dashed;font:600 10px/1.4 "IBM Plex Mono",monospace;letter-spacing:.08em}
.chip.slotname::after{content:"\25BE";margin-left:5px;color:var(--ink-3)}
.movemenu>summary:hover .slotname{border-color:var(--accent);color:var(--accent)}
.movemenu[open]>summary .slotname{background:var(--accent);color:#fff;border-color:var(--accent);border-style:solid}
.movemenu[open]>summary .slotname::after{content:"\25B4";color:#fff}
.moveto.empty{border-style:dashed;color:var(--ink-2)}
.movewhere{font-style:normal;font-weight:400;font-size:11.5px;color:var(--ink-3);margin-left:5px}
.moveto:hover .movewhere{color:#fff;opacity:.85}
.waiverpick{padding-top:0}

/* PLAYING a coin, in the Coin Purse where a person looks for them (19 Sep). One LINE per coin, shut: press the coin
   and it opens with what it does and the ways it can be played right now -- or, in plain words, why it cannot.
   "its too much and too easy to simplify" -- five coins with every control unfolded was most of a screen. */
.coinplay+.coinplay{border-top:2px dashed var(--line-soft)}
.coinplay-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;cursor:pointer;list-style:none;
padding:3px 0;border-radius:10px}
.coinplay-head::-webkit-details-marker{display:none}
.coinplay-head b{font:700 15px/1.2 var(--display)}
.coinplay-head:hover b{color:var(--accent)}
.coinopen{margin-left:auto;font:600 10px/1.2 "IBM Plex Mono",monospace;letter-spacing:.1em;text-transform:uppercase;
color:var(--ink-3)}
.coinopen::after{content:"\25BE";margin-left:6px}
.coinplay[open] .coinopen::after{content:"\25B4"}
.coinplay.noplay .coinplay-head b{color:var(--ink-3)}
.coinwhat{margin:6px 0 10px;font-size:12.5px}
.coinplay .btnrow{margin:0;gap:8px}
.coinplay .seatform{padding:5px 0}
/* THE SAME SPOT ON EVERY LINE, whatever the name is: "we coiuld equally spce out these boxes so that no matter how
   much text there is, the all lie in the same spot." A grid, not a row pushed along by the longest name. */
.coinlines{display:grid;gap:6px;width:100%}
.coinline{display:grid;grid-template-columns:minmax(150px,240px) 180px 180px max-content;gap:8px;align-items:center;
justify-content:start}
.coinline>select{width:100%;min-width:0}
@media (max-width:640px){.coinline{grid-template-columns:1fr 1fr;align-items:stretch}
.coinline>.meta{grid-column:1/-1}.coinline>button{grid-column:1/-1}}
/* The menu hangs BELOW the position as a popover. It has to escape its cell: the slot label sits in a narrow grid
   column, and a menu laid out inside that column stacks into a tall ribbon of wrapped words (seen 19 Sep, the first
   time it was photographed open). Out of the flow, so the row keeps its height and the lineup does not jump.
   FIXED, and parked on <body> while it is open (kit.NAME_MENUS) -- the same escape the team-name menus needed, for
   the same reason. Absolute was clipped by the panel the lineup sits in: "they are getting cut off by the parent box
   ... overlay should have frontward priority" (19 Sep). z-index cannot rescue it, because clipping happens before
   stacking. See the note above .tmenu-box for why fixed alone is not enough either. */
.movemenu{position:relative}
.movebox{position:fixed;z-index:40;min-width:260px;max-width:min(460px,80vw);
display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:10px 12px;border-radius:12px;
border:2px solid var(--edge);background:var(--panel);box-shadow:5px 6px 0 var(--shadow)}
.moveto,.moveno{white-space:nowrap}
/* THE POSITION PICKER IS A COLUMN (21 Sep): name, the positions he can play, where he is now. A workbench, not a
   shop window -- plain rows, the name the button, the man in the slot now at the top and not pressable. */
/* GRIDLINES (21 Sep): "we just need at add some gridlines or some seperater because it seems a bit jumbled
   together." A ruled table: a line round it, between every row and every column, and a shaded head. */
.movetable{flex-basis:100%;width:100%;border-collapse:collapse;font:600 12px/1.3 "IBM Plex Mono",monospace;
border:1.5px solid var(--edge)}
.movetable th{text-align:left;font:700 8.5px/1.4 "IBM Plex Mono",monospace;letter-spacing:.12em;
text-transform:uppercase;color:var(--ink-2);padding:5px 10px;border:1px solid var(--box-edge);
border-bottom:1.5px solid var(--edge)}
.movetable td{padding:5px 10px;border:1px solid var(--box-edge);vertical-align:middle;white-space:nowrap}
.movetable tr,.movetable td{background:none !important}
.movetable thead th{background:var(--box-grey) !important}
.movetable td i{font-style:normal;font-weight:800;color:var(--pos);margin-right:4px}
.movetable td em{font-style:normal;color:var(--ink-3);font-weight:500;margin-left:4px}
.movetable tr.here td{color:var(--ink-3)}
.movetable tr.here td:first-child{font-weight:800;color:var(--ink)}
.movetable tr.spent td{color:var(--ink-3);opacity:.7}
.movetable tbody tr:not(.here):not(.spent):hover td{background:var(--accent-soft) !important}
.movetable .moveto{border:none;background:none;padding:2px 0;font:700 13px/1.2 var(--display);color:var(--accent);
cursor:pointer;border-radius:0}
.movetable .moveto:hover{text-decoration:underline;background:none;color:var(--accent)}
.movenote{flex-basis:100%;margin:2px 0 0;font:600 10px/1.4 "IBM Plex Mono",monospace;color:var(--ink-3)}
.movehead{flex-basis:100%;margin:0;font:600 9.5px/1.4 "IBM Plex Mono",monospace;letter-spacing:.12em;
text-transform:uppercase;color:var(--ink-3)}
.moveto{padding:5px 12px;border-radius:999px;border:2px solid var(--edge);background:var(--panel);color:var(--ink);
font:600 13px/1.2 var(--display);cursor:pointer}
.moveto:hover{background:var(--accent);border-color:var(--accent);color:#fff}
.moveno{padding:5px 12px;border-radius:999px;border:2px dashed var(--line);color:var(--ink-3);font-size:12.5px}
.moveno em{font-style:normal;opacity:.8}
/* A MOVE BEING MADE. It is a tenth of a second, but a button that looks identical the instant after it was
   pressed is a button people press twice -- and twice is two moves. Dimmed and deaf until the rows come
   back; the class goes on in kit.js, where data-inplace is handled, and comes off with the new rows. */
form.working button{opacity:.45;pointer-events:none}
@media (max-width:560px){.movebox{left:12px;right:12px;min-width:0;max-width:none}}

.overweek{margin:16px 0 0;font:600 10px/1.4 "IBM Plex Mono",monospace;letter-spacing:.13em;text-transform:uppercase}
.hero .overweek+.pairstrip{justify-content:flex-start;margin:6px 0 0}
.overchamp{margin:10px 0 0;font-size:15px;display:flex;align-items:center;gap:10px}
.leaguetable .cutline td{padding:0;height:0;border-top:2px dashed var(--accent-2);text-align:right;line-height:0}
.leaguetable .cutline span{position:relative;top:-1px;display:inline-block;padding:0 10px;margin-right:12px;
background:var(--panel);color:var(--accent-2);font:600 9.5px/1 "IBM Plex Mono",monospace;letter-spacing:.12em;
text-transform:uppercase;transform:translateY(-50%)}

/* THE RULES, IN DROP-DOWN BOXES (18 Sep: "its just endless rules. we should have drop down boxes of each type of
   grouping"). One box per grouping, shut until wanted. A shut box is a single line: its name, what is in it in the
   league's own values (so the rulebook reads down the page without opening anything), and how many settings it holds.
   An open one is outlined in the accent so it is obvious which you are in. A box whose values have been touched says
   so, because a change inside a shut box is otherwise invisible. */
.ruleboxes{display:flex;flex-direction:column;gap:9px}
.rulebar{display:flex;justify-content:flex-end;gap:8px;margin-bottom:-1px}
.rulebar .ghost{padding:5px 11px;font-size:12.5px}
.rulebox{background:var(--panel);border:3px solid var(--edge);border-radius:14px;box-shadow:4px 4px 0 var(--shadow);
scroll-margin-top:84px}      /* an address can name a box (#playoffs); it lands under the top bar, not behind it */
.rulebox>summary{list-style:none;cursor:pointer;display:flex;align-items:baseline;gap:12px;padding:12px 16px;border-radius:11px}
.rulebox>summary::-webkit-details-marker{display:none}
.rulebox>summary:hover{background:var(--panel-2)}
.rulebox>summary:focus-visible{outline:3px solid var(--accent);outline-offset:2px}
.rulebox>summary>b{flex:none;font:700 17px/1.25 var(--display);color:var(--ink)}
.rulebox>summary>b::before{content:"\25B8";display:inline-block;width:1.1em;color:var(--ink-3);font-size:.8em}
.rulebox[open]>summary>b::before{content:"\25BE";color:var(--accent)}
/* The digest WRAPS; it is never cut short with an ellipsis. checks/phone_width.py caught the first version clipping it
   on a phone ("...and 5 m"), and clipped words are words the reader cannot see -- on a line whose whole job is to be read. */
.ruledigest{flex:1;min-width:0;font-size:12.5px;line-height:1.5;color:var(--ink-3)}
.ruledigest b{font-weight:600;color:var(--ink-2)}
.rulecount{flex:none;font:600 10px/1.4 "IBM Plex Mono",monospace;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3)}
.rulechanged{display:none;flex:none;padding:1px 9px;border-radius:999px;background:var(--accent-2-soft);color:var(--accent-2);
font:700 10.5px/1.6 var(--display);letter-spacing:.05em;text-transform:uppercase}
.rulebox.edited>summary .rulechanged{display:inline-block}
/* THE TITLE WRAPS ON A PHONE INSTEAD OF RUNNING OFF IT. `flex:none` keeps a rule's name at its full width so
   the note beside it cannot squash it, and on a 390px screen a player's season line -- "2025-26 - 72 games -
   58.14 a game - best 105.75" -- ran 30px past the edge and took the whole page sideways with it, stopping at
   "best 10". Shrinking is allowed here and the line wraps; the note moves under it rather than fighting it. */
@media (max-width:620px){
  .rulebox>summary{flex-wrap:wrap;gap:6px}
  .rulebox>summary>b{flex:0 1 auto;min-width:0}
}
.rulebox[open]{border-color:var(--accent)}
.rulebox[open]>summary{border-bottom:2px dashed var(--line);border-radius:11px 11px 0 0}
.rulebox[open] .ruledigest{visibility:hidden}          /* open, the fields themselves say it */
.rulebody{padding:16px 16px 18px}
.rulebody .form{margin-bottom:4px}
.rulesub{margin:16px 0 8px;font:700 13px/1.3 var(--display);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2)}
.rulesub:first-of-type{margin-top:4px}
.rulenote{margin:0 0 12px}
/* a change the commissioner made that is waiting for its moment (the next matchup, or next season) */
.onitsway{padding:9px 12px;border-radius:10px;background:var(--accent-2-soft);color:var(--ink);font-size:13px;
border:2px dashed var(--accent-2)}
.onitsway b{color:var(--accent-2)}
.rulesave{margin:8px 0 2px}
.rulesplit{margin:14px 2px 0}
.rulebox.locked{box-shadow:none;border-width:2px;border-color:var(--line)}
.rulebox.locked>summary>b{font-size:15px;color:var(--ink-2)}
/* A SEASON A PLAYER MISSED, wearing the same banner as the seasons he played (his, 15 Oct: "it should be a season
   banner ... that says zero"). Same box, same heading, same padding -- the only difference is the missing chevron,
   because there is nothing inside it to open. It wraps on a phone like its neighbours do. */
.seasonnil>p{margin:0;padding:12px 16px;display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px}
.seasonnil>p>b{font:700 17px/1.25 var(--display);color:var(--ink-2)}
@media (max-width:700px){
  .rulebox>summary{flex-wrap:wrap;gap:4px 10px}
  .ruledigest{flex-basis:100%;order:3}
}

/* THE TEST CLOCK: everything that pushes time along by hand, folded into ONE closed line. It is scaffolding -- a
   testing league plays over last season's games -- and it is drawn that way on purpose: dashed, quiet, labelled
   "testing only". On a real season it is not drawn at all. */
.testclock{margin-top:6px;border:2px dashed var(--line);border-radius:12px;padding:0 12px}
.testclock>summary{list-style:none;cursor:pointer;display:flex;align-items:baseline;gap:10px;padding:8px 0;flex-wrap:wrap}
.testclock>summary::-webkit-details-marker{display:none}
.testclock>summary b{font:700 12px/1.3 var(--display);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2)}
.testclock>summary b::before{content:"\25B8  ";color:var(--ink-3)}
.testclock[open]>summary b::before{content:"\25BE  "}
.testclock>summary .meta{font-size:12px}
.testclock[open]{padding-bottom:10px}
.testclock .btnrow{margin-top:8px}
.footnote{margin:14px 2px 0;font-size:12px}
/* the matchup going on NOW, in the arrows */
.steplive{display:inline-flex;align-items:center;gap:5px;margin-left:8px;padding:1px 9px;border-radius:999px;vertical-align:middle;
background:var(--alert-soft);color:var(--alert);font:700 11px/1.5 var(--display);letter-spacing:.08em;text-transform:uppercase}
.steplive i{width:7px;height:7px;border-radius:50%;background:var(--alert);animation:livepulse 1.2s ease-in-out infinite}
.weekscroll .chip.livechip{border-color:var(--alert);color:var(--alert)}
.weekscroll .chip.livechip.on{background:var(--alert);color:#fff}

/* ARROWS AT THE TOP TO CYCLE THROUGH MATCHUPS (18 Sep). One of these on every page that shows a past week. The label
   in the middle opens the whole season for a long jump; most of the time it stays shut and there are just two fat
   arrows. A greyed arrow keeps its place at the end of the season so nothing shifts under the pointer. */
.stepper{display:flex;align-items:center;justify-content:center;gap:10px;margin:0 0 4px;flex-wrap:wrap}
.step{display:grid;place-items:center;width:52px;height:52px;flex:none;border-radius:50%;background:var(--panel-2);
border:3px solid var(--edge);color:var(--ink);font:700 34px/1 var(--display);text-decoration:none;padding-bottom:5px;
box-shadow:3px 4px 0 var(--shadow);transition:transform .15s cubic-bezier(.2,1.6,.4,1),background .15s}
a.step:hover,a.step:focus-visible{background:var(--accent);color:#fff;border-color:var(--accent)}
a.step.prev:hover{transform:translateX(-4px) scale(1.06)}
a.step.next:hover{transform:translateX(4px) scale(1.06)}
a.step:active{transform:scale(.92)}
.step.off{opacity:.28;box-shadow:none;cursor:default}
.stepjump{flex:0 1 auto;min-width:190px;text-align:center}
.stepjump>summary{list-style:none;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 16px;
border-radius:14px}
.stepjump>summary::-webkit-details-marker{display:none}
.stepjump>summary:hover{background:var(--panel-2)}
.stepjump>summary b{font:700 clamp(21px,3.2vw,28px)/1.1 var(--display)}
.stepjump>summary b::after{content:" \25BE";font-size:.6em;color:var(--ink-3)}
.stepjump[open]>summary b::after{content:" \25B4"}
.stepdates{font:600 12.5px/1.3 "IBM Plex Mono",monospace;color:var(--ink-2)}
.stepof{font:500 11px/1.2 "IBM Plex Mono",monospace;color:var(--ink-3);letter-spacing:.05em;text-transform:uppercase}
/* Open, the label grows to hold the season's weeks but stays BETWEEN the arrows, which keep their places. */
.stepjump[open]{flex:1 1 320px;max-width:640px}
.stepper:has(.stepjump[open]){align-items:flex-start}
.stepjump[open]>summary{margin-bottom:4px}
.stepjump .weekscroll{justify-content:center;flex-wrap:wrap}
@media (max-width:520px){.step{width:46px;height:46px;font-size:30px}.stepjump{min-width:150px}}
@media (prefers-reduced-motion:reduce){.pursefab,.step{transition:none}.pursefab .purse-svg{animation:none}
.pursefab:hover,.pursefab:focus-visible,a.step.prev:hover,a.step.next:hover{transform:none}}

/* a coin that was just played: it falls in, squashes, spins, and gets stamped. Shown only when the server says the
   play was accepted, and it takes itself away again. */
.coin-toast{position:fixed;left:50%;top:24%;z-index:60;display:flex;flex-direction:column;align-items:center;gap:12px;pointer-events:none;
transform:translateX(-50%);animation:toastlife 3.3s ease forwards}
.coin-toast .coin{animation:toastdrop .95s cubic-bezier(.22,1.4,.36,1) both}
.coin-toast .coin-disc{animation:toastspin .95s ease-out both}
.coin-toast::before,.coin-toast::after{content:"\2728";position:absolute;top:8px;font-size:30px;opacity:0;animation:sparkle 1.1s ease-in-out .8s 2}
.coin-toast::before{left:-46px}.coin-toast::after{right:-46px;animation-delay:1.05s}
.coin-stamp{position:absolute;top:40px;padding:1px 14px;font:800 27px/1.3 var(--display);letter-spacing:.06em;color:#ff5d8f;
border:4px solid #ff5d8f;border-radius:9px;background:rgba(7,5,12,.6);opacity:0;transform:rotate(-14deg) scale(3);
animation:stampthunk .42s cubic-bezier(.2,1.2,.3,1) .9s forwards}
.coin-toast-name{font:700 19px/1.2 var(--display);color:#ffd84a;background:#14101c;border:3px solid #5e3c00;border-radius:999px;
padding:5px 18px;box-shadow:3px 4px 0 rgba(0,0,0,.55);animation:toastname .45s ease .55s both}
@keyframes toastdrop{0%{transform:translateY(-70vh) scale(.6)}55%{transform:translateY(0) scale(1.14,.84)}72%{transform:translateY(-28px) scale(.95,1.07)}100%{transform:translateY(0) scale(1)}}
@keyframes toastspin{0%{transform:rotateY(0)}100%{transform:rotateY(1080deg)}}
@keyframes stampthunk{0%{opacity:0;transform:rotate(-14deg) scale(3)}70%{opacity:1;transform:rotate(-14deg) scale(.88)}100%{opacity:1;transform:rotate(-14deg) scale(1)}}
@keyframes toastname{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:translateY(0)}}
@keyframes toastlife{0%,84%{opacity:1}100%{opacity:0;visibility:hidden}}
@media (prefers-reduced-motion:reduce){.coin-disc::after,.coinrow .coin,.coin:hover .coin-disc,.coinbtn:hover .coin-disc,.purse-svg,.pc,
.purse-spark,.coin-toast .coin,.coin-toast .coin-disc,.coin-toast::before,.coin-toast::after,.coin-toast-name{animation:none}
.coin-stamp{animation:none;opacity:1;transform:rotate(-14deg)}.purse-spark{opacity:.8}
.poll-overlay,.poll-overlay-card,.poll-from .msgart{animation:none}}

/* Law 1a (21 Sep): while a GitHub copy of the code is public, every page opens with this. Loud on purpose. */
.lawalarm{position:sticky;top:0;z-index:9999;margin:0;padding:14px 18px;background:#d90429;color:#fff;
font:700 15px/1.4 var(--display);border-bottom:4px solid #000;text-align:center}
.lawalarm b{display:inline-block;margin-right:8px;padding:2px 8px;border:2px solid #fff;border-radius:6px}
/* A RECOVERY CODE, shown once (5 Oct). It is read off the screen and copied onto paper, so it is set big, spaced
   and in a face where the letters cannot be mistaken for each other. The alphabet already excludes O/0 and I/1/L. */
.bigcode{margin:4px 0 10px}
.bigcode code{display:inline-block;padding:10px 16px;border:2px dashed currentColor;border-radius:10px;
font:700 clamp(18px,4vw,26px)/1.3 ui-monospace,"Cascadia Mono",Consolas,monospace;letter-spacing:.14em;
word-break:break-all}

/* The watch (4 Oct, lockin/self_watch.py): something about the machine wants looking at. Amber, not red -- a drive left
   unplugged is not a Law broken, and if the two looked the same the red one would stop meaning what it means. */
.lawalarm.watching{background:#f0a202;color:#1a1200;border-bottom-color:#1a1200}
.lawalarm.watching b{border-color:#1a1200}

/* THE BOARD ON A PHONE. The rule from 22 Sep was to drop columns as the screen narrowed -- Pos, Team, Drop, GP,
   Inj and Age below 620, Lasts below 520, Risk and Value below 440 -- on the reasoning that "a phone keeps what a
   pick is made on; the rest waits for a laptop".
   HE OVERTURNED IT ON 13 OCT, with a photograph that is the whole argument: the board scrolled sideways, and he
   had scrolled it, looking for more. "it ends at cost. There's no more than that." Then: "i think we should show
   exactly what we show on computers, but with a scroll wheel."
   The old decision assumed he would not scroll. He did, and found nothing there -- which is worse than a wide
   table, because a table you can scroll tells you where it ends and a hidden column does not exist. So every
   column a laptop draws is drawn here, and the width it needs is paid for out of the three things below rather
   than out of the figures. */
.onphone{display:none}
.nameshort{display:inline}            /* every name is initialled now; see kit.player_chip */
@media (max-width:620px){
/* 1. THE NAME IS INITIALLED, which is his own rule from 19 Sep for exactly this reason: "make all names on the
      roster first initial dot last name." Both forms are in the link (kit.player_chip, short="either") so the
      stylesheet picks one -- a page cannot know how wide a screen is, and a second copy of every row would be
      two things to keep in step. "Nikola Jokic" to "N. Jokic" is about 70px a row.
      The pos-and-club line under the name goes with it: those are real columns again now, and the same fact
      twice on one screen is the thing he keeps catching. */
table.board td,table.board th{padding:5px 5px;font-size:12px}
table.board td.dr-act .btn{padding:3px 9px}
/* 2. THE SQUEEZING STOPS, BECAUSE THE TABLE SCROLLS NOW. This is one correction, not three, and it is the cause
      of three of the four things he listed on the 13th.
      `.board td.c-name` was capped at 118px and the headings were told to wrap, both bought to make fifteen
      columns fit a 318px screen. They did -- and then he said yes to scrolling, which made every one of those
      savings pointless and their side effects permanent:
        "the cateogires banner does not line up with anything"   -- a wrapped heading stacks "#" over "Fpts/Gm"
                                                                    and drops its ? onto a third line, so no
                                                                    label sits over its own column any more
        "the new tags, positions tags, and your pick tags all lay on top of each other"
                                                                 -- a capped cell with nowrap content does not
                                                                    shrink, it OVERFLOWS, so the name and its
                                                                    badges ran into the Pos column beside them
      A column that is as wide as what is in it has neither problem, and in a table you can scroll it costs
      nothing but scroll -- which he asked for in as many words.

      THIS COMMENT WAS BROKEN FROM THE 13th UNTIL THE 14th, and it cost exactly the fault below. It closed here
      and then ran on for six more lines of prose before closing a second time, so those lines sat in the
      stylesheet as raw text -- and a CSS parser that meets garbage at the top level swallows everything up to
      the next `{`, which belonged to the `.board td.c-name` rule under it. The rule written on the 13th to stop
      the badges stacking was therefore never once applied, on the port or on the site. He reported the stacking
      again on the 14th and he was right to: nothing had changed for him. The same mistake, in the same file, in
      the same week -- which is why checks/stylesheet now refuses a comment-close that closes nothing. (And it
      caught this very comment a minute after it was written, because the first draft spelled that sequence out
      in full: there is no escaping inside a CSS comment, so writing it in prose ends the comment right there.)

      NO CAP, AND NO WRAPPING. The cap was kept on the 13th out of a fear that has since stopped being true: an
      uncapped name column would take every spare pixel, because it is the one `width:auto` column among
      `width:1%` ones. There is no spare pixel on a phone -- the fourteen columns come to about 970px inside a
      390px screen -- so `width:auto` is content width and nothing else. With nothing to shrink into, the cell
      no longer needs to wrap, and not wrapping is what makes every row the same height: a man with a DTD badge
      was 50px where a man without was 36, which is the "tags on top of each other" he has now reported twice.
      Capped WITH nowrap really was a collision; uncapped with nowrap is neither. */
.board td.c-name{width:auto;white-space:nowrap}
.board td.c-name .nameline{flex-wrap:nowrap;gap:0 6px}
table.board tbody tr[data-id]>td{height:auto;padding-top:5px;padding-bottom:5px}
/* 3. THE TIER LINES GET SMALLER, NOT BIGGER, and they were getting bigger. `tr.tierbreak td` asks for 10px up at
      line 437, and `table.board td{font-size:12px}` in this very block sits further down the file -- same
      specificity, later wins -- so on the one screen with no width to spare the line that says "13.0 PT DROP .
      93x A NORMAL STEP" was being INFLATED by a fifth and then running off the edge. His, 13 Oct: "the value
      drops are huge as well for some reason." That is the reason.

   SMALLER AGAIN, at his word -- "the value drop bars are still very large. they need to be
   significantly smaller". Shrinking the type alone was never going to do it: what reads as SIZE here is the
   whole band. A full-width coloured rule, capital letters and a tenth of an em between them make a line shout
   whatever its font size, and on a phone one of these sits between every few players. So the capitals go, the
   letters close up, the rule thins to a hairline, and the words step back to the quiet ink -- it marks a gap in
   the board, it is not an announcement. The colours still say how big a cliff it is; they just say it once. */
tr.tierbreak td{font-size:8px!important;letter-spacing:0;text-transform:none;padding:0 6px!important;
line-height:1.25;opacity:.6;color:var(--ink-3)!important}
/* THE COLOUR STAYS IN THE LINE, NOT THE WORDS. Shrinking the type had been tried twice and he said "still very
   large" both times, which was the clue: 8.5px type cannot be large, so SIZE was never what he was reading. It
   was a full-width band of alert red with a 3px rule over it, four of them in the first twenty players -- the
   loudest thing on a screen whose job is the names. The rule keeps the colour, because the step from g3 to g4
   is real information and a hairline can carry it; the words go to the same grey as everything else. */
tr.tierbreak.g3 td,tr.tierbreak.g4 td{border-top-width:1px}
/* 4. THE FILTERS STOP EATING THE SCREEN. His: "we need to compact the sorting options. They make that top banner
      too thick, which means you see less of the board." Photographed at 1:45, the panel took about two thirds of
      the screen before a single player appeared. Everything in it shrinks together -- chips, boxes, the search
      field and its labels -- because shrinking one just moves the problem to whichever is now the tallest. */
/* 5. THE CHIPS STOP STACKING FOR NO REASON. His, 13 Oct: "its more the spaces they fill up. thinks are stacking
      for no reason. for example, center is below the other 4 positions and all is above ... if we compress it, we
      free vertical spacing."
      Two causes, both measurable. `.boardpos` reserves 112px down its RIGHT-HAND SIDE for the round, on every row
      -- the round is absolutely positioned and vertically centred, which is his "the round 1 is in the middle on
      the right" -- so a 318px panel was laying its chips out in 206. And `.roomname` is 19px and nowrap, which
      takes about 150 of what is left, so row one could hold "All" and nothing else.
      The round joins the row instead of hovering beside it, and the name comes down to a size a phone can spare.
      Four rows of chips become two. */
.boardpos{padding-right:0}
.boardpos .boardround{position:static;transform:none;align-self:center;font-size:11px}
/* AND THE ROOM'S OWN NAME STANDS DOWN, which is the single biggest thing on this list. "The Draft Room" is 19px
   and nowrap -- about 150px, half the width of a phone -- for a label on a panel you reached by pressing a tab
   that said Draft Room, with Players / Picks / Close along the top of it and the round beside it. Nothing on
   that screen is in doubt about where you are.
   MEASURED on the room panel at 390px, with the round filled in as the room's own script fills it:
     the chip rows   145px -> 72px      three rows and a bit, down to two
     first player    504px -> 431px
     players seen        8 -> 10
   It is the only change here worth more than a few pixels, and it is the only one that takes something away. */
.boardpos .roomname{display:none}
.boardfilters{gap:5px;margin:7px 0 0}
.boardfilters .chip{padding:4px 9px;font-size:11.5px}
.boardfilters input[type=text]{font-size:12.5px;padding:6px 10px;min-width:0;flex:1 1 130px}
.boardfilters select{font-size:12.5px;padding:4px 6px}
/* AND THE LABELS STAND BESIDE THEIR BOXES INSTEAD OF ABOVE THEM. His, 13 Oct: "there are still things gapping."
   `.boardbox` is a column -- the little capitals over the select -- which is right on a laptop and costs a whole
   line of height each on a phone, twice over, for two words that fit in the space beside them. */
.boardfilters .boardbox{flex-direction:row;align-items:center;gap:4px}
.boardfilters .boardbox .eyebrow{font-size:8px;letter-spacing:.06em}
.boardfilters button{padding:5px 12px;font-size:12.5px}
/* 6. THE GAPS, WHICH ARE WHAT IS LEFT. His, 13 Oct: "we can still compress more of the header above. there are
      still things gapping."
      MEASURED on the room panel at 390px rather than guessed at: the controls panel is 197px tall and holds
      about 127px of controls. The other 70 are padding and the gaps between rows -- 16/16/18 around the pad and
      11px between each of its three rows. That is a laptop's spacing on a screen that has none to give.
      The position chips were the other half: 38px tall, where the Favourites and Rookies chips directly under
      them are 25px. They are the same kind of control one line apart, and the bigger one was simply the one
      nobody had told to be smaller. */
.boardcontrols>.pad{padding:9px 12px 11px;gap:7px}
.boardpos .ghost{padding:4px 10px;font-size:12px;min-height:0}
/* 7. AND THE SENTENCE THAT EXPLAINS THE `?` STANDS DOWN, because the ? is an inch above it on every heading.
      When it is the ONLY thing in the note -- which is every board nobody has filtered -- the note goes with
      it, padding and all. A filtered board still says how many it left, because that is a number the screen
      cannot tell you any other way. */
.widewords{display:none}
.panel>.note:has(>.widewords:only-child){display:none}
}

/* On a PHONE the Coin Purse button sat on top of the bottom row's Draft button (22 Sep). Coins are not played from
   a draft page, so it stands down there at phone width; it is on every other page, and on a laptop, as before. */
/* The coin purse floats over everything, and on a phone it sat on the board's right-hand edge -- where the Draft
   button is on draft night. He asked for it on the draft page (21 Sep), so it stays: smaller, and the board is given
   room below it so the last rows can be scrolled out from under it. The draw has no coins to play, so it goes there. */
@media (max-width:620px){
  .pursefab{width:60px;right:10px;bottom:10px}
  body:has(.pursefab){padding-bottom:78px}
}

/* ---------- the spill: the Coin Purse opened over the page you are on (23 Sep) ----------
   "click the coin purse and both peoples coins spill out onto the page. the blur the back of the matchup page, so
   theoretically, we are still there." The page stays put behind the blur; yours is on the left with its buttons,
   theirs on the right with none. */
html.spilling{overflow:hidden}
.spillwrap{position:fixed;inset:0;z-index:60;display:flex;align-items:flex-start;justify-content:center;
overflow:auto;padding:20px 14px 40px;background:rgba(22,18,10,.44);
backdrop-filter:blur(7px) saturate(.9);-webkit-backdrop-filter:blur(7px) saturate(.9)}
.spillsheet{width:min(1120px,100%)}
/* THE PANEL'S OWN BANNER. Until 6 Oct the title and the Close button were white text with a drop shadow sitting
   straight on the blurred page, so whether they could be read depended on what happened to be behind them -- a
   pale table underneath and the name all but vanished. A shadow is not a background. It is a strip now, like any
   other surface in the app, and it carries the title and the way out. */
.spillbar{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 10px;
padding:12px 16px;border-radius:14px;background:var(--panel);border:2px solid var(--line);
box-shadow:0 10px 24px rgba(0,0,0,.45);color:var(--ink);font:800 20px/1 var(--display)}

/* The same reasoning one level down: who he is, on a surface of its own rather than on the blur. */
.playerhead{margin:0 0 12px;padding:12px 16px;border-radius:14px;background:var(--panel);
border:2px solid var(--line);box-shadow:0 10px 24px rgba(0,0,0,.35)}
.playerhead>:first-child{margin-top:0}
.playerhead>:last-child{margin-bottom:0}

/* ONE BANNER, NOT TWO BOXES (his, 6 Oct: "combine the two boxes"). The title bar belongs to every panel in the
   app, and who he is belongs only to a player's -- so they stay two elements and are STITCHED into one surface
   where both appear: the bar loses its lower edge and the head its upper one. Written as :has() on the sheet so
   it costs nothing anywhere else; a browser without :has() simply sees the two rounded boxes it saw before,
   which is tidy rather than broken. */
.spillsheet:has(.playerhead) .spillbar{margin-bottom:0;border-bottom:none;border-radius:14px 14px 0 0;
box-shadow:none;padding-bottom:10px}
.spillsheet:has(.playerhead) .playerhead{margin-top:0;border-top:none;border-radius:0 0 14px 14px;
padding-top:2px;box-shadow:0 10px 24px rgba(0,0,0,.45)}
.spillcols{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start}
/* A PLAYER IS NOT A PAIR. Two columns is right for two panels side by side and wrong for one thing that reads top
   to bottom: a player's own panel took one column for his name and put three years of games in the other. His,
   3 Oct: "the name is off the left in a big box, and then the game box is off to the right. it looks weird." */
.spillcols>.playerspill{grid-column:1/-1;min-width:0}
.spillcols .spill-in.theirs .panel{opacity:.94}
.spillcols .coinplay.theirs summary{cursor:pointer}
.spillbar .spillshut{background:var(--paper)}
@media (max-width:880px){.spillcols{grid-template-columns:1fr}}
/* The question that takes a Spend button's place. */
.sure{display:inline-flex;align-items:center;gap:8px;font-size:13px}
.sure b{white-space:nowrap}


/* ---------- the purse on the matchup: a thing, in your side of the header (24 Sep) ----------
   "i want the coin purse to stay an entity. i dont want it to be a function of any bar", then "put it between the
   score and name". It sits at the right-hand edge of YOUR side, which is the gap before the score, with a bead
   saying how many coins have a play right now. It wears `pursefab` so the burst finds it, and that class is fixed
   to the bottom-right corner -- undone here, in one place, as it has to be every time. */
a.pursepouch{position:relative;right:auto;bottom:auto;z-index:auto;width:64px;flex:0 0 auto;display:block;
line-height:0;text-decoration:none;filter:drop-shadow(0 3px 0 rgba(0,0,0,.14));transition:transform .18s ease}
a.pursepouch:hover,a.pursepouch:focus-visible{transform:translateY(-3px) scale(1.06) rotate(-3deg)}
a.pursepouch:active{transform:translateY(0) scale(.96)}
a.pursepouch .purse-svg{width:100%;display:block}
a.pursepouch .bead{position:absolute;top:-6px;right:-10px;min-width:26px;height:26px;padding:0 6px;
border-radius:999px;background:var(--accent,#1f7a3d);color:#fff;border:3px solid var(--edge);
display:flex;align-items:center;justify-content:center;font:800 14px/1 var(--display);
box-shadow:0 3px 0 rgba(0,0,0,.2)}
@media (max-width:620px){a.pursepouch{width:52px}
a.pursepouch .bead{min-width:22px;height:22px;font-size:12px;border-width:2px}}



/* "Take It Back (29)" beside a bank just made (24 Sep). Small and quiet: it is for the press you did not mean,
   and it takes itself off the page when the seconds run out. */
form.takeback{display:inline-block;margin-left:8px;vertical-align:middle}
form.takeback .btn,form.takeback .ghost.tiny{padding:2px 9px;font-size:12px;border-width:2px}
form.takeback .tbleft{font:700 12px/1 "IBM Plex Mono",monospace;opacity:.75;margin-left:2px}

/* ---------- a player's profile (25 Sep) ----------
   "wheres the color? you used colors for positions on the matchup page, make it homogenous ... descriptive text
   under names is small and boring. make it bigger." So the banner wears the same position colours the lineup does,
   at a size you can read, and the seasons are a DENSE table -- date, club, opponent, the box score, and what the
   game was worth here -- with a line between weeks and the week's best in gold. */
.whobar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:6px 0}
.whobar .whokey{font:700 11px/1 var(--mono,monospace);color:var(--ink-3);text-transform:uppercase;
letter-spacing:.08em;margin-right:2px}
/* His league, year by year: one line per season on record, under a single label saying whose league this is. */
.wholeague{margin:10px 0 0}
.wholeague>.whokey{display:block;font:700 11px/1 var(--mono,monospace);color:var(--ink-3);
text-transform:uppercase;letter-spacing:.08em;margin-bottom:2px}
.wholeague .whobar{margin:2px 0;align-items:baseline;flex-wrap:nowrap}
.wholeague .whobar .whokey{flex:0 0 auto}
.wholeague .whofacts span{font-size:14px}
/* The dates are the quiet part: a team name is the fact, when he had him is the detail. */
.wholeague .whofacts .meta{font:400 13px/1.2 inherit;color:var(--ink-3)}
.whofacts{display:flex;align-items:center;flex-wrap:wrap;gap:8px}
.whofacts span{font:600 15px/1.2 var(--display);color:var(--ink-2)}
.whofacts span+span::before{content:"·";margin-right:8px;color:var(--ink-3)}
i.pospill{display:inline-block;font:800 13px/1 var(--display);font-style:normal;color:#fff;
background:var(--pos);border:2px solid var(--edge);border-radius:999px;padding:5px 11px;letter-spacing:.04em}
table.gamegrid{width:100%;border-collapse:collapse;font-size:12.5px}
table.gamegrid th{padding:5px 7px;text-align:right;font:700 10px/1.2 var(--mono,monospace);color:var(--ink-3);
text-transform:uppercase;letter-spacing:.05em;border-bottom:2px solid var(--line-soft);white-space:nowrap}
table.gamegrid th.l,table.gamegrid td.l{text-align:left}
table.gamegrid td{padding:4px 7px;text-align:right;border-bottom:1px solid var(--line-soft);white-space:nowrap}
/* Less punchy than it was (his, 25 Sep: "fp is a bit punchy") -- the colour does the shouting now, so the
   weight does not have to. */
table.gamegrid td.fp{font:700 13px/1 var(--display);letter-spacing:.01em}
table.gamegrid tr.weekline td{background:color-mix(in srgb,var(--ink) 8%,var(--panel));text-align:left;
font:700 10px/1.3 var(--mono,monospace);color:var(--ink-2);text-transform:uppercase;letter-spacing:.06em}
/* A CHANGE OF HANDS, laid into the season on the day it happened: who had him from here down. Quieter than a week
   divider -- the week is the unit of the game, this is context -- but it holds a name, so it is not grey. */
table.gamegrid tr.handline td{background:color-mix(in srgb,var(--accent) 10%,var(--panel));
font:600 11px/1.4 var(--display);color:var(--ink-2);letter-spacing:.02em;
border-top:1px solid color-mix(in srgb,var(--accent) 45%,transparent)}
table.gamegrid tr.handline td:first-child{color:var(--ink-3);white-space:nowrap}
table.gamegrid tr.handline b{color:var(--ink)}
/* THE WEEK'S BEST, harder to miss: a deeper gold, a gold edge down the left, and a star on the date.
   The band is weaker at night, because gold laid thickly over a dark panel becomes a pale slab and the row's own
   writing -- the club, the score -- goes out on the one row he most wants to read. */
table.gamegrid tr.highlock td{background:light-dark(color-mix(in srgb,var(--gold,#f4c542) 45%,var(--panel)),
color-mix(in srgb,var(--gold,#f4c542) 14%,var(--panel)));
border-bottom-color:color-mix(in srgb,var(--gold,#f4c542) 60%,var(--line-soft))}
table.gamegrid tr.highlock td:first-child{box-shadow:inset 4px 0 0 var(--gold,#f4c542);font-weight:700}
/* A CLUB IS A PLATE, not loose letters (his, 26 Sep). It brings its own background, so the club's real colours
   work on cream and in the vault alike -- which is the point: a bright gold letter is a whisper on a banknote,
   a gold PLATE is not. Square-ish corners keep it apart from the position pills beside it, which are round, and
   the hairline of its own ink keeps a near-black plate from dissolving into a dark page. */
b.club.pill{display:inline-block;padding:1px 6px;border-radius:5px;
background:var(--plate);color:var(--plate-ink);
/* THE EDGE BELONGS TO THE PAGE, NOT TO THE CLUB (his, 26 Sep): white on the vault, near-black on the banknote.
   It is the page's own ink, so it follows the look without a second set of colours to keep in step -- and it is
   what stops a near-black plate dissolving into a dark page, or a pale one into cream. */
/* THE RIM BELONGS TO THE PAGE, NOT TO THE CLUB (his, 26 Sep, and again on the 27th: "the rims should be
   either black or white so they juxtapose with the theme"). It is the page's own ink, so one line serves both
   looks with no second set of colours to keep in step, and it is what stops a near-black plate dissolving into
   the vault or a pale one into the banknote. The club's two colours are the plate and the letters. */
border:1px solid color-mix(in srgb,var(--ink) 85%,transparent);
font:700 11px/1.5 var(--mono,ui-monospace,monospace);letter-spacing:.06em}
/* A WHITE BORDER ROUND THE NAME (his, 27 Sep: "i think i have a fix for the pistons one. put a white border
   around the name as well"). Only on the pairs that cannot part from each other -- Detroit's red on royal blue,
   the Clippers' pacific blue on naval. The letters are then read against the outline instead of against the
   plate, so both club colours stay at full strength. In em, so it thins with the pill: a 2px stroke that reads
   well at 15px fills the counters in at 9px. paint-order puts the stroke BEHIND the fill, so the letter keeps
   its full colour instead of being eaten into from both sides.

   THINNED to .06em on his word ("make the text border thinner"), from the .11em it first shipped at. Compared
   at 15, 11 and 9px: .11 read as a chunky halo that took over the letter, .045 stopped separating Detroit's red
   from its blue at 9px. .06 is about half the original and still parts them at the smallest size a pill is
   ever drawn. */
b.club.pill.outlined{paint-order:stroke fill;-webkit-text-stroke:.06em var(--plate-edge,#f7f5f0)}
@media (max-width:620px){table.gamegrid{font-size:11px}
table.gamegrid td,table.gamegrid th{padding:3px 4px}
.whofacts span{font-size:13px}}

/* The players list wears the same position colours as a lineup row, and the names are readable (25 Sep). */
i.pospill.small{font-size:11px;padding:3px 8px;border-width:2px;margin-right:3px}
.pname.big{font:800 15px/1.2 var(--display)}

/* The game table scrolls sideways rather than squashing (25 Sep), so a column can be added without ruining it. */
.sidescroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
.sidescroll table.gamegrid{min-width:760px}
/* The same scrollbar again, ABOVE the table (his, 25 Sep), so sideways is reachable from either end of a season.
   It is an empty rail as wide as the table; kit.SIDE_RAILS ties the two together and hides it when it is no use. */
.railtop{overflow-x:auto;overflow-y:hidden;margin:0 0 4px}
.railtop i{display:block;height:1px}

/* LOOKING AHEAD: a week still to come, side by side. Two rosters, no scores -- there is nothing to score yet.
   Written as `.panel .pad.aheadgrid` to beat `.panel .pad`, which stacks its children in a column: a single class
   loses to two however far down the file it sits, and the two sides came out one above the other. */
.panel .pad.aheadgrid{display:grid;grid-template-columns:1fr 1fr;gap:18px;align-items:start}
.aheadside .ghead{display:flex;align-items:center;gap:8px;margin:0 0 6px}
.aheadside table.gamegrid{min-width:0}
@media (max-width:760px){.panel .pad.aheadgrid{grid-template-columns:1fr}}

/* THE TRADE DESK: a row says what a man IS, not just his name (his, 26 Sep: "how are you supposed to know who to
   trade for if you have to click player by player for their information"), grouped by position because that is
   what you shop for -- and SMALL, because it is a little information and it was taking a lot of room. */
/* ONE GRID FOR THE WHOLE LIST, and every row shares its columns (subgrid). Each row used to be a grid of its
   own, so "1fr" was measured against that row's name and nothing lined up with anything: the positions of a man
   with a short name started three characters left of the man below him. */
/* Every column as wide as its widest row and no wider, with the slack parked in one empty track at the
   END -- a 1fr on the name was pushing the pills half a panel away from it (his, 26 Sep). */
.tside .ticks{display:grid;
grid-template-columns:auto auto minmax(0,auto) auto auto auto auto auto minmax(0,1fr);
gap:0 9px}
/* THE DESK SIZES ITSELF TO THE NUMBER OF TEAMS (his, 26 Sep): "we can start with bigger font and size when
   there are two teams and shrink it to add three." Two is the common case and gets the room. */
.mmgrid[data-sides="3"]{grid-template-columns:1fr 1fr 1fr;gap:12px}
.tdesk[data-sides="2"] .tick.trow{font-size:13px;line-height:1.62}
.tdesk[data-sides="2"] .tick.trow .tavg{font-size:13.5px}
.tdesk[data-sides="2"] .tside .ticks{grid-template-columns:auto auto minmax(0,auto) auto auto auto auto auto minmax(0,1fr);
gap:0 12px}
.tdesk[data-sides="3"] .tick.trow{font-size:11px;line-height:1.55}
.tdesk[data-sides="3"] .tside .ticks{grid-template-columns:auto auto minmax(0,auto) auto auto auto auto auto minmax(0,1fr);
gap:0 7px}
.tdesk[data-sides="3"] .tick.trow .meta{font-size:9px}
.tdesk[data-sides="3"] .tick.trow .tavg{font-size:11px}
.tdesk[data-sides="3"] .pospill.tiny{font-size:8px;padding:0 3px}
/* EACH TEAM IN A BOX OF ITS OWN (his, 26 Sep: "separate the teams with white boxes"). Near-white on the vault,
   near-black on the banknote -- the same trick the club pills use, so one rule serves both papers. */
.mmgrid>*{min-width:0}
/* THE MAIN BORDER, not a faded one (his, 1 Oct: "change the outlines for the trade boxes to the main border
   white"). They were a 60% wash of the ink colour, so two teams facing each other read as a suggestion rather
   than as two boxes -- which is the separation he asked for in the first place. Same `--edge` every panel on
   the site is drawn with. */
.tside.tbox{min-width:0;border:2px solid var(--edge);border-radius:11px;
padding:9px 8px 10px;background:color-mix(in srgb,var(--ink) 3%,transparent)}
.tside.tbox .navhead{margin:0 0 6px}
/* WHAT ELSE CAN BE TRADED, ACROSS THE TOP (his): FAAB and the coins as sprites, not a fold of their own. */
.tassets{display:flex;flex-wrap:nowrap;align-items:center;gap:7px;min-width:0;overflow-x:auto;scrollbar-width:none;margin:0 0 8px;padding:0 0 8px;
border-bottom:1px solid color-mix(in srgb,var(--ink) 18%,transparent)}
.tasset{display:inline-flex;align-items:center;gap:4px;margin:0}

.tasset{min-width:0}
.tasset .bid{width:40px;min-width:28px;flex:0 0 auto;padding:2px 3px;font:700 11px/1.3 var(--mono,monospace);text-align:center}
.tasset .coin{flex-direction:row;gap:0}
.tasset .coin-disc{width:27px;height:27px;border-width:2px;box-shadow:inset 0 0 0 2px #ffe680,inset 0 0 0 3px #b47a00,2px 2px 0 var(--shadow)}
.tasset .coin-face{font-size:13px}
.tasset .coin-n{right:-4px;bottom:-3px;font-size:8.5px;min-width:14px;border-width:1px;padding:0 2px}
/* TOO MANY PLAYERS: the list you drop from. Same dense row as the trade desk, with the action on the right. */
.overlist .ticks{grid-template-columns:40px minmax(0,auto) minmax(0,auto) 34px minmax(0,auto) minmax(0,1fr)}
.tick.trow.overrow{cursor:default;padding:2px 5px}
.tick.trow.overrow .ghost{padding:2px 9px;font-size:11px}
.tick.trow.overrow .meta{font-size:10.5px}
/* WHERE HE GOES, asked on his own row once he is ticked (his, 26 Sep). It takes NO space until it is needed:
   reserving a column for it on all thirteen rows to serve the two you are sending cost the names their room,
   and a subgrid track with nothing in it is zero wide. */
.tick.trow .tdest{display:none}
.tick.trow input[type=checkbox]:checked~.tdest{display:block}
.tick.trow .tdest select{font:700 10px/1.3 var(--mono,monospace);padding:1px 2px;max-width:92px;
border-radius:6px;border:1px solid color-mix(in srgb,var(--ink) 35%,transparent);background:transparent;color:inherit}
.navhead .tteam{font:inherit;font-size:12px;padding:2px 4px;border-radius:7px;
border:1px solid color-mix(in srgb,var(--ink) 35%,transparent);background:transparent;color:inherit;
text-transform:none;letter-spacing:0}
.navhead .tdrop{margin-left:6px;text-decoration:none;color:var(--ink-3);font-weight:700}
.navhead .tdrop:hover{color:var(--ink)}
.tdesk[data-sides="3"] .tick.trow .tdest select{font-size:9px;max-width:70px}
.faabtag{font:800 9px/1 var(--mono,monospace);letter-spacing:.08em;color:var(--ink-3);white-space:nowrap}
.tdesk[data-sides="3"] .tasset{gap:2px}
.tdesk[data-sides="3"] .tasset .bid{width:26px;min-width:26px;padding:2px 1px;font-size:10px}
.tdesk[data-sides="3"] .tassets{gap:4px}
.tdesk[data-sides="3"] .faabtag{font-size:8px;letter-spacing:.04em}
.tdesk[data-sides="3"] .tasset .coin-disc{width:18px;height:18px}
.tdesk[data-sides="3"] .tasset .coin-face{font-size:9px}
.tto{font:600 10.5px/1 var(--mono,monospace);color:var(--ink-3);text-transform:none;letter-spacing:0;margin-left:6px}
.tto select{font:600 11px/1 var(--mono,monospace);padding:1px 3px;margin-left:3px}
@media (max-width:1180px){.mmgrid[data-sides="3"]{grid-template-columns:1fr}}
.tick.trow{display:grid;grid-template-columns:subgrid;grid-column:1/-1;
align-items:center;gap:6px;padding:1px 5px;margin:0;border-radius:5px;font-size:12.5px;line-height:1.5}
/* THE COLUMN TITLES. Small, quiet and said once at the top of each team's list -- the average especially,
   because a number beside a name says nothing about itself (his, 26 Sep). */
.tick.trow.thead{font:700 8.5px/1.6 var(--mono,monospace);letter-spacing:.09em;text-transform:uppercase;
color:var(--ink-3);border-bottom-color:color-mix(in srgb,var(--ink) 26%,transparent);padding-bottom:3px}
.tick.trow.thead:hover{background:none}
.tick.trow.thead .num{text-align:right;padding-right:9px}
/* The name column is as wide as the longest name on the list, so a heading hugging its left edge sat next to
   CLUB and looked like part of it. Centred, it reads as the title of the column under it (his, 26 Sep). */
.tick.trow.thead .tname{text-align:center}
.tick.trow .tclub{display:flex}
.tdesk b.club.pill{font-size:10px;padding:0 5px;border-radius:4px;letter-spacing:.04em}
.tdesk[data-sides="3"] b.club.pill{font-size:10px;padding:0 4px}
.tick.trow .tname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* The name is a chip, and a chip is its own box -- so it gets clipped rather than ellipsised, and "Andrew
   Nembhard" reads as "Andrew Nembhar", a different man. The ellipsis has to go on the chip itself. */
.tick.trow .tname>*{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tick.trow input[type=checkbox]{width:14px;height:14px}
.tick.trow:hover{background:color-mix(in srgb,var(--ink) 7%,transparent)}
/* A FAINT LINE BETWEEN MEN, horizontal only -- trying it, his, 26 Sep. Mixed from the ink so it is a pale line
   on the vault and a grey one on the banknote, and never a hard rule on either. */
.tdesk .tick.trow{border-bottom:1px solid color-mix(in srgb,var(--ink) 11%,transparent)}
.tdesk .tick.trow:last-child{border-bottom:0}
.tick.trow input{margin:0}
.tick.trow .tname{font-weight:700}
.tick.trow .tpos{display:flex;align-items:center;gap:3px;overflow:hidden}
.tick.trow .tavg{font:700 12px/1 var(--display);color:var(--accent);text-align:right;
font-variant-numeric:tabular-nums;padding-right:9px}
.tick.trow .tflags,.tick.trow .tbank{display:flex;align-items:center;white-space:nowrap}
/* THE COLOUR IS THE MEANING (his, 27 Sep: "active in green, dtd in a soft orange"). Three steps: playing,
   might play, not playing. The two calm ones are tinted chips so they hold their column without shouting; OUT
   keeps the solid alert badge, because it is the one you must not miss. */
/* THE WEIGHT FOLLOWS THE MEANING, and most of the time the meaning is "nothing to worry about" (his, 27 Sep:
   "they dont need to take up that much room. just informational"). So a calm state is COLOURED TEXT and no
   chip at all, a warning gets the faintest tint, and only OUT keeps a solid badge -- because that is the one
   you must not miss. Read down the column and the loud ones are the only ones that stop you. */
.badge.ok,.badge.nowt,.badge.warn,.badge.quiet{padding:0;background:none;letter-spacing:.05em;
font-weight:700}
.badge.ok{color:light-dark(#1a7a4c,#4fc78d)}
.badge.warn{color:light-dark(#9a5a12,#e5a260)}
.badge.quiet{color:color-mix(in srgb,var(--ink) 52%,transparent)}
.badge.nowt{color:color-mix(in srgb,var(--ink) 30%,transparent)}
.badge.bad{background:var(--alert);color:#fff}
/* Smaller on a trade row than anywhere else: this is a dense list, and the badge is a note beside a name. */
.tick.trow .badge{font-size:8.5px;margin-left:0}
.tick.trow .badge.bad{padding:0 5px}
/* A badge in its own slot needs no left margin: the grid gap is the spacing now. */
.tick.trow .tflags .badge,.tick.trow .tbank .badge{margin-left:0}
.tick.trow .badge,.tick.trow .meta{font-size:9.5px}
.pospill.tiny{font:800 8.5px/1.6 var(--mono,monospace);padding:0 4px;border-radius:6px}
.navhead .ttally{margin-left:8px;font-style:normal;font-weight:600;color:var(--accent);font-size:11px}
.tmoney{margin-top:8px}
.tmoney summary{cursor:pointer;font-weight:700}

/* SIX COLUMNS, THE LAST FOR WHERE THE PICK GOES (21 Oct). A pick row gained a destination when a trade could
   take a third squad, and the row's grid had five columns to put six things in -- so the picker landed in an
   implicit column and came out SIXTEEN PIXELS wide: present, shown on the tick, and far too narrow to read a
   squad name in. Measured in a real browser rather than reasoned about; the markup and the CSS both looked
   right. `auto` costs nothing on the rows that are not ticked, because the picker is display:none there and a
   content-sized column with nothing in it is zero wide. */
.tick.trow.pickrow{grid-template-columns:16px 52px minmax(0,1fr) 66px auto auto}
.tick.trow.pickrow .tname{font:700 13px/1 var(--mono,monospace);letter-spacing:.04em}
.tick.trow.pickrow .tavg{color:var(--ink-2)}
.tick.trow.pickrow .tdest{margin-left:6px}
.tick.trow.pickrow .tdest select{max-width:110px}
.tdesk[data-sides="3"] .tick.trow.pickrow .tdest select{max-width:84px}

/* WHERE THE MONEY GOES, ASKED IN AN OVERLAY (his, 26 Sep), not by a picker in the strip. Fixed and on the
   body: an absolute overlay inside a panel is clipped by it, and z-index cannot save that. */
.askover{position:fixed;inset:0;z-index:80;display:grid;place-items:center;
background:color-mix(in srgb,#000 55%,transparent);backdrop-filter:blur(2px)}
.askcard{background:var(--panel);border:2px solid var(--line);border-radius:14px;padding:16px 18px;
box-shadow:0 18px 40px rgba(0,0,0,.45);max-width:min(92vw,420px)}
.askhead{margin:0 0 10px;font:700 14px/1.4 var(--display);color:var(--ink)}
.askrow{display:flex;flex-wrap:wrap;gap:8px}
.askrow .btn.on{outline:2px solid var(--accent);outline-offset:2px}
/* A box with somewhere to go says so, without becoming a second control. */
.tasset.going .bid{border-color:var(--accent);color:var(--accent)}
.tasset.going::after{content:attr(data-goes);font:700 8px/1 var(--mono,monospace);color:var(--accent);
position:absolute;left:0;right:0;bottom:-9px;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tasset{position:relative}

/* TAKING A TEAM OFF THE DESK is a button you can see, not a grey cross (his, 26 Sep: "theres also no remove a
   team button"). It sits with that column's own name, because that is the team it removes. */
.navhead .tdrop{margin-left:8px;padding:1px 7px;border-radius:999px;text-decoration:none;
border:1px solid color-mix(in srgb,var(--ink) 30%,transparent);color:var(--ink-3);
font:700 9px/1.7 var(--mono,monospace);letter-spacing:.06em;text-transform:uppercase}
.navhead .tdrop:hover{color:var(--ink);border-color:var(--ink)}

/* THE TWO RECORDS, SIDE BY SIDE (his, 26 Sep): open offers and the season's trades, two panels rather than two
   long boxes across the page. They stack on a narrow screen like every other pair here. */
.mmgrid.recpair{align-items:start;margin-top:14px}
.mmgrid.recpair>.panel{margin:0}
/* AN OFFER IS TWO SIDES OF PLUSES AND MINUSES, not the desk again in miniature. */
.offer{border:1px solid color-mix(in srgb,var(--ink) 22%,transparent);border-radius:11px;
padding:10px 11px;margin:0 0 9px}
.osides{display:grid;gap:10px}
.osides[data-in="2"]{grid-template-columns:1fr 1fr}
.osides[data-in="3"]{grid-template-columns:1fr 1fr 1fr}
.oteam{margin:0 0 5px}
.oline{margin:1px 0;font-size:12.5px;line-height:1.5;display:flex;gap:4px;align-items:baseline}
/* Coming in takes the page's own highlight; going out is muted. No invented green: the vault and the
   banknote each have their own accent, and a hardcoded one would be right on only one of them. */
.oline.in{color:var(--accent)}
.oline.out{color:var(--ink-3)}
.oline.none{color:var(--ink-3);font-style:italic}
.oline .chip.plain{color:inherit}
.ofoot{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:9px;
padding-top:8px;border-top:1px solid color-mix(in srgb,var(--ink) 14%,transparent)}
@media (max-width:1180px){.osides[data-in="3"]{grid-template-columns:1fr}}

/* In half a page's width, a trade's legs read down rather than wrapping through each other. */
.tleg{display:block;margin:1px 0}

/* ---------- the Messages dock, inside the spill ----------
   "it doesnt need the layout, i think it can be much simpler. like a row of all the players and a league chat"
   (28 Sep). One row across the top, the conversation under it, a box to type in. The first build had a tab strip
   and a two-column layout -- a page's furniture smuggled into a dock. */
.msgdock{grid-column:1/-1;display:flex;flex-direction:column;gap:12px;min-width:0;width:100%;
background:var(--panel);border:3px solid var(--edge);border-radius:16px;
box-shadow:5px 5px 0 var(--shadow);padding:14px}
.msgrow{display:flex;flex-wrap:wrap;gap:8px}
.msgwho{position:relative;flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;text-decoration:none;
padding:8px 13px;border-radius:999px;border:2px solid var(--line-soft);color:var(--ink);
font:700 13px/1.2 var(--display);white-space:nowrap}
.msgwho.on{border-color:var(--ink);background:color-mix(in srgb,var(--ink) 12%,transparent)}
/* The one thing in the row that LEAVES the dock, so it does not look like another conversation. */
.msgwho.out{border-style:dashed;color:var(--ink-2)}
.msgn{min-width:20px;padding:0 6px;border-radius:999px;background:#d7263d;color:#fff;
font:800 11px/18px var(--display);text-align:center;font-style:normal}
/* Tall enough for a poll with a dozen answers in it, and it opens at the newest line (kit's sweep). */
.msgscroll{display:flex;flex-direction:column;gap:8px;max-height:min(64vh,660px);overflow:auto;padding-right:4px}
.msgline{display:flex;flex-direction:column;gap:2px;padding:9px 12px;border-radius:13px;
border:2px solid var(--line-soft);max-width:min(760px,100%)}
.msgline.said{border-style:dashed;opacity:.85}
.msgline.mine{border-color:var(--accent);background:var(--accent-soft);align-self:flex-end;max-width:85%}
/* What happened, under the line that says it happened: a trade as each side's + and -, a claim as its ladder
   of bids with the manager who took the player on top. */
.msgcard{display:flex;flex-direction:column;gap:5px;margin-top:7px;padding-top:7px;border-top:2px dashed var(--line-soft)}
.msgside,.msgbid{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.msgwhose{min-width:86px;font:700 12px/1.3 var(--display);text-transform:uppercase;letter-spacing:.04em;color:var(--ink-3)}
.msgbits{display:flex;flex-wrap:wrap;gap:6px}
.msgin,.msgout{padding:2px 9px;border-radius:999px;font:700 12px/1.6 var(--display);border:2px solid currentColor}
.msgin{color:#4fc78d}
.msgout{color:var(--alert)}
.msgbid.took .msgwhose{color:var(--ink)}
.msgbid.took .msgfaab{color:#4fc78d;font-weight:800}
.msgfaab{font:700 12px/1.5 var(--display);color:var(--ink-2)}
.msgsend{display:flex;gap:8px}
.msgsend input{flex:1 1 auto;min-width:0}
/* Edit and Remove ride on the line's own byline, so a message carries its tools without a second row. */
.msgline>.meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.msgline>.meta form.inline{display:inline-flex}
/* Asking the league something: shut by default, because most of the time you are here to talk. */
.msgask>summary{cursor:pointer;list-style:none;padding:5px 2px;color:var(--ink-2);
font:700 13px/1.2 var(--display)}
.msgask>summary::-webkit-details-marker{display:none}
.msgask>summary::before{content:"+ "}
.msgask[open]>summary::before{content:"\2212 "}
.msgask>summary:hover{color:var(--ink)}
.msgask .form{display:flex;flex-direction:column;gap:9px;padding:8px 0 2px}
.spillwrap.tall .spillsheet{max-height:86vh}
/* THE DRAFT ROOM IS THE WIDEST THING IN THE APP: a board of fourteen columns with a rail beside it. At the
   1120px every other panel uses, six of the board's columns were off the right-hand edge. It takes the screen
   it is given, and no more than it needs. */
.spillwrap.tall:has(.spillswitch) .spillsheet{width:min(1760px,100%)}
/* A PANEL OVER A PANEL (29 Sep): a player's report opens over the board, which is itself a panel. It sits above
   and darkens a little more, so it is obvious which one you are reading and which one you go back to. */
.spillwrap.stacked{z-index:70;background:rgba(10,7,16,.58)}
.spillwrap.stacked .spillsheet{width:min(560px,100%)}
.newsspill .pad{padding:16px 18px 18px}
.newsspill h2{margin:2px 0 6px}
.newsspill p{margin:0 0 8px}

/* ---------- THE DRAFT LOG: one box of mocks, not a page of panels (his, 30 Sep) ----------
   "lets put all mock drafts in a box so that if there are more than fit on the screen, we can use a scroll wheel
   to go down. i also think we can compact the boxes a bit. maybe try and make all the names fit on 1 line."
   Each mock was a panel with a heading, a note, a wrapped block of fourteen names and a row of buttons -- three
   filled a screen. A row now: when it ran and how it ended, who you came away with, and the two things you can
   do with it. The BOX scrolls, so ten mocks take the same room as three. */
/* ONE BOX, TWO SECTIONS (his, 30 Sep). The hero keeps its own padding for the words and the buttons; the line
   runs the full width of the box, so the two sections read as two halves of one thing rather than two boxes. */
.hero.logbox{padding-bottom:0}
/* THE DRAFT LOG'S BANNER COVERS THE WHOLE BOX (27 Oct). It was pinned to a wrapper around the header for a
   day, because a drawing behind a list of kept mocks is a drawing under rows of text -- but the real reason
   it could not simply fill the box is that the box has NO FIXED HEIGHT: 256px empty, up to 62vh with a full
   log. Art is `object-fit:cover`, so a picture with a subject in it becomes a magnified fragment as the box
   grows. The answer was the drawing, not the plumbing: a page of ruled cells has no subject to lose, so it
   survives any crop at any ratio, and the wrapper went away again.
   The list paints its own background over it, so entries never sit on the ruling. The empty state's "Nothing
   yet" does NOT, deliberately -- with no record to show, the ledger is what fills the card. */
.hero.logbox:has(.heropin) .mocklist{background:var(--panel)}
/* THE DIVIDING RULE IS BACK, BEHIND THE BOOK (his, 28 Oct: "draft log need a line seperator"). Hiding it
   was right while the art was a full-card PATTERN: a rule drawn across a picture reads as a seam, which is
   what "what is this hard line thats going across the screen" was about. The art is one object now, and
   with the rule gone the first kept mock ran straight into the header with nothing between them.
   So it is drawn, and the book sits in FRONT of it: the line crosses the card, reaches the book and
   disappears under it, which is what a line under a thing on a desk does. `.hero>*:not(.heroart)` gives
   every child z-index 1, so without this the rule would tie with the book and win on document order. */
.hero.logbox:has(.heropin) .logsplit{z-index:0;margin-right:0}
.hero.logbox:has(.heropin)>p{max-width:54%}
@media (max-width:860px){.hero.logbox:has(.heropin)>p{max-width:none}}
/* THE SECOND LAYER: the pen and pot, sized to the box instead of cropped to fill it. `object-fit:cover` is
   right for a backdrop and wrong for a thing -- it is why the ledger had to become a pattern in the first
   place. This one is placed against the box, so it keeps its shape at every width and at every height the
   log grows to. It sits in the clear right-hand third, clear of the fade, and it paints ABOVE the backdrop
   and BELOW the words: the scrim is the hero's ::after, which comes later in the box, so a z-index of 0 is
   not enough to stay in front of it -- hence 1, matching the words it must not cover. It never does, because
   they stop at 54% and it starts past 70%. */
/* THE BOOK sits at the bottom right and runs off the bottom edge, so it reads as a ledger open on a desk
   rather than a cut-out floating in a box (the card's own `overflow:hidden` does the cropping).
   ANCHORED TO THE TOP IN PIXELS, not to the bottom in percentages, and that is the whole trick: this card
   is 256px tall when the log is empty and grows to 62vh once it fills with mocks. Anything measured from
   the bottom slides away from the heading exactly when the list arrives and the heading is all you can
   still see. Measured: the heading ends at 125, so the book starts just under it. */
/* SIZED BY WIDTH, in steps (his, 27 Oct: "it should probably have different size settings on every pixel
   state"). It was a flat 210px everywhere, which is small on a desk and proportionally enormous at 980 --
   the card is 1158 wide at a desk and 674 at a small window, so a fixed height means the book covers 44%
   of one and 76% of the other.
   THE CARD DOES NOT GROW TO FIT IT, and that is his correction: "i didnt mean expand it vertically, i meant
   horizontally ... dont necessarily expand the bottom of the banner to fit the whole image". A floor on the
   card's height was the wrong answer -- the book is meant to be a bigger PICTURE, cropped by the bottom of
   the banner like a ledger lying half off the edge of a desk. The top stays at 128, just below where the
   heading ends, so it never shares a line with the words at any size; everything below the card's edge is
   simply out of frame.
   AND IT STARTS AT THE TOP, not under the heading -- his "move it up, there barely any of the art on it".
   It sat at y128 because I had read the h1's BOUNDING BOX, which is full width whatever it says. Measured
   properly, with a range round the text itself, the heading ends at 18% of the card and the longest line --
   the note underneath -- at 27%. The whole width above the book was empty and it was being held down for
   nothing. The steps below keep its left edge clear of the longest line at each size, which is the thing
   that actually constrains it: at a small window that note runs to 47% and the book has to give way. */
/* THE BOOK ENDS WHERE THE LINE ENDS, which is what makes the line stop AT it (his, 28 Oct: "the line isnt
   stopping the book"). It was inset 3% from the right, and the dividing rule bleeds 20px past the padding
   box to reach the card's edge -- so the rule went under the book and CAME OUT THE OTHER SIDE, a 33px stub
   of line floating in the gap. Measured at three widths: 33px, 24px, 18px. The rule's right bleed is
   dropped and the book sits at right:0, so both finish on the same pixel and the line simply disappears
   under the book. The rule still bleeds LEFT, because that end should reach the card's edge. */
/* IT IS CUT OFF BY THE RULE, NOT SHRUNK TO FIT ABOVE IT (his, 29 Oct: "i dont want the whole book show.
   I want it to stop at the line but look like this", with a photograph of a big book sliced across the
   bottom). It protruded past the line the day before, and I answered by making it smaller -- 250 down to
   180 -- which fixed the overlap and lost the picture: a whole book, floating, with clean panel under it.
   The line is a CROP, which is what a ledger lying half off the edge of a desk actually looks like.
   SO THE HEIGHT IS THE SCALE AND THE CLIP IS THE FRAME, and the two are now separate things. Measured on
   the real page, with a kept mock in the log so the rule has something to separate: the card's top is at
   153, the book starts at 170 and the rule sits at 353 at EVERY width -- the heading never wraps and the
   buttons are always one line, so the header is a constant 200px and the book's window is 183 of it.
   Anything taller than 183 is simply out of frame, so the steps below no longer decide how much of the book
   you see. They decide how BIG it is drawn, which is the thing that was wrong.
   AND 183 IS NOT A CONSTANT ALL THE WAY DOWN, which only a measurement at the edge of each breakpoint
   found. The heading is `clamp(28px,4.6vw,44px)`, so it is pinned at 44px only while the window is 956 or
   wider; between there and 860 -- where the book leaves -- it shrinks with the window and takes the header
   down with it, 4px by 876. A flat 183 hung the book over the line again in exactly that band. So the
   window is written the way the header is actually built: everything above the rule that never moves, plus
   the heading's own line box at whatever size the clamp is giving it. Measured at both ends: 183 wide,
   179 at 876, flush on the line at both. */
.hero.logbox{--book-window:calc(137px + 1.05 * min(44px,4.6vw))}
.hero.logbox .heropin{position:absolute;right:0;top:14px;bottom:auto;height:230px;width:auto;
z-index:1;pointer-events:none;object-fit:contain;
clip-path:inset(0 0 max(0px,100% - var(--book-window)) 0)}
/* The steps keep the book's WIDTH to about half the card at every size, which is what holds its left edge
   clear of the heading and the buttons underneath; the card narrows from 1158 to 638 while a fixed height
   would not. A step wants to stay ABOVE the window, or the clip stops biting and the book floats clear of
   the line again -- which is the fault this whole block is about.
   ONE BAND CANNOT HAVE BOTH, and pretending otherwise is what would put the drawing under the heading.
   Between 901 and 980 the league rail is still beside the card and the window is narrow, so the card is
   down to 619px: a book tall enough to reach the line is 454px wide there and its left edge lands INSIDE
   the end of "Draft Log", which is large text with no background of its own. So that band gets a small
   book that stops short of the line, and it is the only one that does. Running under a BUTTON is allowed
   -- a button has its own solid background and reads as something lying on the ledger -- but running
   under bare text is not, so the heading is what each step is measured against.
   AND IT GOES BACK UP AT 900, which looks like a mistake and is not: that is where the rail stacks ABOVE
   the card (the media block at line 309) and the card goes from 619px wide back up to 841. Measured. */
@media (max-width:1400px){.hero.logbox .heropin{height:215px}}
@media (max-width:1100px){.hero.logbox .heropin{height:185px}}
@media (max-width:980px){.hero.logbox .heropin{height:150px}}
@media (max-width:900px){.hero.logbox .heropin{height:205px}}
@media (max-width:860px){.hero.logbox .heropin{display:none}}
/* NO SCRIM HERE. The fade exists to keep words legible over a backdrop, and this card has no backdrop any
   more -- one object, over at the right, with clean panel everywhere the words are. A gradient over nothing
   would only tint the card. */
.logsplit{height:1px;background:var(--line-soft);margin:16px -20px 0}
.hero.logbox .mocklist{margin:0 -20px}
.hero.logbox .mockrow:last-child{border-bottom:0}
.mocklist{max-height:62vh;overflow:auto}
.mockrow{padding:9px 14px;border-bottom:1px solid var(--line-soft)}
.mockrow:last-child{border-bottom:0}
.mockrow.open{background:var(--panel-2)}
/* When it ran and what you can do with it, on one line; the names get the full width of the box below it. */
.mocktop{display:flex;align-items:center;justify-content:space-between;gap:14px}
.mockwhen{display:flex;align-items:baseline;gap:10px;min-width:0}
.mockwhen b{font:800 13px/1.2 var(--display);white-space:nowrap}
.mockwhen .meta{font-size:11px;white-space:nowrap}
/* ONE LINE OF NAMES. Fourteen short names fit at this size in the room there is; if a roster ever overruns it,
   the line slides sideways rather than wrapping into a block and making every row a different height. */
.mocktook{margin-top:5px;white-space:nowrap;overflow-x:auto;overflow-y:hidden;scrollbar-width:thin;
padding-bottom:2px}
.mocktook .chip.plain{font-size:11.5px;padding:1px 5px}
.mockacts{flex:0 0 auto;display:flex;align-items:center;gap:8px}
.mockacts .ghost{padding:4px 10px;font-size:12.5px}
.mockboard{margin:0 14px 12px}
@media (max-width:900px){
  .mocktop{flex-wrap:wrap}
  .mockwhen{flex:1 1 100%}
}

/* A button waiting for its second press (kit.py, `data-again`). Loud, because it is about to throw work away --
   the same alert colour the board uses for a risk, not the accent, which here means "chosen". */
.ghost.arming,.btn.arming{background:var(--alert-soft);border-color:var(--alert);color:var(--alert)}

/* THE TRADE DESK HAS NO BOX AROUND ITS BOXES (his, 1 Oct): "remove the large box around all the involved teams
   and just have a box around each individual team. more seperation there is good." Each team keeps its own
   `.tside.tbox`; what went is the panel they all sat inside, which made the desk read as one slab. The heading
   is what a panel's title was, without the frame. */
.deskwrap{margin:14px 0}
/* THE HEADING AND THE THING THAT GROWS THE DESK, on one line (his, 1 Oct: "put add a team next to propose a
   trade"), and the heading in the main ink rather than the muted grey -- "change the color for propose a trade
   as well". It names the thing you are doing; it should not be quieter than the boxes under it. */
.deskhead{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:0 0 10px 2px}
.deskhead>h2{font:800 15px/1.2 var(--display);letter-spacing:.06em;text-transform:uppercase;
color:var(--ink);margin:0}
.deskhead .seatform{margin:0}
.deskwrap>.pad{padding:0}
/* SEND THE OFFER IS NOT TOUCHING THE BOX ABOVE IT (his, 1 Oct: "send the offer is touching the bottom of the
   box abolve it. give it a little bit of space please"). The grid ended and the button row began in the same
   breath, so the button read as part of the last team's box rather than as the thing that sends both. */
.deskwrap .tdesk+.btnrow,.deskwrap form>.btnrow{margin-top:14px}


/* THE GAME WHEEL (6 Oct). A hero stacks its children, so the two arrows came out as full-width bars above and
   below the heading. It is a row: arrow, the game, arrow -- the same shape as the matchup stepper it was asked
   to echo ("instead of the week wheel, make a game wheel"). */
.hero.gamewheel{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:16px}
.gamewheel>.btn.ghost,.gamewheel>.btn.ghost.off{flex:0 0 auto;width:48px;padding:10px 0;text-align:center;
font-size:22px;line-height:1}
.gamewheel>.off{opacity:.3;pointer-events:none}
.gwhead{flex:1 1 auto;text-align:center;min-width:0}
.gwhead h1{display:flex;align-items:center;justify-content:center;gap:10px;margin:0}
.gwhead h1 i{font-style:normal;font:700 13px/1 "IBM Plex Mono",monospace;color:var(--ink-3);letter-spacing:.1em}
.gwhead .meta{margin:6px 0 0}

/* The two ways to read a week, under the stepper they both drive. Quiet: it is a switch, not a headline. */
.weekviews{display:flex;justify-content:center;gap:8px;margin:2px 0 0}
.weekviews .chip{font-size:12px}
.weekviews .chip.on{background:var(--accent);color:var(--paper);border-color:var(--accent)}

/* ---------- THE GAMES PANEL (7 Oct): the night's games as a list you scan ----------
   A schedule is a different KIND of page from a matchup card -- it is read by running your eye down it, so it is
   a plain vertical list with one line per game, not a row of cards. One day to a panel, the date at its head. */
/* The panel is what narrows, not the rows inside it. Capping the ROW left its underline stopping short of the
   panel's edge, which reads as a broken table rather than a tidy one. */
/* width:100% is NOT redundant beside max-width here: these panels are items in a flex column, and `margin:0 auto`
   on one of those centres it AND shrinks it to its contents -- so the panels came out as narrow as their longest
   row and sat adrift under a full-width heading. The same trap as .mwrap and .hero.tight above. */
.gday{margin:0 auto 14px;max-width:720px;width:100%}
/* [tip time][away][at][home][where it is up to]. The two clubs take equal room so every line agrees, the time
   leads because the list is ordered by it, and the state sits in a fixed column on the end to run the eye down. */
/* NARROWER AND TALLER (his, 7 Oct), and everything on it a size up with it -- "make the boxes less wide and
   taller. make the default on this page a bit more zoomed in". Done by growing this page's own parts rather than
   zooming the page: a real zoom would take the top bar and the chrome with it, and those belong to the app, not
   to one list. Narrowing also closes the gap the two sides used to sit either side of. */
.grow{display:grid;grid-template-columns:58px minmax(0,1fr) 26px minmax(0,1fr) 92px;align-items:center;gap:8px;
padding:17px 18px;border-bottom:1px solid var(--line);color:var(--ink);text-decoration:none}
.grow:last-child{border-bottom:none}
a.grow:hover{background:var(--panel-2)}
.gtip{font:600 12px/1 "IBM Plex Mono",monospace;color:var(--ink-3);font-variant-numeric:tabular-nums}
/* The phone shape of this row is at the bottom of this section, beside .gwhen -- the part of it that moves. */
.gside{display:flex;align-items:center;gap:9px;min-width:0}
.gside.right{flex-direction:row-reverse}
/* The record under the badge: the badge is taller than its three letters, so this costs the row no height. */
.gclub{display:flex;flex-direction:column;align-items:center;gap:3px}
.gclub .club{font-size:12px;padding:3px 7px}
.grec{font:600 10.5px/1 "IBM Plex Mono",monospace;color:var(--ink-3);font-style:normal;font-variant-numeric:tabular-nums}
/* WHO IS HAVING THE BEST GAME, in the room the row was wasting. Quiet on purpose: the score is the headline and
   this is the sentence under it, so it must never be read first. */
.gled{display:flex;flex-direction:column;gap:2px;min-width:0;font:600 11.5px/1.3 "IBM Plex Mono",monospace;
color:var(--ink-3)}
.gled i{font-style:normal;color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gside.right .gled{align-items:flex-end;text-align:right}
.gat{text-align:center;color:var(--ink-3);font:600 11px/1 "IBM Plex Mono",monospace}
/* THE REAL SCORE, and the winner in bold once there is one. This page is about the GAME, so its number is the
   only number on the line -- no fantasy total, which would make it the matchup card again. */
/* The LOSER is what moves, not the winner. Lifting the winning score left both looking much the same, because
   there is not much room above a figure that is already white -- dropping the beaten one well back says which
   way a game went at a glance, down a column of twelve. A game still being played marks neither. */
.gscore{font:800 21px/1 var(--figures),"IBM Plex Mono",monospace;font-variant-numeric:tabular-nums;
color:var(--ink)}
.grow:has(.won) .gside:not(.won) .gscore,.gtop:has(.won) .gside:not(.won) .gscore{color:var(--ink-3);font-weight:600}
.grow:has(.won) .gside:not(.won) .club,.gtop:has(.won) .gside:not(.won) .club{opacity:.62}
.gwhen{text-align:right;font:600 12px/1.3 "IBM Plex Mono",monospace;color:var(--ink-3);text-decoration:none}
.gwhen.gplaying{color:var(--accent)}
.gwhen.gdone{color:var(--ink-3)}
.gwhen.gsoon{color:var(--ink-2)}
/* FOUR COLUMNS ON A PHONE, NOT THREE, AND THAT MISSING ONE IS THE WHOLE FAULT. This rule was written for a row
   of [away][at][home], and the row has a tip-off time in front of them. With three columns the time took the
   away club's column, the away club took the 22px meant for the word "at", and the home club wrapped to a line
   of its own -- so a phone showed two badges at different heights, the away one squeezed to 15px, and the score
   off the screen. Measured 12 Oct: the grid came out [141px 22px 141px] and the home side started a second row.
   You could not tell which club was which, which is the only thing a schedule is for.
   The state still drops to a line of its own underneath -- it is the one part of the row that reads perfectly
   well there -- and the leader line goes, because it is the one part built to be read second ("quiet on
   purpose ... must never be read first") and it is what was eating the room the clubs needed. Nothing else is
   given up: time, both clubs, both records, both scores and the state all stay on a 390px screen. */
@media (max-width:620px){
  .grow{grid-template-columns:58px minmax(0,1fr) 22px minmax(0,1fr);row-gap:6px;padding:13px 14px}
  .gwhen{grid-column:1/-1;text-align:center}
  .grow .gled{display:none}
}

/* A SCORE THAT IS GONE: FADED, AND NOTHING ELSE. His, 7 Oct: "what about only faded out text. the line is a bit
   intrusive." Three goes at this. The first was the near-black grey the rest of the page uses for a dead thing,
   and it was too dark to read; the second kept the live white and drew a line through it, which read clearly but
   put a second mark on the row for a fact the colour was already telling. So: one step down from the white a
   score being played wears, which is dim enough that the eye passes over it on a busy card and bright enough to
   read when the eye stops. Nothing is struck out and nothing is added. */
.mscore.gone{color:var(--ink-2);text-decoration:none}

/* FANTASY AND GAMES, BOTH ALWAYS THERE. His, 10 Oct: "i think we need to add a fatnasy and games button instead
   of them switching out on each other. it helps with knowing what page you are currently on." One button that
   renamed itself told you where it would take you and nothing told you where you were, and the two readings
   look the same at a glance. The side you are standing on is a plain word, not a control: it cannot be pressed,
   so it is not a second way to the page you are already on. */
.switchpair{display:inline-flex;align-items:stretch;gap:0}
.switchpair>*{border-radius:0}
.switchpair>*:first-child{border-top-left-radius:10px;border-bottom-left-radius:10px}
.switchpair>*:last-child{border-top-right-radius:10px;border-bottom-right-radius:10px;margin-left:-2px}
.ghost.here{background:var(--accent);border-color:var(--accent);color:#fff;cursor:default;
display:inline-flex;align-items:center;padding:8px 15px;font-size:13.5px;font-weight:600;
border-width:2px;border-style:solid;white-space:nowrap}

/* A LEAGUE THAT IS NOT FULL SAYS SO, above the draft tickets rather than inside them -- how many people are in
   the league is read before who picks when, and it is a fact about the league rather than about the draft. */
.notfull{margin:0 0 10px;color:var(--ink-2)}
.notfull b{color:var(--accent)}

/* ---------- WHAT JUST HAPPENED, in the banner's own empty half ----------
   His, 11 Oct, pointing at the gap between a club's badge and the score: "the open space in teh banner is
   perfect for it. you say who did what, its stays for a few seconds and fades to the next stat. each stat is
   time stamped ... each side of the banner shows each times live plays."
   The right place for the reason his instincts usually are: a new thing goes in room the layout is already
   wasting rather than in height it has to grow. One line at a time reads as something HAPPENING; forty lines
   read as a document, and the document is one press away on the score. */
/* A BUBBLE, so it reads as one thing. His, 11 Oct: "i think we should put the live play-by-play in a bubble so
   its a bit more singular." Loose in the banner it read as a caption that had drifted in beside the badge.
   The plate is also what makes it obviously pressable, which it is -- it opens the whole game log. */
/* IT MUST BE TOLD TO GROW. Every beat inside is absolutely positioned -- which is what stops the banner
   reflowing as they change -- so the bubble has no content to take its width from. `flex:0 1 auto` left it
   24px of padding and nothing else: a row of empty pills. */
.gticker{position:relative;flex:1 1 auto;min-width:120px;max-width:340px;height:30px;overflow:hidden;
display:block;padding:4px 12px;border-radius:999px;background:var(--panel-2);
border:1.5px solid var(--line-soft);box-sizing:content-box;text-decoration:none;
transition:border-color .15s ease,background .15s ease}
.gticker:hover,.gticker:focus-visible{background:var(--box-grey);border-color:var(--edge);outline:none}
/* THE SIDE WHOSE TURN IT IS NOT steps aside rather than disappearing: visibility keeps its space, so the banner
   holds its shape and the score does not shuffle sideways every few seconds. */
.gticker.waiting{visibility:hidden}
.gticker:focus-visible{outline:3px solid var(--accent);outline-offset:2px}
.gteam .gticker,.mteam .gticker{margin-left:14px}
.mteam.right .gticker{margin-left:0;margin-right:14px}
/* Every beat is stacked in the same place and only the current one is shown, so nothing reflows as they change
   and the banner cannot grow or shrink while a game is on. */
/* THE PLAY SITS IN THE MIDDLE OF ITS BUBBLE. His, 12 Oct: "push the play by play text into the middle of the
   bubble for their respective areas." The bubble is told to grow, so it is nearly always wider than the line
   inside it -- and pinned to one end, a short play like "J. Wilson block" left a lake of empty plate beside it,
   which reads as a bubble that failed to fill rather than as a bubble holding one play. Centred, the plate
   looks the same whatever the play happens to say, and a long one and a short one no longer start in
   different places as the ticker steps on. */
.gbeat{position:absolute;top:4px;bottom:4px;left:12px;right:12px;display:flex;align-items:center;gap:7px;
justify-content:center;white-space:nowrap;overflow:hidden;
text-overflow:ellipsis;opacity:0;transition:opacity .45s ease,transform .45s ease;transform:translateY(5px);
pointer-events:none}
.gbeat.on{opacity:1;transform:none}
.gbeat i{font:700 8.5px/1 "IBM Plex Mono",monospace;letter-spacing:.08em;color:var(--ink-3);font-style:normal;
flex:0 0 auto}
.gbeat b{font:700 12.5px/1.2 var(--display);color:var(--ink);flex:0 0 auto}
.gbeat span{font-size:11.5px;color:var(--ink-2);overflow:hidden;text-overflow:ellipsis}
@media (prefers-reduced-motion:reduce){.gbeat{transition:none}}
.calm .gbeat{transition:none}
/* The score says it can be pressed without becoming a button: the numbers are the point, not a control. */
.mscorebtn{display:inline-flex;align-items:baseline;gap:10px;text-decoration:none;border-radius:10px;
padding:2px 8px;transition:background .15s ease}
.mscorebtn:hover,.mscorebtn:focus-visible{background:var(--panel-2);outline:none}
.mscorebtn:focus-visible{outline:3px solid var(--accent);outline-offset:2px}

/* BOTH SIDES AT ONCE, in the spill the score opens. Two columns, newest first. */
.ppwrap{display:grid;grid-template-columns:1fr 1fr;gap:18px;padding:4px 2px 2px}
@media (max-width:700px){.ppwrap{grid-template-columns:1fr}}
.ppside{min-width:0}
.pplist{list-style:none;margin:6px 0 0;padding:0;display:flex;flex-direction:column;gap:2px;
max-height:min(60vh,520px);overflow:auto}
.pplist li{display:grid;grid-template-columns:62px minmax(0,1fr);gap:4px 9px;align-items:baseline;
padding:5px 8px;border-radius:7px;background:var(--box-grey)}
.pplist li:nth-child(odd){background:var(--panel-2)}
.pplist li i{font:700 8.5px/1.6 "IBM Plex Mono",monospace;letter-spacing:.07em;color:var(--ink-3);
font-style:normal;grid-row:span 2}
.pplist li b{font:700 12.5px/1.2 var(--display);color:var(--ink)}
.pplist li span{font-size:11.5px;color:var(--ink-2);grid-column:2}

/* THE PLAY-BY-PLAY SPILL SITS IN THE MIDDLE OF THE SCREEN. His, 11 Oct: "the play by play spill has to be
   centered on the screen." Every spill is pinned to the TOP by default, which is right for the tall ones -- the
   board, a player's history -- because they are scrolled and a centred tall panel has its head off the screen.
   This one is a fixed pair of columns that scroll inside themselves, so it can be centred, and only it is:
   scoped by what the panel contains rather than by a flag somebody has to remember to pass. */
/* Even padding too, or "centred" is 20px high: the wrapper carries 20px above and 40px below, which is right
   for a panel pinned to the top and a visible lean on one that is not.
   THE COIN PURSE GOES WITH IT (his, 11 Oct: "center the coin screen the same way we did with the play by
   play"). Same reasoning: both are panels of a FIXED height that scroll inside themselves, so neither has a
   head to lose off the top of the screen the way the board or a player's history would.
   `.spill-in` is the purse's own wrapper and nothing else's, so this reaches it and no other spill. Note what
   is NOT done here: the purse's two halves are yours and theirs, which is exactly what the two-column grid in
   .spillcols exists for -- so unlike the play-by-play it keeps the grid and only wants the centring. */
.spillwrap:has(.ppwrap),.spillwrap:has(.spill-in){align-items:center;padding-bottom:20px}
/* AND IT TAKES THE WHOLE SHEET. `.spillcols` is a two-column grid -- it exists for "your side / their side"
   panels -- so a panel that is not built that way lands in ONE cell and sits in half the width, off to one
   side. That is what "the plays arent centered" was: not the wrapper, the grid inside it. Same remedy the
   order stage already uses a thousand lines up. */
.spillcols>.ppbar,.spillcols>.ppwrap{grid-column:1/-1;width:100%}
/* Earliest or latest, above the two columns. */
/* Two questions on one bar now -- the order, and which periods -- so it is allowed to wrap rather than run off
   the panel on a phone, and a hairline keeps the two groups from reading as one long row of chips. */
.ppbar{display:flex;align-items:center;gap:8px;padding:2px 2px 10px;flex-wrap:wrap;row-gap:6px}
.ppsep{width:1px;align-self:stretch;margin:1px 3px;background:var(--line);flex:0 0 auto}
.ppbar .meta{font:700 9px/1 "IBM Plex Mono",monospace;letter-spacing:.1em;text-transform:uppercase}
.ppbar .chip[aria-current]{background:var(--accent);border-color:var(--accent);color:#fff}

/* ---------- THE BANKRUPTCY COIN'S PICKER: every starting slot, down the page ----------
   His, 11 Oct: "You should see all the spots in a selection vertically as you would on the matchup page. It
   lets you choose any of those spots." A dropdown is what the Options Coin uses and it is wrong here -- that
   coin asks which of YOUR slots to improve, where this one asks which slot to take off BOTH squads. That is a
   choice made by reading two line-ups against each other, so the slots are a list you run your eye down. */
.bankruptlist{display:flex;flex-direction:column;gap:4px;margin-top:4px}
.bankruptpick{display:flex;align-items:baseline;justify-content:space-between;gap:12px;width:100%;
text-align:left;padding:7px 12px;border-radius:9px}
.bankruptpick b{font:700 13px/1.2 var(--display)}
.bankruptpick .meta{font-size:11px}
/* It takes something away, so it reads like a warning rather than an ordinary button once you are on it. */
.bankruptpick:hover,.bankruptpick:focus-visible{background:var(--alert);border-color:var(--alert);color:#fff}
.bankruptpick:hover .meta,.bankruptpick:focus-visible .meta{color:#fff}

/* A PURSE WITH ONE HALF TAKES THE WHOLE SHEET. `.spillcols` is two columns because the purse usually shows
   yours AND theirs -- but before a season, or where there is no opponent, there is only yours, and it was
   sitting in the left-hand cell with the right half of the panel empty. His screenshot of it, 11 Oct.
   Only when it IS the only one, so the ordinary two-up purse is untouched. */
.spillcols>.spill-in:only-child{grid-column:1/-1;width:100%}

/* THE BANKRUPTCY FACE IS A DRAWING, so the sepia strike every emoji face wears has to come off it -- that
   filter exists to make a coloured emoji look stamped into gold, and it turns a white grid into a brown one.
   The drawing carries its own colours (his: "white with a red line"), and takes the same little shadow the
   other faces get so it still sits ON the coin rather than floating above it. */
.coin[data-coin="bankruptcy"] .coin-face{filter:none;text-shadow:none;display:inline-flex;align-items:center}
/* 1.14em, not 1em. An emoji glyph paints OUTSIDE its em box -- that is why the raccoon and the pig fill their
   discs -- and a drawing told to be 1em obediently is not, so it read a size smaller than every neighbour. */
.coin-face .coinart{width:1.14em;height:1.14em;display:block;
filter:drop-shadow(0 1px 0 rgba(94,60,0,.55))}

/* ---------- THE WAY BACK OUT OF A PAGE THE TOP BAR SENT YOU TO (12 Oct) ---------- */
/* His: "clicking the settings, the profile icon, coin purse, or messages should always have an x button to return
   to the exact page you clicked it from ... everytime i click settings, i can only go back to home because it
   already thinks i am in fantasy. people may get annoyed with this system."
   It lives in the PAGE's banner, not in the top bar: the bar belongs to the app, and this is about the page you
   are standing on. It is drawn over the banner art where there is any, because the art is decoration and a way
   out is not -- hence the z-index and the solid disc behind it.
   LAST IN THIS FILE ON PURPOSE. `.hero.with-art` sets a padding-right of its own, in two places, one of them
   inside a phone media query. Those tie with `.hero:has(.closex)` on specificity, so the only thing that decides
   is which comes later -- and the room for this button has to win, or a heading wraps underneath it. */
/* `.hero>a.closex`, NOT `.closex`, and that is not belt-and-braces -- it is the whole thing working at all.
   Line 111 says `.hero>*:not(.heroart){position:relative;z-index:1}`, which lifts a banner's words above its
   art. That selector scores higher than a bare class, so it wins no matter how far down this file the class
   sits: measured, the button came out `position: relative` with `top:10px; right:12px` applied and ignored,
   and it sat at the top LEFT in the flow. Specificity beat order, which is the one thing being last cannot fix. */
.hero>a.closex{position:absolute;top:10px;right:12px;z-index:3;display:grid;place-items:center;
width:34px;height:34px;border-radius:50%;background:var(--panel-2);border:2.5px solid var(--edge);
color:var(--ink-2);box-shadow:2px 3px 0 var(--shadow);text-decoration:none;
transition:transform .15s cubic-bezier(.2,1.6,.4,1),background .15s,color .15s,border-color .15s}
.hero>a.closex:hover,.hero>a.closex:focus-visible{background:var(--accent);color:#fff;border-color:var(--accent);
transform:rotate(90deg) scale(1.06);outline:none}
.hero>a.closex:active{transform:translate(2px,2px);box-shadow:none}
.hero:has(.closex){padding-right:58px}
@media (max-width:620px){.hero:has(.closex){padding-right:52px}}
@media (prefers-reduced-motion:reduce){.hero>a.closex{transition:none}
  .hero>a.closex:hover,.hero>a.closex:focus-visible{transform:none}}

/* THE COLUMN MANAGER (15 Oct), drawn from his own sketch: the slots ARE the board, left to right, with a bank
   underneath holding everything not on it. His words with the drawing: "there should be a system at the bottom
   with a column bank. yyou can drag the column bank to an empty column slot. the slots are ordered left to
   right. there are buttons to add and remove a column slot."

   NOTHING TOUCHES THE EDGE. His, the same evening: "we need to move the boxes a little bit to the right because
   they are touching the border. same with the bottom text." The panel's own padding stops at the box, so every
   row in here carries its own.

   A SLOT IS AS WIDE AS ITS NAME, not a fixed block -- "i want the boxes where you drag to be smaller, the same
   width the name would take up." An EMPTY one has no name to be as wide as, so it keeps a small fixed width:
   something you are about to drop a column into still has to be a target you can hit. */
.colslotbar{margin:0 0 10px 14px}
/* touch-action on the CONTAINERS, not only on the chip. Once the pointer leaves the chip it is over the slot
   or the bank, and a phone would start scrolling the row out from under the drag. */
/* BOTH OF THESE SCROLL WHEN THEY HAVE TO (his, 15 Oct): a board can carry more columns than fit across, and
   the bank can hold every one of them at once. Neither may grow the panel it sits in. The scrollbar is left
   visible on purpose -- a box that scrolls with no sign that it scrolls is a box with things hidden in it. */
/* EVERY BOX THE SAME HEIGHT, ALWAYS (his, 15 Oct). `stretch` makes each slot as tall as the tallest, so a
   filled one, an empty one and the Player all line up however many samples are in them -- and the row keeps a
   floor of its own, so a board with every column banked does not collapse to a sliver. */
.colslots{display:flex;gap:8px;align-items:stretch;overflow-x:auto;overflow-y:hidden;
padding:4px 14px 12px;min-height:118px;touch-action:none;scrollbar-width:thin}
.colslot{flex:0 0 auto;min-height:104px;border:2px dashed var(--line);border-radius:10px;display:flex;
flex-direction:column;align-items:stretch;gap:6px;padding:6px;background:var(--panel-2)}
.colslot>.colchip{flex:none}                 /* the title keeps its own height; the sample takes the slack */
.colslot>.colsample{flex:1}
.colslot:empty{flex-basis:64px}
/* AN EMPTY SLOT SAYS "EMPTY" (his, 15 Oct). It was a dash, on the reasoning that the dashed border already
   says it is waiting for something -- and a dash is a thing you have to work out, where a word is a thing you
   read. Small and quiet, because it is a state and not a value. */
.colslot:empty{align-items:center;justify-content:center}
.colslot:empty::after{content:"Empty";font:700 10px/1 "IBM Plex Mono",monospace;letter-spacing:.12em;
text-transform:uppercase;color:var(--ink-3)}
.colbanklabel{margin:6px 0 6px 14px;font:700 11px/1.4 "IBM Plex Mono",monospace;letter-spacing:.1em;
text-transform:uppercase;color:var(--ink-3)}
.colbank{display:flex;flex-wrap:wrap;gap:8px;min-height:56px;max-height:148px;overflow-y:auto;margin:0 14px;
padding:10px;border:2px solid var(--line);border-radius:10px;background:var(--panel-2);align-content:flex-start;
touch-action:none;scrollbar-width:thin}
/* THE SAME CHIP IN A SLOT AND IN THE BANK, because it is the same column. One that changed shape when it moved
   would read as a different thing, which is the fault this whole screen exists to avoid. */
/* The Player: the one column nobody arranges. No grip, because there is nothing to take hold of, and a solid
   border rather than the dashed one the empty slots wear -- it is not waiting for anything. */
/* THE PLAYER SLOT. Its width is set by the longest name in it, the title is centred over them, and the names
   themselves hug the left edge without touching it -- his, 15 Oct, all three. A name is read from its start,
   so a column of them centred is a column with a ragged left edge and nothing to run the eye down. */
.colslot.colfixed{border-style:solid;border-color:var(--edge)}
.colslot.colfixed .colchip{justify-content:center}
/* THE LONGEST NAME SETS THE WIDTH, and the title is centred in whatever that comes to. Every other sample is
   clipped with an ellipsis if it has to be, because a number that does not fit can be read on the board -- a
   name that does not fit is a different player. So this one alone is allowed its full width, and the slot
   grows to it rather than cutting "G. Antetokounmpo" off at the edge. */
.colslot.colfixed .colsample>span{text-align:left;padding:0 8px;width:max-content;
overflow:visible;text-overflow:clip}
.colchip.fixed{cursor:default;border-style:solid;border-color:var(--line)}
.colchip.fixed b{color:var(--ink-2)}
.colchip{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:2px solid var(--edge);
border-radius:9px;background:var(--panel);cursor:grab;touch-action:none;user-select:none}
.colchip b{font:700 13px/1.25 var(--display);color:var(--ink);white-space:nowrap}
/* The one in the document, while a copy of it is in your hand: faint, so it reads as the place it will land. */
.colchip.lifted{cursor:grabbing;border-style:dashed;border-color:var(--accent);opacity:.35}
/* ...and the copy, under the pointer. Fixed and parked on <body>, so the panel's scrolling box cannot clip it;
   pointer-events:none, or it would be the only thing the drag can ever find under the cursor. */
.colchip.flying{position:fixed;z-index:9999;pointer-events:none;cursor:grabbing;margin:0;
border-color:var(--accent);box-shadow:4px 4px 0 var(--shadow);opacity:.95}
.colgrip{flex:none;color:var(--ink-3);letter-spacing:-3px;font-size:14px}
/* The one switch on this screen that is not a column: the value-drop lines between the rows. It sits on the
   top row with the two slot buttons, so it is spaced from them rather than from the edge. */
.colslotbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px}
.coldrops{margin:0 0 0 6px;gap:8px}
.coldrops .colname{font-size:13px;color:var(--ink-2)}
.colform .btnrow{margin:12px 14px 16px}
/* WHAT THE COLUMN WOULD LOOK LIKE, right under its own slot (his, 15 Oct), with the slots' own gap between
   them doing the separating. The values are the REAL board's cells -- the preview fetches three of its rows and
   each slot takes the ones belonging to its column -- so a sample cannot drift from the board it samples. */
.colsample{display:flex;flex-direction:column;gap:3px;padding-top:5px;border-top:2px dashed var(--line)}
.colsample>span{min-height:19px;font:600 12px/1.6 "IBM Plex Mono",monospace;color:var(--ink-2);
text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.colsample .pill{font-size:10px;padding:1px 5px}
/* THE NAME IS INSIDE `.nameline`, so hiding that line hid the only thing the Player slot is there to show --
   his reason for asking for it back: "the preview texts without it dont actually help if you dont know who it
   is." What comes off is the row's FURNITURE: the phone-only line that repeats his club, and the injury and
   rookie badges, which in a 90px sample are three glyphs with nowhere to sit. */
.colsample .onphone,.colsample .rook,.colsample .newsbtn,.colsample .injflag{display:none}
.colsample .nameline{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.colsample .pname,.colsample .pname a{font-size:11px;font-weight:600}
.colsample a{pointer-events:none;color:var(--ink-2);text-decoration:none}
/* The name sample is written as plain text (see kit.COLUMN_WIRING), so it needs no link styling of its own. */
/* The fetched rows themselves are never shown: they are where the samples are read from. */
.colpreview{display:none}
/* The sentence is at the TOP, above the slots, so it is read before the thing it describes is touched. The
   button row then carries the panel's bottom padding itself -- with nothing under it, it sat against the
   border, and a row of controls touching the edge reads as cut off. */
.coltop{margin:0 14px 12px}
@media (max-width:620px){
  .colslotbar,.colbanklabel,.colform .btnrow,.colfoot{margin-left:10px}
  .colslots{padding-left:10px;padding-right:10px}
  .colbank,.colpreview{margin-left:10px;margin-right:10px}
}


/* ---- THE TEAM BUILDER ------------------------------------------------------------------------------------
   The roster spots you drag men into. Deliberately the SAME furniture as the column manager's slots -- dashed
   while empty, solid once filled -- because they are the same gesture and a person who has arranged their
   columns already knows how this works. His, 16 Oct: "it looks the exact same as the mock draft tab, but
   instead of drafting, you can drag the players to the roster spot."

   THEY WRAP RATHER THAN SCROLL SIDEWAYS. A roster is fourteen deep and a sideways strip would hide the end of
   your own team behind a scrollbar -- the column slots can scroll because you read them left to right, but
   these you read as a squad. */
.teamspots{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 14px 0}
.teamslot{flex:0 0 auto;min-width:168px;min-height:46px;border:2px dashed var(--line);border-radius:10px;
display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--panel-2)}
.teamslot.full{border-style:solid;background:var(--panel)}
/* THE SPOT NUMBER IS NOT THE PICK NUMBER and must not be read as one: the spot says where a man PLAYS. Which
   of your picks he costs you is the round beside it, and whether that round is realistic is the matching in
   the answer below -- three different questions that used to look like one. */
.spotno{font:600 11px/1 var(--mono);color:var(--ink-2);min-width:18px}
/* THE ROUND HE IS YOUR PICK FOR (his, 18 Oct: "you need to understand what rounds im getting them at").
   Mono and dim, the width of "R14" so fourteen slots line up down the rail rather than stepping in and out
   as the numbers reach two digits. It only exists on a filled slot; empty ones carry the text "". */
.spotround{font:700 11px/1 var(--mono);color:var(--accent);min-width:24px;flex:0 0 auto;letter-spacing:.02em}
.teamslot:not(.full) .spotround{display:none}
.spotempty{font:600 11px/1 var(--mono);color:var(--ink-2);letter-spacing:.06em;text-transform:uppercase}
.spotman{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.spotpos{font:600 11px/1 var(--mono);color:var(--ink-2)}
/* UNDER THE POINTER, so a drop is never a guess about where it landed. */
.teamslot.dropping{border-style:solid;border-color:var(--accent);background:var(--panel-3,var(--panel))}
/* THE MAN IN YOUR HAND. Parked on <body> and fixed, for the reason the drop-out menus are: anything absolute
   gets clipped by the panel it started in, and z-index cannot save it. */
/* WHILE A MAN IS IN YOUR HAND, NOTHING SELECTS. Only for the length of the drag: leave it on and the board
   becomes permanently unselectable, so you could no longer highlight a name to read or copy it. His, 18 Oct:
   "when i drag and drop on pc, it highlights all the text i drag through and moves the scroll wheel vertically
   or horizontally" -- the scrolling is that selection auto-scrolling once it reaches the edge, so one rule
   answers both. The class goes on at lift and comes off at drop, at cancel, and when the window loses focus.

   THE BOARD IS LOCKED, THE ROSTER IS NOT, which is exactly what he asked for: "when im moving a name, it
   should lock the board, but keep the scroll wheel movable on the roster area." So this stops SELECTION, not
   scrolling, and overflow is left alone -- the roster rail still scrolls under a drag, which is the whole
   point of being able to drag to it. */
body.dragging,
body.dragging *{user-select:none;-webkit-user-select:none}
.dragman{position:fixed;z-index:9999;pointer-events:none;transform:translate(-50%,-140%);
padding:5px 10px;border-radius:8px;border:1px solid var(--line);background:var(--panel);
font:600 12px/1 var(--sans);box-shadow:0 6px 18px rgba(0,0,0,.35)}
/* A BOARD ROW YOU CAN PICK UP. Only inside the builder -- everywhere else a row is for reading, and a grab
   cursor on it would promise a gesture that does nothing. */
.boardspill:has(.teamspots) .board tbody tr{cursor:grab}
/* THE BOARD SCROLLS INSIDE THE BUILDER, which it did not, and that made the tab useless -- his: "the board
   doesnt have a scroll wheel. Cant do anything without it."

   `.boardspill .tablewrap{max-height:none}` is right for the Draft Room, where the room's own flex column
   gives the board a bottom to stop at and the rail beside it does the same. The builder has no rail and no
   such column -- it is a stack of spots with a board under it -- so with the cap removed all 529 rows simply
   rendered as one 19,390-pixel block and nothing scrolled at all. Measured, not guessed: the wrapper's
   scrollHeight equalled its clientHeight exactly.

   So this gives it a bottom of its own. More specific than the rule above, so it wins, and a stated height
   rather than a flex fill because there is nothing here for it to flex against. */
/* THE 62vh CAP IS GONE AND THE RAIL IS WHY. It was needed while the builder was a stack with nothing to give
   the board a bottom. It is the room layout now, and .roomboard>.panel>.tablewrap gives the board its own
   bottom edge exactly as the Draft Room board gets one -- a stated height here would only fight it.

   THE SPOTS DO NOT SCROLL WITH THE BOARD, which is the whole point of the move: you can be two hundred rows
   down and still have somewhere to drop. */
.roomrail .teamspots{max-height:none;gap:5px}
/* EVERY SPOT HAS TO BE A DROP TARGET, so every spot has to be on the screen. Fourteen at the width they are
   given outside the rail came to 756px and the last bench spots fell off the bottom, which puts a scroll
   between you and the slot you are dragging to -- the same fault as the spots scrolling away, one box in.
   Compact in the rail and only there: one per row, the position in front, the man filling the rest. */
.roomrail .teamslot{min-width:0;width:100%;min-height:32px;padding:4px 8px;gap:7px}
.roomrail .teamslot .spotno{min-width:34px}

/* A CONTROL IN THE DRAFT BAR KEEPS ITS OWN WIDTH. `mline` is a full-width row everywhere else, so the Roster
   Spots form stretched across the bar and put its Set button at the far right, a hand away from the number it
   sets. In a bar of buttons every control is as wide as the thing it does. */
.draftbar form.mline,.draftbar form.inline{display:flex;gap:8px;align-items:center;width:auto;flex:0 0 auto;margin:0}
.draftbar form.mline input[type=number]{width:72px}


/* ---- THE TEAM BUILDER'S ANSWER --------------------------------------------------------------------------
   His, 16 Oct: "Definitely stylize it." It was a sentence and a bare table.

   THE WHOLE-TEAM NUMBER IS THE ANSWER and nothing competes with it, so it is set large and alone on its line.
   The men under it are the REASON for it -- sorted worst first, with a bar you can scan without reading a
   single figure, because on an ambitious build one man is the whole story and he should be findable at a
   glance. */
.oddsbox{border:1px solid var(--line);border-radius:12px;padding:14px 16px;margin:0 0 14px 0;
background:var(--panel-2)}
.oddshead{display:flex;align-items:baseline;gap:10px;margin:0 0 2px 0;flex-wrap:wrap}
.oddshead b{font:800 30px/1 var(--sans);letter-spacing:-.01em}
.oddshead span{color:var(--ink-2)}
.oddsmeta{margin:0 0 12px 0;font:600 11px/1.3 var(--mono);color:var(--ink-2);
letter-spacing:.04em;text-transform:uppercase}
.oddswhy{margin:0 0 8px 0}
.oddslist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.oddsman{display:flex;align-items:center;gap:10px}
.oddsname{flex:0 0 auto;min-width:150px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* THE BAR TAKES THE SLACK, so every row lines up however long the names are. */
.oddsbar{flex:1;height:8px;border-radius:999px;background:var(--line);overflow:hidden;min-width:60px}
.oddsbar i{display:block;height:100%;background:var(--accent);border-radius:999px}
.oddsman .pill{flex:0 0 auto;min-width:48px;text-align:center}
@media (max-width:640px){.oddsname{min-width:110px}.oddshead b{font-size:24px}}


/* ---- WHERE THE ROUNDS BREAK, AND WHERE YOUR PICKS LAND --------------------------------------------------
   Theirs, 17 Oct, and both are switched on from the Columns tab beside the value-drop lines.

   They are ROW ARITHMETIC, not a forecast -- the tenth row down is ten picks away whatever the board is
   sorted by -- which is why neither has to hide when somebody sorts by rebounds. The tool learned that the
   hard way, after this was built as a prediction twice: "the highlighted player is meant to show you your
   next pick numerically, not who your most likely to get." */

/* THE ROUND LINE is the same device as the value-drop line, quieter, and it reads as an end rather than a
   heading: it sits UNDER the last man of the round, so it says what has gone, not what is coming.

   SMALLER AGAIN ON 16 OCT, theirs. A round line is a boundary, not a row: it should cost the board as little
   height as a line can and still be read. It was taking most of a player's row for three words. */
.board tr.roundline td{padding:1px 11px;background:var(--panel-2);color:var(--ink-2);
font:700 8px/1.4 var(--mono);letter-spacing:.1em;text-transform:uppercase;text-align:center;
border-top:1px solid var(--line);border-bottom:1px solid var(--line)}

/* THE ONE YOU ARE ON IS THE LOUDEST THING ON THE BOARD, on purpose, and the later ones are quiet. You need to
   see where you are choosing from NOW; the rest you only need to be aware of. */
.nexttag{display:inline-block;margin-left:7px;padding:1px 6px;border-radius:3px;
background:var(--accent);color:#fff;font:700 9.5px/1.6 var(--mono);letter-spacing:.05em;white-space:nowrap}
.latertag{display:inline-block;margin-left:7px;padding:1px 5px;border-radius:3px;
background:var(--panel-2);color:var(--ink-2);border:1px solid var(--line);
font:700 9.5px/1.6 var(--mono);letter-spacing:.05em;white-space:nowrap}
.board td.c-name .nexttag,.board td.c-name .latertag{align-self:center;margin-left:0}

/* IN THE TEAM BUILDER EVERY PICK IS LOUD, and the reason is that the builder has no clock. The quiet/loud
   split earns its keep in the Draft Room, where exactly one of your picks is the one you are choosing from
   RIGHT NOW and the rest are things to be aware of. Nothing is on the clock here: you are planning all
   fourteen at once, so singling one out would be picking a favourite at random.

   STYLING ONLY, DELIBERATELY. Theirs, 17 Oct: "is there anyway to make every tag only on the team builder
   highlighted, but still affected by the same controls on the column page?" Whether the tags are drawn at
   all is still the one tick in the Columns tab, read server-side for both boards -- this changes how they
   look where they are shown, and nothing about when. */
.boardspill:has(.teamspots) .latertag{background:var(--accent);color:#fff;border-color:var(--accent)}


/* THE GRIP THAT SAYS A ROW CAN BE MOVED, theirs: "we shouuld also add the 3 lines showing the names are
   draggable between number and name." The column manager's own handle, so the gesture looks the same wherever
   the app offers it. Quiet until the row is under the pointer -- fifty of them shouting at once would be a
   pattern rather than a hint. */
/* touch-action ON THE GRIP AND NOWHERE ELSE. A finger on a row must still SCROLL -- that is how you read
   529 men on a phone -- so the row cannot claim the gesture, or the board becomes unscrollable. The grip can,
   because nobody pans by putting their thumb on a six-pixel handle, and without it the browser takes the pan,
   cancels the pointer stream and the drag dies in mid-air. His, 18 Oct: "the drag-n-drop features dont work
   on touch screen like iphones or ipads." The matching half is in BUILDER_WIRING, which starts a touch drag
   only from a grip. */
.board td.c-name .rowgrip{color:var(--line);font:700 12px/1 var(--mono);letter-spacing:0;
cursor:grab;user-select:none;touch-action:none;flex:0 0 auto;margin-right:1px}
.board tbody tr:hover .rowgrip{color:var(--ink-2)}
.boardspill:has(.teamspots) .board td.c-name .rowgrip{cursor:grab}


/* THE SET BUTTONS NEVER FLASH. They exist only for a browser with no JavaScript, where a `data-applies`
   dropdown cannot submit itself -- so with a script present they should never be painted at all, not painted
   and then removed. Theirs: "i can see the set buttons pop up and change the format for a second, and then
   they go away." `js` is put on <html> in the head (kit.JS_FIRST) before the first frame. */
html.js [data-applies-button]{display:none}


/* ---- WHAT A ROSTER SPOT SHOWS, EMPTY AND FULL -----------------------------------------------------------
   Every slot carries every part (player_pages.builder_slots) and this decides which are seen, so a slot the
   drag fills looks exactly like one the server drew. Theirs: "all i want to see is the name and the positions
   they can play ... i want there to be 3 lines indicating that you can still move them in front of there
   name, and the x to put them back." */
.teamslot:not(.full) .spotgrip,
.teamslot:not(.full) .spotman,
.teamslot:not(.full) .spotpos,
.teamslot:not(.full) .spotdrop{display:none}
/* Once somebody is in it the slot's own label steps aside: the man is the thing to read. */
.teamslot.full .spotno,
.teamslot.full .spotempty{display:none}
.teamslot .spotgrip{color:var(--ink-2);font:700 12px/1 var(--mono);letter-spacing:0;cursor:grab;
user-select:none;touch-action:none;flex:0 0 auto}
.teamslot.full .spotman{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.teamslot.full .spotpos{flex:0 0 auto;font:600 11px/1 var(--mono);color:var(--ink-2)}
/* THE POSITIONS TRAVEL WITH THE NAME while he is in your hand. */
.dragman .dragpos{margin-left:7px;font:600 10px/1 var(--mono);opacity:.75}


/* GREEN FOR A SLOT HE CAN PLAY, RED FOR ONE HE CANNOT, while he is over it. The border alone carries it: a
   filled background under a dragged name is harder to read, not easier, and the slot may already hold
   somebody. */
.teamslot.dropping.dropgood{border-style:solid;border-color:var(--accent);background:var(--accent-soft)}
.teamslot.dropping.dropbad{border-style:solid;border-color:var(--alert);background:var(--alert-soft);
cursor:not-allowed}


/* A MAN ALREADY IN YOUR TEAM IS GREY ON THE BOARD, and he comes back the moment the x lets him go. His own
   class, not the `mine`/`drafted` ones the Draft Room uses for players really taken in a draft: the builder
   is a plan, nobody has been drafted, and two meanings sharing one class is a bug this app has paid for.
   Theirs: "when a name is taken on the board, grey it out, and when someone hits x, the color comes backk." */
/* A ROW YOU HAVE TAKEN READS AS A GAP WITH A GHOST IN IT -- theirs: "it should look like theres a spot open
   on the board and you can see who was once there."

   DONE TO THE WHOLE CELL, not to the text colour. Setting a colour on the row left the two loudest things on
   it untouched: the name is an <a> carrying its own colour, and the club is a PILL with its own background,
   so both simply won -- "the name and team colors still arent greyed out, so its hard to tell who you
   picked." Greyscale and opacity apply to everything inside, including things that have not been written yet
   -- a badge, a tag, a flag added next month -- which is the only version of this that stays true. */
.board tbody tr.inteam > td{filter:grayscale(1);opacity:.38}
/* The star stays usable: you may still want to favourite a man you have penciled in. */
.board tbody tr.inteam > td.c-fav{filter:none;opacity:.75}

/* THE X SITS STRAIGHT IN ITS OWN BUBBLE. It was padding and a line-height, which centres a glyph only by
   luck and did not: a round button is a box with the mark in the middle of it, which is what a grid says. */
.teamslot .spotdrop{display:inline-flex;align-items:center;justify-content:center;
width:20px;height:20px;padding:0;border-radius:50%;line-height:1;font-size:14px;flex:0 0 auto}


/* ---- THE DRAWER ------------------------------------------------------------------------------------------
   Teams you have built, with what each came out at. The CHANCE leads every line, because it is the thing you
   are comparing them on -- a list of names you would have to read before you could rank them is a list you
   rank slowly. */
.savedlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
.savedteam{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid var(--line);
border-radius:10px;background:var(--panel-2)}
.savedodds{flex:0 0 58px;text-align:right;font:800 20px/1 var(--sans);letter-spacing:-.01em}
.savedbody{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.savedbody .meta{font:600 10.5px/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase}
/* The names run to one line and stop: the drawer is for choosing between teams, not for reading one. */
.savedwho{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink-2);font-size:12.5px}
.savedacts{flex:0 0 auto;display:flex;gap:6px}
@media (max-width:640px){
  .savedteam{flex-wrap:wrap}
  .savedacts{width:100%}
}


/* ---- THE ROSTER'S SHAPE, ONE CONTROL PER POSITION --------------------------------------------------------
   Theirs: "it should be control for each position." Nine small boxes read as one control rather than nine, so
   they share a border and sit tight together, with the position above each number. The total is SHOWN at the
   end and cannot be typed: it is what the counts add up to, and a figure you could set separately would be a
   figure that contradicts them. */
.shapebar{display:flex;align-items:flex-end;gap:2px;flex-wrap:wrap;padding:4px 8px;margin:0;
border:1px solid var(--line);border-radius:10px;background:var(--panel-2)}
.shapeone{display:flex;flex-direction:column;align-items:center;gap:2px}
.shapeone>span{font:700 9px/1 var(--mono);letter-spacing:.06em;color:var(--ink-2)}
.shapeone select{width:46px;padding:3px 4px;text-align:center}
.shapetotal{margin-left:8px;align-self:center;white-space:nowrap}

/* ---- YOUR TEAM, WITH THE ODDS BESIDE IT ------------------------------------------------------------------
   Theirs: "next to YOUR TEAN should be the odds calcutor." The heading and the button share a line; the
   answer lands under the count, inside the same box, so the question and what it answered stay together. */
/* THE `teamhead` ROW IS GONE. It put the heading and the button on one line, which is what squeezed the
   heading against the panel's top border; the button is out of the flow now and the heading needs no special
   treatment at all. */
.teampanel>.note{padding:2px 14px 8px 14px}
.teampanel .oddsbox{margin:0 14px 10px 14px}
/* AND IN THE RAIL THE NAME GIVES WAY FIRST. A row wants 150px of name, 60 of bar, 48 of percentage and two
   gaps -- 278px -- and the rail's box has about 234, so the percentage was pushed clean off the edge and the
   one number each row exists FOR was not on the screen at all. Measured: the box ended at 1495 and the pill
   sat at 1496. The name is already set to cut with an ellipsis, so it is the part that can afford to. */
.teampanel .oddsname{min-width:0;flex:1 1 auto}
.teampanel .oddsbar{flex:0 1 54px;min-width:30px}


/* THE ODDS BUTTON IS OUT OF THE FLOW, in the open space at the panel's top right, so the heading sits where a
   heading sits instead of being squeezed against the border to make room for it. The heading keeps space for
   it rather than running underneath -- the same arrangement the Draft Room's round number has in its hero. */
.teampanel{position:relative}
.teampanel>h2{padding-right:146px}
.teampanel .oddsbtn{position:absolute;top:10px;right:12px;margin:0}
.teampanel .oddsbtn .btn{padding:6px 11px;font-size:12px;white-space:nowrap}


/* THE NAME IS THE THING YOU CHANGE HERE, since the builder no longer asks for one -- so it is a field rather
   than a label, sitting where the name sat. The players underneath are what identify a team whether or not
   anybody ever names it. */
/* SAVE AND RESET ARE ONE ROW. Two separate forms are two blocks, so they stacked down the rail and read as
   unrelated; they are the two things you do to a build, and they belong beside each other. The selector needs
   both classes: `.panel .pad` is already a COLUMN flex and carries more weight than a single class, so a plain
   `.buildacts{display:flex}` inherited the column and only moved the buttons to the middle. */
/* AND ITS TOP PADDING BACK. `.panel .pad` sets `padding:0 16px 16px` because a heading normally fills the top
   of the box; with the heading gone the buttons sat against the border. */
.panel .pad.buildacts{flex-direction:row;align-items:center;justify-content:center;gap:10px;padding:16px}
.buildacts form{margin:0}

.savedname{display:flex;align-items:center;gap:6px;margin:0}
/* A NAME IS SHORT. Letting the field take the whole row made one small label look like the row's main
   event and pushed Load and Remove to the far edge, a hand's width from the team they belong to. */
.savedname input{flex:0 1 260px;min-width:0;padding:3px 7px;font-weight:700}
.savedname button{flex:0 0 auto;padding:3px 8px;font-size:11px}


/* WHOSE PICK IT IS, in the Control room's rail. The name is the whole box, so it is read at a glance from the
   board rather than hunted for -- you are picking for a different seat every turn. */
.onclock .whoseup{margin:0;font-weight:800;font-size:17px;letter-spacing:.2px}
.onclock .meta{margin:3px 0 0}


/* THE MODE BOX KEEPS ITS SIZE WHILE IT IS OPEN. A select is as wide as its widest option, and the options grow
   to a full sentence the moment the list opens -- without a width the box itself would jump wider under the
   pointer. The popup is free to be wider than the box, which is where the sentence is read. */
#dr-mode{width:124px}


/* AND THE OTHER TWO BOXES KEEP THEIR SIZE while their list is open, for the reason #dr-mode does: the options
   grow to a sentence as the list opens, and without a width the box would jump wider under the pointer. */
#dr-seat{width:104px}
#dr-seats{width:118px}


/* WHICH MODE A MOCK WAS RUN IN, on its row in the Draft Log. A quiet chip: it is how you tell two mocks apart
   when you come back to them, not something to read first. */
.modetag{display:inline-block;margin-top:3px;padding:1px 7px;border:1px solid var(--line);border-radius:999px;
font:700 9.5px/1.6 var(--mono);letter-spacing:.09em;text-transform:uppercase;color:var(--ink-2)}


/* THE STAT HIGHLIGHTER. A picked game is lit along its whole row, and the strip at the foot adds them up. */
.gamerow{cursor:pointer}
/* A HIGHLIGHTER YOU CANNOT SEE IS NOT ONE. The first version was a few points of lightness over the grid's own
   green and read as a rendering artefact beside the gold of a week's best. It has to be obvious at a glance,
   because the whole act is "these ones, not those". */
.gamegrid tbody tr.picked>td{background:light-dark(hsl(150 52% 84%),hsl(150 44% 27%));
box-shadow:inset 0 1px 0 light-dark(hsl(150 40% 76%),hsl(150 34% 34%))}
.gamegrid tbody tr.picked>td:first-child{box-shadow:inset 4px 0 0 var(--accent),
inset 0 1px 0 light-dark(hsl(150 40% 76%),hsl(150 34% 34%))}
/* IT FLOATS OVER THE LIST, and says so. Sticky pins it to the scroller's CONTENT edge, which in a panel is its
   bottom padding in from the window -- measured, 768 of 808 -- so a row can show in the band beneath it. A
   shadow makes that read as a strip lying over the games rather than one wedged between two of them. */
.spanbar{position:sticky;bottom:0;z-index:3;margin-top:10px;padding:10px 14px;
background:var(--panel-2);border:1px solid var(--line);border-radius:12px;
box-shadow:0 6px 18px light-dark(rgba(0,0,0,.18),rgba(0,0,0,.55))}
.spanlead{display:flex;align-items:center;justify-content:space-between;gap:12px}
.spanlead b{font:800 15px/1.3 var(--sans)}
.spanstats{display:flex;flex-wrap:wrap;gap:4px 14px;margin-top:6px}
.spanstat{font:600 12.5px/1.5 var(--mono);white-space:nowrap}
.spanstat i{font-style:normal;color:var(--ink-2);letter-spacing:.08em;margin-right:5px}

/* THE MOCK'S THREE, in the board's banner beside the round (theirs, 16 Oct). They are chips because everything
   else on that row is one, and the form wrapping each must not break the row's flow. Note `.mockact`, singular:
   `.mockacts` above is the Draft Log's row of buttons and has nothing to do with these. */
.boardpos .mockact{display:inline-flex;margin:0}
.boardpos .mockact .chip{white-space:nowrap}

/* ...AND THEY SIT BESIDE THE ROUND, not at the far left of the row with it (theirs, 16 Oct: "put all the
   controls next to 'Round N'"). The round is pinned absolutely to the right and `.boardpos` reserves its width
   in padding, so pushing the first of these over fills the row to exactly where the round begins. Measured, it
   was 321px away before. */
.boardpos .mockact:first-of-type{margin-left:auto}

/* A COLUMN OF YOUR OWN. Marked, quietly, because a number nobody else can see should not look like one
   the app is asserting -- it is yours, and the board should say so without shouting it. */
.board td.cmine{color:var(--accent);font-variant-numeric:tabular-nums}
