/* skins-live.css — bridges the LIVE app to the skin variables.  - Claude 7/16/26
 *
 * Loaded LAST (after desktop.css + skin-base + skin + modes + buttons) by
 * skinswitch.js. With the <body id> stripped, the body#bodyN company rules are
 * off and skin-base already re-themes the tokenized selectors; this sheet mops up
 * the remaining hardcoded DataForce colors in desktop.css base rules that
 * skin-base doesn't have a matching selector for. v1 — expect to iterate from
 * screenshots of real pages. Colors all resolve from the active skin/mode.
 *
 * body.dfx-darkpanel = "the content panel is DARK". Every on-dark adjustment below keys off THIS,
 * not [data-theme="dark"]. They mean the same thing for the Light/Dark toggle, but Classic renders
 * each skin's OWN surfaces — and Onyx is a dark skin, so in Classic its rows were dark while the
 * stylesheets' hardcoded #000 cell text stayed black-on-dark (Lee 7/26: mobile Free Agent / Waiver
 * / Draft history grids). skinswitch.js sets the class for Dark mode OR a dark skin in Classic;
 * add future dark skins to DARKSKINS there and they inherit all of this. The class selector has
 * the SAME specificity as the attribute selector it replaced, so no cascade changed. - Claude 7/26/26
 */

/* ══ SEMANTIC STATUS PALETTE (go / warn / stop) ═══════════════════════════════════════════════════
   The live-draft timer already established this vocabulary (--dfx-go green, amber, --danger) but
   declared it inside body.dfx-fullpage, so nothing else could use it. Lee 7/26 asked for the same
   three colors on the Trade History statuses and the Trade Bar, so hoist them to body level. These
   are deliberately SEMANTIC, not the skin accent: "approved" must read as approved on every skin.
   Each has a value tuned for a light panel and one for a dark panel (same pattern as --attention /
   --attention-ink), because a green dark enough to read on white is too dark to read on #161b22.
   Values are unchanged from the live-draft block, so the draft timer renders identically.
   FILL vs INK is a real split, not tidiness: --dfx-go is the live-draft box FILL (used at 20% over the
   panel, plus as a border), so it must stay exactly as it was or the draft timer changes. Text needs a
   much darker value to clear 4.5:1 — a green readable on white is far too dark to use as a box fill.
   Same relationship --attention / --attention-ink already has. The INK values are tuned against the
   DIMMEST light row surface (SBFFC #dcdcdc), which is the binding case: a slightly darker background
   demands darker text, not lighter, because contrast = (Lbg+.05)/(Lfg+.05). - Claude 7/26/26 */
body                 { --dfx-go:#1a8f57;                                  /* FILL: live-draft box + trade bar */
                       --dfx-warn-fill:#e0a400; --dfx-warn-line:#c58a00;  /* FILL: amber box + its border */
                       --dfx-stop-fill:var(--danger);                     /* FILL: trade-bar red segment */
                       --dfx-go-ink:#0f6b3f;                              /* TEXT on a light panel */
                       --dfx-warn-ink:#7d4e00;
                       --dfx-stop-ink:#9b2c22;
                       --dfx-info-ink:color-mix(in srgb, var(--complementary) 45%, black); }
/* STATUS CHIPS — the lobby's paid / held / dropped buttons and the draft-vote yes/no buttons and
   legend. Found 7/26 by auditing desktop.css's color legend: nothing in the harness touched them,
   and their specificity (#main table#leagues button.paid ~ 2 ids + class) buries skin-base's
   #main button, so they stayed DataForce green/pink on EVERY skin and both modes.
   These are pale CHIPS carrying black text, not solid bands — mapping them straight onto --dfx-go
   would put black text on a mid-green and wreck the contrast. So they get their own tint pair:
   a light wash of the semantic color with dark ink, flipping to a deeper wash with light ink on a
   dark panel. They stay recognisably green/red on every skin, so nobody has to relearn what "paid"
   looks like. Consumed by the base sheets as var(--chip-go, #4AE371) etc., so production keeps
   today's exact colors until the tokens ship. - Claude 7/26/26 */
body                 { --chip-go:color-mix(in srgb, var(--dfx-go) 28%, #ffffff);      --chip-go-ink:#0d3b26;
                       --chip-stop:color-mix(in srgb, var(--danger) 20%, #ffffff);    --chip-stop-ink:#4a1712;
                       --chip-neutral:color-mix(in srgb, var(--ink) 6%, #ffffff);     --chip-neutral-ink:#333333; }
body.dfx-darkpanel   { /* headings: the base sheets pin #003333 ("Headings" in desktop.css's legend), which is
                          3.80:1 on a dark content panel. modes.css lifts --heading for data-theme=dark but
                          NOT for a dark skin in Classic (Onyx), so set it for every dark panel. - Claude 7/26/26 */
                       --heading:color-mix(in srgb, var(--accent) 60%, white);
                       /* link hover has to move AWAY from the surface: darkening is the right
                          direction on a white panel, the wrong one here. - Claude 8/3/26 */
                       --link-hover:brightness(1.35);
                       --chip-go:color-mix(in srgb, var(--dfx-go) 40%, #161b22);      --chip-go-ink:var(--ink);
                       --chip-stop:color-mix(in srgb, var(--danger) 42%, #161b22);    --chip-stop-ink:var(--ink);
                       --chip-neutral:color-mix(in srgb, var(--ink) 14%, #161b22);    --chip-neutral-ink:var(--ink);
                       --dfx-go:#3fbd7e;                                  /* FILL lifts on a dark panel */
                       /* --danger #c0392b is dark, so on a dark panel the trade-bar red segment sat almost
                          on top of its own track (onyx/classic measured 1.32x) and read only by hue. Lift
                          it like green and amber already are. The live-draft pink/redBox keep plain
                          --danger — they are 26-30% TINTS over the panel, a different problem. */
                       --dfx-stop-fill:color-mix(in srgb, var(--danger) 62%, white);
                       --dfx-go-ink:#3fbd7e;                              /* TEXT lifts to match */
                       --dfx-warn-ink:#e0a400;
                       --dfx-stop-ink:color-mix(in srgb, var(--danger) 55%, white);
                       --dfx-info-ink:color-mix(in srgb, var(--complementary) 60%, white); }

/* content-panel edge: in Dark (and Light) mode #main is a dark/near-white panel on a
   same-toned page, so it needs an outline to separate from the page. Only Light/Dark
   set --border; Classic leaves it transparent (its light-on-dark panel needs none, so
   today's look is untouched). A subtle surface tone — NOT off-white, which glares. */
/* The visible panel's OUTER edge is #background (it wraps #main with an ~8px inset),
   so the separating border belongs HERE, not on #main — a border on #main floats ~8px
   inside the real edge. Also match #background's fill to the content surface so no white
   strips show. Classic leaves --border transparent => no outline (today's look untouched). */
#background { background-color:var(--content-bg); border:1px solid var(--border, transparent); }
#main { border:0; }

/* Buttons (league chips in the lobby/marketplace, etc). Consume the MODE-ONLY tokens and
   fall back to the skin's --btn-* — so Classic is untouched but Light/Dark actually win.
   (Naming them --btn-bg in modes.css tied the cascade with body.skin-*{--btn-bg} and lost.)
   The .join/.paid/.dropped buttons keep their own higher-specificity semantic colors. */
#main button, #home button           { background-color:var(--btn-surface, var(--btn-bg));
                                       border-color:var(--btn-line, var(--btn-border));
                                       color:var(--btn-ink, var(--btn-text)); }

/* LOBBY ACTION BUTTONS LOST THEIR EDGE (Lee 8/6/26, reported on Gridiron: "the JOIN and PAY buttons
   have lost their borders or background colors"). Both halves of that sentence are right, and it is
   NOT a Gridiron bug — Gridiron is just the worst case.
   The carve-out noted three lines above is the cause. desktop.css:803-816 pins these six classes to a
   hardcoded #F8F8F7 fill with black text at 2-ids-plus-a-class, which outranks everything the skin
   system has to say about their COLOUR. Before skins that was fine, because desktop.css never set
   `border` on them either, so they were drawn with the BROWSER'S default button border — a grey bevel
   that gave the chip its silhouette for free. skin-base.css:135 then replaced that UA border with
   `1px solid var(--btn-border)`, and ten of thirteen palettes set --btn-border:transparent (correct
   for a FILLED button, fatal for one whose fill is hardcoded near-white). So the silhouette went, and
   the fill had nothing to fall back on.
   MEASURED, fill vs the row behind it: ember dE 0.4 · gridiron 1.7 · midnight 1.7 · primetime 1.7 ·
   varsity 2.2 — all under 3, i.e. the same colour to the eye, with no border. Five more sit at 6.0-6.7.
   Only dataforce, sbffc and tailgate escaped, and only because they set a real --btn-border.
   FIX: give the edge back, anchored to currentColor rather than to --btn-border. That is exactly the
   precedent Lee approved on 7/27 for button.secondary, for this same defect and the same reason — a
   token that is transparent in most palettes cannot be an outline. currentColor is the label, which
   every skin already guarantees is readable on this fill, so the edge cannot vanish. 40% lands it at
   ~#959594: dE 24.9 from the button fill and worst-case 19.1 from the row across all 13 skins, both
   far above this project's 8 floor.
   NO LAYOUT CHANGE: skin-base already reserved 1px of transparent border, so this only colours space
   that was always there. The widths in desktop.css and the yellow :hover are deliberately untouched.
   paid/held are included so the column does not show bordered JOIN beside unbordered PAID — they were
   legible only because --chip-go is green. - Claude 8/6/26 */
#main table#leagues button.join,
#main table#leagues button.pay,
#main table#leagues button.hold,
#main table#leagues button.leaveShow,
#main table#leagues button.paid,
#main table#leagues button.held      { border:1px solid color-mix(in srgb, currentColor 40%, transparent); }

/* The lobby's ID-column chips are NOT buttons — they're <a class='myLeagues'> (admin.php:1539)
   hardcoded #8DC7BC / #4A9586, which is why the #main button rule never reached them. */
#main table#leagues a.myLeagues      { background-color:var(--btn-surface, var(--btn-bg));
                                       border-color:var(--btn-line, var(--btn-border));
                                       color:var(--btn-ink, var(--btn-text)); }
/* :focus-visible, NOT :focus, throughout this section (Lee 8/7/26). A mouse click FOCUSES the thing it
   clicked, and focus does not go away when the pointer does, so on `:focus` the lift stayed switched on
   after the customer moved the mouse away — most visibly on the live draft page's CLEAR button, which has
   no state class of its own and so just sat there lit. :focus-visible matches only focus the browser
   judges worth showing (keyboard/tab, not a mouse click), which kills the stickiness while KEEPING the
   affordance for keyboard users — and that matters here because a button has no other focus indicator:
   skin-base.css:148 draws the accent focus ring for textarea/input/select only, so for a button this lift
   IS the entire focus signal. Deleting :focus outright would have stranded keyboard navigation.
   EACH PAIR IS SPLIT INTO TWO RULES ON PURPOSE. A browser that doesn't know :focus-visible discards the
   WHOLE selector list it appears in — so keeping it in one comma-list with :hover would take the hover
   styling down with it on anything older than ~2022. Split, such a browser drops only the focus rule and
   hovering still works. - Claude 8/7/26 */
#main table#leagues a.myLeagues:hover { background-color:var(--btn-surface, var(--btn-bg));
                                       filter:var(--hover-lift); }
#main table#leagues a.myLeagues:focus-visible
                                     { background-color:var(--btn-surface, var(--btn-bg));
                                       filter:var(--hover-lift); }

/* Links column buttons (RULES / ROST / PICKS / MFL) are <a class='enabled'> — hardcoded
   #8DC7BC teal with a yellow hover, same as the myLeagues chips. Give them the button tokens. */
#main table#leagues a.enabled        { background-color:var(--btn-surface, var(--btn-bg));
                                       border-color:var(--btn-line, var(--btn-border));
                                       color:var(--btn-ink, var(--btn-text)); }
#main table#leagues a.enabled:hover  { background-color:var(--btn-surface, var(--btn-bg));
                                       filter:var(--hover-lift); }
#main table#leagues a.enabled:focus-visible
                                     { background-color:var(--btn-surface, var(--btn-bg));
                                       filter:var(--hover-lift); }

/* HOVER: desktop.css turns buttons yellow (#F4FA58, its documented "Buttons (hover)").
   Replace with the brightness lift so each button shifts its OWN color. */
#main button:hover, #home button:hover, #searchDisplay button:hover
                                     { background-color:var(--btn-surface, var(--btn-bg));
                                       filter:var(--hover-lift); }
#main button:focus-visible, #home button:focus-visible, #searchDisplay button:focus-visible
                                     { background-color:var(--btn-surface, var(--btn-bg));
                                       filter:var(--hover-lift); }
/* the league action buttons keep their semantic white base — just lift it instead of yellow */
#main table#leagues button.join:hover,      #main table#leagues button.buy:hover,
#main table#leagues button.pay:hover,       #main table#leagues button.hold:hover,
#main table#leagues button.leaveShow:hover
                                     { background-color:#F8F8F7; filter:var(--hover-lift); }
#main table#leagues button.join:focus-visible,  #main table#leagues button.buy:focus-visible,
#main table#leagues button.pay:focus-visible,   #main table#leagues button.hold:focus-visible,
#main table#leagues button.leaveShow:focus-visible
                                     { background-color:#F8F8F7; filter:var(--hover-lift); }

/* SELECTED BEATS HOVER (Lee 8/7/26). The lift above is a brightness FILTER layered over whatever
   background a button has, so on a button that carries a persistent selected state it sits on top of the
   selected fill: click one and, with the cursor still parked on it, you keep seeing the BRIGHTENED version
   of the new colour. :hover is only re-evaluated when the pointer moves, so the state change appears to
   wait for you to move the mouse — the actual complaint. Cancel the lift once a button IS the selection:
   there is nothing left to preview, and the real selected colour shows the instant you click.
   :focus IS INCLUDED DELIBERATELY, and is the part that's easy to miss — the rules above fire on
   `:hover, :focus` both, and a clicked button KEEPS focus, so cancelling only :hover would leave the lift
   stuck on anyway until the customer clicked something else.
   An ALLOW-LIST, not a blanket `[class*=elected]`, because three families would be hurt by it:
     - jQuery dialog buttons: .ui-state-active is the PRESSED state, not a selection, and it shares one
       rule with hover/focus (line ~368) — a blanket rule would flatten every dialog button.
     - Wonk icon buttons (.iconSelected/.tinyIconSelected): selection there is a BORDER-colour change with
       no fill, so there is no masked colour to reveal — cancelling hover would be pure loss of feedback.
       Lee 8/7/26 had no preference; left excluded on those grounds.
     - Misc Interfaces .on — see the carve-out immediately below.
   NOT handled here, on purpose: the nav menubar has the same annoyance but via a background OVERRIDE
   (desktop.css:317 `:hover` at (1,2,1) out-ranks :318 `.menuSelected` at (1,1,1)), not the filter, so
   filter:none would do nothing for it. Lee 8/7/26: menubar doesn't need it. */
#main button.selectedButton:hover,  #main button.selectedButton:focus,
#main button.on:hover,              #main button.on:focus,
#main table#friends button.friendSelected:hover,
#main table#friends button.friendSelected:focus,
body.dfx-wonk #main table.wonk button.menuSelected:hover,
body.dfx-wonk #main table.wonk button.menuSelected:focus,
body.dfx-wonk #main table.wonk button.statsSelected:hover,
body.dfx-wonk #main table.wonk button.statsSelected:focus
                                     { filter:none; }
/* .Done — the "already sent" state on the draft engine's Message 1-7 buttons (draftengine.js:223) and on
   the commish send-message / filler-message buttons (commish.js:540, 8768+). Needs the BACKGROUND restated
   as well as the lift cancelled, which is what makes it different from the states above.
   fullpage.css:76 and desktop.css:1215 declare `#main button.Done {background-color:var(--content-bg)}` at
   specificity (1,1,1) — but the blanket hover rule up at line ~144 is `#main button:hover`, ALSO (1,1,1),
   and this sheet loads last. Equal specificity, later wins: while the button was hovered OR focused the
   hover rule re-set background-color to --btn-surface, so .Done never turned white at all under the
   cursor, and :focus kept it that way after the pointer left (Lee 8/7/26: "clicked Message 1, moved the
   mouse to Message 2, still showed the hover colour"). Restating the white here at (1,2,1) settles it:
   hover still lifts the button BEFORE it's sent, and the instant it's clicked it goes white and stays
   white, cursor parked on it or not. - Claude 8/7/26 */
#main button.Done,   #main button.Done:hover,   #main button.Done:focus,
#main a.Done button, #main a.Done button:hover, #main a.Done button:focus
                                     { background-color:var(--content-bg); filter:none; }
/* CARVE-OUT: Miscellaneous Interfaces (mfl.php) deliberately NEUTRALISES the .on fill (line ~622, Lee
   8/6/26) so its data-source toggles don't read as primary — .on there is bold but otherwise identical to
   .off. With no distinct fill to reveal, killing the lift would leave two buttons that look the same and
   behave differently on hover, so .on keeps normal feedback on that page. Wins on id count (2 vs 1). */
/* :focus-visible here too — this rule RESTORES the lift, so leaving it on plain :focus would put the
   post-click stickiness back for exactly these buttons. The cancel rules above can stay on the broader
   :focus, because cancelling on a mouse click is the outcome we want anyway. - Claude 8/7/26 */
#main:has(#scoringAudit) button.on:hover,
#main:has(#scoringAudit) button.on:focus-visible { filter:var(--hover-lift); }

/* EDITING A FIELD: desktop.css washes inputs pale yellow (#FFFBDF) on hover/focus, which
   fights every skin. Use the skin's own affordance instead: hover = accent border only;
   focus = a faint accent tint + the accent focus RING that skin-base already draws. */
/* NOTE: border-color alone is useless as a hover cue in Classic — there --input-border and
   --accent are the SAME color (#4A9586), so the border never visibly changed. Use a faint
   tint + a soft halo instead, which reads in every skin/mode. Focus keeps a stronger ring,
   and is declared AFTER hover so it still wins while the field is focused. */
#main textarea:hover, #main input.lineEntry:hover, #main select.lineEntry:hover,
#main input.topLineEntry:hover, #main input.bottomLineEntry:hover
                                     { background-color:color-mix(in srgb, var(--accent) 6%, var(--input-bg));
                                       border-color:var(--accent);
                                       box-shadow:var(--input-shadow), 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent); }
#main textarea:focus, #main input.lineEntry:focus, #main select.lineEntry:focus,
#main input.topLineEntry:focus, #main input.bottomLineEntry:focus
                                     { background-color:color-mix(in srgb, var(--accent) 11%, var(--input-bg));
                                       border-color:var(--accent); outline:none;
                                       box-shadow:var(--input-shadow), 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent); }

/* HOST distinction in the lobby (Leagues.host): desktop.css rows are tr.DFF (#CFE7E2) vs
   tr.MFL (#F7FBFA, washed out) — but those are (1,1,2), same as skin-base's
   `#main tr.detail td {background:var(--row-bg)}`, so tokenizing row-bg flattened them into
   one color and the cue was lost.
   REBASED 8/3/26 (Lee: make the lobby read like the Locker Room sample). The sample's detail rows are
   plain var(--row-bg), so the DataForce rows — the default, and the bulk of the lobby — are now exactly
   that, and the lobby's body reads like every other table on the site. That leaves MFL as the deviation,
   which is the right way round: the thing that needs pointing out is the one that looks different.
   MFL takes a --complementary tint rather than a weaker --accent one. Two shades of the same mint is a
   difference you have to be told about; a different HUE is one you can see, and --complementary is the
   palette's designated second identity, so it stays on-brand in all 14 skins. 14% keeps it soft enough
   to read as a row rather than a highlight.
   THE LEGEND SWATCHES MUST TRACK THESE. They are the only thing on the page that says which is which,
   so they carry the same two expressions verbatim — change one pair, change the other. */
#main tr.DFF td                      { background-color:var(--row-bg); }
#main tr.MFL td                      { background-color:color-mix(in srgb, var(--complementary) 14%, var(--row-bg)); }
/* keep the legend swatches identical to the rows they describe */
#main table#legend td.DFF            { background-color:var(--row-bg);
                                       border-color:var(--panel-border); }
#main table#legend td.MFL            { background-color:color-mix(in srgb, var(--complementary) 14%, var(--row-bg));
                                       border-color:var(--panel-border); }

/* APEX: NO MFL TINT, BECAUSE ON APEX IT DISTINGUISHES NOTHING (Lee 8/6/26 — he sent the Apex lobby
   beside Tailgate's and asked for Tailgate's cell colour).
   The tint above is right on DataForce, where it marks the rare MFL league: 382 DFF against 5 MFL in
   the 2026 lobby. On Apex it fires on EVERY row — 284 of 284 open 2026 leagues are host=2 (checked on
   prod) — so it stops being a highlight and just becomes the table's background colour, a greyer one
   than the palette actually specifies.
   That is what Lee was looking at. Sampled from his screenshots: Apex cells rendered #cad9cd, Tailgate
   #e1e8d3. Apex and Tailgate declare the SAME --row-bg (#e1e8d3) — the whole difference was this rule,
   and the arithmetic matches to the byte: Apex's --complementary #3f7fa6 at 14% over #e1e8d3 gives
   exactly #cad9cd. Nothing was wrong with the palette; a highlight was covering it.
   Scoped on co-apex, the company IDENTITY class (never user-changeable), so it cannot follow anyone
   who picks a skin. DataForce and SBFFC keep the tint and its meaning.
   ⚠ THE LEGEND: the note above says the swatches must track the rows, so the DFF/MFL swatches are
   deliberately left alone here — on Apex the legend explains a distinction the table no longer draws.
   Whether Apex should show that legend row at all is a CONTENT question, not a colour one, so it is
   Lee's call rather than something to bury in a stylesheet. - Claude 8/6/26 */
/* ⚠ :not(.swatch) IS THE WHOLE POINT OF THIS SELECTOR — it is not tidying. Written first as a plain
   `td`, this rule PAINTED OVER THE LEVEL COLOUR SWATCHES and Lee lost the entire left column (8/6/26).
   The swatch cell is a td in the same row, and it takes its colour from `#main tr.allLeagues td.copper`
   and friends at (0,1,2,2) — while this selector, with body + .co-apex + #main + tr + .MFL + td, is
   (0,1,2,3). One element more specific, so it won every swatch in the lobby and flattened it to the
   row colour. Excluding the cell is what keeps this rule aimed at the row background it was written
   for. Any future variant of this rule needs the same exclusion. - Claude 8/6/26 */
body.co-apex #main tr.MFL td:not(.swatch) { background-color:var(--row-bg); }

/* "This is your team" highlight rows (tr.myTeam) on Free Agent History / Waiver History (and any
   other myTeam list) were a hardcoded #C0E0DA teal that ignored the skin. Emphasise them per skin
   with an accent-tinted row surface (same accent-over-row-bg family as the DFF highlight rows,
   a touch stronger so the current team clearly stands out from the plain --row-bg rows) — follows
   every skin + dark mode. - Claude 7/18/26 */
#main tr.myTeam td                   { background-color:color-mix(in srgb, var(--accent) 30%, var(--row-bg));
                                       color:var(--ink); }

/* TOGGLE BUTTONS (.on / .off) — e.g. the marketplace "and / or" pair (#orphanand/#orphanor).
   desktop.css styles ONLY .off, and lumps it in with :disabled (#F5F5F0), so an unselected
   toggle looks *disabled* while .on has no rule and looks like an ordinary button — there's
   no signal for which is active. Give the pair a real state: ON = solid accent (bold),
   OFF = the normal muted button. (:disabled keeps its own grey — we only touch button.off.) */
#main button.on                      { background-color:var(--accent); color:#fff;
                                       border-color:var(--accent); font-weight:bold; }
#main button.off                     { background-color:var(--btn-surface, var(--btn-bg));
                                       color:var(--btn-ink, var(--btn-text));
                                       border-color:var(--btn-line, var(--btn-border)); font-weight:normal; }

/* ...EXCEPT ON MISCELLANEOUS INTERFACES, WHERE .off MEANS SOMETHING ELSE ENTIRELY (Lee 8/6/26:
   "the buttons for some of these, like Fantasy Guru, used to be white — meaning, not ready for use
   yet this year"). He is right and the rule above is what took it away.
   ONE CLASS, TWO MEANINGS. On the marketplace and/or pair, .off = "not the selected half of a
   toggle", and making that look disabled was wrong — which is what the rule above fixed. On
   views/mfl.php, .off = "this feed is not available this season", which IS a disabled state, and
   desktop.css:710 was right to group it with :disabled at #F5F5F0. Restyling .off site-wide fixed
   the toggle and silently broke the availability flag; the only surviving difference on this page
   was font-weight, which is not a signal anyone would read as "unavailable".
   Scoped by #loadGuru — the Fantasy Guru LOAD button itself, which renders and appears in no other
   view (checked). NOT #getPlayers, which I used first and which never matched anything: that id is
   inside an HTML COMMENT in views/mfl.php, so it is in the source but never in the DOM. Confirm a
   hook is RENDERED, not merely present in the file. Same :has() page-scoping used for #scoringAudit
   further down this file.
   Treatment: the muted panel-neutral chip with faded ink and a dashed edge. Dashed because colour
   alone should not carry it — these sit beside .on buttons in the same row and the page is read at a
   glance, so the edge style says "not available" even before the fill registers. --chip-neutral is
   already the skin-aware "nothing here" surface (it is what the lobby's dropped chip uses) and it
   follows dark panels, which a hardcoded #F5F5F0 would not.
   NOT :disabled and not pointer-events:none — the buttons still work if Lee wants to run one, and
   this is a status hint, not an enforcement. - Claude 8/6/26 */
#main:has(#loadGuru) button.off      { background-color:var(--chip-neutral);
                                       color:color-mix(in srgb, var(--chip-neutral-ink) 55%, transparent);
                                       border:1px dashed color-mix(in srgb, var(--chip-neutral-ink) 45%, transparent);
                                       font-weight:normal; }

/* button.selectedButton = the ACTIVE item of a toggle group (e.g. the ALL/PAYPAL/ACH/PAPER mode
   picker on Withdrawal Requests). desktop.css hardcodes it #4A9587 green — map the selected state
   to the skin accent so it matches (same treatment as the .on toggles). - Claude 7/18/26 */
#main button.selectedButton          { background-color:var(--accent); color:#fff; border-color:var(--accent); }
#main button.selectedButton:hover    { background-color:var(--accent); filter:var(--hover-lift); }
#main button.selectedButton:focus-visible
                                     { background-color:var(--accent); filter:var(--hover-lift); }

/* "My Watch List" is a TOGGLE (.off / .on), but desktop.css gives it #8DC7BC when OFF and
   #F5F5F0 when ON — two arbitrary fills where neither reads as "active". Make the state
   obvious and skin-aware: OFF = the normal muted button; ON = solid accent, bold. */
#main button#myWatchList.off         { background-color:var(--btn-surface, var(--btn-bg));
                                       color:var(--btn-ink, var(--btn-text));
                                       border-color:var(--btn-line, var(--btn-border)); font-weight:normal; }
#main button#myWatchList.on          { background-color:var(--accent); color:#fff;
                                       border-color:var(--accent); font-weight:bold; }

/* Links column (marketplace): "Contact Owner" / "Make an Offer" / "Watch" are anchors
   (a.contact / a.watch) — pin them to the link color so they follow the skin. The
   disabled chip beside them is hardcoded #F5F5F0 with a #4A9586 border. */
#main table#leagues a.contact,
#main table#leagues a.watch          { color:var(--link, var(--accent)); }
#main table#leagues span.notEnabled  { background-color:var(--btn-surface, var(--btn-bg));
                                       color:var(--btn-ink, var(--btn-text));
                                       border-color:var(--btn-line, var(--btn-border)); }

/* ===================== COMPLEMENTARY "info / aside" surfaces =====================
   A soft companion color — --complementary, one hand-picked hue per skin (DataForce = the
   existing package-deal blue) — for the app's ASIDES: help text, training popups, dialogs,
   and the marketplace package-deal / buyer's-guide boxes. One identity, distinct from the
   primary accent and from danger/attention. Pattern (per Lee): a tinted background + a border
   shade that ALSO colors the text. --complementary-ink is that border+text color (mode-aware:
   deepened on light panels, lightened on dark); bg is a light tint of --complementary over the
   content surface. All of these previously hardcoded yellow/blue with no skin awareness. */

/* shared "callout" box: help panel, popups + popup-list page, dialog body, the blue boxes */
#main div.narrative, #home div.narrative,
#popup, #main table#popupTable td,
#orphanGuide, #packageDeals, #openOrphanGuide, #openPackageDeal,
.ui-dialog
                                     { background-color:color-mix(in srgb, var(--complementary) 14%, var(--content-bg));
                                       border-color:var(--complementary-ink, var(--complementary));
                                       color:var(--complementary-ink, var(--complementary)); }
/* Headings INSIDE the callout follow the box's OWN text colour, not the page heading colour, so they
   always contrast with the box tint. Two rules outrank the plain `#main h1` and had to be beaten:
   `#main h1` (~805, var(--ink)) AND, on mobile Classic, `body.dfx-mobile[data-theme="classic"] #main
   h1` (~1849, var(--page-text) — light, meant for the dark Classic #main box). The narrative is a
   LIGHT island inside that dark box, so on Classic the heading went light-on-light; the third selector
   below (specificity 1,3,3) beats the 1,2,2 Classic rule. The box's body text already uses
   --complementary-ink and is readable, so the heading matches it. - Claude 8/13/26 */
#main div.narrative :is(h1,h2,h3,h4),
#home div.narrative :is(h1,h2,h3,h4),
body.dfx-mobile[data-theme="classic"] #main div.narrative :is(h1,h2,h3,h4),
body.dfx-mobile[data-theme="classic"] #home div.narrative :is(h1,h2,h3,h4)
                                     { color:var(--complementary-ink, var(--complementary)); }

/* …except the TEXT in the two popup surfaces, which goes to --ink (Lee 8/3/26). The callout pattern
   above colors its text with the same --complementary-ink it uses for the border, which reads as a
   tinted block — fine for a short aside, harder work for the paragraphs these two carry.
   --ink, not #000: it is the mode-aware black (#111 in Classic, #1b222a in Light, #e6edf3 in Dark), so
   this stays black where black is right and does not go black-on-dark in Night Game. It also brings
   them into line with .ui-dialog .ui-dialog-content, which has taken --ink since 7/21.
   Background and border are deliberately left on the callout rule — these are still asides, they just
   read as text now. Links are unaffected: #popup a is a hardcoded #FF9933 (desktop.css:172), so they
   stay distinguishable from the body copy. - Claude 8/3/26 */
#popup, #main table#popupTable td    { color:var(--ink); }

/* Training Popups (table#popupTable) on MOBILE: mobile.css:856 sets each cell to display:table-row so
   the three cells stack vertically — but `padding` does NOT apply to table-row boxes, so the 1em padding
   is dropped and the text jams against the cell edges. Make the stacked cell a block (still full-width,
   still stacked) so its padding applies again, with a small gap between boxes. - Claude 7/25/26 */
body.dfx-mobile #main table#popupTable td { display:block; padding:1em; margin-bottom:12px; }

/* solid-complementary headers: help-panel header + dialog titlebar (the "Hey!" bar) */
div#narrHeader,
.ui-dialog .ui-dialog-titlebar, .ui-dialog .ui-widget-header
                                     { background:var(--complementary); color:#fff; }
/* Dialog BODY text: jQuery UI's .ui-widget-content pins it dark (#333), so in dark mode it went
   dark-on-dark — unreadable. Pin the content to the mode-aware --ink, and drop the hardcoded white
   button pane so the tinted dialog surface shows through. Skin-wide (all dialogs). - Claude 7/21/26 */
.ui-dialog .ui-dialog-content        { color:var(--ink); }
.ui-dialog .ui-dialog-content a      { color:var(--complementary-ink, var(--complementary)); }
.ui-dialog .ui-dialog-buttonpane     { background:transparent;
                                       border-top:1px solid color-mix(in srgb, var(--complementary) 30%, transparent); }

/* buttons living inside these asides -> solid complementary */
#popup button,
#main #orphanGuide button, #main #packageDeal button
                                     { background-color:var(--complementary); color:#fff; border-color:var(--complementary); }
#popup button:hover,
#main #orphanGuide button:hover, #main #packageDeal button:hover
                                     { background-color:var(--complementary); filter:var(--hover-lift); }
#popup button:focus-visible          { background-color:var(--complementary); filter:var(--hover-lift); }

/* DIALOG BUTTONS (the OK / Yes / No in .ui-dialog-buttonpane) — Lee 8/3/26. They take the same solid
   --complementary the other callout buttons above already take, so a dialog's button matches its own
   titlebar instead of sitting there as the pale #8DC7BB legacy button.
   THE SPECIFICITY HERE IS NOT DECORATION. A first attempt used `.ui-dialog .ui-dialog-buttonpane
   button` (0,2,1) and silently lost, twice, to a sheet nothing else in the skin layer touches:
       css/dialog.css:44   .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button.ui-button
                           { border:1px solid #4A9586; background:#8DC7BB; color:#fff; }
   That is (0,5,1) and outranks it whatever the load order. It was invisible from the markup too —
   the dialog is appended to BODY, not inside #main, so none of the #main button rules were involved
   at all; the matching-rule dump on the live page is what actually found it. Matching dialog.css's
   selector shape and adding .ui-widget (which every .ui-dialog carries) puts this at (0,6,1), so it
   wins on specificity rather than on being loaded later.
   The .ui-state-* variants are listed because ui-lightness styles buttons through those classes and
   `.ui-widget-content .ui-state-hover` would otherwise take the hover — that theme's hover is a
   YELLOW #fdf5ce glass PNG, thoroughly off-skin. background-image:none kills the PNG.
   Not touching dialog.css:40 `.ui-dialog .ui-button-icon-only{display:none}` — that is what hides the
   titlebar close X, and it is deliberate, not a casualty of the navy titlebar. - Claude 8/3/26 */
.ui-dialog.ui-widget .ui-dialog-buttonpane .ui-dialog-buttonset button.ui-button
                                     { background:var(--complementary); background-image:none;
                                       color:#fff; border:1px solid var(--complementary); }
/* DELIBERATELY still :focus, not :focus-visible, unlike the button rules higher up (Lee 8/7/26). Two
   reasons. jQuery UI also adds its OWN .ui-state-focus CLASS on focus, listed right below, so swapping the
   pseudo would only half-change the behaviour and leave the class doing the same thing. And there is no
   stickiness to cure here anyway: these buttons dismiss the dialog they live in, so the element is gone
   immediately after the click. - Claude 8/7/26 */
.ui-dialog.ui-widget .ui-dialog-buttonpane .ui-dialog-buttonset button.ui-button:hover,
.ui-dialog.ui-widget .ui-dialog-buttonpane .ui-dialog-buttonset button.ui-button:focus,
.ui-dialog.ui-widget .ui-dialog-buttonpane .ui-dialog-buttonset button.ui-button.ui-state-hover,
.ui-dialog.ui-widget .ui-dialog-buttonpane .ui-dialog-buttonset button.ui-button.ui-state-focus,
.ui-dialog.ui-widget .ui-dialog-buttonpane .ui-dialog-buttonset button.ui-button.ui-state-active
                                     { background:var(--complementary); background-image:none;
                                       color:#fff; border-color:var(--complementary);
                                       filter:var(--hover-lift); }

/* ---------- APEX DIALOGS IN OLIVE (Lee 8/6/26, on Apex's request that popups match their headers) ----
   RE-POINTS THE TOKEN INSIDE THE DIALOG ONLY, rather than restating the six dialog rules above. Every
   one of them already reads var(--complementary) / var(--complementary-ink); custom properties inherit,
   so declaring the pair ON .ui-dialog re-colours the titlebar, the tinted body, the border, the links
   and the OK/Yes/No buttons together, and cannot leak past the dialog's own subtree. A value set
   directly on the element also beats the one apex.css sets on body, so no specificity games.
   NOTHING ELSE MOVES — checked against the other 33 selector groups that read --complementary:
   #narrHeader (which SHARES the titlebar rule), #popup, the narrative boxes, #orphanGuide,
   #packageDeals, #searchDisplay, the MFL legend swatch, and every Wonk / live-draft / auction surface
   all sit outside .ui-dialog and keep Apex's blue. DataForce and SBFFC are untouched via co-apex.
   WHY --nav-base AND NOT THE HEADER THEY POINTED AT. The titlebar rule hardcodes color:#fff, and white
   fails on both pale header olives — 2.10:1 on --header-bg #a6bb7b and 2.43:1 on --major-bg #97af65.
   Matching those literally would have meant flipping the titlebar to dark ink, i.e. restructuring the
   dialog's look rather than re-hueing it. --nav-base #44611f is the darkest olive Apex already uses AS
   a header band (the site nav), so it satisfies "match the headers" while keeping the white-on-colour
   pattern intact — and it is a token they already own, not a new colour invented for them.
   MEASURED, all passing: white on the titlebar 7.06:1 (the blue it replaces managed only 4.38, so this
   is a contrast IMPROVEMENT); titlebar against the tinted body 5.73:1 light / 5.64 classic; links
   7.55:1 light and 5.59:1 dark; body copy 15.34:1 light and 13.29:1 dark.
   Written as var(--nav-base), not a literal, so the dialogs keep tracking the nav if Apex ever re-tints.
   Dark mode needs the lifted ink, exactly as modes.css:97 does for the blue.
   ⚠ Known and accepted: anything rendered INSIDE a dialog that also reads --complementary (a .dfx-pill,
   say) turns olive within that dialog. That is consistent for the popup rather than a leak. - Claude 8/6/26 */
body.co-apex .ui-dialog              { --complementary:var(--nav-base);
                                       --complementary-ink:color-mix(in srgb, var(--nav-base) 80%, black); }
body.co-apex[data-theme="dark"] .ui-dialog,
body.co-apex.dfx-darkpanel .ui-dialog { --complementary-ink:color-mix(in srgb, var(--nav-base) 60%, white); }

/* The competitors-page callout (div.yellowBox) is hardcoded #FFFF99 with a DataForce-teal
   #8DC7BB border and — again — NO text color, so it would go light-on-yellow in Dark.
   PROMOTED TO THE EMPHASIS TREATMENT (Lee 8/5/26: "make that banner attention"). It was a 12% accent
   tint — safe, but it read as just another panel, and this is the one line on the page doing the
   selling ("Because we also have the biggest payouts!"). Now the filled --attention with its paired
   --attention-text, the same pairing the Locker Room shows as the Emphasis sample, so what a customer
   sees here is a colour they can already name.
   Border set to --attention as well rather than kept as a contrasting edge: at 1px against a filled
   block it only muddied the corner radius. The box reads as one solid banner.
   The pairing is designed to be legible, and measures 6.3-9.7:1 on thirteen palettes. DataForce is the
   exception at 4.46:1 (white on #E82510) — a hair under the 4.5 body-text line, and exactly what its
   badges already ship. Acceptable here because the text is bold, but it is the palette to look at if
   this ever needs to carry longer copy.
   NOT the live-draft warning box: body.dfx-fullpage #main div.yellowBox further down this file is a
   different thing (the countdown's amber state) and outranks this on specificity, so the draft engine
   is untouched. - Claude 8/5/26 */
#main div.yellowBox                  { background-color:var(--attention);
                                       border-color:var(--attention);
                                       color:var(--attention-text); }

/* Search-results popup (#searchDisplay) restyled to match the HELP-text callout: the same
   --complementary tint background + --complementary-ink border/text as the help panel above,
   in place of its hardcoded #F8F8F8 white / teal border / black text. Section date-headers and
   the black player-name body text ride the same complementary-ink so the whole box reads as one
   aside; article links stay on --link like everywhere else; Close button already skinned. - Claude 7/17/26 */
#searchDisplay                       { background-color:color-mix(in srgb, var(--complementary) 14%, var(--content-bg));
                                       border-color:var(--complementary-ink, var(--complementary));
                                       color:var(--complementary-ink, var(--complementary)); }
#searchDisplay div.section           { color:var(--complementary-ink, var(--complementary)); }
#searchDisplay a, #searchDisplay a:visited { color:var(--link, var(--accent)); }


/* Third-party page logo chips (FantasyPros / Footballguys). The class is called
   "blackbackground" but it's actually #4A9586 — DataForce's --accent — so it stayed teal on
   every other skin. These logos are white artwork that needs a mid-tone chip behind it, and
   --accent is exactly that (and is literally the same value today, so DataForce is unchanged).
   --accent is a BRAND token, so it stays mid-tone in Light/Dark too and the white logos keep
   their contrast in every mode. */
#main div.blackbackground            { background-color:var(--accent); }

/* Shopping cart: the only hardcoded color on the page is the cell fill — a pale peach
   #F5E8E2 with a #DAAC96 tan border, unrelated to any skin, and (again) with no text color
   so it would go light-on-peach in Dark. It's a plain listing table, so give it the standard
   row surface: consistent with every other table in the app and correct in all three modes. */
#main table#cart td                  { background-color:var(--row-bg); border-color:var(--panel-border);
                                       color:var(--ink); }

/* GOLD CTA STARBURSTS — NO CSS RULE. div.spin wraps join.png, firstteam.png and referafriend-att2.png;
   the last was already orange-red and the other two were legacy gold. They are now BAKED into
   refer-a-friend's own colors (claude-work/recolor_cta.js, originals kept as images/*.gold.bak.png),
   so there is nothing for this sheet to do.
   A CSS filter was tried twice and abandoned. join.png and referafriend-att2.png are the SAME artwork
   — identical alpha on 100% of pixels, differing only in the lettering — and no single
   hue-rotate/saturate/brightness reproduces one from the other, because that is one linear map applied
   to every pixel and this gold->red relationship is not linear. Solved against the --attention token it
   went muddy brown; re-solved against refer-a-friend's own nine tonal quantiles it was closer but still
   not it. Baking learns the map per color instead and lands 74% of pixels within 30/255 of
   refer-a-friend, with the remainder confined to rows 41-131 — the lettering, which is meant to differ.
   If new gold art appears, recolor it; do not reach for a filter. - Claude 8/3/26 */

/* shopping cart: skinswitch.js swaps the gold cart <img> for this masked <span>. The cart
   PNG is the alpha MASK (its color is ignored) and --accent fills the silhouette, so the
   cart becomes the skin color and follows light/dark for free. (First of the "gold images"
   moved onto the palette; HELP/VIDEO/refer come later.) */
.dfx-cart                            { display:inline-block; width:50px; height:50px; vertical-align:middle;
                                       background-color:var(--accent);
                                       -webkit-mask:url(/images/cart.png) center/contain no-repeat;
                                               mask:url(/images/cart.png) center/contain no-repeat; }

/* ALERT BELL — skinfx.js swaps the gold alarm.png <img> for this masked <span>, same trick as the
   cart: the PNG is the alpha mask and the palette fills the silhouette. Takes --complementary, the
   HELP button's color (Lee 8/3/26). aspect-ratio carries the 88x103 art, so only the height needs
   setting and skinfx.js can pass through whatever height the markup asked for.
   The shake and the header's 16px both come from desktop.css rules written as `img#alarm`, which a
   <span> cannot match — re-hung here rather than edited there. @keyframes shake is global, so this
   references desktop.css's own definition. - Claude 8/3/26 */
/* BRUSHED SILVER 8/4/26 (Lee). Was var(--complementary) — a flat fill, which is what made it read dull:
   shine is a gradient, not a colour. The ramps are defined in skin-base and are the same on every skin.
   background-color stays as the fallback under the gradient, so a browser that cannot paint the
   gradient still gets a solid silver bell rather than an invisible one. - Claude 8/4/26 */
.dfx-alarm                           { display:inline-block; height:20px; aspect-ratio:88/103;
                                       vertical-align:middle;
                                       background-color:#6b6b71; background-image:var(--alarm-metal-dark);
                                       -webkit-mask:url(/images/alarm.png) center/contain no-repeat;
                                               mask:url(/images/alarm.png) center/contain no-repeat;
                                       animation:shake 4s ease-in-out 1; }
/* ...and the LIGHT ramp wherever the surface under it is dark. dfx-darkpanel is the signal bodyAttr()
   already sets for "this page's surface is dark" — Night Game, and Onyx even in Classic. Without this
   the in-prose bell would be dark silver on a dark panel. - Claude 8/4/26 */
body.dfx-darkpanel .dfx-alarm        { background-color:#c0c0c6; background-image:var(--alarm-metal-light); }
/* ON THE DARK HEADER FRAME the bell is an ALERT, and --complementary is 2.38:1 on the near-black page
   top — it would all but disappear exactly where it has to be noticed. --complementary-bright is the
   answer this sheet already gives for "the help color, on a dark band" (see #fullPageHeader .dfx-pill),
   so the bell follows the HELP button rather than diverging from it. 16px matches the old img#alarm
   rule: the bell is bigger than the 14px hub picture and crowds the loading spinner. - Claude 8/3/26 */
#title #titleCenter .dfx-alarm       { height:16px; background-color:#c0c0c6;
                                       background-image:var(--alarm-metal-light); }

/* HELP / VIDEO rebuilt as real skin pills by skinswitch.js (were gold text-buttons). They're
   help/learning affordances, so they take --complementary (matching help panel/popups/dialogs).
   VIDEO keeps its clapperboard picture. The wrapping <a> still carries the click handler. */
/* SCOPED TO #main 7/28/26. Unscoped, this also caught the mobile top bar: its "Page Help" button is
   an a.pageText too (common/mobiletopbar.php), so it picked up float:left AND the 8px bottom margin.
   That grew the Page Help CELL, and because those buttons are one table ROW, all six grew with it —
   icon, label and padding alike. Lee reported it as "elongated buttons" and it dated from the day the
   harness first injected this sheet.
   It took a rule-by-rule bisection to find, because nothing here matches the button you are LOOKING
   at — the offender is a sibling cell, and table rows share their height. Worth remembering the next
   time a cell is the wrong size and no rule seems to touch it.
   The pills these rules style all live inside #main, so scoping costs nothing. - Claude 7/28/26 */
#main a.pageText, #main a.playVideo  { float:left; margin:0 8px 8px 0; }
.dfx-pill                            { display:inline-flex; align-items:center; gap:5px; cursor:pointer;
                                       background:var(--complementary); color:#fff; font-weight:bold;
                                       font-size:13px; line-height:1; padding:2px 11px; border-radius:13px;
                                       border:1px solid var(--complementary);
                                       /* shadow tied to the complementary color, not the skin's brand --shadow
                                          (which is e.g. Pill's violet glow) */
                                       box-shadow:0 2px 5px color-mix(in srgb, var(--complementary) 40%, transparent);
                                       transition:filter .12s ease; }
.dfx-pill:hover                      { filter:var(--hover-lift); }
/* pills rendered inside body text as an ILLUSTRATION of a button (skinfx.js .dfx-static) — no
   click handler, so no pointer cursor and no hover lift. - Claude 8/3/26 */
.dfx-pill.dfx-static                 { cursor:default; }
.dfx-pill.dfx-static:hover           { filter:none; }
/* …and SMALLER than the real button (Lee 8/3/26, on About DFF -> Training Popups). At the button's own
   13px text and 16px icon a pill stands about 22px tall inside a ~21px line box, so it forces the
   leading apart wherever it appears mid-sentence — views/popups.php puts HELP and VIDEO in the middle
   of two tips, right beside a 12px tooltip bubble, which is what makes the mismatch obvious.
   Sized in em, not px, so they track whatever text they land in — desktop, mobile, and popup.php's
   tip text all render these at different base sizes. The real buttons are untouched: they keep the
   px sizes above, because a control you aim at should not shrink with its surrounding copy. */
.dfx-pill.dfx-static                 { font-size:.8em; padding:1px 7px; gap:4px; border-radius:9px;
                                       vertical-align:baseline; }
.dfx-pill.dfx-static .dfx-q,
.dfx-pill.dfx-static .dfx-vicon      { width:1.1em; height:1.1em; }
.dfx-pill.dfx-static .dfx-q          { font-size:.85em; }
.dfx-help .dfx-q                     { display:inline-flex; align-items:center; justify-content:center;
                                       width:16px; height:16px; border-radius:50%; background:#fff;
                                       color:var(--complementary); font-size:11px; }
/* HELP briefly took the skin's accent instead of --complementary (8/3/26) and came out green in
   DataForce, no longer matching VIDEO. Reverted the same day (Lee): HELP and VIDEO are the same
   family of affordance and must be the same shade, which is the plain .dfx-pill --complementary
   above. Nothing skin-specific here on purpose. - Claude 8/3/26 */
/* 18px -> 16px, 8/3/26 (Lee): the pill's height is set by its icon, not its 13px/1 label, so an 18px
   icon made VIDEO exactly 2px taller than HELP, whose .dfx-q is 16px. Matching the two makes the pills
   the same height everywhere they appear — the real buttons AND the copies rendered inline in prose
   (popups.php, videos.php, popup.php's tip text), which skinfx.js rebuilds as pills from the same CSS.
   TWEET shares .dfx-vicon and comes into line too. The SVGs are viewBox 24 so they just scale.
   - Claude 8/3/26 */
.dfx-vicon                           { width:16px; height:16px; display:block; }

/* loading indicator: the old wait.gif is a dated 2017 barber-pole bar. skinswitch.js swaps
   it for this skin-colored CSS spinner — crisp at any size, follows --accent, no image. */
.dfx-spinner                         { display:inline-block; width:18px; height:18px; box-sizing:border-box;
                                       vertical-align:middle;
                                       border:3px solid color-mix(in srgb, var(--accent) 22%, transparent);
                                       border-top-color:var(--accent); border-radius:50%;
                                       animation:dfx-spin .7s linear infinite; }
@keyframes dfx-spin                  { to { transform:rotate(360deg); } }

/* header loading spinner relocated (by skinswitch.js) to spin as a ring AROUND the centre hub icon
   (#alarm) while a page loads. Anchored to the STABLE #titleCenter cell (NOT #alarm, whose innerHTML
   is rewritten on every page switch). #titleCenter is the positioning context; left is fixed at 50%
   (the icon is horizontally centred) and skinswitch.js sets top to the icon's vertical centre. The
   ring is sized larger than the ~16px icon so it encircles it; pointer-events:none so it never
   blocks the icon's click. - Claude 7/18/26 */
#title #titleCenter                  { position:relative; }
#title #titleCenter #pleaseWait      { position:absolute; left:50%; transform:translate(-50%,-50%);
                                       margin:0; padding:0; line-height:0; z-index:5; pointer-events:none; }
/* ⚠ THIS SIZE IS A FALLBACK ONLY — IT IS NOT WHAT YOU SEE. skinfx.js centerHeaderSpinner() writes
   width/height INLINE on every sweep (icon size + 10), and inline wins over this sheet at any
   specificity. This value governs only the instant before that runs, and the case where no hub icon
   is found to measure. It sat at 34px while the page rendered 30px, so "shrink the ring" was edited
   here once with no visible effect — change skinfx.js instead. 26px matches what the JS now produces
   for the 16px bell, so the fallback no longer disagrees with the page. - Claude 8/12/26 */
#title #titleCenter #pleaseWait .dfx-spinner { width:26px; height:26px; border-width:3px; }
/* the alert bell (img#alarm, 20px) is a touch bigger than the plain hub picture (14px) and crowds
   the spinner ring — shrink it slightly so it sits comfortably inside. Only the bell carries
   id="alarm", so the normal hub icon is untouched. - Claude 7/18/26 */
#title #titleCenter img#alarm        { height:16px; }
/* Commissioner hub icon is a WHITE fedora (commish.png) — reads great on the dark frame, but the
   white body vanishes on the light-mode header (only the black band shows). A thin dark outline
   defines the hat + band on any background; invisible on the dark frame. - Claude 7/18/26 */
#title #titleCenter img[src*="commish"] { filter:drop-shadow(0 0 1px rgba(0,0,0,.7)) drop-shadow(0 0 1px rgba(0,0,0,.5)); }

/* "USE MOBILE VERSION" — the button title.php emits into #titleCenter when a phone loads the desktop
   site. Lee, 8/8/26: "The button pushes the Admin Hub and icon too high. It also could be highlighted
   the complementary color."
   ⚠ MEASURED, AND THE CAUSE IS NOT WHAT IT LOOKS LIKE: the button does NOT move the hub title. Measured
   on beta at 1319px and forced to 412px, with and without the button, in all three modes — the title and
   its icon stay at top 10.7 either way. What the button actually does is grow the header by 17.1px
   (#title 99.5 -> 116.6), because it is in normal flow inside a cell whose padding-top (7.5mm,
   desktop.css:209) was hand-tuned to align the hub with the BOTTOM of the ADMINISTRATION button back when
   the h2 was the ONLY thing in that cell. So the title is pinned high while the cell grows underneath it,
   and the group reads as top-heavy — which on a phone, where the whole fixed-width header is scaled down
   to fit, leaves the icon a few px from the top edge and looking clipped.
   FIX: take the button OUT OF FLOW. #titleCenter is already position:relative for the spinner above, so
   the button can hang in the 62.7px of dead space under the title. Header growth measured 17.1px -> 0,
   title unmoved, button 29px in a 45.6px gap.
   LABEL IS NOW ONE LINE — "Use Mobile", shortened in title.php (Lee 8/8/26: "what if we just decreased
   the text?"). Measured: the full "Use Mobile Version" on one line is 123px against a 104px cell and
   overhangs 10px each side, so the short label is the only one that fits without escaping the cell.
   Height 29px -> 18px, and the freed room pays for a 12px font instead of 10px, which matters because a
   phone renders this fixed-width header scaled down.
   `> br` NOT `br` is kept deliberately even though title.php no longer emits the leading <br>: the two
   dead title variants (classictitle.php, title-with-fadeout.php — 0 includers today) still carry the old
   "<br><button>Use Mobile<br>Version</button>" markup, and an unscoped `br` rule would kill the br INSIDE
   the button so the label reads "Use MobileVersion" with no space. That exact bug shipped in my first
   test and was caught by asserting on rendered innerText, not by reading the CSS.
   Colour: --complementary-bright is the documented on-dark-band variant (header pills), but ONLY
   dataforce.css defines it — the other 13 palettes have --complementary alone, so the fallback is
   load-bearing, not decoration. - Claude 8/8/26 */
#title #titleCenter #mobileVersionSection      { position:absolute; left:50%; bottom:4px;
                                                 transform:translateX(-50%); margin:0; padding:0; line-height:0; }
#title #titleCenter #mobileVersionSection > br { display:none; }
#title button#useMobileVersion                 { background-color:var(--complementary-bright,var(--complementary));
                                                 color:#fff; border:0; border-radius:6px; padding:3px 8px;
                                                 font-size:12px; line-height:1.15; white-space:nowrap;
                                                 cursor:pointer; box-shadow:0 1px 2px rgba(0,0,0,.35); }
/* ⚠ THE CELL MUST BE TOP-ALIGNED WHENEVER THE BUTTON SHOWS, AND THE DISCRIMINATOR IS THE BANNER, NOT THE
   THEME. I first shipped this as a light-mode-only rule and it was wrong — Lee: "you already position it
   correctly when the skin uses a banner image", which is the whole diagnosis in one line.
   Only `body.has-banner` gets `vertical-align:top; padding-top:7.5mm` (desktop.css:209). WITHOUT a banner
   the cell falls to `vertical-align:bottom` with no padding, so the hub title sits LOW (measured top 58.6,
   bottom 92.2) and the out-of-flow button at bottom:4px lands right on top of it (button top 66) — the
   title and the button occupying the same space. That is the case Lee was looking at, and it is why my
   first fix "didn't work": my whole test page carried has-banner, the one state where nothing was wrong.
   Top-aligning the cell whenever the button is present makes all SIX states identical — banner and
   no-banner x classic/dark/light: hub top 10.7, button top 66.1, 21.8px clear between them, header height
   unchanged at 99.5. It also supersedes the light-mode-only rule that was here, because light+banner is
   just one of the six.
   Anchoring the button to the cell's TOP instead was never an option: the header table sits at `top:-28px`
   and overflows its container by 20px, so the cell's own top is at viewport -18 and a top-anchored button
   would render off-screen.
   4 ids (:has() counts #mobileVersionSection) beats desktop.css's 3, so this wins regardless of load
   order, and :has() keeps every ordinary desktop page untouched. - Claude 8/8/26 */
#title table#topLeft tr td#titleCenter:has(#mobileVersionSection)
                                               { vertical-align:top; padding-top:7.5mm; }

/* Elements retired in the redesign — hidden via the harness (source unchanged):
   - the breadcrumb "&#8592; Go Back" link on every page  (bread.php:70, #gobackbutton wraps #goback;
     hide the wrapper so the JS $("#goback").show() toggle can't reveal it)
   - the "PLACE BIDS" / "NOMINATE" tab buttons on the Email Auction and Nominate Player pages
     (views/auction.php:5-6 and views/nominate.php:3-4; #auctionBidButton / #auctionNominateButton).
     REFRESH PAGE (#refreshPage) is intentionally left in place. - Claude 7/18/26 */
#gobackbutton                        { display:none; }
#main #auctionBidButton,
#main #auctionNominateButton         { display:none; }
/* ⚠ MOBILE REUSES THESE TWO IDS FOR SOMETHING ELSE ENTIRELY, and the rule above was hiding those too
   (Lee 8/7/26: "I don't see a button on either").
   On DESKTOP (views/auction.php, views/nominate.php) they are the retired PLACE BIDS / NOMINATE tab
   buttons, correctly hidden. On MOBILE (mobile-v2/auction.php, mobile-v2/nominate.php) the same ids
   are the ONLY navigation between the two pages — "Nominate a Player" and "Bid On Players" — and
   hiding them strands the user on whichever page they landed on. Same id, opposite meaning, and the
   selector above never distinguished them.
   Written as an addition rather than by rescoping the rule above, so desktop cannot be affected by
   this at all. body.dfx-mobile is set by skinfx.js from #zoomlevel2, so these appear once it runs.
   - Claude 8/7/26 */
body.dfx-mobile #main #auctionBidButton,
body.dfx-mobile #main #auctionNominateButton { display:inline-block; }
/*   - the "MISC MFL" / "FETCH ROSTERS" tab buttons on the Miscellaneous Interfaces / Fetch Rosters
       pages (views/mfl.php:2-3, views/mflrosters.php; #mflMiscButton / #mflRostersButton). */
#main button#mflMiscButton,
#main button#mflRostersButton         { display:none; }
/*   - the "FUNCTIONS" / "WAIVERS" / "LOCK TEAMS" tab buttons on the Process Waivers page
       (views/runwaivers.php:2-4; #specialFunctions / #runWaivers / #lockTeams).
   ⚠⚠ QUALIFIED WITH `button` BECAUSE #runWaivers IS TWO DIFFERENT ELEMENTS (fixed 8/8/26). Lee:
   "why isn't it listing leagues for me on this page" — Team Emails showed no league list. The data was
   never missing: 689 rows and 84,855 characters of table were sitting in #data. This rule was hiding
   them, because `id='runWaivers'` is BOTH the WAIVERS tab button here AND the league <table> that
   commish.php emits in SIX places — fetchTeamEmails, fetchGuillotine, fetchWaivers, fetchSaveKeepers,
   fetchLapse and fetchOrphanValues. An unqualified #id selector cannot tell them apart, so a rule
   written to hide one button silently blanked the league list on six commissioner pages, and had been
   doing so since it went in between 17 and 20 July.
   Worst case is fetchWaivers(): the button and the table carry the SAME id IN ONE DOCUMENT, so no
   id-based selector can ever separate them — the element type is the only thing that can, which is why
   this is `button#` and not something cleverer. The other five ids in this block resolve to exactly one
   <button> each (checked), but they are qualified too: it costs nothing and the next id reused as a
   table would reintroduce this bug in a form nobody would look for.
   The real defect is the duplicate id in commish.php; that is Lee's file, so this is the safe half of
   the fix and the CSS no longer depends on ids being unique. - Claude 8/8/26 */
#main button#specialFunctions,
#main button#runWaivers,
#main button#lockTeams               { display:none; }
/*   - the "MY MESSAGES" / "SEND MESSAGE" tab buttons on the Send A Message page
       (views/sendmessage.php:2-3; #myMessagesButton / #sendMessageButton). SEND NOW (#sendMessage)
       is intentionally left in place. */
#main button#myMessagesButton,
#main button#sendMessageButton        { display:none; }
/* Miscellaneous Interfaces page (mfl.php): no button here is a "primary" action, so the accent-
   filled .on data-source toggle buttons should NOT read as primary. Neutralise their fill on this
   page only (kept bold as a subtle active cue; the off buttons already look muted). Scoped via
   #scoringAudit, which is unique to this page. - Claude 7/18/26 */
#main:has(#scoringAudit) button.on   { background-color:var(--btn-surface, var(--btn-bg));
                                       color:var(--btn-ink, var(--btn-text));
                                       border-color:var(--btn-line, var(--btn-border)); }

/* marketplace PROMO BANNERS (flash-sale / bonus / auction / package / free-year): hardcoded
   gold #FFCC33 with a teal border and black text. Per the "gold" plan, promo -> ATTENTION.
   Solid attention fill + white text; the flash-sale comic burst (white bg) and the light
   free-burst both pop on the vermilion. */
#main div#showBonusAmountParagraph,
#main div#showSaleAmountParagraph,
#main div#showAuctionParagraph,
#main div#showPackageParagraph,
#main div#showFreeYearParagraph      { background:var(--attention); color:var(--attention-text);
                                       border-color:var(--attention); }

/* checked checkboxes/radios use the browser's default blue — hand them the skin accent */
input[type="checkbox"], input[type="radio"] { accent-color:var(--accent); }

/* rules.php entries are <a class="openHead"><h2>…</h2></a> — the h2's own color
   (#003333, desktop.css "= Headings") overrides the anchor, so the link color never shows
   and it goes near-black on a dark panel. Let a heading INSIDE a link take the link color.

   HEADINGS ARE NOT AN ACCENT (Lee 8/3/26). They used to take --heading, an accent mix, so
   every h1/h2 on the page carried the skin color — on the Rulebook that put the title, the
   category heads AND the clickable rule heads all in teal, and the only thing that separated
   a link from a label was a shade. Headings now take --ink: near-black on a light panel,
   near-white on a dark one, in every palette and mode. Color on this page now means
   "clickable" and nothing else. h3/h4 are listed too — the base sheets don't color them
   today, but the rule should hold if they ever do. - Claude 8/3/26 */
#main h1, #main h2,
#main h3, #main h4                   { color:var(--ink); }
#main a h1, #main a h2,
#main a h3, #main a h4               { color:var(--link, var(--accent)); }
/* mobile.css:764 pins the FAQ page's h2 to a hardcoded pale teal (#8DC7BB) at 2 ids, which outranks
   the rule above. It was 1.9:1 on the white panel even before this change. - Claude 8/3/26 */
#main #question h2                   { color:var(--ink); }

/* The body revealed when you OPEN a rule (and the article / player-news bodies) is
   hardcoded to DataForce's pale teal #E7F3F1 with a #4A9586 border, so it stays a light
   mint box on a dark page and never follows the skin. Those two values ARE DataForce's
   --panel-bg/--panel-border, so tokenizing leaves Classic pixel-identical. */
#main div.openDiv                    { background-color:var(--panel-bg); border-color:var(--panel-border); color:var(--ink); }
#main #articles div.openDiv          { color:var(--ink); }
#main #myPlayerNews div.openDiv      { background-color:var(--panel-bg); border-color:var(--panel-border); color:var(--ink); }
/* openHead hover was a hardcoded green (#59955C) regardless of skin */
#main a.openHead:hover h2,
#main a.openHead:focus h2            { color:var(--link, var(--accent)); filter:brightness(1.15); }

/* LINKS ON A LIGHT SURFACE, CLASSIC. modes.css defines --link for Light and Dark only, so in Classic
   it is unset and every consumer falls back — and the right fallback depends on the SURFACE. The
   containers below all paint themselves var(--content-bg), the light panel: #main, #searchDisplay
   (desktop.css:263), #home #newAtDF (650) and #footer #homeFooter (146). #home is a SIBLING of #main
   in index.php, which is why this is a list of surfaces and not just "#main".
   Links on the DARK page chrome are deliberately absent — #title, #signInOut and #footer's admin /
   permalink rows read `var(--link, <lifted>)` or --link-page and must keep the lifted color.

   THIS LIVES HERE, NOT IN A SKIN FILE (Lee 8/3/26, asking why DataForce had it and nobody else did —
   it shouldn't have). The rule is about surfaces, which every skin shares; only the VALUE is per-skin,
   and that is --link-light in each skin file. Behaviour in the shared sheet, values in the palette.
   :not(.dfx-mobile) — mobile Classic puts #main on a DARK box, and the light tables inside it have
   their own block below. :not(.dfx-darkpanel) — Onyx's Classic panel really is dark (skin.php marks
   it), so darkening its links would push them into the background, which is the exact bug this rule
   exists to prevent. - Claude 8/3/26 */
body[data-theme="classic"]:not(.dfx-mobile):not(.dfx-darkpanel) #main,
body[data-theme="classic"]:not(.dfx-mobile):not(.dfx-darkpanel) #searchDisplay,
body[data-theme="classic"]:not(.dfx-mobile):not(.dfx-darkpanel) #home #newAtDF,
body[data-theme="classic"]:not(.dfx-mobile):not(.dfx-darkpanel) #footer #homeFooter
                                    { --link:var(--link-light, color-mix(in srgb, var(--accent) 85%, black)); }

/* links (desktop.css hardcodes #4A9586) */
#main a, #main a:visited            { color: var(--link, var(--accent)); }
/* HOVER (Lee 8/3/26: "so subtle that I can't see it"). It was brightness(1.12), which on a light
   panel LIGHTENS the link — it moves toward the background, so the one state that should be
   loudest was the lowest-contrast one, and 12% of a mid-teal is barely a shade anyway. The fix is a
   filter that moves the link AWAY from its surface instead of toward it: --link-hover darkens on a
   light panel, lightens on a dark one.
   An underline was added alongside it and REMOVED the same day (Lee) — color carries the hover on
   its own now, which is the point of reversing the direction. Do not reintroduce it without asking:
   it was rejected on looks, not on effectiveness. - Claude 8/3/26 */
#main a:hover, #main a:focus        { color: var(--link, var(--accent)); filter: var(--link-hover, brightness(.62)); }
#topMessage a,
#searchDisplay a:hover, #searchDisplay a:focus { color: var(--link, var(--accent)); }

/* "red" text in the lobby (BETA MODE, "417 leagues filled", live-draft date, "Hey Lee!",
   "10 of 12" in Members) is an ATTENTION cue, not a danger/destructive one — so it takes
   --attention. NOTE: my earlier `.red` rule (0,1,0) never applied: desktop.css uses
   `#main span.red` (1,1,2) and kept winning. These match its specificity. */
#main p.red, #main span.red, #main td.red,
#main p#hostMessage, span#invite,
#main table#leagues td span.red      { color:var(--attention-ink, var(--attention)); }
/* Block attention messages (p.red — "Waivers are closed…" and similar on Free-Agent/Draft/Nominate) had a
   hardcoded WHITE background (mobile.css:513) that clashes with the skin (Lee 7/25). Replace it with a
   subtle, mode-aware attention-tinted callout (over --content-bg, so light in Light mode / dark in Dark)
   with a soft attention border. Inline span.red is untouched (no background here). - Claude 7/25/26 */
#main p.red {
  background:color-mix(in srgb, var(--attention) 14%, var(--content-bg)) !important;
  border:1px solid color-mix(in srgb, var(--attention) 40%, transparent) !important;
  border-radius:6px;
  /* This is a BOX now, not a line of text, so it needs breathing room — desktop.css gives p.red no
     padding at all (mobile.css gives .5em), so the message sat flush against the border it just
     gained (Lee 7/28). Wider than tall: a one-line callout reads better with the text inset from the
     ends than centred in a tall band. Set here rather than in the base sheets because the box is a
     skin-layer construct — if this rule ever goes, its padding should go with it. - Claude 7/28/26 */
  padding:.5em .8em;
}

/* ...except the Prize Boost copy, which should be BOLD ONLY (no color): the legend
   "Prize Boosts" and the "Prize boost: $24/$48/$72" chips are both span.red too. */
#main td.white span.red,
#main a.bonusMoney span.red          { color:inherit; font-weight:bold; }
/* ...and except the LEGEND's "Prize Boosts", which Lee wants back in the emphasis color (8/3/26).
   Splitting it off rather than reopening the rule above, because that rule is doing two jobs: this
   phrase, and the "Prize boost: $24/$48/$72" chips inside a.bonusMoney. The chips sit ON a colored
   money link and stay bold-only — coloring those would be a different decision and was not asked for.
   Same expression as the other attention text in the lobby, so it deepens on a light panel and lifts
   on a dark one instead of being one fixed red: 4.38:1 in Classic, 6.33 in Light, 6.61 in Dark.
   Classic is a shade under AA for 13px bold; it is the palette's own --attention on the palette's own
   panel, so tightening it belongs to the token, not to this one phrase. Stays bold via the rule above. */
#main table#legend td.white span.red { color:var(--attention-ink, var(--attention)); }

/* ============ LOCKER ROOM on MOBILE: the selected control was invisible in Classic ============
   mobile.css marks the chosen option with `outline:3px solid #333` drawn OUTSIDE the button, and
   flips it to #f2f2f2 under body.dfx-darkpanel. Its comment names the two dark cases it knew about —
   Night Game, and Classic on a dark skin (Onyx) — and misses the third: MOBILE Classic puts #main on
   a dark box for EVERY skin. dfx-darkpanel is not set there (skin.php only raises it for dark mode or
   a dark skin), so the outline stayed #333 and was drawn on #161b22: 1.37:1, i.e. nothing. Measured
   on the page, not inferred. #f2f2f2 on that panel is 15.45:1.
   That outline was carrying the whole cue, because the two button states are otherwise almost
   identical — selected #dadde1 against unselected #d7e2e0 is 1.03:1, which is why Lee read it as
   "very light, indistinguishable" rather than as missing.
   Scoped to dfx-mobile + Classic, so desktop (light panel, #333 correct) and mobile Day/Night (already
   handled) are untouched. Onyx also matches here and lands on the same #f2f2f2 it already had.
   - Claude 8/3/26 */
body.dfx-mobile[data-theme="classic"] #main #lrControls button.lrOn { outline-color:#f2f2f2; }

/* ===================== LOCKER ROOM: the preview panel's dead band ======================
   The Wonk is the last item in .lrActions, and desktop.css gives him `margin-left:auto;
   margin-right:72px` plus `position:relative; top:-0.5in`. Between the auto margin and the 72px
   inset he no longer fits on the buttons' line, so the wrapping flex row opens a SECOND line for
   him — and then top:-0.5in lifts him visually back up beside the buttons, leaving the line he
   came from standing empty. That empty line is the gap above the "Hey!" band (Lee 8/3/26).
   -48px is exactly the 0.5in he is lifted by, so the layout gives back precisely the space he
   vacated: measured on the page, the row goes 102px -> 54px, the emphasis band moves up 48px, and
   his own rendered position does not shift by a single pixel. Left in the skin sheet rather than
   changing desktop.css, whose positioning is deliberate and Lee's. - Claude 8/3/26 */
#main div.lrPanel .lrActions .wonk-oval { margin-bottom:-48px; }

/* ===================== LOCKER ROOM: "Your color scheme" ================================
   Every color the SKIN itself declares, with a name you can quote back at me. Values are not
   printed from the stylesheet — lockerroom.js resolves each token through a probe that inherits
   from <body> and reads the computed result, so the list follows the live preview and the Lighting
   mode instead of showing what the palette file happens to say. Sits on --content-bg (the page
   white) as Lee asked, which also makes it the one place you can see the pale tokens at all.
   - Claude 8/3/26 */
#main div.lrScheme                   { background:var(--content-bg); color:var(--ink);
                                       border:1px solid var(--panel-border);
                                       border-radius:var(--radius,6px); padding:1em; margin-top:1em; }
#main div.lrScheme .lrSchemeTitle    { font-weight:bold; font-size:1.15em; margin:0 0 .6em; }
/* section heads. A rule under each keeps the two groups from reading as one long list when the grid
   happens to fill its last row exactly. - Claude 8/3/26 */
#main div.lrScheme .lrSchemeGroup    { font-weight:bold; font-size:.9em; margin:0 0 .5em;
                                       padding-bottom:.25em;
                                       border-bottom:1px solid color-mix(in srgb, var(--ink) 20%, transparent); }
#main div.lrScheme .lrSchemeGrid + .lrSchemeGroup { margin-top:1.1em; }
#main div.lrScheme .lrSchemeGrid     { display:grid; gap:.4em .9em;
                                       grid-template-columns:repeat(auto-fill, minmax(215px, 1fr)); }
#main div.lrScheme .lrSwatch         { display:flex; align-items:center; gap:.5em; min-width:0; }
#main div.lrScheme .lrChip           { width:20px; height:20px; flex:0 0 auto; border-radius:4px;
                                       border:1px solid color-mix(in srgb, var(--ink) 35%, transparent); }
#main div.lrScheme .lrName           { font-weight:bold; flex:1 1 auto;
                                       overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#main div.lrScheme .lrHex            { flex:0 0 auto; font-family:ui-monospace,Consolas,monospace;
                                       font-size:.8em; padding:1px 5px; border-radius:3px;
                                       border:1px solid color-mix(in srgb, var(--ink) 25%, transparent);
                                       background:color-mix(in srgb, var(--ink) 6%, var(--content-bg)); }

/* lobby legend glyphs (friends / crown / beginner) are dark line-art PNGs, so they vanish
   on a dark page. brightness(0) invert(1) forces any glyph to solid white regardless of
   its original color; unset in Classic/Light so they stay as-is. */
#main img[src*="friends.png"],
#main img[src*="blackcrown.png"],
#main img[src*="beginner.png"],
#main img[src*="tooltip.png"]        { filter:var(--glyph-filter, none); }

/* The download button (bread.php:1997 + adp/adp2/faq/remoteserver — ~6 pages) is a TWO-TONE
   glyph: black rounded square with a white arrow. --glyph-filter would flatten it to a solid
   white blob and lose the arrow, so it needs a true invert instead — black<->white swaps and
   the arrow survives, giving the white-on-dark version. Not scoped to #main: it also renders
   in the breadcrumb bar. */
img[src*="download.png"]             { filter:var(--glyph-invert, none); }

/* Reorder arrows (smallup.png / smalldown.png = a white arrow on a black square; the a.move links on the
   Draft, Waivers, Free-Agent/Add-Drop and Nominate lists — and the desktop versions). They're white-on-
   black in every mode, so the black button blends into the dark page. In DARK mode INVERT them (black
   arrow on white) so they stand out; and round the corners slightly for a softer look in every mode.
   Targeted by src, so it fixes desktop too (Lee 7/25). - Claude 7/25/26 */
#main img[src*="smallup.png"], #main img[src*="smalldown.png"]                       { border-radius:2px; }
body.dfx-darkpanel #main img[src*="smallup.png"],
body.dfx-darkpanel #main img[src*="smalldown.png"]                              { filter:invert(1); }

/* input + button variants not covered by skin-base */
#main input.buttonEntry             { background: var(--btn-bg); color: var(--btn-text); }
#main input.bids, #main input.deposits, #main input.tfd
                                    { background: var(--input-bg); border-color: var(--input-border); color: var(--input-text); }
#home button, #searchDisplay button { background: var(--btn-bg); color: var(--btn-text); border-color: var(--btn-border); }

/* misc accented borders / icons / titles */
#main img.icon, #main img.support   { border-color: var(--panel-border); background: var(--input-bg); }
#main a.hovering,
.opaque-big:hover, .opaque-big.hover { border-color: var(--accent); }

/* home-page boxes: the "off" (pre-hover) title/label are fixed greys (#bbb / #aaa). Those
   read fine on the DARK classic page but nearly vanish on the light one. Derive them from
   --page-text (muted toward the page) so they stay legible in every mode — still clearly
   "off", just readable. Hover already switches to --accent. These sit on the transparent
   #home, i.e. directly on the page, so --page-text is the right reference. */
.opaque span.opaque-header           { color:color-mix(in srgb, var(--page-text) 72%, var(--page-bottom)); }
.opaque span.opaque-label            { color:color-mix(in srgb, var(--page-text) 58%, var(--page-bottom)); }
/* On the dark hero (dfx-herobg — the home/splash), these cards carry a DARK background image, so the
   72%/58% muting drags the label (and to a lesser degree the header) toward the dark --page-bottom and
   reads dark-on-dark in Google's rendered view (Lee 8/13). Lift the --page-text share on the hero only.
   Direction still follows the skin: --page-text resolves LIGHT on the dark hero, DARK on a light card,
   so this raises contrast the right way in both. Light skins off the hero keep the softer look. - Claude 8/13/26 */
body.dfx-herobg .opaque span.opaque-header { color:color-mix(in srgb, var(--page-text) 90%, var(--page-bottom)); }
body.dfx-herobg .opaque span.opaque-label  { color:color-mix(in srgb, var(--page-text) 80%, var(--page-bottom)); }
.opaque:hover span.opaque-header, .opaque.hover span.opaque-header { }
/* CRAWLER-ONLY reveal. crawlerRender() (server-side bot render) tags each .opaque card .dfx-reveal;
   humans NEVER get this class, so their tap-to-reveal is completely unchanged. Mirror the :hover state
   so a bot sees the copy (create-account help + buttons) instead of an empty "Touch Me" teaser, and
   lighten the unfolded content on the dark hero the same way the header/label are. - Claude 8/13/26 */
.opaque.dfx-reveal div.opaque-content,
.opaque.dfx-reveal span,
.opaque.dfx-reveal table,
.opaque.dfx-reveal button,
.opaque.dfx-reveal a           { opacity:1; }
.opaque.dfx-reveal span.opaque-label { opacity:0; }
body.dfx-herobg .opaque.dfx-reveal div.opaque-content { color:var(--page-text); }
#footer div#homeFooter              { border-color: var(--panel-border); }
table#orphanRescue tr.rescueTitle td { }

/* company affiliate banner (Apex / SBFFC). desktop.css positions it only via
   body#body2|body#body4 — which the harness strips — so preserve its LAYOUT here
   id-independently and theme its color from the skin. Harmless on pages without it. */
#dataForceBanner {
  position:absolute; left:0; top:0; width:100%; z-index:5;
  padding:15px; text-align:center; font-style:italic;
  background:var(--accent-bg, #23819C); color:#fff;
}

/* lobby section dividers ("Upcoming Live Drafts", "All Other Contests"): desktop.css:730
   hardcodes #D6C485 (tan) with no body#bodyN prefix, so it's identical on every skin and
   mode. Other companies already override it to their own subHeader tone (body1 -> #a9c5eb),
   so the intended role is a mid-light banded header => --header-bg keeps that exact visual
   weight while taking the skin's color instead of the tan. */
#main tr.divider td                  { background-color:var(--header-bg); color:var(--header-text); }
/* …but in the LOBBY that left two different things wearing one color: the section titles ("Upcoming
   Live Drafts", "All Other Contests") and the column-label row above them were both --header-bg, so
   the table read as one flat band with no hierarchy. The Locker Room sample is the shape Lee wants
   (8/3/26) and it has three tiers, so the lobby now gets the same three:
       section title  -> tr.divider    = --major-bg / --major-text   (the solid band)
       column labels  -> tr#leagueHeader.header = --header-bg / --header-text  (already correct)
       league rows    -> tr.detail     = --row-bg, with MFL tinted (see the HOST block above)
   Scoped to table#leagues, which is the lobby AND My Leagues — they share this markup and the same
   legend, so they should not diverge. tr.divider on any other page keeps the rule above. */
/* REPLACED 8/4/26 (Lee). The band is no longer a strong tier — it is a LIGHT shade of the palette's
   third brand hue (--divider, see any palette file) carrying black print. Two things were wrong with
   --major-bg here: on Tailgate and Apex it sat dE 4.4 from the column-label row above it, so the table
   read as one flat band, and its own white-on-#97af65 text measured 2.29:1. The new band measures
   12.0-12.8:1 for black print on every palette, and at least dE 8 from BOTH neighbours.
   Black is a literal, not var(--ink): --ink follows the mode and goes light in Night Game, but this band
   is deliberately light in every mode, so its print has to stay dark. --divider-text exists in
   skin-base for anyone who needs to override it. - Claude 8/4/26 */
#main table#leagues tr.divider td    { background-color:var(--divider, var(--major-bg));
                                       color:var(--divider-text, #111111); }

/* ============================== BREADCRUMBS ==================================
   The FINAL crumb (td#box3) is the current page — NOT a link — but desktop.css colors it
   #23819C, a link-ish teal that both ignores the skin and misreads as clickable next to the
   real link. Mute it toward the panel so it reads as "you are here" in every skin/mode. */
#main table#crumbs td#box3           { color:color-mix(in srgb, var(--ink) 68%, var(--content-bg)); }
/* the league picker in the same bar is hardcoded black/#8DC7BB/#4A9586 — a black box on a
   light panel in Light mode. Same component, same class of bug. */
#main table#crumbs td#box2 select    { background-color:var(--input-bg); color:var(--input-text);
                                       border-color:var(--input-border); }
#main table#crumbs td#box2 select:hover,
#main table#crumbs td#box2 select:focus { background-color:var(--input-bg); filter:brightness(1.08); }

/* ============================== PRIZES PAGE ==================================
   table.prizes wraps a grid of inner table.data blocks, all hardcoded to the DataForce
   palette (#4A9586 borders, #E7F3F1 cells, #8DC7BB section heads). The cells set a
   background but NO text color, so the text inherits --ink — which flips light in Dark
   mode and lands light-on-pale-teal. Tokenizing gives both a readable pairing per mode;
   the classic values are identical to --panel-border/--row-bg/--header-bg so Classic
   doesn't move. */
#main table.prizes                   { border-color:var(--panel-border); }
#main table.prizes td.heading        { border-color:var(--panel-border); }
#main table.prizes td.majorHeader    { color:var(--major-text); }
#main table.data                     { border-color:var(--panel-border); }
#main table.data td                  { color:var(--ink); }
#main table.data td.section          { color:var(--header-text); }

/* LEVEL SWATCHES — backgrounds deliberately left alone (they're the level identity).
   Only the TEXT is pinned: it was inheriting --ink, so in Dark mode the labels went light
   on light swatches (Tin Baby #DEDEDE, gold #FFDF00, diamond #8CEFFD) and vanished.
   #111 is exactly classic's --ink, so Classic is unchanged and every mode stays readable.
   Blackdiamond keeps its own gold-on-black pairing. */
#main tr.allLeagues td.twinbaby,
#main tr.allLeagues td.tinbaby,
#main tr.allLeagues td.bronze,
#main tr.allLeagues td.gold,
#main tr.allLeagues td.silver,
#main tr.allLeagues td.platinum,
#main tr.allLeagues td.diamond,
#main tr.allLeagues td.ruby          { color:#111; }
/* COPPER is the one DARK swatch, so it is the one that needs the opposite pairing. It sat in the list
   above taking #111 on #856341 — 3.47:1, under AA — because that list was written to fix light
   swatches vanishing in Dark mode and nobody measured the dark one. White scores 5.44:1.
   (Measured, not guessed: of the nine swatches copper is the only one where white beats dark.)
   - Claude 7/28/26 */
#main tr.allLeagues td.copper        { color:#ffffff; }
#main tr.allLeagues td.blackdiamond  { color:#FFCC00; }

/* jquery.highlight.js search matches (span.highlight, e.g. Rules-page search) are hardcoded
   background:yellow (desktop.css:353, mobile.css:709) — jarring and skin-blind, and the bright
   yellow clashes in dark mode. Use the --attention token instead: it's a self-contained fill+text
   pairing (orange #d14a1f + white), so a match reads clearly on EVERY skin and in light OR dark,
   independent of the page/panel color. Small radius so it reads as a marker. - Claude 7/25/26 */
.highlight { background-color:var(--attention) !important; color:var(--attention-text,#fff) !important; border-radius:2px; padding:0 1px; }

/* RULES-TAB info tables — Scoring Rules (table.scoring), Weekly Schedule (table#schedule) and Lineup
   Options (table.ruleGrid, a class added to its otherwise-bare <table>s). Same two bugs, one fix,
   DESKTOP + MOBILE, every mode:
   (1) No cell borders anywhere (desktop.css only sets column widths, mobile.css nothing), so with just
       skin-base's row backgrounds the near-white Light-mode header/rows blended into a borderless block.
   (2) mobile.css:535/539 hardcode tr.header/tr.detail text to #000; skin-base flips the background dark
       in Dark mode but that black text stayed -> black-on-dark, unreadable (desktop has no such hardcode,
       so it read fine). Restate the token text pairing at a specificity that beats mobile.css, and add a
       themed grid. - Claude 7/25/26 */
/* table.centerMe = the mobile hub's League/Message alarms table (core.php:4036 adds the class only on
   mobile; desktop is a bare table). Same bare-table symptoms as the rules pages, so give it the same
   grid treatment. - Claude 7/25/26
   CORRECTED 7/26 (Lee: the mobile LIVE DRAFT grew borders it never had). `.centerMe` is a CENTERING
   class, not a grid marker — ~20 tables carry it and only ONE is a data grid: the alarms table. All the
   others are layout (the Wonk's #wonkTable / #siteButtons / #wonkSlider, and #topTicker on
   mobilelive.php, which is what Lee saw). Bordering every .centerMe cell was wrong in principle, and it
   had already drawn blood once — the body.dfx-wonk exemption further down this file was the first
   patch. So gate the grid on the table ACTUALLY being one: the alarms table is the only .centerMe with
   tr.header / tr.detail rows; every layout one uses bare <tr>. Child combinators keep the :has() cheap
   and stop a nested table's rows from qualifying the outer one. - Claude 7/26/26 */
#main table.scoring, #main table#schedule, #main table.ruleGrid                                                   { border-collapse:collapse; }
#main table.scoring td, #main table#schedule td, #main table.ruleGrid td                                             { border:1px solid var(--panel-border, var(--accent)); }
#main table.centerMe:has(> tbody > tr.header, > tbody > tr.detail)                                                { border-collapse:collapse; }
#main table.centerMe:has(> tbody > tr.header, > tbody > tr.detail) td                                                { border:1px solid var(--panel-border, var(--accent)); }
#main table.scoring tr.header td, #main table#schedule tr.header td, #main table.ruleGrid tr.header td, #main table.centerMe tr.header td { background:var(--header-bg); color:var(--header-text); }
#main table.scoring tr.detail td, #main table#schedule tr.detail td, #main table.ruleGrid tr.detail td, #main table.centerMe tr.detail td { background:var(--row-bg);    color:var(--ink); }

/* MOBILE TEAM-MANAGER HUB team cards (table#appLeagues td.appTeam). Each card is a little table:
   td.roundtop (level-colored band, rounded top) + td.appPic (pink #ECD5CA helmet panel) + td.roundbottom
   /div.appLeague (the level+lineup band, e.g. "Dynasty Deep", rounded bottom) + the team name/status below.
   (2) The bottom band carried a tan-marble TEXTURE in production; that image was dropped for the skins work
       (Lee doesn't want the marble back) — but the band went transparent, so the card looked unclosed
       ("bottom missing"). Refill it with a light, warm, skin-TINTED fill (sandstone base + a touch of
       --accent) so the card closes with its rounded bottom exactly like production. Kept light in every
       mode (dark text) to match the always-light helmet panel above it.
   (3) span.teamStatus is hardcoded #F8F8F8 (near-white) — invisible on the light page in Light mode. Hand
       it the mode-aware --ink so it reads in every mode (it sits on the page surface, not the card). - Claude 7/25/26 */
body.dfx-mobile #main span.teamStatus { color:var(--ink) !important; }
body.dfx-mobile #main table#appLeagues td.roundbottom,
body.dfx-mobile #main table#appLeagues div.appLeague {
  background-color:color-mix(in srgb, var(--accent) 20%, #e8dfd2) !important;
  color:#1b222a !important;
  border-radius:0 0 8px 8px;
}
/* CLASSIC mode: the mobile hub #main box is DARK (#161b22), so the team-card text below the cards must be
   LIGHT (Light/Dark modes already handle this via --ink / the mode --link rules).
   (1) team-name links fell through to the classic TABLE-link rule (--accent-bg, which is tuned for the
       light contest cells) -> too dark on the dark box. Use the lifted --link (the same lightened shade
       Dark mode uses) so they read light, on-brand.
   (2) span.teamStatus was pinned to --ink, which stays #111 in Classic -> dark-on-dark, unreadable. Use
       --page-text, the light on-box text Classic already gives #main. - Claude 7/25/26 */
body.dfx-mobile[data-theme="classic"] #main a.appTeamName   { color:var(--link) !important; }
/* table#preferences (Setup) is the same case: it is fully TRANSPARENT in Classic, so its links sit on
   the dark box, but the Classic table-link rule hands them --accent-bg, which is tuned for the LIGHT
   contest cells. Gridiron made that #475569 on #161b22 = 2.28:1. Use the lifted --link, exactly as
   a.appTeamName above.
   NOTE the general shape of this whole family of bugs: a rule picks a token for "tables", but whether
   a given cell is light or dark is not something the selector can know. Each transparent-on-dark table
   has to be named. The contrast scanner (claude/contrastscan.js) is how they get found — they are
   invisible, so reading the page will not surface them. - Claude 7/28/26 */
body.dfx-mobile[data-theme="classic"] #main table#preferences a { color:var(--link) !important; }
body.dfx-mobile[data-theme="classic"] #main span.teamStatus { color:var(--page-text) !important; }
/* ...but the lifted --attention-ink that mobile Classic sets for its DARK #main box is wrong INSIDE the
   light table cells that sit on that box. "Select keepers" came out #f2814d on a --row-bg of #f8fafc:
   2.5:1. Custom properties inherit, so re-setting the token on the light row surfaces hands their
   descendants the deepened variant without touching anything on the dark box. Same fix shape as the
   league-level pairing: the token follows the SURFACE, not the page. - Claude 7/28/26 */
/* MOBILE CLASSIC puts its #main box on a DARK surface, so --link and --attention-ink are lifted to
   read on it. But the TABLE CELLS sitting on that box are LIGHT (--row-bg is light in Classic for
   every palette except Onyx, which is a dark-content skin by design), and the lifted variants are far
   too pale there: "Select keepers" 2.5:1, the bracketed projections 2.03:1, the (Q) status flags
   2.5:1. Custom properties inherit, so re-declaring both tokens on the cell hands every descendant
   the DEEP variants — the same expressions modes.css Light uses — without disturbing anything drawn
   directly on the dark box. Onyx is excluded because its cells really are dark.
   Scoped to the row types that actually CARRY a surface (--row-bg / --header-bg). It was briefly
   widened to every td, which was wrong: transparent cells sit on the DARK box and need the
   lifted variants, so the blanket rule pushed the Setup and Player News links back under AA.
   The token has to follow the SURFACE, and only these rows have one. - Claude 7/28/26 */
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table.rounded:not(#options),
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#news,
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#injuries {
  --attention-ink:#a83c12;
  --link:var(--link-light, color-mix(in srgb, var(--accent) 85%, black));	/* per-skin on-light link - Claude 8/3/26 */
  /* the base ink too. Making these tables opaque without this just inverts the bug: text that used to
     inherit the light --page-text from the dark box now sits on a LIGHT table at 1.21:1. NOT
     !important, so any cell that sets its own color still wins. - Claude 7/28/26 */
  color:var(--ink);
}

/* Tips & Tricks "The Wonk" (images/wonk1.jpg) is a JPG, so its white background is baked in and reads
   as a hard white rectangle — especially jarring on the dark page. Clip it to an OVAL cameo (Lee) with
   a themed frame; the ellipse trims only the white corners, leaving the character. Affects desktop too. - Claude 7/25/26 */
/* $= exact file so this hits the Tips page's wonk1.jpg ONLY, not the Wonk-button wonk1-t.png (Lee 7/25) */
#main img[src$="wonk1.jpg"] { border-radius:50%; background:#fff; padding:6px; box-sizing:content-box;
                          border:2px solid var(--panel-border, var(--accent)); box-shadow:var(--shadow, 0 2px 6px rgba(0,0,0,.35)); }
/* ...but NOT inside the Wonk page's tiny tab buttons. The note above says the $= selector hits the Tips
   page only; that was never true — wonk.php's four tabs use the same wonk1.jpg — it just never showed,
   because wonk.php did not load the skin layer until 8/4/26. fullpage.css:215 pins those buttons to a
   fixed width:60px, so the cameo's 6px padding + 2px border (+16px each way) pushed the label out of its
   box: "Waiver" wrapped, that one button grew to 52px tall while its siblings stayed 36, and the text
   spilled past the rounded edge. The cameo is decoration for a portrait, not for a 24px icon in a fixed
   box, so switch it off there rather than widening a button the base sheet owns. - Claude 8/4/26 */
#main button.tinyWonkIcon img[src$="wonk1.jpg"] { border-radius:0; background:none; padding:0; border:0;
                          box-shadow:none; box-sizing:border-box; }

/* Weekly-schedule "Date/Time" column (td.highlightColumn) is hardcoded #C0E0DA with NO text
   color, so in Dark the text inherits light --ink onto a pale teal cell. #C0E0DA is really
   DataForce's --row-bg with extra accent, so derive it the same way the DFF rows are done:
   Classic stays ~identical, every skin gets its own tint, and Dark gets a readable pairing. */
#main table#schedule td.highlightColumn
                                     { background-color:color-mix(in srgb, var(--accent) 22%, var(--row-bg));
                                       color:var(--ink); }

/* Roster / dashboard / standings / draft-board colored cells use HARDCODED light pastels
   (position colors QB..DE, team red/blue, start/bench, standings rows) with NO text color,
   so the text inherits --ink and goes WHITE-on-light in Dark mode -> unreadable. Those cells
   are ALWAYS light, so pin the text dark in every mode. (Headers keep their own white.) */
#main table.roster td.QB, #main table#roster td.QB, #main table#draftBoard td.QB,
#main table.roster td.RB, #main table#roster td.RB, #main table#draftBoard td.RB,
#main table.roster td.WR, #main table#roster td.WR, #main table#draftBoard td.WR,
#main table.roster td.TE, #main table#roster td.TE, #main table#draftBoard td.TE,
#main table.roster td.PK, #main table#roster td.PK, #main table#draftBoard td.PK,
#main table.roster td.DE, #main table#roster td.DE, #main table#draftBoard td.DE,
#main table.roster td.redTeam, #main table.roster td.blueTeam,
#main table.roster td.positionBoxRed, #main table.roster td.positionBoxBlue,
#main table#roster td.positionBoxRed, #main table#roster td.positionBoxBlue,
#main table.roster td.startPos, #main table.roster td.benchPos,
#main table.dashboard tr.startPos td, #main table.dashboard tr.benchPos td,
#main table.roster tr.highlighted td
                                     { color:#111; }

/* ⚠ `#main table.roster tr.standings td` WAS IN THE LIST ABOVE AND DOES NOT BELONG THERE — removed
   8/15/26 after a customer sent screenshots of the League Standings page unreadable on three
   different skins (Onyx, Primetime, Red Zone).
   The list above is "cells that are ALWAYS LIGHT". Standings rows are not: desktop.css:975 paints
   them `background-color:var(--row-bg)`, a SURFACE token that follows the mode — #161b22 in Night
   Game, #1e293b on Onyx in Classic. Pinning #111 on top of that measured **1.09:1** in Night Game
   and **1.29:1** on Onyx Classic. Letting the cells inherit --ink instead gives 14.64:1 and 11.82:1.
   Day Game is effectively unchanged (18.88:1 -> 16.04:1), which is why this is a straight removal
   rather than a dark-only override.
   THIS HIT EVERY PALETTE, not the three reported — any skin in Night Game, plus Onyx in Classic.
   ⚠ THE TRAP IS THAT `tr.standings` MEANS TWO DIFFERENT TABLES, and only one of them was ever fixed.
   play.php emits it in the Season Schedule (line 1157) AND on the League Standings page (2125/2135/
   2142). The Season Schedule's cells are ALL classed — weekBox / oppoBox / scoreBox / winnerBox —
   and lines 1274-1277 below already re-theme those four with `!important`, so the schedule never
   showed the bug and is untouched by this removal. The Standings page's cells are UNCLASSED
   (`<td nowrap>`, .center, .right, .visitData), so nothing overrode the pin and they stayed #111.
   That is also why rule (8) at ~line 2139 excludes `.standings` with the note "pinning them #111
   made them dark-on-dark (Lee 7/25)" — the same defect, found from the schedule side, fixed there
   with a `:not()` on an `!important` rule that could never reach this earlier non-important one.
   The level-9 red/blue team cells inside these rows ARE hardcoded light pastels and keep their own
   pin on the `td.redTeam, td.blueTeam` line above, so they are unaffected. - Claude 8/15/26 */

/* starter-slot labels (QB/RB/... on the dashboard's start rows) are a hardcoded light teal
   #C0E0DA. Derive from the accent so they match the skin — kept ALWAYS light (mix over white,
   not the surface) so the dark label text stays readable in every mode. ~34% ≈ today's teal
   on DataForce, and each other skin gets its own tint. */
#main table.dashboard tr.startPos td, #main table.roster td.startPos, #main table.roster tr.highlighted td
                                     { background-color:color-mix(in srgb, var(--accent) 34%, white); }
/* bench = a LIGHTER shade of the starter tint (declared AFTER startPos so bench cells that
   also carry the startPos class win). Pin the label text dark too, now that it's a light cell. */
#main table.roster td.benchPos, #main table.dashboard tr.detail td
                                     { background-color:color-mix(in srgb, var(--accent) 14%, white); color:#111; }

/* rosters-page player names are LINKS inside the light position cells; keep them DARK in every
   mode (--link lightens in Dark -> unreadable on the pastels). */
#main table.roster td.QB a, #main table.roster td.RB a, #main table.roster td.WR a,
#main table.roster td.TE a, #main table.roster td.PK a, #main table.roster td.DE a,
#main table#roster td.QB a, #main table#roster td.RB a, #main table#roster td.WR a,
#main table#roster td.TE a, #main table#roster td.PK a, #main table#roster td.DE a
                                     { color:#111; }

/* SEASON SCHEDULE / results tables (team-name header + weekly opponent rows) were hardcoded #4A9586
   header / #8DC7BB number / #E7F3F1 cells. Make them proper skin SURFACES so they follow the skin AND
   dark mode. Targeted at the schedule cell classes, so the standings red/blue-team cells (which stay
   light with dark text) are untouched. NOTE: the Season Schedule table is table.rosterS (plural), the
   roster page is table.roster (singular) — cover BOTH or the schedule falls back to #111 dark-on-dark
   in Dark mode (Lee 7/25). - Claude 7/25/26 */
#main table.roster td, #main table.rosters td                          { border-color:var(--panel-border); }
#main table.roster tr.header td, #main table.rosters tr.header td       { background-color:var(--major-bg); color:var(--major-text); }
#main table.roster tr.standings td.oppoBox,  #main table.rosters tr.standings td.oppoBox,
#main table.roster tr.standings td.scoreBox, #main table.rosters tr.standings td.scoreBox    { background-color:var(--row-bg) !important; color:var(--ink) !important; }
#main table.roster tr.standings td.weekBox,  #main table.rosters tr.standings td.weekBox,
#main table.roster tr.standings td.winnerBox, #main table.rosters tr.standings td.winnerBox  { background-color:var(--header-bg) !important; color:var(--header-text) !important; }

/* Sortable column headers (Player Listing / Fantasy Points) are <th> in tr.header. skin-base
   only tokenised header <td>, so these stayed the hardcoded #8DC7BB teal. Cover the <th>
   headers, the default sort-label color (was #003333, unreadable on a dark header), the
   hover cue, and the active-sort state. */
#main tr.header th, #main tr.subHeader th, #main tr.safeGuardHeader th
                                     { background-color:var(--header-bg); color:var(--header-text); }
#main table.sortable th[data-sort]   { color:var(--header-text); }
#main table.sortable th[data-sort]:hover
                                     { }
#main table.sortable th[data-sort].sorting-asc,
#main table.sortable th[data-sort].sorting-desc
                                     { }
/* Player Listing body cells (#report table.sortable td) were hardcoded #000 -> dark-on-dark in Dark mode,
   and had no cell borders in any mode. Same recurring data-table pattern: token text + themed grid. - Claude 7/25/26 */
#main #report table.sortable td       { color:var(--ink) !important; }
#main #report table.sortable td,
#main #report table.sortable th       { border:1px solid var(--panel-border, var(--accent)) !important; }

/* Draft Boards left sidebar column (the vertical "Single Year / Startup / Rookie/Free Agent"
   labels) is td.header — a class on the cell, inside a normal row — NOT tr.header td, so
   skin-base's header tokenisation missed it and it kept the hardcoded #8DC7BB teal. Give it the
   same skin header surface as the Drafts/Auctions header row. - Claude 7/18/26 */
#main table#boards td.header         { background-color:var(--header-bg); color:var(--header-text); }

/* Trade tables — Trade Bait, Offer Trade, Trades Offered To Me, Trades I Am Offering, Trade History.
   The team-name+date header rows (tr.tradeHeader) and the notes/player rows (tr.tradeDetail /
   tr.tradeSubHeader) were hardcoded teal/#E7F3F1 and never followed the skin (7/18).
   THREE-BAND HIERARCHY, now applied to BOTH layouts (Lee 7/26 — was mobile-only):
     tr.tradeHeader    -> --major-bg / --major-text   (strong band; --major-bg is a BRAND token that
                          modes.css leaves alone, so it stays the skin color on a dark panel instead
                          of collapsing toward --row-bg the way --header-bg does)
     tr.tradeSubHeader -> --header-bg / --header-text (mid band)
     tr.tradeDetail    -> --row-bg / --ink            (pale rows)
   NOTE the two layouts started from DIFFERENT production colors, so this means different things:
     MOBILE  (mobile.css:673-675) really has 3 bands — #4A9586 white / #8DC7BB / #E7F3F1 — and the
             7/18 mapping had flattened them to 2. There this RESTORES production.
     DESKTOP (desktop.css:1038-1040) only ever had 2 — header #8DC7BB, sub-header AND detail both
             #E7F3F1 — so this is a deliberate visual CHANGE: the header deepens to the strong band
             and the sub-header gains the mid band it never had. Same fix Lee wanted for the same
             reason (sub-header was indistinguishable from the rows), but flagged because desktop
             production will look different, not merely more skin-aware. - Claude 7/26/26 */
#main tr.tradeHeader td              { background-color:var(--major-bg);
                                       color:var(--major-text-aa, var(--major-text)); }
#main tr.tradeSubHeader td           { background-color:var(--header-bg); color:var(--header-text); }
#main tr.tradeDetail td              { }
/* Classic gets its sub-band separation free from the skin's own palette (DataForce #8DC7BB over
   #E7F3F1 = 1.68x), but Light/Dark swap in modes.css's NEUTRAL surfaces, where --header-bg and
   --row-bg are near-identical (#eef1f5/#ffffff = 1.13x, #20272f/#161b22 = 1.15x) and the band
   disappears. Tint it with the skin accent in those two modes only, so Classic keeps exact
   production fidelity. Keyed to the MODE, not .dfx-darkpanel — this is about the neutral mode
   surfaces being too close together, not about the panel being dark. - Claude 7/26/26 */
body[data-theme="light"] #main tr.tradeSubHeader td,
body[data-theme="dark"]  #main tr.tradeSubHeader td
                                     { background-color:color-mix(in srgb, var(--accent) 24%, var(--header-bg)); }

/* ── MOBILE-ONLY frame + grid for table.tradeList ───────────────────────────────────────────────
   The three-band color mapping above is now SHARED with desktop; only the box treatment stays
   mobile-scoped. mobile.css pins `border:none` on all three row types and puts a lone hardcoded
   #4A9586 frame on the table (which clashed on non-teal skins), so mobile gets the same themed
   frame + separate-borders/rounded-corner recipe as table.mobileTable. Mobile rows are single-cell,
   so the grid is horizontal rules only. Corner radii are set explicitly because Trade History emits
   tr.tradeHeader WITHOUT class='rounded' — see the border-collapse note on table.mobileTable.
   NOT carried to desktop: those tables are 3-column and Lee has not asked for a grid there. - Claude 7/26/26 */
body.dfx-mobile #main table.tradeList {
  border-collapse:separate !important; border-spacing:0 !important;
  background-color:transparent !important;
  border:1px solid var(--panel-border, var(--accent)) !important;
  border-radius:6px;
}
body.dfx-mobile #main table.tradeList td   { border:0 !important;
                                             border-bottom:1px solid var(--panel-border, var(--accent)) !important; }
body.dfx-mobile #main table.tradeList > tbody > tr:last-child  > td              { border-bottom:0 !important; }
body.dfx-mobile #main table.tradeList > tbody > tr:first-child > td:first-child  { border-top-left-radius:6px; }
body.dfx-mobile #main table.tradeList > tbody > tr:first-child > td:last-child   { border-top-right-radius:6px; }
body.dfx-mobile #main table.tradeList > tbody > tr:last-child  > td:first-child  { border-bottom-left-radius:6px; }
body.dfx-mobile #main table.tradeList > tbody > tr:last-child  > td:last-child   { border-bottom-right-radius:6px; }

/* ── TRADE HISTORY status words (Lee 7/26) ──────────────────────────────────────────────────────
   Were fixed colors that ignored the skin: only two of the six were colored at all — "Approved"
   hardcoded CSS `green` (#008000) and "Under Review" as span.red — and the other four (Pending
   Approval / Overturned / Declined / Retracted) were plain text. Put them on the go/warn/stop
   palette, mapped by MEANING rather than blanket-red-for-negative:
     green  = Approved            (the good terminal state)
     amber  = Pending Approval, Under Review   (in flight — "Under Review" was red, which read as an
              error when it is really a caution)
     stop   = Overturned, Declined            (actively reversed or rejected)
     muted  = Retracted           (you withdrew it yourself — not a bad event, so no alarm color)
   trade.php keeps its original `red`/`green` classes ALONGSIDE the new semantic ones, so production
   is unchanged until Lee promotes the CSS. These are declared AFTER the span.red rule at the top of
   this file (same specificity) so the semantic color wins here. - Claude 7/26/26 */
#main span.statusApproved            { color:var(--dfx-go-ink); font-weight:bold; }
#main span.statusPending,
#main span.statusReview              { color:var(--dfx-warn-ink); font-weight:bold; }
#main span.statusOverturned,
#main span.statusDeclined            { color:var(--dfx-stop-ink); font-weight:bold; }
#main span.statusRetracted           { color:var(--ink); opacity:.7; font-weight:bold; }
/* "Agreed to Prepay" was span.red — it is a commitment, not a warning; make it an informational
   note on the complementary (aside/info) color instead of alarm red. */
#main span.statusPrepay              { color:var(--dfx-info-ink); font-weight:bold; }

/* ── TRADE BAR (table.balancer, built by tradecalculator.php) ────────────────────────────────────
   A 5-segment gauge: the lit cell moves from either end (lopsided) to the centre (fair). Colors were
   the CSS keywords red / yellow / green plus a #F5EFE0 cream for the unlit cells and a hardcoded
   #4A9586 teal border — none skin-aware, and pure `yellow` on a white panel is nearly unreadable.
   Move to the same go/warn/stop palette. The unlit cells become a neutral surface tone so the lit one
   carries all the signal. NOT scoped to mobile: the bar renders on the desktop trade pages (the mobile
   branches of loadRespond/loadMyOffers don't emit it), and Lee asked for the bar itself. - Claude 7/26/26 */
#main table.balancer td               { border-color:var(--panel-border, var(--accent)); }
/* ⚠ THE UNLIT CELLS ARE OPAQUE. They were `color-mix(--ink 14%, TRANSPARENT)` from 7/26 to 8/7/26, so
   the tint composited onto whatever sat behind the bar. That reasoning was half right and it produced a
   real bug (Lee 8/7/26: "the trade bar on the Approve Trades page is not the same color as it is on the
   Offer a Trade page").
   The bar has two homes: #balancerBox on the content panel, and — desktop only — INSIDE the
   tr.tradeHeader band. Translucent meant the SAME rule gave two different results:
       Offer a Trade   behind #fafefb  -> track #d9ddda   lit-vs-track 2.99   reads as a gauge
       Approve Trades  behind #4A9586  -> track #428376   lit-vs-track 1.08   reads as one green square
   The 7/26 note optimised for "is the track visible against the backdrop" and never checked "is the LIT
   cell visible against the track" — which is the only thing the gauge actually communicates. Measured
   across all 14 palettes, translucent bottoms out at 1.04 (onyx/Dark): the lit cell becomes invisible.
   Opaque instead. Worst lit-vs-track is now 2.99 Classic / 3.11 Light / 4.94 Dark.
   THE COST IS REAL AND IS ACCEPTED: on a dark band the opaque track can approach the band's own
   lightness (1.14 on midnight/Dark), so the track stops reading as a separate strip. That is the
   lesser failure — the cells keep their own border (the rule above), which carries the 5-segment
   structure, and the lit cell stays emphatic. Losing the track outline costs you the frame; losing the
   lit cell costs you the answer. - Claude 8/7/26 */
#main table.balancer td.grey          { background-color:color-mix(in srgb, var(--ink) 14%, var(--content-bg)); }
#main table.balancer td.green         { background-color:var(--dfx-go); }
#main table.balancer td.yellow        { background-color:var(--dfx-warn-fill); }
#main table.balancer td.red           { background-color:var(--dfx-stop-fill); }
/* the disabled-bar messages ("Trade bar disabled until…") ride in a td.grey — keep them readable */
#main table.balancer td.grey          { color:var(--ink); }

/* League Chat page: the message box (#chat) and emoji tray (#emoji) were #4A9586-bordered white
   (#F8F8F8) panels, and the entry field (#chatEntry) a #4A9586-bordered #E7F3F0 box with teal
   text — none followed the skin or dark mode. Move them onto skin surfaces (background-color only,
   so the football watermark image on #chat survives). Team-name color (span.team, #4A9586) ->
   mode-aware --link; the commish-red message color stays as its alert cue. - Claude 7/18/26 */
#main #chatRoom div#chat,
#main #chatRoom div#emoji             { background-color:var(--content-bg); border-color:var(--accent); color:var(--ink); }
#main #chatRoom div#chat span.team    { color:var(--link, var(--accent)); }
#main input#chatEntry                 { color:var(--input-text); border-color:var(--accent); }

/* Action-tier buttons on real app pages: buttons.css gives .primary/.danger padding:9px 18px, but
   the app's plain buttons (e.g. League Maintenance's unclassed leagueButtons) carry no author
   padding — just the browser default — so the tiered pair rendered taller than their neighbours.
   Revert the tier padding to the UA default so primary/danger match the norm height. - Claude 7/18/26 */
#main button.primary, #main button.danger, #main button.secondary { padding:revert; }

/* SIDE padding scales with the CORNER dial (Lee 8/4/26: "in Cuddly the buttons look crowded").
   desktop.css:687 gives every plain button a flat .2em all round, which was chosen for square corners.
   It does not survive rounding: MAKE A DEPOSIT is 22px tall, so Cuddly's 14px radius CLAMPS to 11px and
   each end cap eats ~11px of the button's own width — against 2.56px of padding, the label ends up
   sitting right where the curve starts.
   So derive the side padding from --radius instead of picking a number: Chiseled (0) is byte-identical
   to today, Curvy gains 1.8px a side, Cuddly 4.2px (MAKE A DEPOSIT 119px -> 127px). TOP/BOTTOM are left
   alone — the caps only eat horizontally, and height is what the rule above is protecting.
   FACTOR was .45, cut to .30 the same day (Lee: "a little too much, take away about 1/3 of what you just
   added"). .30 is exactly two thirds of the added amount, not of the total — Chiseled still gains nothing
   and the .2em base is untouched.
   The three tier classes are listed so this still reaches them past that `padding:revert`; it only sets
   the two side longhands, so their reverted height is untouched.
   Audited on the lobby (455 buttons): no button overflows its parent and the document does not gain any
   horizontal scroll, in either state. - Claude 8/4/26
   FACTOR CUT AGAIN .30 -> .24 (Lee 8/5/26, the squeezed lobby HOLD button). The overflow audit above
   was the wrong test for that button, and this is why: the lobby's action buttons carry an EXPLICIT
   width (desktop.css:797-811, join 3.6em / hold 3.5em / buy + pay 3.2em ...), and a <button> is
   border-box in every UA sheet. So on those the padding cannot push the box outwards the way it does
   on MAKE A DEPOSIT — nothing overflows, which is exactly what the audit reported — it eats INWARD out
   of a fixed box and squeezes the label instead. An overflow check can never see that.
   .24 gives back a fifth of the Cuddly inset, 4.2px -> 3.36px a side. Paired with a small width bump on
   the lobby buttons themselves (desktop.css), since padding alone cannot fix a box that is fixed.
   Chiseled is still byte-identical and the .2em base is still untouched.
   NOTE the matching rule for FIELDS below still uses .30 — fields size to their own content, so they
   have none of this problem, and Lee asked about buttons. Left deliberately out of step. - Claude 8/5/26 */
#main button,
#main button.primary, #main button.danger, #main button.secondary {
  padding-left:calc(.2em + var(--radius, 0px) * .24);
  padding-right:calc(.2em + var(--radius, 0px) * .24);
}

/* LETTERING SIZE MATCHING (Lee 8/4/26: "Clean and Casual lettering is simply too small"). The reasoning
   and the measurements live on $axisCss['skinFont'] in skin.php; this is the half that applies it.
   --font-xh is set ONLY by Clean and Casual, so Classic resolves to `none` and is untouched.
   Paired with the family selector in desktop.css:57 / mobile.css:3 / fullpage.css:4, which is `*` —
   so this is `*` too, and for the same reason: the family lands on every element, so the adjustment has
   to as well or the two disagree on a per-element basis.
   THE RESETS BELOW ARE REQUIRED, not tidiness. font-size-adjust normalises whatever family an element
   actually uses, so any element that declares its OWN family would be re-sized to Verdana's x-height —
   and those are the decorative and monospace ones, where that is wrong: Brush Script and Apple Chancery
   have small x-heights and would balloon inside fixed-size bursts, and the monospace blocks are sized to
   their own grid. The rule is simply: if we did not choose the font, do not adjust it. - Claude 8/4/26 */
*                                     { font-size-adjust:var(--font-xh, none); }
span.freeBurstText,
#showFreeYearParagraph .freeBannerText,
#main div.promotion,
#main div#filefont,
#mfl-output,
#main table.dashboard td span#clock,
#main div.lrScheme .lrHex             { font-size-adjust:none; }

/* ⚠ THE LINE-BOX COMPENSATION NEVER REACHED THE NAV BAR. Lee, 8/10/26: the nav buttons are taller in
   Clean than in the other two, which lifts the banner art off its baseline.
   The two halves of the Lettering fix are applied at DIFFERENT SCOPES, and that is the whole bug:
       font-size-adjust  ->  `*`      (the rule directly above — every element)
       --font-lh         ->  `#main`  (desktop.css:61)
   #menubar sits OUTSIDE #main (measured: menubar at char ~15.4k, #main at ~22.0k in the served home
   page), so the nav gets the ENLARGED GLYPHS with none of the line-height compensation that keeps them
   in the same box. mobile.css:12 already puts --font-lh on `body` and so never had this; the #main
   scoping on desktop is the anomaly.
   Measured on beta, nav button / #topPart height:
       Classic  32.0 / 152.3      Casual  32.8 / 153.8      CLEAN  36.0 / 157.0
   That +4.7px on #topPart is exactly the banner displacement Lee is looking at.
   ⚠ AND IT IS WHY SHRINKING THE FONT CANNOT FIX IT — which is what he asked me to do. Sweeping
   --font-xh down: 0.5469 -> 36.0px, 0.54/0.53 -> 35.2, 0.52/0.51/0.50 -> 34.4, and removing
   font-size-adjust ENTIRELY still leaves 34.4 against Classic's 32.0. The residual 2.4px is Segoe UI's
   intrinsic `normal` line box, not the size adjustment, so the size knob tops out at 60% of the problem
   and spends the print size Lee asked to have INCREASED on 8/4 ("Clean and Casual is simply too small")
   to get there. Pinning the line box costs nothing and lands on the number exactly.
   Scoped to the BUTTON and its inner <a>, not to body: the <a> carries its own line-height, so a body-
   level pin does not inherit through it (tried it — nav stayed at 36.0), and a body-level pin also
   moved Classic's #topPart by 0.5px because it overrides a line-height the base sheet sets elsewhere.
   VERIFIED A NO-OP FOR CLASSIC: Classic emits no --font-lh at all, so var() falls back to `normal`,
   which is already the computed value. Simulated with `--font-lh:initial` (guaranteed-invalid, forces
   the fallback) — button 32.00 and #topPart 152.34 byte-identical with and without this rule.
   After: Clean 31.8 / 152.8 and Casual 31.8 / 152.8, both within 0.5px of Classic. - Claude 8/10/26 */
#menubar button.menubarButton,
#menubar button.menubarButton a       { line-height:var(--font-lh, normal); }

/* ⚠ SAME BUG, SECOND SITE: THE HOME PAGE CARDS. Lee 8/11/26: "the buttons overlap the text in some
   boxes ... it doesn't happen in production."
   `#home` is a SIBLING of `#main`, not a child (measured in the served page: #main opens at char
   ~22.0k and closes before #home opens at ~22.2k), so desktop.css:61's `#main {line-height:
   var(--font-lh)}` never reaches it — exactly the way #menubar was missed on 8/10. The cards therefore
   render Clean/Casual with the ENLARGED GLYPHS from the universal font-size-adjust rule above and
   NONE of the line-box compensation, so every paragraph is taller than it was designed to be.
   WHY THAT SHOWS UP AS OVERLAP RATHER THAN A TALLER CARD: desktop.css:71 pins the buttons with
   `position:absolute; bottom:10px`, so they are OUT OF FLOW and reserve no space at all. The card
   height is set by the text alone, and the buttons are painted on top of whatever ends up there. Any
   increase in text height lands underneath them. That fragility is pre-existing and independent of
   the skin — the Lettering axis just supplies the extra height that exposes it.
   Only Clean and Casual set --font-lh, so this is a NO-OP in Classic (var() falls back to `normal`,
   which is already the computed value) — the same reasoning verified byte-for-byte on 8/10 for the
   nav buttons.
   ⚠ NOT the DOCTYPE. index.php:62 deliberately emits `<!DOCTYPE html SYSTEM ...` on beta and
   `... PUBLIC ...` everywhere else. That beta doctype is malformed — SYSTEM takes ONE identifier and
   it is given two — but per the HTML5 parsing rules it lands in no-quirks mode either way (public id
   missing, system id not in the quirks list, and the trailing string only triggers the bogus-DOCTYPE
   state, which does not set force-quirks). It is worth tidying; it is not this.
   ⚠ NOT a CSS difference between environments either: css/desktop.css is BYTE-IDENTICAL beta vs prod,
   and skins-live.css differs only in comment text. The environments diverge in skin.php and in each
   database's saved Lettering, not in these rules. - Claude 8/11/26 */
#home                                 { line-height:var(--font-lh, normal); }

/* FIELDS get the same treatment (Lee 8/4/26, straight after the buttons). Identical cause: desktop.css:702
   gives input/select.lineEntry a flat .2em, and skin-base:104 rounds them by --radius — a 23px-tall field
   with Cuddly's 14px radius clamps to 11.5px caps against 2.67px of padding, so the value starts inside
   the curve. Same .30 factor as the buttons ON PURPOSE: fields and buttons sit in the same forms and a
   different inset between them would read as a misalignment. KEEP THE TWO IN STEP — when the factor was
   cut from .45 on 8/4 it was cut in both blocks for that reason.
   The selector list is copied from skin-base's field rule rather than invented, so the set that gets the
   RADIUS is exactly the set that gets the compensation — which is also what keeps checkboxes out (they
   are not in that list, have radius 0, and must stay 13x13).
   All three of the stacked-address classes are included together so a top/bottom pair cannot end up with
   different insets. Audited on My Account (25 fields): no field clips its own content (scrollWidth vs
   clientWidth), none overflows its parent, no horizontal scroll. - Claude 8/4/26 */
#main textarea, #main input.lineEntry, #main select.lineEntry,
#main input.topLineEntry, #main input.bottomLineEntry, #main input.addressBottom {
  padding-left:calc(.2em + var(--radius, 0px) * .30);
  padding-right:calc(.2em + var(--radius, 0px) * .30);
}

/* Playoff brackets: the L-shaped connector lines (td.btop/bright/bbottom/bleft) are hardcoded
   #4A9586 teal. They're a vivid accent line by design, so tie them to --accent (teal on
   DataForce, indigo on Midnight, etc.). border-*-color keeps the 2px solid width. */
#main table.brackets td.btop         { border-top-color:var(--accent); }
#main table.brackets td.bright       { border-right-color:var(--accent); }
#main table.brackets td.bbottom      { border-bottom-color:var(--accent); }
#main table.brackets td.bleft        { border-left-color:var(--accent); }
/* desktop.css:979 `#main table.brackets h2` (1,1,2) outranks the `#main h2` rule above, so the
   playoff-bracket heading kept the old accent color after headings moved to --ink. - Claude 8/3/26 */
#main table.brackets h2              { color:var(--ink); }

/* SET LINEUP page (table.lineup / table.match / table.droppable): hardcoded DataForce teal
   (#4A9586 border + position label, #8DC7BB hover/drop/pic-out) and #E7F3F1 player cells ->
   skin surfaces (follow skin + dark mode). The red/blue team position labels stay semantic. */
#main table.lineup                   { border-color:var(--panel-border); }
/* Cell dividers REMOVED 8/4/26 (Lee): they were added 7/25 to give the slot cards visible cell
   boundaries, but on the real Set Lineup page they read as gridmarks ruled through every slot and the
   bench. The cards are already delineated by the rounded outer frame and by the lineupPos block's own
   background, so nothing is lost. Do not re-add — this was tried and rejected. - Claude 8/4/26 */
#main table.lineup td.lineupPos      { }
#main table.lineup td.lineupPlayer,
#main table.lineup td.lineupOppo,
#main table.lineup td.lineupWhen,
#main table.lineup div.picIn         { color:var(--ink); }
#main table.lineup tr.drag:hover td.lineupPlayer,
#main table.lineup tr.drag:hover td.lineupPoints,
#main table.droppable tr td.lineupPlayer,
#main table.droppable tr td.lineupPoints,
#main table.lineup div.picOut,
#main table.match span.drag:hover    { background-color:color-mix(in srgb, var(--accent) 28%, var(--row-bg)); }

/* BENCH BOTTOM-LEFT CORNER (Lee 8/4/26). On the bench, td.lineupPlayer is the FIRST cell, so its
   div.picOut (the tinted block behind the headshot) sits right on the table's bottom-left corner — and
   two things made that corner square:
     1. picOut is 33px tall + 2px padding-top = 35px, but the cell's content box is 34px, so it painted
        1px past the bottom edge and hid the cell's bottom border along its own 36px width;
     2. a child never follows its parent's border-radius (the td has no overflow clip, and it can't have
        one — span.nameOnly is shifted up 25px and would be cut off), so picOut's square corner covered
        the rounded arc the td had already drawn.
   Fixing the height alone leaves the square corner, and rounding alone leaves the missing border, so both
   are needed. Scoped to the last row's first cell: elsewhere the 1px spill lands on the next row and is
   invisible. Chiseled (--table-radius:0) is unaffected; this became obvious with Cuddly's 12px.
   Starter slots are not affected — there td:first-child is lineupPos, a plain cell. - Claude 8/4/26 */
#main table.lineup.rounded-all > tbody > tr:last-child > td:first-child div.picOut,
#main table.lineup.rounded-all > tr:last-child > td:first-child div.picOut {
  box-sizing:border-box; height:34px;               /* 2px padding-top + 32px = the cell's content box */
  border-bottom-left-radius:var(--table-radius, 6px);
  /* overflow:hidden IS LOAD-BEARING, NOT TIDINESS — it fixes the indent Lee reported 8/6/26, where the
     LAST bench player's name sat ~32px right of every other row. img.lineupPicture is float:left, and the
     name is not a sibling of the picture: div.picOut is a plain block, so span.nameOnly starts on the NEXT
     line box and is dragged back up beside the photo by {position:relative;top:-25px;left:36px}
     (desktop.css:1005). That only works while the float stays inside picOut. Shrinking the content box to
     32px above put the float's bottom edge ~0.6px BELOW picOut, and an escaped float displaces the
     following line box — so the name was pushed right by the float's own width (27px img + 2px picIn
     padding-left + 3px margin-left = 32px), exactly the offset measured. overflow:hidden makes picOut a
     block formatting context, which contains the float so it can no longer displace anything. Nothing is
     visibly clipped: only picIn's 3px bottom margin overflows, and margins paint nothing. Verified in
     Chrome against the real box metrics — name left 48.6px normally, 80.6px with this rule before the fix,
     48.6px after. It also makes picOut clip its children to the radius, which is what the corner fix
     wanted anyway. Do not drop this line while the height override stands. - Claude 8/6/26 */
  overflow:hidden;
}
/* ...AND THE ROW HAS TO BE TALL ENOUGH TO HOLD IT (Lee 8/6/26: "the drawn box ends higher than it
   should"). The 8/4 note above asserts "the cell's content box is 34px". IT IS NOT, and that wrong
   number is why every attempt at this corner has under-corrected. The cell's content box is
   div.playerName, which desktop.css:1002 fixes at height:2em — 26px at the page's 13px base. So a
   34px picOut hangs 7.2px BELOW the table's bottom border and paints outside the drawn box.
   This is long-standing, not new: every row's photo overlaps the row beneath it by the same 7px. It
   is invisible everywhere else because it laps onto an identically-coloured row — only the LAST row
   has an edge to cross, and only once skins started drawing that edge did it become visible.
   Setting playerName's height equal to picOut's on that one row makes the content box exactly
   contain the photo. Measured: spill 7.2px -> -0.8px, row 26.4 -> 34.4px, and the [score] cell drifts
   0.4px, i.e. nothing else moves. Deliberately NOT padding-bottom on the row, which measures
   identically but only because 8px happens to equal 34px minus 2em at THIS font size; tying the row
   to picOut's own height instead survives a font change. Do not use min-height either — with
   height:auto the line box below the photo adds on and the row balloons to 49.6px. - Claude 8/6/26 */
#main table.lineup.rounded-all > tbody > tr:last-child > td:first-child div.playerName,
#main table.lineup.rounded-all > tr:last-child > td:first-child div.playerName { height:34px; }

/* roster / standings tables (best-effort; harmless if a selector is absent) */
#main table.roster td               { border-color: var(--panel-border); }
#main table.roster tr.header td      { background: var(--major-bg); }
#main table.roster tr.subHeader td   { background: var(--header-bg); color: var(--header-text); }

/* menubar dropdown corners: the <ul> is the rounded, clipped "card" (border-radius +
   overflow:hidden), so the <li>s inside must be SQUARE. desktop.css rounds them (4px) and
   skin-base's nav grouping rounds them again (--radius) — those rounded item corners then
   reveal whatever sits behind the TRANSPARENT ul, which in Light mode is the white content
   panel => white notches at every corner. Square the items and give the ul the nav fill so
   no seam can show through in any mode. */
#menubar span.dropdown ul                  { background:var(--nav-base); }
#menubar span.dropdown ul li               { border-radius:0; }
/* leftover from the old bordered-list dropdown: desktop.css puts a white hairline under
   the LAST item, which reads as a bright line across the bottom of the modernized card
   (which uses subtle inset dividers instead). Drop it. */
#menubar span.dropdown ul li.last          { border-bottom:0; }

/* NAV LABELS THAT ARE <a> (Lee 8/4/26: "Onyx in Classic — the menu buttons are dark instead of white").
   Not by design, a mispaired token. skin-base:70 already gives the nav BUTTONS and the dropdown <li>
   --nav-text, but wherever the label is an ANCHOR the base sheet colours it separately with
   var(--major-text) — desktop.css:223 (#title navButton a), :315 (#menubar button a), :334
   (#menubar span.dropdown a) and :1242 (#bigmenubar button). The surface under all four is a nav
   surface, --nav-base, not --major-bg, so text and background were coming from two different pairs.
   It never showed because --major-text and --nav-text hold the SAME value in 13 of the 14 palettes.
   Onyx is the one that separates them: its --major-bg is a bright cyan, so --major-text is correctly a
   near-black #052430 — which then leaked onto the blue nav and turned those labels dark. The Hub Menu
   items were the obvious ones; "My Leagues" (#leaguesButton, a bare button with no dropdown) and the
   hub buttons' own anchors were dark too.
   Measured across every palette: Onyx 3.94 -> 4.10 on --nav-base, and all 13 others are byte-identical
   before and after, so this cannot move a shipped look.
   States listed explicitly so none can fall back to the generic `a:hover` accent at desktop.css:366.
   Placed BEFORE the mobile Hub Menu block below, which is more specific and still wins on the phone.
   NOTE this fixes the RESTING state only. Onyx's --nav-hover (#0ea5e9) is light enough that white on it
   is 2.77 — but that is already true today for every nav button, so it is a palette question, not this
   one. - Claude 8/4/26 */
#title button.navButton a,
#title button.navButton a:visited,
#title button.navButton a:focus,
#title button.navButton a:hover,
#menubar button a,
#menubar button a:visited,
#menubar button a:focus,
#menubar button a:hover,
#menubar span.dropdown a,
#menubar span.dropdown a:visited,
#menubar span.dropdown a:focus,
#menubar span.dropdown a:hover,
#bigmenubar button,
#bigmenubar button a                       { color:var(--nav-text); }

/* menubar HOVER was always blue on every skin: desktop.css hardcodes #1F88A7 (its
   documented "menubar hover" color) on `#menubar button.menubarButton:hover` — and that
   extra .menubarButton class (1,2,1) outranks skin-base's `#menubar button:hover` (1,1,1).
   It also has no body#bodyN prefix, so the id-strip never disabled it. Match the
   specificity and hand hover back to the skin. */
#menubar button.menubarButton:hover,
#menubar button.menubarButton:focus        { background-color:var(--nav-hover); background-image:none; }
#menubar span.dropdown li:hover,
#menubar span.dropdown li:focus-within     { background-color:var(--nav-hover); background-image:none; }
#bigmenubar button.menubarButton:hover,
#bigmenubar button.menubarButton:focus     { background-color:var(--nav-hover); }
#title button#useMobileVersion:hover,
#title button#useMobileVersion:focus       { background-color:var(--nav-hover); }

/* ============================== MOBILE (v2) =================================
   Scoped to body.dfx-mobile (skinswitch tags mobile-v2 pages, which carry the fixed
   hub bar #zoomlevel2) so none of this can touch desktop. mobile.css hardcodes teal
   (#4A9586 / #8DC7BA); here we hand those to the skin tokens so every skin/mode
   follows. Body background itself is skin-base's page gradient (black→teal classic,
   light in Light mode) — nothing to do here for that. - Claude 7/22/26 */

/* Round hub circles (Lobby / Rules / Account / …) — keep the 3-D radial look but in
   the skin accent: light rim -> accent -> shaded core, all derived from --accent. */
body.dfx-mobile table.mobileIcons td .circle {
  background: radial-gradient(circle at 30% 30%,
              color-mix(in srgb, var(--accent) 74%, white),
              var(--accent) 60%,
              color-mix(in srgb, var(--accent) 72%, black));
  border-color: color-mix(in srgb, var(--accent) 55%, white);
}

/* "Sign out" link sits on the dark hero — a lightened accent so it reads on dark and
   still carries the skin color. Hover -> the accent itself. */
body.dfx-mobile #signInOut a,
body.dfx-mobile #signInOut a:visited,
body.dfx-mobile #signInOut a:focus   { color:var(--link, color-mix(in srgb, var(--accent) 60%, white)); }
body.dfx-mobile #signInOut a:hover   { color:var(--accent); }

/* Footer links (FAQ · Help Me · Contact Us · …) — follow the skin link color. */
body.dfx-mobile #footer a,
body.dfx-mobile #footer a:visited,
body.dfx-mobile #footer a:focus      { color:var(--link, color-mix(in srgb, var(--accent) 60%, white)); }
body.dfx-mobile #footer a:hover      { color:var(--accent); }

/* Home hero: the crest logo (#logo = newlogo3.png, transparent) sits on a DARK stadium-field
   backdrop (#mobileBackground = mobilebackground.png). That field art only reads on dark, so in
   LIGHT mode we drop it and let the crest stand alone on the light page — logo unchanged/same
   size — so Light mode is light throughout the home page. - Claude 7/22/26 */
body.dfx-mobile[data-theme="light"] #mobileBackground { background-image:none; }
/* #content carries a solid black backdrop (mobile.css). In EVERY mode we clear it so the page
   background GRADIENT shows through the whole content area — including the strip above the #main box
   ("Welcome, …/Sign out") and the home hero's gaps. The #main box paints its own surface on top; this
   just stops the black band. (Lee: that area should never be flat black in any mode.) - Claude 7/22/26 */
body.dfx-mobile #content                             { background-color:transparent !important; }

/* HOME + SPLASH ONLY: kill the page GRADIENT so the side strips stop reading as teal. (Lee, 8/1/26)
   The strips are not a bug — play.js:84 scales #zoomlevel by windowWidth/340 while #content is 310px,
   so the column always occupies 310/340 = 91.2% of the screen and 4.4% is left over on each side.
   Measured 4.5% on Lee's phone. The rule directly above makes #content transparent in every mode, so
   what shows in that margin is body's black -> --page-bottom gradient (skin-base.css:56). Flattening
   it to --page-top alone means the margin matches the content backdrop and the page reads edge to
   edge. The strip still exists; it just stops being visible.

   Targeted with :has() rather than a body class because home/splash arrive by AJAX, and skinfx.js
   only tags the body by page TYPE at load — there is no per-page hook to hang this on. #newAtDF is
   unique to mobile-v2/home.php and #trainingDivs to mobile-v2/splash.php (checked against all of
   mobile-v2). Browsers without :has() (pre-Chrome 105 / Safari 15.4) simply keep the old look — the
   degradation is the status quo.

   ⚠ THE VISIBILITY TEST IS LOAD-BEARING — do not simplify it to :has(#newAtDF). home/splash render
   into #home (the mobileadmin.php shell, route /mobile-home), and common.js:249 navigates away with
   $('#home').hide() — it HIDES that markup, it never empties it. So #newAtDF stays in the DOM for the
   rest of the session, a bare :has(#newAtDF) stays matched on every later page, and the gradient
   never comes back (Lee hit exactly this, 8/1/26). What we actually need to ask is "is #home showing
   right now", and #home is only ever touched by .html()/.show()/.hide() — no animations anywhere in
   js/ — so jQuery's inline style is a reliable proxy: .hide() writes `display: none`, .show() clears
   it back to "". Verified round-trip on beta: home = match, nav to Rules = no match, back to home =
   match. If #home ever starts being toggled by a CLASS instead, this silently stops working — at that
   point swap it for a body class set in the gotPage2() branch that already tests these two pages.

   NOT the `background:` shorthand: that would also drop --page-image, and Apex sets one
   (apex.css:8, the football watermark) while DataForce sets none. Splitting image from color keeps
   Apex intact. --page-top is per-mode (#000 classic / #05070a dark / #eef1f5 light), so this stays
   skin-correct instead of hardcoding black. - Claude 8/1/26 */
body.dfx-mobile:has(#home:not([style*="display: none"]) #newAtDF),
body.dfx-mobile:has(#home:not([style*="display: none"]) #trainingDivs)
                                                     { background-image:var(--page-image, none); background-color:var(--page-top); }

/* Walkthrough link ("Touch here for a navigation walk-through") lives in that hero. It was pinned
   #8DC7BA (light teal) to read on the dark field; hand it to the skin link token so it reads on the
   dark field (classic/dark → light accent) AND on the light page once the field is gone (light →
   dark accent). - Claude 7/22/26 */
body.dfx-mobile #title a#walkthrough,
body.dfx-mobile #title a#walkthrough:visited { color:var(--link, color-mix(in srgb, var(--accent) 60%, white)); }
body.dfx-mobile #title a#walkthrough:hover    { color:var(--accent); }

/* Content surfaces: mobile.css / rounded.css hardcode teal boxes (#E7F3F1) and #4A9586. Hand them
   to the mode tokens so the content follows Classic(now dark)/Dark/Light. rounded.css draws the box
   outline as per-CELL borders (first/last child), which BREAKS on the lobby: SELECT ALL's rowspan
   makes one row a 3-cell row, and that phantom cell's border is the stray line to the right of it.
   So for mobile we replace the per-cell outline with a single rounded border on the TABLE itself —
   same look, no phantom cell, no stray line. - Claude 7/22/26 */
/* CLASSIC on mobile = the NATIVE / production look: a black page with the content sitting directly
   on it (transparent boxes), teal rounded borders, light text, teal links; the contest table keeps
   its native teal header rows + dark cell text. So in Classic we apply almost NOTHING — we only undo
   the one harness rule that breaks it: skin-base `#main{background:--content-bg;color:--ink}` carries
   DESKTOP-classic values (light box, dark text) that are wrong on mobile. Light/Dark get the real
   token surfaces. - Claude 7/22/26 */

/* #options (the checkbox picker) = a single rounded OUTLINE, no internal grid — matches desktop's
   `table#options{border:1px solid}`, and the single table border sidesteps the SELECT-ALL rowspan
   "stray line" (phantom 3rd column) the per-cell scheme produced. */
body.dfx-mobile #main table#options { border:1px solid var(--panel-border, var(--accent)) !important; border-radius:6px; }
body.dfx-mobile #main table#options > tbody > tr > td { border:0 !important; }

/* The contest list #leagues and the money/data grids get a visible CELL GRID matching desktop's
   intensity (desktop.css `#main table#leagues td{border:1px solid #4A9586}` → skin --panel-border;
   desktop.css isn't loaded on mobile). We want SINGLE lines AND rounded corners: border-collapse must
   stay `separate` for the radius to render, but full per-cell borders double the interior lines. So the
   TABLE draws the rounded outer border, and each cell draws only its RIGHT+BOTTOM interior line; the
   last column/row drop theirs so the outer edge isn't doubled with the table border. - Claude 7/22/26 */
body.dfx-mobile #main table#leagues,
body.dfx-mobile #main table.data,
body.dfx-mobile #main table#money {
  border-collapse:separate !important; border-spacing:0 !important;
  border:1px solid var(--panel-border, var(--accent)) !important; border-radius:6px;
}
body.dfx-mobile #main table#leagues td,
body.dfx-mobile #main table.data td,
body.dfx-mobile #main table#money td {
  border:0 !important;
  border-right:1px solid var(--panel-border, var(--accent)) !important;
  border-bottom:1px solid var(--panel-border, var(--accent)) !important;
}
body.dfx-mobile #main table#leagues tr td:last-child,
body.dfx-mobile #main table.data tr td:last-child,
body.dfx-mobile #main table#money tr td:last-child { border-right:0 !important; }
body.dfx-mobile #main table#leagues tr:last-child td,
body.dfx-mobile #main table.data tr:last-child td,
body.dfx-mobile #main table#money tr:last-child td { border-bottom:0 !important; }

/* Restore the PAGE FRAME on mobile. mobile.css:386 gives `#main{border:2px solid #F8F8F8;
   border-radius:6px}` (the rounded frame around the whole mobile page), but skins-live.css:20's DESKTOP
   rule `#main{border:0}` strips it on mobile too. Bring it back for mobile, THEMED (Lee): teal
   --panel-border in Classic, the mode's --border in Light/Dark. border-radius:6px comes from mobile.css. */
body.dfx-mobile #main { border:2px solid var(--border, var(--panel-border, var(--accent))); }

/* Financial Transactions (table#money): each transaction is TWO rows — a Date/Amount/Balance data row
   plus a full-width description row ("Paid for League: …", a single td colspan=3). Tint that 2nd line a
   slightly different shade so each transaction reads as its own band (readability). Base the tint on
   --row-bg — the SAME surface the data rows use (teal #E7F3F1 in DataForce classic, #161b22 dark, #fff
   light) — so it's a consistent, visible delta in every skin/mode (mixing into --content-bg instead
   washed out on DataForce classic, whose data rows are teal, not content-bg). - Claude 7/23/26 */
body.dfx-mobile #main table#money td[colspan="3"] {
  background-color:color-mix(in srgb, var(--ink) 14%, var(--row-bg, var(--content-bg))) !important;
}
/* #money isn't a table.rounded, so the dark-mode --ink text remap below never reached it -> mobile.css's
   hardcoded black text stayed, giving dark-on-dark. Cover #money too. - Claude 7/23/26 */
body.dfx-mobile.dfx-darkpanel #main table#money td { color:var(--ink) !important; }

/* CLASSIC: the #main box takes the DARK-mode surface shade (#161b22) so the framed box reads as a
   solid dark panel (Lee) rather than pure page-black; text stays light. Page titles used to read
   --heading, which is why Classic is handed Dark mode's light-teal --heading below; SINCE 8/3/26 they
   read --ink instead and are handled by their own rule further down — the --heading value is kept
   because other consumers still read it. The content tables sit transparent on the box; their own teal
   headers / cells are native mobile.css (production). - Claude 7/22/26, amended 8/3/26 */
/* The Classic box is now dark (#161b22), so the on-dark TEXT tokens must use Dark mode's LIFTED
   shades, not the deep light/classic ones — otherwise attention text (--BETA MODE--, "417 leagues
   filled", live-draft dates — all span.red -> var(--attention-ink,--attention)) and complementary/
   heading text read too dark on the panel. Match Dark mode. - Claude 7/22/26 */
body.dfx-mobile[data-theme="classic"]       { --heading:color-mix(in srgb, var(--accent) 60%, white);
                                              --attention-ink:#f2814d;
                                              --complementary-ink:color-mix(in srgb, var(--complementary) 60%, white);
                                              --link:color-mix(in srgb, var(--accent) 55%, white); }
body.dfx-mobile[data-theme="classic"] #main               { background:#161b22 !important; color:var(--page-text) !important; }
/* ⚠ PAGE TITLES WENT BLACK-ON-BLACK HERE ON 8/3/26 (Lee, The Locker Room on a phone). The block above
   says "Page titles (#main h1/h2 read --heading) match Dark mode by giving Classic the same --heading" —
   that stopped being true the same day, when headings were moved OFF --heading and onto --ink so that
   color on a page would mean "clickable" and nothing else (see the note at #main h1,h2,h3,h4 above).
   --ink is mode-aware in Light and Dark, but CLASSIC HAS NO data-theme ENTRY IN modes.css, so it falls
   through to skin-base's :root #111111 — written for the LIGHT desktop panel. On mobile the Classic
   #main box is forced dark (#161b22, the rule directly above, for every skin) and #111 on #161b22 is
   1.06:1. Invisible, and invisible on EVERY page with a title, not just this one.
   Fixed the same way the sibling tokens on this box already are: use --page-text, the light on-box text
   Classic gives #main (exactly what span.teamStatus was switched to for this box on 7/25).
   NOT by re-declaring --ink on the box — that token is also read INSIDE the light tables sitting on the
   box (table#news / #injuries / .rounded), and flipping it there would simply invert the bug into
   light-on-light. The token has to follow the SURFACE, not the page; the companion rule below re-pins
   any heading that sits on one of those light table surfaces. - Claude 8/3/26 */
body.dfx-mobile[data-theme="classic"] #main h1,
body.dfx-mobile[data-theme="classic"] #main h2,
body.dfx-mobile[data-theme="classic"] #main h3,
body.dfx-mobile[data-theme="classic"] #main h4            { color:var(--page-text); }
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table.rounded  :is(h1,h2,h3,h4),
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#news     :is(h1,h2,h3,h4),
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#injuries :is(h1,h2,h3,h4) { color:var(--ink); }
/* STRUCTURAL FIX 7/28/26. Classic used to leave these tables TRANSPARENT so the dark #main box showed
   through — but Light and Dark modes both give them var(--content-bg), and Classic being the odd one
   out is what generated a whole family of contrast bugs. With transparent tables, whether any given
   cell is light or dark depends on which rows happen to carry --row-bg, and NO SELECTOR CAN KNOW
   THAT. I chased it row class by row class (tr.detail, tr.header, then tr.a.dragBench, td.lineupOppo)
   and every fix pushed a different page under AA, because the same token has to be deep on a surfaced
   cell and lifted on a transparent one.
   Giving the table a surface settles it: every cell is light, so the DEEP tokens are correct inside
   any table, and the lifted ones stay correct on the box itself. One rule each, no enumeration.
   #options keeps its own transparency (deliberate, see below) and is excluded from the token rule. */
body.dfx-mobile[data-theme="classic"] #main table.rounded,
/* #news and #injuries (Player News) carry no class at all, so .rounded never reached them and
   they stayed transparent — their headlines and "More..." links kept failing. - Claude 7/28/26 */
body.dfx-mobile[data-theme="classic"] #main table#news,
body.dfx-mobile[data-theme="classic"] #main table#injuries { background-color:var(--content-bg) !important; }
/* ...and their TEXT with them. Unlike the .rounded tables, whose cells set their own color, these two
   inherit --page-text from the dark #main box. Give them a surface without giving them ink and you
   simply invert the bug: light text on a light table, 1.21:1. Every background needs its paired text
   color — the same rule the palettes follow. - Claude 7/28/26 */
/* ⚠ ...BUT NOT THE SECTION-HEADER ROW, WHICH IS A DIFFERENT SURFACE. Found by the mobile contrast
   sweep 8/10/26: "My Player News" measured 1.29:1 on Gridiron and Primetime, 1.41 on Varsity, and
   under 2.5 on nine of the twelve palettes — invisible, in Classic only.
   `tr.majorHeader td` carries `background-color:var(--major-bg)` from mobile.css:554 — the palette's
   DARK header band, not the light table surface this block is about. The `td` selector above swept it
   up anyway, and because it is `!important` it beat mobile.css's own `color:var(--major-text)` pairing.
   So the header row was being given the ink for a surface it does not sit on.
   That is the exact principle the note above states — the token follows the SURFACE, not the table —
   applied one level too coarsely. Excluding the row hands it straight back to `--major-text`, which is
   the colour the palette already chose to pair with `--major-bg`; nothing new is invented here.
   Nine palettes go from 1.29-2.49:1 to 7.5-14.6:1. The three that do NOT recover (DataForce 2.99,
   Onyx 2.24, Tailgate 2.06) fail for a different reason — their own --major-text/--major-bg pairing —
   which is a palette-level question and is tracked separately, not papered over here. - Claude 8/10/26 */
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#news,
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#news tr:not(.majorHeader) td,
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#news h3,
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#injuries,
body.dfx-mobile[data-theme="classic"]:not(.skin-onyx) #main table#injuries tr:not(.majorHeader) td { color:var(--ink) !important; }

/* "Commissioner:" WAS PURE #ff0000 AND NOT SKINNED AT ALL. Found by the mobile contrast sweep 8/10/26:
   span.commish is hardcoded `color:red` in four places in mobile.css (638, 778, 821, 827) and two in
   desktop.css, so it never moved with the palette and failed on almost every surface — 3.15-4.44:1
   across all twelve palettes, worst on Varsity 3.15, Red Zone 3.20, Overtime 3.24.
   --danger is the right token: it is the palette's own red, chosen per palette, and it keeps the cue
   RED, which is the whole point of the label (it marks who the commissioner is in chat). It is not a
   "destructive" use of the token, but it is the only per-palette red the system has, and inventing a
   thirteenth would be worse.
   ⚠ TWO RULES, BECAUSE ONE COLOUR CANNOT DO BOTH SURFACES. modes.css deliberately leaves --danger
   alone in every mode ("brand tokens are left ALONE, so every skin stays recognisable"), so on a dark
   panel the palette red stays dark and Onyx actually got WORSE with a naive swap: 3.66 -> 3.03. The
   dark branch lightens it with the same recipe modes.css uses for --link.
   50% not 55%: at 55 Varsity's very deep #6b1230 only reached 4.24 on the dark box. At 50 the worst
   palette on any dark surface is 4.85. Light surfaces run 4.79-11.50 except Onyx, which is a dark
   palette and takes the dark branch in Classic and Night anyway.
   ⚠⚠ AND MY FIRST ATTEMPT AT THIS WAS DEAD ON ARRIVAL — caught only by re-measuring after deploying.
   I wrote `#main span.commish` (1 id, 1 class, 1 element). Every hardcoded rule is more specific than
   that: `#main table.dashboard td span.commish` is (1,2,3). The colour stayed rgb(255,0,0) and the
   page looked exactly as before. This is the THIRD time this shape has bitten in this file — see the
   `.red` note above, which says in as many words "my earlier .red rule (0,1,0) never applied ...
   These match its specificity." I read that note, wrote the fix, and made the same mistake anyway.
   **The base sheets are the specificity floor here: mirror their selectors exactly.** skins-live.css
   loads after css/*, so equal specificity plus later order wins, with nothing left to escalate.
   All nine live selectors are reproduced below (4 in mobile.css, 2 in desktop.css, 3 in fullpage.css);
   the dark-panel variants add one class each, so they sit above their own light twin.
   ⚠⚠⚠ NOT SHIPPED — I BUILT IT, MEASURED IT, AND IT WAS WORSE THAN THE HARDCODED RED. Left here as
   a record so the next person does not repeat the same three steps.
   With the selectors above matched and both branches parsing, measured on the mobile Dashboard in
   Classic: Varsity 1.74:1, Red Zone 2.08, Gridiron 3.23, Tailgate 3.23, DataForce 3.84 — against
   3.15-4.44 for the plain red it replaced. Worse nearly everywhere.
   THE REASON IS THE ONE THIS FILE ALREADY KNOWS: on mobile, Classic forces #main to #161b22 for EVERY
   palette, but `dfx-darkpanel` is set only for Onyx (skinswitch.js DARKSKINS={onyx:1}). So the
   light-surface branch — the palette's deep red — was landing on a DARK box. A deep #6b1230 on
   #161b22 is 1.74:1; pure red at least had luminance.
   Gating on `body.dfx-mobile[data-theme="classic"]` as well is not sufficient either, because within
   that state the surface still varies per palette: `.rounded` tables take var(--content-bg), which
   only SOME palettes declare — where it is missing the declaration is invalid, the table stays
   transparent, and the text sits on the dark box after all. So the correct fix needs the surface
   resolved per palette, not per mode, and that is a bigger piece of work than a token swap.
   ⇒ span.commish stays hardcoded red for now. It is the mildest of the sweep's findings (3.15-4.44,
   legible but under AA) and a wrong fix is worse than the known state. - Claude 8/10/26 */

/* LIGHT / DARK: content boxes take the mode surface; Dark lifts the (mobile.css-blacked) contest text
   to --ink so it reads on the dark panel. */
body.dfx-mobile[data-theme="light"] #main table#options,
body.dfx-mobile[data-theme="light"] #main table.rounded,
body.dfx-mobile.dfx-darkpanel  #main table#options,
body.dfx-mobile.dfx-darkpanel  #main table.rounded { background-color:var(--content-bg); border-color:var(--border) !important; }
body.dfx-mobile.dfx-darkpanel  #main table.rounded td { color:var(--ink) !important; }
/* CLASSIC: table#options fell through to skin-base's --panel-bg (WHITE on flat) while its labels inherit
   the classic light text -> light-on-light (Lee 7/25). Make the box transparent so the dark classic #main
   shows through and the light labels read (the --panel-border frame keeps it defined). - Claude 7/25/26 */
body.dfx-mobile[data-theme="classic"] #main table#options,
body.dfx-mobile[data-theme="classic"] #main table#options td { background-color:transparent !important; }
/* PRIZES PAGE exception: the level-swatch headers (tr.allLeagues td.heading.tinbaby/gold/…) keep their
   hardcoded LIGHT backgrounds (#DEDEDE, #FFDF00, …) in every mode, so their text must stay DARK. The
   generic dark `table.rounded td{color:--ink !important}` above (for the lobby's dark contest cells)
   forces them light -> pale-on-pale = unreadable. Re-pin the swatch text dark, matching the desktop
   prizes fix (skins-live.css:432-441). Black Diamond keeps its own gold-on-black. - Claude 7/25/26 */
body.dfx-mobile.dfx-darkpanel #main table.prizes tr.allLeagues td.heading              { color:#111 !important; }
body.dfx-mobile.dfx-darkpanel #main table.prizes tr.allLeagues td.heading.blackdiamond { color:#FFCC00 !important; }

/* Content links (H2H / Best Ball / Weekly / Full / Live Draft / … — server-rendered <a> at #23819C)
   follow the skin. Light/Dark: the mode-aware --link. Classic: the contest/data TABLE cells are
   light-teal, so table links take the skin's link-on-light color (--accent-bg = DataForce's native
   #23819C = production; other skins fall back to --accent). Non-table Classic links (e.g. "Full Screen
   version") stay native light-teal on the black page. - Claude 7/22/26 */
body.dfx-mobile[data-theme="light"] #main a, body.dfx-mobile[data-theme="light"] #main a:visited,
body.dfx-mobile.dfx-darkpanel  #main a, body.dfx-mobile.dfx-darkpanel  #main a:visited { color:var(--link); }
body.dfx-mobile[data-theme="classic"] #main table a,
body.dfx-mobile[data-theme="classic"] #main table a:visited { color:var(--accent-bg, var(--accent)) !important; }
body.dfx-mobile #main a:hover { color:var(--accent); }

/* ===== MOBILE HUB MENU — restyle #menubar (a full-page list here, not a desktop dropdown) as a clean
   accordion menu. skinswitch.js adds the accordion behaviour (tap a section header to open/close it)
   and toggles `.dfx-open` on each span.dropdown. Desktop untouched (all scoped body.dfx-mobile). - Claude 7/22/26 */

/* sections stack full-width */
body.dfx-mobile #main #menubar,
body.dfx-mobile #main #menubar span.dropdown { display:block; width:auto; margin:0; padding:0; }

/* SECTION HEADER: flat, left-aligned bar with a caret that flips when open (no more chunky teal button) */
body.dfx-mobile #main #menubar button.menubarButton {
  display:block; width:100%; box-sizing:border-box; margin:0; text-align:left; cursor:pointer;
  padding:.85em 2.4em .85em 1em; font-size:1.05em; font-weight:bold; line-height:1.2;
  background:transparent !important; background-image:none !important; box-shadow:none;
  color:var(--heading, var(--accent)) !important;
  border:0 !important; border-bottom:1px solid var(--panel-border, var(--border)) !important; border-radius:0 !important;
  transform:none !important; position:relative;
}
body.dfx-mobile #main #menubar button.menubarButton a { color:inherit !important; font-weight:bold; }
body.dfx-mobile #main #menubar button.menubarButton::after {
  content:'\2304'; position:absolute; right:.9em; top:50%; transform:translateY(-60%);
  font-size:1.2em; opacity:.7; transition:transform .2s ease;
}
body.dfx-mobile #main #menubar span.dropdown.dfx-open button.menubarButton::after { transform:translateY(-30%) rotate(180deg); }
body.dfx-mobile #main #menubar button.menubarButton:hover,
body.dfx-mobile #main #menubar button.menubarButton:focus {
  background:color-mix(in srgb, var(--accent) 14%, transparent) !important; transform:none !important;
}

/* ITEMS: collapsed by default (accordion); shown when the section is open */
body.dfx-mobile #main #menubar span.dropdown ul { display:none; list-style:none; margin:0; padding:0; width:auto; position:static; visibility:visible; background:transparent !important; background-image:none !important; }
body.dfx-mobile #main #menubar span.dropdown.dfx-open ul { display:block; }

/* each item = full-width tappable row, indented, subtle divider, right chevron */
body.dfx-mobile #main #menubar span.dropdown ul li {
  display:block; position:relative; margin:0; padding:0; list-style:none; float:none;
  background:transparent !important; background-image:none !important;
  border:0 !important; border-bottom:1px solid color-mix(in srgb, var(--panel-border, var(--border)) 50%, transparent) !important;
}
body.dfx-mobile #main #menubar span.dropdown ul li a {
  display:block; width:auto; box-sizing:border-box; padding:.75em 2.2em .75em 2em;
  font-size:.95em; font-weight:normal; color:var(--link, var(--accent)) !important;
  background:transparent !important; background-image:none !important; position:relative;
}
body.dfx-mobile #main #menubar span.dropdown ul li a::after {
  content:'\203A'; position:absolute; right:1em; top:50%; transform:translateY(-55%); opacity:.5; font-size:1.15em;
}
body.dfx-mobile #main #menubar span.dropdown ul li:hover,
body.dfx-mobile #main #menubar span.dropdown ul li:focus-within {
  background:color-mix(in srgb, var(--accent) 10%, transparent) !important;
}

/* NEW badge (Blog / Glossary): the gold red-on-yellow rotated pill from desktop.css:312 — recreated
   here since desktop.css isn't loaded on mobile — pinned to the RIGHT of the row so it follows the
   item (not the next line). Kept GOLD on purpose (see note to Lee): a "NEW" flag should POP with one
   consistent high-viz color rather than blend into each skin. - Claude 7/22/26 */
body.dfx-mobile #main #menubar span.dropdown ul li .newBadge {
  position:absolute; right:2.6em; top:50%; transform:translateY(-50%) rotate(-8deg);
  display:inline-block; font:bold 9px/1 Arial,Helvetica,sans-serif;
  letter-spacing:.5px; padding:2px 4px; border-radius:3px; box-shadow:0 1px 1px rgba(0,0,0,.4);
  white-space:nowrap; pointer-events:none; z-index:2;
}

/* NEW badge color: --attention (the emphasis red-orange, held global across skins) with white text,
   instead of the gold/dark-red. One rule recolors it everywhere under the harness (menu badges +
   any placed on a column header etc.). --attention-text (#fff) is the readable text on that fill. - Claude 7/23/26 */
.newBadge { display:inline-block; background:var(--attention) !important; color:var(--attention-text, #fff) !important;
            font:bold 9px/1 Arial,Helvetica,sans-serif; letter-spacing:.5px; padding:2px 4px; border-radius:3px;
            box-shadow:0 1px 1px rgba(0,0,0,.4); white-space:nowrap; vertical-align:middle;
            transform:translateY(-2px) rotate(-8deg); }		/* the rakish tilt (desktop.css:312 has it, but mobile.css has no base rule) - Claude 7/25/26 */

/* NEW badge on the sortable player-listing header (Targ col): inline, it widened the column and shifted
   the other columns. HANG it just above the word instead — absolute so it takes no layout space; the th
   is the positioning context. - Claude 7/23/26 */
#report table.sortable thead th          { position:relative; }
#report table.sortable thead th .newBadge {
  position:absolute; left:50%; top:-9px; transform:translateX(-50%) rotate(-8deg); margin:0;
}

/* ===== Dashboard (My Roster / Draft Status / trades / standings) — desktop AND mobile - Claude 7/25/26 =====
   (1) The interior lines were DOUBLED, and on 7/25 I collapsed the borders to single them. That was the
       wrong lever: border-radius does not apply to a collapsed-border table, so from then on the panels
       drew a SQUARE frame around cell backgrounds that Chrome still clipped to the radius — a white notch
       at all four corners of every panel, obvious once Cuddly took the radius to 12px (Lee 8/4).
       The doubling was really caused by (6) below putting a border on all four sides of every cell, so fix
       it there instead and keep border-collapse SEPARATE, which is what the radius needs. Same pattern as
       the mobile #leagues/#money grids above. rounded.css already declares separate + border:0 on
       table.rounded, so this rule simply goes away rather than being reversed. - Claude 8/4/26 */
/* (2) SEE MORE (button.dashButton) sits on the dark Draft-Status band; in Dark mode --btn-surface darkens
       it to near-black. Give it the solid, on-brand accent fill it already has in Classic. */
body.dfx-darkpanel #main button.dashButton { background-color:var(--accent) !important; border-color:var(--accent) !important; color:#fff !important; }
/* (4) Draft-Status countdown clock (span#clock): give it a proper timer look — a dark pill with the skin
       accent border — instead of the plain text it became under the skin. Keeps
       mobile.css/desktop.css float:left so the band layout is unchanged.
       MONOSPACE REMOVED (Lee 8/5/26). This span is not only digits: when the auction closes, play.js
       replaces its contents with the WORDS "Time Expired" (js/play.js:4512). Courier New advances
       every glyph at .6em whatever it is, so twelve proportional characters became twelve wide ones,
       and the 1px letter-spacing added eleven more — the pill grew past its slot and crowded the
       "Auction Status" heading beside it. Production has never had this because the rule is mine.
       The reason monospace was here in the first place was to stop the ticking countdown jittering as
       digit widths change. font-variant-numeric:tabular-nums buys exactly that — fixed-width figures —
       without touching how letters are set, so the digits stay steady AND the words render in the
       skin's own face at the width production has. letter-spacing goes with it; it was only there to
       make the Courier read as a timer. - Claude 8/5/26 */
#main table.dashboard td span#clock {
  display:inline-block; padding:.15em .5em; margin-right:.4em;
  background-color:#0d1117 !important; color:#e6edf3 !important;
  border:1px solid var(--accent) !important; border-radius:4px;
  font-weight:bold; font-variant-numeric:tabular-nums;
}
/* (5) DARK mode: several non-roster dashboard tables kept a light cell background (empty-state cells like
       "You have no messages") -> light-on-light, unreadable. Force them to white-on-dark. My Roster
       (#roster) is EXCLUDED — its position-colored cells + dark-pinned links are intentional and Lee
       likes them. Section header bands are restored so they stay distinct. Applies desktop + mobile so
       both match (Lee). - Claude 7/25/26 */
body.dfx-darkpanel #main table.dashboard:not(#roster) td               { background-color:var(--content-bg) !important; color:var(--ink) !important; }
body.dfx-darkpanel #main table.dashboard:not(#roster) tr.majorHeader td { background-color:#334155 !important; }
body.dfx-darkpanel #main table.dashboard:not(#roster) tr.header td      { background-color:var(--header-bg) !important; }
/* (6) All modes: skin-theme these tables' borders (mobile/desktop hardcode teal #4A9586 as a fallback).
       REWRITTEN 8/4/26. It used to put a border on all four sides of every cell, which doubled every
       interior line — that is what (1) collapsed the table to hide, at the cost of the rounded corners.
       Instead the TABLE draws nothing (rounded.css already sets border:0) and each cell draws only its
       RIGHT + BOTTOM line, with the first column adding LEFT and the first row adding TOP. Every line is
       then drawn exactly once, and because the borders stay SEPARATE the corner cells' radius from
       rounded.css's .rounded-all rules actually renders — border and background curve together.
       #roster is no longer excluded: desktop.css:1104 and mobile.css:773 already resolve its border to
       var(--panel-border), so this changes its geometry only, never its color. - Claude 8/4/26 */
#main table.dashboard { border-collapse:separate !important; border-spacing:0 !important; border:0 !important; }
#main table.dashboard > tbody > tr > td,
#main table.dashboard > tr > td {
  border:0 !important;
  border-right:1px solid var(--panel-border, var(--accent)) !important;
  border-bottom:1px solid var(--panel-border, var(--accent)) !important;
}
#main table.dashboard > tbody > tr > td:first-child,
#main table.dashboard > tr > td:first-child  { border-left:1px solid var(--panel-border, var(--accent)) !important; }
#main table.dashboard > tbody > tr:first-child > td,
#main table.dashboard > tr:first-child > td  { border-top:1px solid var(--panel-border, var(--accent)) !important; }
/* (7) My Roster (#roster) stays colorful (position tints) and is excluded from (5) — BUT in Dark mode the
       mobile `table.rounded td{color:--ink !important}` rule + the dark link rule forced its cell text and
       player-name links LIGHT on those LIGHT position tints -> light-on-light. Pin the roster BODY text +
       links DARK in Dark mode (the "My Roster" header band is excluded so it stays light on its slate).
       Desktop already renders dark; this makes mobile match. - Claude 7/25/26 */
body.dfx-darkpanel #main table#roster tr:not(.majorHeader):not(.header) td,
body.dfx-darkpanel #main table#roster tr:not(.majorHeader):not(.header) td a { color:#111 !important; }

/* (8) The ROSTERS page (table.roster — a class, distinct from the dashboard #roster id above) has the
   SAME problem: in Dark mode the mobile `table.rounded td{color:--ink !important}` rule forces the
   player-row text + links LIGHT on the light position tints (Pos/Name/Bye/Pts) -> light-on-light. Pin the
   player rows DARK. The tr.header (team-name band) and tr.subHeader (column band) are dark, so they're
   excluded and stay light. tr.standings is ALSO excluded: the Season Schedule nests a table.roster whose
   tr.standings cells are DARK surfaces (themed above) — pinning them #111 made them dark-on-dark (Lee
   7/25). Desktop + mobile. - Claude 7/25/26 */
body.dfx-darkpanel #main table.roster tr:not(.header):not(.subHeader):not(.majorHeader):not(.standings):not(.detail) td,
body.dfx-darkpanel #main table.roster tr:not(.header):not(.subHeader):not(.majorHeader):not(.standings):not(.detail) td a { color:#111 !important; }

/* Friends & Avoids page (table#friends, which also carries class roster+standingsTable). Two fixes:
   (a) Dark mode: the roster dark-text pin above leaked in via tr.detail -> team-name cells were
       dark-on-dark. Pin the detail cells to --ink (light on the dark cell); #friends id specificity wins,
       desktop + mobile.
   (b) Selected button: mobile.css fills it hardcoded pink (#E994AB) and makes it 75px vs the 55px
       unselected -> disliked pink + grew on click. Use the skin's --complementary fill (white text; every
       skin's --complementary is a mid-tone that takes white), and keep it the SMALL unselected size on
       mobile (55px) so it doesn't resize. Desktop is already 75/75. box-sizing:border-box so any border
       swap can't shift layout. - Claude 7/25/26 */
body.dfx-darkpanel #main table#friends tr.detail td { color:var(--ink) !important; }
#main table#friends button.friendSelected {
  background-color:var(--complementary) !important; border-color:var(--complementary) !important; color:#fff !important;
}
body.dfx-mobile #main table#friends button.friendSelected { width:55px !important; }		/* match mobile unselected (55px) */
#main table#friends button.friendNotSelected,
#main table#friends button.friendSelected { box-sizing:border-box !important; }

/* De-teal other data tables that hardcode DataForce teal in their OWN (table-specific) selectors, so they
   beat the skin-base header/border tokens: strong teal HEADERS -> the skin's major band, teal CELL BORDERS
   -> the skin's --panel-border. Identical on DataForce, skin-themed elsewhere (e.g. ember). Same class of
   fix as the dashboard; desktop + mobile. My Roster excluded (Lee). Widen this list as more pages surface.
   - Claude 7/25/26 */
#main table.standingsTable tr.header td  { background-color:var(--major-bg) !important; color:var(--major-text) !important; }
#main table.standingsTable td,
#main table#draftBoard, #main table#draftBoard td,
#main table#articleTable td              { border-color:var(--panel-border, var(--accent)) !important; }
/* The ROUNDED-table frame: css/rounded.css draws the outer edge on first/last cells hardcoded teal
   #4A9586, overriding the harness's themed skins/rounded.css on the top/left sides. Theme the frame so
   every rounded table's outer border follows the skin (identical on DataForce). `body` prefix guarantees
   we out-specify css/rounded.css regardless of load order. - Claude 7/25/26 */
body #main table.rounded > tbody > tr > td:first-child,
body #main table.rounded > tr > td:first-child        { border-left-color:var(--panel-border, var(--accent)) !important; }
body #main table.rounded > tbody > tr:first-child > td,
body #main table.rounded > tr:first-child > td        { border-top-color:var(--panel-border, var(--accent)) !important; }
body #main table.rounded > tbody > tr > td:last-child,
body #main table.rounded > tr > td:last-child         { border-right-color:var(--panel-border, var(--accent)) !important; }
body #main table.rounded > tbody > tr:last-child > td,
body #main table.rounded > tr:last-child > td         { border-bottom-color:var(--panel-border, var(--accent)) !important; }

/* Identity Verification (ID-webcam) hint/label text: desktop.css hardcodes `#main table.webcam tr td
   {color:#000}` (black), which the harness --ink can't reach — so the labels + .smallPrint hints go
   black-on-dark (unreadable) in Dark mode. Hand the cell text to --ink so it follows the surface in
   every mode (dark text on light, light text on dark). - Claude 7/23/26 */
#main table.webcam tr td                 { color:var(--ink); }

/* Dark mode: give text fields a subtle INSET shadow so they read as recessed inputs (depth), on top of
   the lighter surface + visible border from modes.css. Excludes buttons/checkboxes/radios. - Claude 7/23/26 */
body.dfx-darkpanel #main input:not(.buttonEntry):not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]),
body.dfx-darkpanel #main select,
body.dfx-darkpanel #main textarea   { box-shadow:inset 0 1px 2px rgba(0,0,0,.35); }

/* Mobile textareas have NO inner padding: mobile.css:401 (#main textarea) sets only width/height/radius,
   and neither skin-base nor mobile.css adds padding — so typed text jams into the top-left corner
   (Support Ticket .notes, and every other textarea). Text INPUTS aren't affected (mobile.css:473/544 pad
   them); desktop textareas already pad (desktop.css:705). Universal mobile fix; border-box so the padding
   stays inside the fixed width instead of overflowing the frame. - Claude 7/25/26 */
body.dfx-mobile #main textarea { padding:.5em; box-sizing:border-box; }

/* fullScreen marker (⤢ = "opens the full-screen/desktop version"). play.js:413 already fills this span
   with `&nbsp;&nbsp;<img whitefullscreen.png>`, so we DON'T add an icon (that double-rendered it) — we
   just pin the span (carrying its img) to the RIGHT of the row, collapse play.js's &nbsp; spacing, and
   drop the › chevron on those rows since the ⤢ IS the affordance. - Claude 7/22/26 */
body.dfx-mobile #main #menubar span.dropdown ul li .fullScreen {
  position:absolute; right:22px; top:50%; transform:translateY(-50%);   /* px, NOT em — the font-size:0 (collapses play.js's &nbsp;) would zero out an em */
  width:14px; height:14px; margin:0; padding:0; font-size:0; line-height:0; pointer-events:none; z-index:2;
}
body.dfx-mobile #main #menubar span.dropdown ul li:has(.fullScreen) a::after { content:none; }

/* The X markers (fullScreen items + the blackfullScreen on the standalone My Leagues link) recolored
   to the SKIN: mask the monochrome PNG and fill with --accent (one white source recolors both the
   white and black variants). play.js's <img> is hidden; the masked span IS the icon. - Claude 7/22/26 */
body.dfx-mobile #main #menubar .fullScreen,
body.dfx-mobile #main #menubar .blackfullScreen {
  background-color:var(--accent) !important;
  -webkit-mask:url(/images/whitefullscreen.png) center / contain no-repeat;
          mask:url(/images/whitefullscreen.png) center / contain no-repeat;
}
body.dfx-mobile #main #menubar .fullScreen img,
body.dfx-mobile #main #menubar .blackfullScreen img { display:none !important; }

/* My Leagues is a standalone link (direct child of #menubar, NO submenu) -> no caret, and show its X
   at the right of the header row. - Claude 7/22/26 */
body.dfx-mobile #main #menubar > button.menubarButton::after { content:none !important; }
body.dfx-mobile #main #menubar > button.menubarButton .blackfullScreen {
  position:absolute; right:1em; top:50%; transform:translateY(-50%);
  width:15px; height:15px; font-size:0; pointer-events:none; z-index:2;
}

/* ========================== HOME PAGE + FOOTERS ==============================
   #home content sits on the PAGE background; the news box and home footer are light
   boxes ON that page. desktop.css hardcodes every link color here (#23819C news +
   home footer, the global a{#8DC7BA} for "amazing payouts", #f8f8f8 admin footer), so
   none of them follow the skin. Split by SURFACE: on-page links use --link-page,
   on-panel (inside a light box) links use --link. */
#home a, #home a:visited, #home a:focus    { color:var(--link-page, color-mix(in srgb, var(--accent) 60%, white)); }
#home #newAtDF a, #home #newAtDF a:visited { color:var(--link, var(--accent)); }
#home #newAtDF a:hover, #home #newAtDF a:focus { color:var(--link, var(--accent)); filter:var(--link-hover, brightness(.62)); }

/* home footer = a light box -> on-panel link color */
#footer #homeFooter a, #footer #homeFooter a:visited     { color:var(--link, var(--accent)); }
#footer #homeFooter a:hover, #footer #homeFooter a:focus { color:var(--link, var(--accent)); filter:var(--link-hover, brightness(.62)); }
#footer div#homeFooter                     { border-color:var(--panel-border); }
/* admin footer sits ON THE PAGE with white text/border -> invisible on a light page */
#footer #adminFooter a, #footer #adminFooter a:visited     { color:var(--link-page, color-mix(in srgb, var(--accent) 60%, white)); }
#footer #adminFooter a:hover, #footer #adminFooter a:focus { color:var(--link-page, color-mix(in srgb, var(--accent) 60%, white)); filter:var(--link-page-hover, brightness(1.30)); }
#footer div#adminFooter                    { border-color:var(--page-text); color:var(--page-text); }

/* commissioner/volunteer permalink at the page bottom (#thisURL, injected by common.js when
   $volunteer==1) — a plain footer link that fell through to the hardcoded green base link color.
   It sits on the dark page frame, so use the page-tuned lightened-accent link like the header /
   admin-footer links. - Claude 7/18/26 */
#footer #thisURL a, #footer #thisURL a:visited     { color:var(--link-page, color-mix(in srgb, var(--accent) 60%, white)); }
#footer #thisURL a:hover, #footer #thisURL a:focus { color:var(--link-page, color-mix(in srgb, var(--accent) 60%, white)); filter:var(--link-page-hover, brightness(1.30)); }

/* The news-box "glow" is not a shadow — it's 3 nested rings of hardcoded yellow
   (#FFDF00 -> #ffef80 -> #fff7bf -> #fffbdf) around a near-white core. It reads well on a
   black page but goes muddy on a light one. Derive the rings from --accent so the halo
   matches the skin in every mode, and let the core follow the content surface. */
#home div#newAtDF                          { border-color:color-mix(in srgb, var(--accent) 70%, white);
                                             background-color:color-mix(in srgb, var(--accent) 42%, white); }
#home div#newAtDF2                         { border-color:color-mix(in srgb, var(--accent) 22%, white);
                                             background-color:color-mix(in srgb, var(--accent) 22%, white); }
#home div#newAtDF3                         { border-color:color-mix(in srgb, var(--accent) 10%, white); }
#home span#newAtDF, #home table#newAtDF td { }
/* "What's new" inner box (#newAtDF3) uses --content-bg + --ink, which go DARK in Dark mode — but on
   mobile it should stay a LIGHT box like the white splash cards beside it (Lee). Redeclare the light
   surface tokens on the box in Dark mode so its bg (var--content-bg), text (var--ink) and links
   (var--link) all flip to light-box values. Mobile-only; desktop untouched. - Claude 7/23/26 */
body.dfx-mobile.dfx-darkpanel #home #newAtDF3 {
  --content-bg:#ffffff; --ink:#1b222a;
  --link:var(--link-light, color-mix(in srgb, var(--accent) 85%, black));	/* per-skin on-light link - Claude 8/3/26 */
}

/* HOME / SPLASH black backdrop: on the hero pages (body.dfx-herobg, set by skinswitch when the
   crest-on-field hero is visible), the whole #content sits on solid BLACK so the dark hero + circle
   buttons blend into it and the light content boxes (What's new, splash cards) pop. Inner pages keep
   the transparent #content (page gradient shows). Uses both flag classes to outrank the general
   `body.dfx-mobile #content{background:transparent}`. - Claude 7/23/26 */
body.dfx-mobile.dfx-herobg #content        { background:#000 !important; }

/* The page title (e.g. "The Wonk") sits in .mobileLogo, directly on that forced-black #content. But
   inside this subtree the palette is re-declared to the light-theme variant, so --page-text / --ink
   there resolve DARK (#1b222a) — meant for a light surface, not this black one. Result: dark-on-dark.
   The backdrop is unconditionally black regardless of skin, so pin the title to a fixed light tone
   rather than a (locally-dark) skin var. - Claude 8/13/26 */
body.dfx-mobile.dfx-herobg #content .mobileLogo h1 { color:#f4f7fa; -webkit-text-fill-color:#f4f7fa; }

/* ============================= HEADER / TITLE AREA =============================
   These all sit on the PAGE background (not inside #main), and desktop.css assumes
   that page is always dark — so everything here is hardcoded white/teal and washes
   out in Light mode. Tokenize so each follows the skin + mode. */

/* white title text -> follows the page text color (dark on the light page) */
#title span#hubTitle                       { color:var(--page-text); }
#title span#seasonLong a,
#title span#seasonLong a:visited,
#title span#seasonLong a:focus             { color:var(--page-text); }

/* the little white hub icons (house / helmet / commish) above the hub name are white
   PNGs, invisible on a light page. Darken them via a mode token (none = stay white). */
#title span#hubTitle img                   { filter:var(--icon-filter, none); }

/* header links ("Sign out") — desktop.css hardcodes #8DC7BA so it never follows the
   skin. These sit on the PAGE, so Classic/Dark want a lightened accent and Light a
   darkened one. navButton links keep their own higher-specificity white rule. */
#title a, #title a:visited, #title a:focus { color:var(--link-page, color-mix(in srgb, var(--accent) 60%, white)); }
/* …and they had NO :hover rule at all, so "Sign out" / "Sign in" / House Rules / Help-FAQ did nothing
   on hover (Lee 8/3/26). Same treatment as the body links: a filter that moves the link away from its
   surface — --link-page-hover lifts by default because this header sits on the dark page gradient in
   Classic, and modes.css flips it to a darken in Light. (Underline dropped, as on #main a.) */
#title a:hover, #title a:focus             { filter:var(--link-page-hover, brightness(1.30)); }
/* header nav BUTTONS contain anchors too, and the hover rule above would brighten just their LABEL
   against a button face that does not move — they are buttons, not text links. (This also carried the
   underline exclusion until the underline was dropped; desktop.css:364 already pins every link to
   text-decoration:none, so nothing here needs to say it.) - Claude 8/3/26 */
#title button a:hover, #title button a:focus { filter:none; }

/* running clock — clock.css hardcodes background-color:#4A9586 */
.digital-clock                             { background-color:var(--accent); }

/* strip above the logo: the icon column (Coach box + search box + clock) is three stacked block
   divs pulled onto one line with big position:relative top: offsets. Relative positioning keeps
   their ORIGINAL flow height reserved (~90px), so the whole left block is pushed down and the
   logo (measured) starts ~36px below the top-aligned hub buttons. Rather than fight the reserved
   height, shift the ENTIRE left table up as one unit with position:relative (leaves its layout
   space intact -> every icon keeps its offset). The matching negative margin-bottom then reclaims
   the 36px that position:relative left reserved, so #title shrinks and #menubar + the rest of the
   page slide up too (closing the empty strip that would otherwise sit above the menu). - Claude 7/18/26 */
/* Scoped to DataForce (its right column is .side; Apex uses .apexside, SBFFC .sbffcside): only the
   DataForce header has the coach/search/clock column that creates the strip, so only it should be
   shifted. Apex/SBFFC headers have no such column and must not be pulled up. - Claude 7/18/26 */
#title:has(.side) #topLeft                 { position:relative; top:-28px; margin-bottom:-28px; }

/* Coach Blitz + magnifier boxes: hardcoded `border:1px solid white` -> skin color */
#title div#igotgameplans5,
#title div#searchbox                       { border-color:var(--accent); }
/* magnifier fit (long-standing): the box is 29x29 but ALSO carried a 1px border AND
   .4px padding while the <img> was a fixed 29px — so it could never fit, and the box
   color leaked out as slivers on the sides. box-sizing + padding:0 + a trimmed,
   TRANSPARENT png (swapped in by skinswitch.js) makes the glass fill the box cleanly. */
/* fill behind the magnifier = the SAME lighter surface the search field uses (desktop.css
   hardcodes #CFE7E2 there). --input-bg is that surface in token form, so the little box and
   the search popup match exactly and both follow the skin/mode. */
#title div#searchbox                       { background-color:var(--input-bg); box-sizing:border-box;
                                             padding:0; overflow:hidden;
                                             /* match Coach Blitz box exactly: #igotgameplans5 is content-box
                                                29px + .4px padding + 1px border = 31.8px outer. Pin the (border-box)
                                                magnifier to that same outer size so the two boxes are identical. - Claude 7/17/26 */
                                             width:31.8px; height:31.8px; }
#title div#searchbox div#search2,
#title div#searchbox input#searchbar       { background-color:var(--input-bg); color:var(--input-text); }
#title div#searchbox img#searchpng2        { width:100%; height:100%; object-fit:contain;
                                             display:block; border-radius:0; }

/* ============================================================================
   THE WONK avatar frames.  A transparent Wonk (images/wonkN-t.png) sits inside a
   skin-tinted OVAL (full body) or head-CIRCLE, both ringed in --accent.  The tint
   and ring recolor per skin + light/dark via color-mix, so no image work per skin.
   Size with --wonk-size (default 130px), e.g. style="--wonk-size:220px".  The oval takes any
   full-body pose (wonkN-t.png); the circle takes the pre-framed head crop (wonk1-head.png).  Markup:
     <span class="wonk-oval"><img src="images/wonk8-t.png" alt="The Wonk"></span>
     <span class="wonk-circle"><img src="images/wonk1-head.png" alt="The Wonk"></span>
   - Claude 7/17/26 */
.wonk-oval, .wonk-circle {
	--wonk-size:130px;
	display:inline-flex; justify-content:center; overflow:hidden; vertical-align:middle;
	background-color:color-mix(in srgb, var(--accent) 20%, var(--content-bg, #fff));
	border:3px solid var(--accent); box-sizing:border-box; line-height:0;
}
.wonk-oval  { height:var(--wonk-size); width:calc(var(--wonk-size) * 0.68);
	border-radius:50%; align-items:flex-end; }
.wonk-oval  img { height:calc(var(--wonk-size) * 0.90); width:auto; max-width:none; margin-bottom:3%; }
.wonk-circle{ height:var(--wonk-size); width:var(--wonk-size);
	border-radius:50%; align-items:center; }
.wonk-circle img { height:calc(var(--wonk-size) * 0.82); width:auto; max-width:none; }
/* the wrapped <img> keeps its class="wonkButton" (desktop.css:348 float:right + bottom/left
   padding), which shoves the head up-right inside the frame — neutralise it. */
.wonk-oval img, .wonk-circle img { float:none; padding:0; }

/* THE WONK'S OWN LINK (a.dfx-wonk-link, tagged by skinfx.js wonkUnit()).
   Five per-page branches in skinfx.js REPOSITION the Wonk, and until 8/7/26 they moved the
   .wonk-oval span alone — which tore the image out of its <a href="...-wonk"> and left the anchor
   behind, empty and 0x0. Seven of the eleven desktop Wonk buttons rendered correctly and clicked
   nowhere.
   The fix SWAPS THE NESTING so the link sits inside the frame, around the image:
     <span class="wonk-oval"><a class="dfx-wonk-link" href="lineup-wonk"><img></a></span>
   The relocated node stays the span, so the surrounding layout cannot shift. (Moving the anchor
   instead was tried and abandoned twice — display:contents cannot take keyboard focus, and
   inline-block measured clean on four pages then pushed Set Lineup's bench down 15px. Details in
   skinfx.js wonkUnit().)
   This rule makes the anchor REPRODUCE THE SPAN'S OWN FLEX CENTRING, so inserting it between the
   frame and the figure changes nothing about where the figure sits: same justify-content, same
   align-items per shape (oval = flex-end, circle = center), filling the frame so the whole badge
   stays clickable rather than just the image.
   FOCUS RING AND HOVER GO ON THE SPAN, via :has(). The span sets overflow:hidden, so an outline
   drawn on the anchor inside it would be clipped away. :has() is already the page-scoping idiom in
   this file (#main:has(#mypct), #main:has(#scoringAudit)).
   :focus-visible only, and split into its own rule, per the convention at the top of this file.
   Only the five relocating branches get this class; the other six Wonk buttons keep their original
   <a><span><img></span></a> markup and are unaffected. - Claude 8/7/26 */
.wonk-oval  > a.dfx-wonk-link,
.wonk-circle > a.dfx-wonk-link { display:flex; width:100%; height:100%;
                                 justify-content:center; line-height:0; }
.wonk-oval  > a.dfx-wonk-link  { align-items:flex-end; }
.wonk-circle > a.dfx-wonk-link { align-items:center; }
.wonk-oval:has(> a.dfx-wonk-link:hover),
.wonk-circle:has(> a.dfx-wonk-link:hover)          { filter:var(--hover-lift); }
.wonk-oval:has(> a.dfx-wonk-link:focus-visible),
.wonk-circle:has(> a.dfx-wonk-link:focus-visible)  { outline:3px solid var(--accent); outline-offset:2px; }

/* the Set-Lineup wonkButton circle sits high/left of the projection dropdown; nudge the whole
   circle down + right (position:relative shifts it visually without disturbing the row). */
.wonk-circle.dfx-wonk-lineup { position:relative; top:12px; left:14px; }
/* inline-in-text wonkButtons (Owner Setup etc.): lift him up ~half his height and collapse his
   line footprint with negative margins so the floated circle stops pushing "...The Wonk" to wrap. */
.wonk-circle.dfx-wonk-inline  { margin-top:-25px; margin-bottom:-25px; position:relative; left:48px; }
/* PRIZE SHARING gets the inline treatment UNDONE (Lee 8/6/26: "the wonk image needs moved left and
   down"). The nudge above is tuned for a wonkButton sitting INLINE IN A PARAGRAPH — lift him half his
   height, pull the line footprint in, push him clear of the text. On Prize Sharing he is not inline in
   anything: prizeshare.php emits the anchor as the FIRST node of the view, so desktop.css:348's
   float:right parks him at the top-right of the content and the +48px then shoves him out over the
   panel's right edge, with -25px holding him up level with the breadcrumb. Both offsets are wrong here
   rather than too large, so they are zeroed, not retuned: left:0 drops him back inside the panel at the
   float's own right edge, and margin-top:0 lets him sit in normal flow below the breadcrumb row.
   margin-bottom stays collapsed so he does not add a blank band above "Prize Sharing".
   Scoped by its own class because .dfx-wonk-inline is the shared fallback for every wonkButton that
   matches no earlier branch (Owner Setup and others), and those DO want the inline nudge.
   - Claude 8/6/26 */
/* PRIZE SHARING gets the inline nudge undone AND pulled further left (Lee 8/6/26: "moved left and
   down", then twice more "still 3/4 inch too far right").
   SCOPED BY #mypct, NOT BY A CLASS FROM skinfx.js. My first two attempts tagged the Wonk from
   JavaScript after testing location.pathname, and neither ever applied: common.js display() injects
   these pages by AJAX, so the URL never changes and the test was reading the previous page's path.
   #mypct is the share-percentage input on views/prizeshare.php — it renders, and it appears in no
   other view (checked). Same :has() page-scoping used for #scoringAudit further down.
   The offsets: .dfx-wonk-inline is tuned for a Wonk sitting INLINE IN A PARAGRAPH (Owner Setup) —
   lift him half his height, push him clear of the text. Here he is not inline in anything;
   prizeshare.php emits the anchor as its FIRST node, so desktop.css:348 float:right parks him hard
   against the content's right edge, level with the breadcrumb. So margin-top:0 drops him into normal
   flow, and left runs to -120px: the inline rule's own +48px has to be cancelled BEFORE any of Lee's
   3/4 inch counts, and 48 + 72 = 120. That is the arithmetic my -72px missed — it was measured from
   a position the page never actually had. margin-bottom stays collapsed so he adds no blank band
   above the "Prize Sharing" heading. - Claude 8/6/26 */
#main:has(#mypct) .wonk-circle.dfx-wonk-inline { left:-120px; margin-top:0; }

/* SET LINEUP player names are <a class="a"> links hardcoded to color:black (desktop.css:683),
   which vanish on the dark row surface in dark mode.  Pin them to --ink, and swap the yellow
   hover box for a skin-accent tint. */
#main table.lineup td.lineupPlayer a.a,
#main table.lineup td.lineupPlayer a.a:visited { color:var(--ink); }
/* [BRACKETED] legend + the bracketed projected points are both span.projection (desktop.css:1005
   hardcodes #338575 teal, low-contrast on the dark row). --link is the mode-aware accent ink. */
#main span.projection { color:var(--link); }
#main table.lineup td.lineupPlayer a.a:hover,
#main table.lineup td.lineupPlayer a.a:focus {
	background-color:color-mix(in srgb, var(--accent) 20%, var(--content-bg));
	border-color:var(--accent); color:var(--ink); }

/* ⚠ ...AND THE SAME LINKS EXIST OUTSIDE table.lineup, WHERE THE FIX ABOVE NEVER REACHED THEM.
   Lee, 8/10/26: "On the Onyx classic skin, the draft page, when I mouse over players in the boxes, it
   highlights white print with a white box, unreadable."
   Exactly right, and it is this rule's scope that is wrong rather than anything new. desktop.css:680
   hovers EVERY `#main a.a` to a hardcoded pale yellow `#FFF7B7`, and desktop.css:679 colours the link
   `var(--ink, black)`. Onyx is the one palette that overrides --ink — `#e5e7eb`, light, because its
   content panel is dark — so on Onyx the hover is light text on a near-white box: **1.13:1**. Every
   other palette keeps --ink #111111 and reads 17.3:1 on that same yellow, which is why this only ever
   showed up on Onyx.
   The 8/6 fix above solved it for Set Lineup and was scoped `table.lineup td.lineupPlayer`. But the
   SAME `a.a` links are emitted by play.php:3789/3872 into the drag-and-drop boxes on Draft, and the
   `tr.a` rows play.js:2296 makes draggable are shared by Nominate, Waivers and Free Agents too — none
   of which are inside table.lineup. So the treatment simply never applied there. Lee predicted the
   other pages before I looked; he was right.
   Unscoping it to `#main a.a` matches desktop.css:680's own specificity (1 id, 2 classes, 1 element)
   and skins-live loads after css/*, so it wins on order. The lineup rule above is left in place: it is
   more specific, identical in effect, and carries its own history.
   Verified across 12 palettes x 3 lightings: 10.43-14.98:1, worst case Onyx Classic (1.13 -> 10.43).
   --content-bg is always defined (skin-base #FAFEFB, overridden by modes.css per mode and by onyx), so
   the color-mix can never fall back to the raw yellow. - Claude 8/10/26 */
#main a.a:hover,
#main a.a:focus {
	background-color:color-mix(in srgb, var(--accent) 20%, var(--content-bg));
	border-color:var(--accent); color:var(--ink); }

/* TEAM MANAGER (Play) hub footer links: the tr.displayPageFooter row (FAQ · Fair Play · …),
   which falls to the general a{color:#8DC7BA}. Point them at --complementary. The Admin hub
   footer (#adminFooter) and the home footer (#homeFooter) are both left at their originals. */
/* Each skin's own link color (--link is the mode-aware accent-ink; classic falls back to a
   light accent since that footer sits on dark chrome). Not the complementary hue. */
#footer tr.displayPageFooter a, #footer tr.displayPageFooter a:visited {
	color:var(--link, color-mix(in srgb, var(--accent) 55%, #fff)); }
#footer tr.displayPageFooter a:hover, #footer tr.displayPageFooter a:focus {
	color:color-mix(in srgb, var(--link, var(--accent)) 68%, #fff); }

/* INJURY REPORT (table#injuries, Player News): the mint fill was a background on the SQUARE
   table (desktop.css:893 #E7F3F1) while the rounding lives on the corner cells, so the table's
   square corners poked past the rounded box and read as white bleed on a dark page. Move the
   fill onto the (rounded) cells + make the table transparent. The accent-tinted surface also
   fixes the low-contrast light-on-light-mint text that dark mode had. */
#main table#injuries        { background-color:transparent; }
#main table#injuries td     { background-color:color-mix(in srgb, var(--accent) 12%, var(--content-bg)); }
#main table#injuries h3     { color:var(--ink); }		/* headings are ink now, not an accent - Claude 8/3/26 */

/* ============================ EMAIL DRAFT PAGE (email-draft) ============================ */
/* Drop zones (draft.css:9 — all three hardcoded #C0E0DA teal + faint text). Tint each to its
   MEANING (Look=info/complementary, Draft=go/accent, Remove=danger) over the surface, and pin
   the labels to --ink so they're readable. The colored icons (chart/check/X) stay. */
#main td#lookAtMe { background-color:color-mix(in srgb, var(--complementary) 16%, var(--content-bg)); border-color:var(--complementary); }
#main td#draftMe  { background-color:color-mix(in srgb, var(--dfx-go, #1a8f57) 18%, var(--content-bg)); border-color:var(--dfx-go, #1a8f57); }   /* Draft = go-green (Lee 7/21); carries to email-draft + nominate - Claude 7/21/26 */
#main td#dropMe   { background-color:color-mix(in srgb, var(--danger) 16%, var(--content-bg)); border-color:var(--danger); }
#main table#picks span.bigLetters,
#main table#picks span.smallLetters { color:var(--ink); }
#main table#picks td.hovering       { border-color:var(--accent); }   /* keep the 3px, recolor off #4A9586 */

/* List boxes: Available Player List / Pre-Draft List / Watch List (draft.css:8 div.draftBox
   #E7F3F1 square-light). Accent-tinted surface + --ink text + accent border; title -> heading. */
#main div.draftBox   { background-color:color-mix(in srgb, var(--accent) 8%, var(--content-bg));
                       border-color:var(--accent); color:var(--ink);
                       /* skin the scrollbar (Match Players boxes + draft list boxes). Firefox: */
                       scrollbar-width:thin;
                       scrollbar-color:var(--accent) color-mix(in srgb, var(--accent) 12%, var(--content-bg)); }
/* WebKit/Blink (Chrome/Edge/Safari): accent thumb on a faint accent-tinted track */
#main div.draftBox::-webkit-scrollbar        { width:12px; height:12px; }
#main div.draftBox::-webkit-scrollbar-track  { background:color-mix(in srgb, var(--accent) 12%, var(--content-bg)); border-radius:6px; }
#main div.draftBox::-webkit-scrollbar-thumb  { background:var(--accent); border-radius:6px;
                       border:2px solid transparent; background-clip:padding-box; }
#main div.draftBox::-webkit-scrollbar-thumb:hover { background:color-mix(in srgb, var(--accent) 82%, #000); }
/* ...but the Match Players boxes (#NFLdata/#MFLdata) get an OVERLAY scrollbar from the niceScroll
   plugin (commish.js), painted with hardcoded inline colors (cursor #8DC7BB / border #4A9586 /
   rail #CFE7E2) that replace the native bar — that's why it flashed skin-colored then reverted.
   Repaint the plugin's cursor + rail to the skin. !important is required to beat its inline styles.
   - Claude 7/18/26 */
.nicescroll-cursors  { background-color:var(--accent) !important; border-color:var(--accent) !important; }
.nicescroll-rails    { background-color:color-mix(in srgb, var(--accent) 12%, var(--content-bg)) !important; }
/* the player names are <a class="drag"> links, so #main a{#23819C} wins over the box color —
   pin them to --ink so they're light/readable in dark mode. */
#main div.draftBox a, #main div.draftBox a:visited { color:var(--ink); }
#main span.boxTitle  { color:var(--heading, var(--ink)); }

/* DRAFT-PAGE TEXT FIELDS (Lee 8/5/26: the Comment box is white-on-white in Onyx Classic).
   draft.css:29 pins `#main table#draft input.lineEntry` to a hardcoded background-color:#F8F8F8, and
   :30 pins hover/focus to #FFF7B7. Those sit at specificity (2,1,2) — TWO ids, because table#draft is
   an id — while the generic field rules in this sheet are `#main input.lineEntry` at (1,1,2). So the
   hardcoded near-white won everywhere on this page, and only the COLOR of the text followed the skin.
   On thirteen palettes that is invisible (their panels are light anyway). On Onyx it is the whole bug:
   Onyx is the one DARK-CONTENT palette in Classic, so --input-text is #e5e7eb sitting on #F8F8F8.
   Onyx's own tokens already say what this should be — --input-bg:#1f2937 with --input-text:#e5e7eb,
   "dark input, light text" — so nothing needed inventing here; the tokens just had to be able to reach
   the field. Adding `body` takes these to (2,1,3) and (2,2,3), one type selector past draft.css.
   Hover and focus reuse the exact accent-tint idiom the generic fields use above (6% / 11% into
   --input-bg, accent border, widening halo), so a field inside table#draft now behaves identically to
   one outside it instead of flashing #FFF7B7. - Claude 8/5/26 */
body #main table#draft input.lineEntry,
body #main table#draft select.lineEntry
                     { background-color:var(--input-bg); color:var(--input-text);
                       border-color:var(--input-border); }
body #main table#draft input.lineEntry:hover,
body #main table#draft select.lineEntry:hover
                     { background-color:color-mix(in srgb, var(--accent) 6%, var(--input-bg));
                       border-color:var(--accent);
                       box-shadow:var(--input-shadow), 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent); }
body #main table#draft input.lineEntry:focus,
body #main table#draft select.lineEntry:focus
                     { background-color:color-mix(in srgb, var(--accent) 11%, var(--input-bg));
                       border-color:var(--accent); outline:none;
                       box-shadow:var(--input-shadow), 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent); }

/* THE WONK on the draft page: a full-body OVAL parked lower-left, just inside the panel edge
   (skinswitch tags it dfx-wonk-draft). position:fixed so it stays put; float from the wrapper
   is cleared. */
/* sit just ABOVE the Tweet button without pushing anything: absolute over the tweet cell (which
   skinswitch sets position:relative), out of flow. Nudge the figure a bit right inside the oval. */
.wonk-oval.dfx-wonk-draft     { position:absolute; bottom:100%; right:0;
                                margin:0 0 14px 0; float:none; }
.wonk-oval.dfx-wonk-draft img { transform:translateX(5%); }

/* Waiver Bids / Free Agents: oval flush-right just above the Look At Me box (out of flow). */
.wonk-oval.dfx-wonk-look      { position:absolute; bottom:100%; right:0;
                                margin:0 0 15px 0; float:none; }
.wonk-oval.dfx-wonk-look img  { transform:translateY(12px); }   /* figure nudged down inside the oval */
/* Offer Trade: oval flush-right just above the message textarea (wrapped in a relative div). */
.dfx-wonk-tradewrap           { position:relative; }
.wonk-oval.dfx-wonk-trade     { position:absolute; bottom:100%; right:8px; margin:0 0 12px 0; float:none; }
/* Trades Offered To Me: laptop oval, flush-right at the top of the trade list. */
.wonk-oval.dfx-wonk-respond    { float:right; margin:-12px 0 6px 12px; }   /* nudged up ~3mm */
/* Set Lineup: pointing oval flush-right with the bench (bench wrapped in a shrink-to-width box
   so right:0 lands on the bench table's own right edge, not the far page edge).
   LIVE AGAIN 8/5/26. This rule, and the five other per-page placements in this file, were dead from
   the moment skinfx.js began wrapping wonks site-wide: desktop.css declared `#main span.wonk-oval`
   at specificity (1,1,1) against these class-only selectors, so its position:relative won, and once
   the element was relative its top/left beat this bottom/right too. Measured on the live page:
   editing the numbers here moved the Wonk by exactly (0,0). That selector is now scoped to
   div.lrActions (desktop.css), which is the sample row it was drawn for, so these apply again. */
.dfx-wonk-benchwrap           { position:relative; width:-moz-fit-content; width:fit-content; }
.wonk-oval.dfx-wonk-lineup    { position:absolute; bottom:100%; right:0; margin:0 0 8px 0; float:none; }

/* Email Auction: laptop oval floated at the top-right of the leagueSide (same pose/place as Draft);
   the Tweet button is relocated (by skinswitch.js) to sit on its own line just above Refresh Page,
   so here it drops its float and rides the right-aligned leagueSide flow. */
.wonk-oval.dfx-wonk-auction     { float:right; margin:0 0 6px 8px; }
.wonk-oval.dfx-wonk-auction img { transform:translateX(5%); }
a.tweet.dfx-tweet-down          { display:block; }
a.tweet.dfx-tweet-down .dfx-tweet { float:none; }

/* "Tweet Your Draft Board" rebuilt as a skin pill (dfx-tweet), reusing .dfx-pill's complementary
   styling; just keep it on one line. */
.dfx-pill.dfx-tweet  { white-space:nowrap; float:right; }   /* restore the original tweet float:right so the oval sits flush to its right edge */

/* ── Full-screen pages that use #fullPageHeader (Draft Board, Live Stats, live draft/auction) ──
   fullpage.css hardcodes a black surround/header/footer (+ a stray blue #beta on the beta box) and
   near-white header text. Lee wants the skin's own page/content backgrounds to carry it, a skin
   header band, and the header logo/HELP/REFRESH on-brand. Shared chrome is scoped to
   body.dfx-fullpage; the Draft-Board-only bits stay on #draftboardtitle, and the Live-Stats
   table columns are scoped to table.livegames.  - Claude 7/18/26 */
/* let the skin page gradient show around #main (was #000 / blue #beta) */
body.dfx-fullpage #content,
body.dfx-fullpage #beta                { background-color:transparent; }
body.dfx-fullpage #footer              { background-color:transparent; }
/* the "outside border" of the page IS the skin page-gradient showing in #main's margin — set that
   frame (fullpage.css #main margin:1em) to ~4mm */
body.dfx-fullpage #main                { margin:4mm; }
/* buttons that wrap an <a> (e.g. draft board's "Join a league today!"): #main a colors the link
   teal, unreadable on the button in Classic. Inherit the button's own text color. */
body.dfx-fullpage #main button a,
body.dfx-fullpage #main button a:visited,
body.dfx-fullpage #main button a:hover { color:inherit; }
/* ...and everywhere else, which the fullpage-only version left broken (Lee 8/3/26, Getting Started on
   Primetime: black text on a navy button). Same bug, wider blast radius than anyone noticed — that page
   alone has six of these. The button paints itself correctly; it is the ANCHOR inside that goes wrong,
   because desktop.css:1283 pins a.gettingStarted to var(--ink). That was right while every skin's
   button was pale #8DC7BC and --ink was the readable choice on it; it stopped being right the moment a
   palette could set a DARK button. Primetime's --btn-bg is #1e3a8a, and --ink is #111111.
   `inherit` rather than a token: it takes whatever the button resolved to, which is
   var(--btn-ink, var(--btn-text)) — so it stays correct in Day and Night Game too, where the button
   text comes from a different token than in Classic.
   a.gettingStarted is listed explicitly because desktop.css's rule carries a class (1,1,2) and would
   outrank the bare `#main button a` (1,0,2); listed, it ties and wins on load order. - Claude 8/3/26 */
#main button a,
#main button a:visited,
#main button a:hover,
#main button a.gettingStarted          { color:inherit; }
/* skin-styled top header band (replaces the old black strip): strong accent band, light major-text,
   square corners, thin, inset by #main's padding */
body.dfx-fullpage #main div#fullPageHeader { background-color:var(--major-bg); color:var(--major-text);
                                                 padding:2px 10px; }
/* header title text fullpage.css pinned to near-white (#FAFEFB) — use the band's major-text */
body.dfx-fullpage #main td span#title  { color:var(--major-text); }
/* vertically centre the band contents (logo + title block) on one middle line */
body.dfx-fullpage #fullPageHeader > table > tbody > tr > td,
body.dfx-fullpage #fullPageHeader table#draftboardtitle td { vertical-align:middle; }
/* inline-block so a two-line title ("Live<br>Stats") stacks left-aligned as a unit beside the logo
   instead of the 2nd line wrapping under the logo (was the broken "Live / Stats" layout) */
body.dfx-fullpage #fullPageHeader span#title              { display:inline-block; vertical-align:middle; }
body.dfx-fullpage #fullPageHeader table#draftboardtitle   { display:inline-block; vertical-align:middle; }
/* the big league <select> was black w/ teal text + border — give it the skin input surface */
body.dfx-fullpage #main select#selectLeague { background-color:var(--input-bg);
                                                 color:var(--input-text); border-color:var(--accent); }
/* Same league selector on MOBILE (#crumbs select / #selectLeague): mobile.css:456 hardcodes DataForce
   teal (#8DC7BB bg + #4A9586 border), which clashes on other skins — give it the skin input surface like
   desktop (Lee 7/25). - Claude 7/25/26 */
body.dfx-mobile #main div#crumbs select,
body.dfx-mobile #main select#selectLeague { background-color:var(--input-bg) !important;
                                            color:var(--input-text) !important; border-color:var(--accent) !important; }
/* new crest logo (swapped in by skinswitch.js swapFullpageLogo) sized to the header row; un-float so
   it sits inline and vertically centres against the title */
body.dfx-fullpage #fullPageHeader img#logo { height:48px; width:auto; float:none; vertical-align:middle; }
/* HELP pill inline in the right header cell (drop its default float) */
body.dfx-fullpage #fullPageHeader a.pageText { float:none; margin:0 0 0 8px; }
body.dfx-fullpage #fullPageHeader .dfx-pill  { vertical-align:middle; }
/* REFRESH styled to match the Tweet/HELP pills; line-height matches the icon'd pills' height so they
   line up. On the teal --major-bg band, all these pills use --complementary-bright (plain
   complementary is too low-contrast against the band — same reason HOME does). */
body.dfx-fullpage #fullPageHeader button#reloadPage,
body.dfx-fullpage #fullPageHeader button#reloadPage:focus
                                               { background-color:var(--complementary-bright); color:#fff;
                                                 border:1px solid var(--complementary-bright); border-radius:13px;
                                                 font-weight:bold; font-size:13px; line-height:18px; padding:2px 11px;
                                                 box-shadow:0 2px 5px color-mix(in srgb, var(--complementary-bright) 40%, transparent);
                                                 vertical-align:middle; }
body.dfx-fullpage #fullPageHeader button#reloadPage:hover { filter:var(--hover-lift); }
/* HELP / Tweet pills that sit on the header band get the bright variant too (their .dfx-pill base is
   plain complementary, correct everywhere else — on light content — so only override in-band) */
#fullPageHeader .dfx-pill                      { background:var(--complementary-bright); border-color:var(--complementary-bright);
                                                 box-shadow:0 2px 5px color-mix(in srgb, var(--complementary-bright) 40%, transparent); }

/* DRAFT ENGINE STATUS BAR (Lee 8/17/26) — draftengine.php's new one-line strip sitting directly above
   the #fullPageHeader band. draft.css gives it the hardcoded Available-Player-List look (#E7F3F1 /
   #4A9586); re-tint it here with the SAME formula skins-live already uses for div.draftBox (line 2539,
   accent 8% over --content-bg + accent border + --ink text) so the bar reads as a light shade of the
   header band on every palette instead of staying teal. Scoped body.dfx-fullpage for specificity
   (2,1,1) — draft.css's `#main #draftStatusBar` is (2,0,0) and would otherwise tie-and-win only on load
   order; explicit is safer. Only draftengine.php has this element, so nothing else is affected.
   :not(.statusAlert) (Lee 8/17/26, HELP ME): in the alert state the bar goes the page's fixed #ff3300
   red to match button.redButton, which skins-live deliberately never re-tints. Standing aside here is
   what lets draft.css's plain `.statusAlert` rule win without !important. - Claude 8/17/26 */
body.dfx-fullpage #main #draftStatusBar:not(.statusAlert):not(.statusWarn) { background-color:color-mix(in srgb, var(--accent) 8%, var(--content-bg));
                                                 border-color:var(--accent); color:var(--ink); }
body.dfx-fullpage #main #draftStatusBar:not(.statusAlert):not(.statusWarn) #draftStatusText { color:var(--heading, var(--ink)); }

/* Draft-Board-only: round-number column (was fixed teal #8DC7BB) -> skin header band; keep the
   relocated Tweet pill inline next to REFRESH/HELP */
body.dfx-draftboard #main table#draftBoard td.number { background-color:var(--header-bg);
                                                 color:var(--header-text); }
body.dfx-draftboard #fullPageHeader .dfx-pill.dfx-tweet { float:none; margin:0 0 0 8px; }

/* Live Stats (livegames.php) columns — scoped to table.livegames so no normal page is hit. The Pos
   column (QB/RB/.../Bench) is a hardcoded teal #8DC7BB td.subHeader; make it the skin header band,
   bench cells a touch lighter to keep the starter/bench distinction. The bottom big-play ticker
   (#003333) becomes the major band. Game-status cells (notStarted/playing/redzone/allDone) carry
   their own meaning and must keep matching the legend swatches — note that as of 8/15/26 the
   in-progress cell deliberately SHARES this bench colour (Lee's call); the value now lives in
   --dfx-bench-fill so the two cannot drift apart. See the Live Stats status block further down. */
#main table.livegames tr.detail td.subHeader   { background-color:var(--header-bg); color:var(--header-text); }
#main table.livegames tr[class*="benchLine"] td.subHeader
                                               { background-color:var(--dfx-bench-fill); }
#main table.livegames tr.feedRow td            { background-color:var(--major-bg); border-top-color:var(--accent); }
#main table.livegames .feedCell .play          { color:var(--major-text); }
/* 1px border around each game */
#main table.livegames                          { border:1px solid var(--accent); }
/* Matchups roster reuses table.livegames — its player cells were hardcoded #000 text + #F8F8F8 WHITE
   borders -> dark-on-dark with ugly white borders in Dark mode (Lee 7/25). Token the text and skin-theme
   the cell borders. (subHeader/feed cells keep their own colors via more-specific rules above.) - Claude 7/25/26 */
#main table.livegames td                       { border-color:var(--panel-border, var(--accent)) !important; }
/* Vertical rule splitting the owner's block from the opponent's (Lee 8/20/26). livegames.php tags the
   LAST left-side cell of every row with .divide - the boundary has no other structural hook, since the
   cell index moves with $useMinutes and the row shapes differ. Mobile stacks the teams, has no boundary,
   and never gets the class.

   DRAWN AS A PSEUDO-ELEMENT, NOT A BORDER, AND THAT IS THE WHOLE POINT. This table is
   border-collapse:separate with the default 2px spacing, so a border-right on the boundary cell gives
   three wrong things at once: it is 2px not 1px, the 2px white gutter SURVIVES beside it so the channel
   reads as line-plus-gap instead of one line, and - worst - every row gutter is also 2px, so the border
   is chopped into per-row dashes. A border on a cell cannot span the gaps between rows. So: no border.
   A 1px bar is absolutely positioned INTO the gutter, and top/bottom -2px make each row's segment bleed
   across the horizontal gutter into its neighbours, which welds them into one unbroken line. (Fixing
   this with border-collapse:collapse would work but would strip the white gutters off every cell in the
   table - far too big a change for a divider.) - Claude 8/20/26 */
#main table.livegames td.divide                { position:relative; }
#main table.livegames td.divide::after         { content:''; position:absolute; top:-2px; bottom:-2px; right:-1px; width:1px; background-color:var(--accent); pointer-events:none; }
/* Matchups: skinswitch.matchupWonk() moves the inline Wonk head INTO the "Game Matchups" h1; position it
   absolutely at the header's right edge so it sits beside the (still-centred) title without moving it. - Claude 7/25/26 */
#main h1.dfx-matchup-head                       { position:relative; }
#main h1.dfx-matchup-head a.dfx-matchup-wonk    { position:absolute; right:48px; top:50%; transform:translateY(-50%); margin:0; }		/* nudged left from the right edge: 6 -> 24 -> 42 -> 48px - Claude 7/25/26 */
/* Player Search (table#playersearch): cell/header borders were hardcoded teal #4A9586 -> a green grid
   that clashes with the skin (Lee 7/25). Theme them to --panel-border. - Claude 7/25/26 */
#main table#playersearch td, #main table#playersearch th { border-color:var(--panel-border, var(--accent)) !important; }

/* League/Team banner (div#leagueHeader — top of many pages) + its two icon buttons: notepad
   (span.dashboardWonk) and dashboard (span.dashboardIcon). Were hardcoded tan (#d6c485) block + rust
   (#B05F3C) buttons, skin-blind. Make them skin-aware and attractive (Lee 7/25): the block gets a subtle
   glossy gradient of the skin's header band (--major-bg) with a soft top highlight + white header text;
   the buttons become dark skin-accent chips (dark enough that the light icons still read) with a soft
   shadow. - Claude 7/25/26 */
#main div#leagueHeader {
  background:linear-gradient(180deg, color-mix(in srgb, var(--complementary) 82%, #ffffff) 0%, var(--complementary) 100%) !important;
  color:#ffffff !important;
  border-color:color-mix(in srgb, var(--complementary) 55%, #000000) !important;
  box-shadow:inset 0 1px 0 color-mix(in srgb, #ffffff 35%, transparent);
}
#main span.dashboardIcon, #main span.dashboardWonk, #main span.dashboardNote {	/* dashboardNote = the notepad button (left); was missed, so it stayed rust while the dashboard button themed - Claude 7/25/26 */
  background-color:color-mix(in srgb, var(--accent) 68%, #000000) !important;
  border-color:color-mix(in srgb, var(--accent) 45%, #000000) !important;
  box-shadow:0 1px 2px rgba(0,0,0,.35);
}

/* Draft / Waivers / Free-Agent (Add-Drop) / Nominate list boxes (div.draftBox — #boxForPlayerList,
   #boxForDraftList, #boxForWatchList, and the same on the other pages) go DARK in Dark mode, but the
   player-name links + bold labels inside stayed hardcoded #000 -> black-on-dark, unreadable (Lee 7/25).
   Lift them in Dark mode: links -> --link, the rest -> --ink. All four pages share div.draftBox. - Claude 7/25/26 */
body.dfx-darkpanel #main div.draftBox              { color:var(--ink) !important; }
body.dfx-darkpanel #main div.draftBox a           { color:var(--link) !important; }
/* the actual content sits in a NESTED div (div#watchList/#playerList/#bidList/#dropList/#myPlayers, class
   wide-NN-N) that hardcodes color:#000 -> the empty-state text ("No players on list") and any non-link
   text stayed black-on-dark. Cover nested divs (+ b/td); the span.boxTitle keeps its accent color. - Claude 7/25/26 */
body.dfx-darkpanel #main div.draftBox div,
body.dfx-darkpanel #main div.draftBox b,
body.dfx-darkpanel #main div.draftBox td          { color:var(--ink) !important; }

/* Draft History list (table.mobileTable). Three fixes (Lee 7/25):
   (1) detail cells were hardcoded #000 -> black-on-dark in Dark mode; lift them to --ink (keep the teal
       td.mobileDraftList player-name cells).
   (2) it's border-collapse:separate over a WHITE table bg, so the "cell borders" were really white gaps
       that disappear in Light/Classic; collapse it and give it a real themed grid in every mode.
   (3) tint the 2nd line of each record (the player-name row, the one holding td.mobileDraftList) a slightly
       different shade, same recipe as the #money striping. - Claude 7/25/26 */
body.dfx-darkpanel #main table.mobileTable td:not(.mobileDraftList) { color:var(--ink) !important; }
#main table.mobileTable tr:has(td.mobileDraftList) td   { background-color:color-mix(in srgb, var(--ink) 10%, var(--row-bg, var(--content-bg))) !important; }

/* (4) WHITE CORNERS + SQUARE BOX (Lee 7/26). Two entangled bugs:
   - mobile.css paints the TABLE itself #F8F8F8, so wherever a corner cell is rounded the square table
     corner shows through the notch as a white triangle (visible in PRODUCTION too).
   - fix (2) above used border-collapse:collapse to get a single-line grid — but in the COLLAPSED border
     model browsers IGNORE border-radius on cells. So rounded.css's `tr.rounded > td:first/last-child`
     6px silently stopped painting and the box went square-cornered. (Computed style still reports 6px,
     which is why this reads as "the class is there but nothing happens".)
   Both go away with the same separate-borders recipe already used for table#leagues/.data/#money:
   keep border-collapse SEPARATE (radius needs it) with zero spacing, let the TABLE draw the rounded
   outer frame, and have each cell draw only its right+bottom line — last column and last row drop
   theirs so the outer edge isn't doubled. The four corner cells get the radius explicitly (not relying
   on rounded.css, so it holds even where the header row lacks class='rounded'), and 6px is hardcoded to
   match rounded.css rather than --table-radius, which some skins set to 8px = a visible mismatch
   between the frame and the cell it clips. - Claude 7/26/26 */
#main table.mobileTable {
  border-collapse:separate !important; border-spacing:0 !important;
  background-color:transparent !important;
  border:1px solid var(--panel-border, var(--accent)) !important;
  border-radius:6px;
}
#main table.mobileTable td {
  border:0 !important;
  border-right:1px solid var(--panel-border, var(--accent)) !important;
  border-bottom:1px solid var(--panel-border, var(--accent)) !important;
}
#main table.mobileTable > tbody > tr > td:last-child             { border-right:0 !important; }
#main table.mobileTable > tbody > tr:last-child > td             { border-bottom:0 !important; }
#main table.mobileTable > tbody > tr:first-child > td:first-child { border-top-left-radius:6px; }
#main table.mobileTable > tbody > tr:first-child > td:last-child  { border-top-right-radius:6px; }
#main table.mobileTable > tbody > tr:last-child  > td:first-child { border-bottom-left-radius:6px; }
#main table.mobileTable > tbody > tr:last-child  > td:last-child  { border-bottom-right-radius:6px; }

/* table.smallPrintMobile WITHOUT the mobileTable class — Penalties (play.php:1180), Original Bids
   (3213) and Player Moves (4863). mobile.css:642 pins those cells `color:black` and nothing ever
   lifted it, so they were black-on-dark in Dark mode too, not just under a dark skin. Same lift the
   mobileTable grids get. (Header cells are unaffected: skin-base's tr.header rule already wins.)
   These three still have no cell borders — worth a dfxGrid class on the <table> when those pages
   come up in the sweep. - Claude 7/26/26 */
body.dfx-darkpanel #main table.smallPrintMobile td      { color:var(--ink) !important; }

/* Future Year Draft Picks (table.futurePicks — class added in loadFuture): the detail cells (Round / From
   owner) were hardcoded #000 by the generic tr.detail rule -> black-on-dark in Dark mode (Lee 7/25). Lift
   them to --ink. (The colspan-2 header fix is in loadFuture.) - Claude 7/25/26 */
body.dfx-darkpanel #main table.futurePicks tr.detail td { color:var(--ink) !important; }
/* themed cell borders (had none in Light or Dark - Lee 7/25). Only the LEAF cells (skip the wrapper cell
   that holds the nested inner table, so no double frame). - Claude 7/25/26 */
#main table.futurePicks td:not(:has(table)) { border:1px solid var(--panel-border, var(--accent)) !important; }

/* dfxGrid = SHARED "data grid" class for plain generated tables (tr.header/tr.detail) that otherwise fall
   to the generic #000 detail text + no borders (ADP page etc.). Add class='dfxGrid' to the <table> in its
   generator. This is the seed of the shared-grid consolidation (mobileTable/futurePicks can migrate to it
   later). - Claude 7/25/26 */
#main table.dfxGrid td:not(:has(table))            { border:1px solid var(--panel-border, var(--accent)) !important; }
body.dfx-darkpanel #main table.dfxGrid tr.detail td { color:var(--ink) !important; }
/* Empty (no-opponent) right side: skinswitch.js wraps the game table and inserts .dfx-noOppo behind
   it carrying a random Tall-set player image. The overlay covers just the opponent columns (right
   45%), sits behind the cells (z-index:-1 inside an isolated wrapper), and the player is centred +
   contained + faded to ~12% — so it shows only through the empty opponent cells, one large image
   centred, a different one per game. */
.dfx-noOppo-wrap                               { position:relative; display:inline-block; isolation:isolate; }
.dfx-noOppo                                    { position:absolute; top:0; right:0; width:45%; height:100%;
                                                 z-index:-1; pointer-events:none; opacity:.12;
                                                 background-repeat:no-repeat; background-position:center; background-size:auto 62%; }

/* ── Live Stats: Points-cell status coding (Lee's revised scheme, 7/19/26 — IN-PROGRESS FILL
      RESTORED 8/15/26) ─────────────────────────────────────────────────────────────────────────
   The 7/19 scheme flattened not-started AND in-progress to the plain row shade, betting that the
   0.00-vs-blank score (emitted by livegames.js when a game starts) was enough to show who had
   kicked off. Lee 8/15: it isn't — with the fill gone, a live game and a finished game are told
   apart only by allDone's faint neutral, so the page lost its at-a-glance read. The in-progress
   FILL is back (--dfx-live-fill, below); not-started stays flat, since "no fill" is now exactly
   what it means. The two RING signals are unchanged in kind: a 2px gently-rounded inset box-shadow
   (no layout shift), REDZONE = danger, RECENT ACTIVITY = complementary, each blinking a few times
   then settling. livegames.js re-adds the class each ~15s refresh, which restarts the blink for its
   cycle. Because .blink and .unblink carry the SAME animation, the JS 1s blink/unblink swap doesn't
   restart it (identical animation-name = one continuous run to settle). A finished/bye game fades.
   Scoped to the full-screen pages. */

/* --dfx-bench-fill = the Bench label cell's colour, hoisted out of the livegames Pos-column rule
   below (it was inline there) so it has ONE definition. Kept as a token even though the in-progress
   cell no longer uses it — it is still the bench cell's own value.

   --dfx-live-fill = "this player's game is underway". SALMON, Lee's choice 8/15/26 (gold shipped
   first that afternoon and was swapped out the same day; the two are interchangeable here because
   both are fixed values that clear every test — see the rule below).

   ⚠ IT IS A FIXED VALUE, NOT A MIX OF THE SKIN'S OWN COLOURS, AND THAT IS THE WHOLE POINT.
   Two derived fills were tried first and both were rejected: a 14% amber wash of --row-bg, then the
   Bench tint (--header-bg 55% over the panel). Both FAILED THE SAME WAY, and it took measuring 60
   surfaces to see it — a derived fill inherits each palette's neutrality, and roughly half the
   palettes have near-neutral surfaces (Ember, Gridiron, Midnight, Sandlot, Varsity, SBFFC…), so the
   fill collapsed onto allDone, the finished-game grey. The Bench tint collided on 36 of 60 surfaces,
   worst dE 1.3. "In progress" and "finished" looking identical is the one failure this cell cannot
   have. A fixed WARM hue separates by HUE instead of lightness, and every neighbour this cell has —
   the row, the finished grey, the bench tint — is neutral or cool, so nothing comes near it.
   Measured over 20 palettes x Classic/Day Game/Night Game: min dE 17.6 from the row, 20.0 from
   allDone, 12.2 from the bench cell, min score contrast 11.65:1. Zero conflicts.
   ⚠ #FFD2C2 IS THE PALEST SALMON THAT HOLDS. One step paler (#FFDDD0) drops to dE 6.5 from the
   BENCH cell on Leather, under the 8 floor — so if this is ever lightened, re-measure against
   --dfx-bench-fill, which is the binding neighbour on the warm palettes. The row is not the
   constraint here; on a warm palette the bench tint is.
   DO NOT "improve" this by deriving it from a skin token. That is the bug, twice. - Claude 8/15/26 */
body               { --dfx-bench-fill:color-mix(in srgb, var(--header-bg) 55%, var(--content-bg));
                     --dfx-live-fill:#FFD2C2; }
/* Dark-panel companion (Onyx + Night Game). SOLVED, not picked: the redzone ring is --dfx-stop-fill,
   which is deliberately LIFTED PALE on a dark panel, so too light a fill converges with it. #45271D
   is the lightest value that clears the 3:1 non-text floor with margin (redzone ring 3.21, activity
   ring 4.29, score contrast 10.87); #4A2A20 and lighter fall under. - Claude 8/15/26 */
body.dfx-darkpanel { --dfx-live-fill:#45271D; }

/* Both rings are lifted off their raw tokens because they now sit on a FILLED cell, not the bare
   row. Measured on the Bench fill across the 20 palettes x Classic/Day Game/Night Game (60
   surfaces), against the 3:1 non-text working floor:
     redzone  --danger 1.17 min (46 surfaces under 3:1)  ->  --dfx-stop-fill  3.33 min, none under
     activity --complementary 1.39 min (16 under)        ->  --dfx-info-ink   3.90 min, none under
   Both replacements are the palette's OWN tuned on-panel variants of the same hue (declared in the
   SEMANTIC STATUS PALETTE at the top of this file), so the signals keep their meaning and their
   colour family — they just stop disappearing into the fill. - Claude 8/15/26 */
@keyframes dfx-lg-ring-danger { 0%,100% { box-shadow:inset 0 0 0 2px var(--dfx-stop-fill); }  50% { box-shadow:inset 0 0 0 2px transparent; } }
@keyframes dfx-lg-ring-comp   { 0%,100% { box-shadow:inset 0 0 0 2px var(--dfx-info-ink); }   50% { box-shadow:inset 0 0 0 2px transparent; } }
/* not started: flat row shade (overrides fullpage.css's pale hardcoded fill) */
body.dfx-fullpage #main tr.detail td.notStarted { background-color:var(--row-bg); }
/* game in progress */
body.dfx-fullpage #main tr.detail td.playing    { background-color:var(--dfx-live-fill); }
/* recent activity: complementary ring, blink-a-few-then-settle (.blink + .unblink share it).
   Keeps the live fill UNDER the ring: the JS drops .playing when it adds .blink (livegames.js:462),
   so without this a cell would drop back to the plain row shade for a cycle every time it scored —
   the fill would flicker off at exactly the moment the cell is drawing your eye. - Claude 8/15/26 */
body.dfx-fullpage #main tr.detail td.blink,
body.dfx-fullpage #main tr.detail td.unblink,
body.dfx-fullpage #main div#hints td.blinkLegend { background-color:var(--dfx-live-fill); border-radius:5px;
                                                 box-shadow:inset 0 0 0 2px var(--dfx-info-ink);
                                                 animation:dfx-lg-ring-comp .84s ease-in-out 3; }
/* redzone: danger ring, blink-a-few-then-settle (listed last so it wins if a cell is both) */
body.dfx-fullpage #main tr.detail td.redzone    { background-color:var(--dfx-live-fill); border-radius:5px;
                                                 box-shadow:inset 0 0 0 2px var(--dfx-stop-fill);
                                                 animation:dfx-lg-ring-danger .84s ease-in-out 3; }
/* finished / bye: faded neutral */
body.dfx-fullpage #main tr.detail td.allDone    { background-color:color-mix(in srgb, var(--ink) 12%, var(--content-bg)); }
@media (prefers-reduced-motion:reduce){
  body.dfx-fullpage #main tr.detail td.blink,
  body.dfx-fullpage #main tr.detail td.unblink,
  body.dfx-fullpage #main tr.detail td.redzone,
  body.dfx-fullpage #main div#hints td.blinkLegend { animation:none; } }
/* legend: drop the "game not started" row only — with no fill of its own there is nothing to show.
   "Game in progress" is BACK (it has a colour again). Scoped to #hints so live game rows that carry
   td.playing are NOT hidden. - Claude 8/15/26 */
body.dfx-fullpage #main div#hints tr.detail:has(td.notStarted) { display:none; }

/* ── Wonk landing header (wonk.php, scoped by its unique #recolor) ── - Claude 7/19/26 ─────────
   (1) HOME up beside the logo: its wrapper table rides the band's middle line as an inline-block
   next to the (already un-floated) logo, instead of dropping below it. A little longer, and it
   takes --complementary-bright so it stands out on the teal band; icon whitened by skinswitch.js.
   (2) The wonk "selected/hover" magenta (#ff33ff) and the selected-source pink (#ffccff) become the
   complementary color. */
body.dfx-wonk #fullPageHeader td table:has(button.wonkReload) { display:inline-block; vertical-align:middle; margin-left:12px; }
body.dfx-wonk #fullPageHeader button#reloadPage.wonkReload {
                                                 width:auto; padding-left:15px; padding-right:17px;
                                                 background-color:var(--complementary-bright);
                                                 border-color:var(--complementary-bright); }
body.dfx-wonk #fullPageHeader button#reloadPage.wonkReload:hover { filter:var(--hover-lift); }
body.dfx-wonk #fullPageHeader button#reloadPage.wonkReload img  { height:16px; vertical-align:middle; margin-right:5px; }
/* The skin fill above is scoped to #fullPageHeader, which only the full-screen (desktop) layout has.
   On mobile the same HOME/reload button lives under #crumbs > p.center, so it never got skinned and
   fell back to the lavender default — under a WHITE icon, i.e. light-on-light. Give the mobile button
   the same complementary fill so the white icon reads. - Claude 8/13/26 */
body.dfx-mobile.dfx-wonk button#reloadPage.wonkReload {
                                                 background-color:var(--complementary-bright);
                                                 border-color:var(--complementary-bright);
                                                 color:#fff; }
/* selected Source button (Mine/etc.): pink #ffccff -> complementary fill, white text */
body.dfx-wonk #main table.wonk button.menuSelected,
body.dfx-wonk #main table.wonk button.statsSelected { background-color:var(--complementary); color:#fff; border-color:var(--complementary); }
/* selected / hovered Wonk button: magenta #ff33ff -> complementary border + label */
body.dfx-wonk #main table button.iconSelected,
body.dfx-wonk #main table button.tinyIconSelected { border-color:var(--complementary); }
body.dfx-wonk #main table button.iconSelected td,
body.dfx-wonk #main table button.tinyIconSelected td { color:var(--complementary); }
body.dfx-wonk #main button.wonkIcon:hover,
body.dfx-wonk #main button.tinyWonkIcon:hover { border-color:var(--complementary); }
/* Player list (#wonkList) + its matching legend (#wonkLegend): the alt-row ownership stripes are
   hardcoded teal/pink in fullpage.css. Retokenise so they read in every mode and, per Lee, the
   "Owned by You" pink -> the skin complementary. Three ownership states (see proj.php legend):
     plain detail row = Unowned         -> keeps --row-bg from skin-base
     .s1 = Owned by Another Team        -> light ACCENT tint  ("teal = someone else", as before)
     .s2 = Owned by You                 -> light COMPLEMENTARY tint ("navy = mine", was pink)
   Both are light tints of --content-bg, so dark text/links stay legible and the stripes track
   light/dark automatically. Both ids are needed to beat fullpage's #main #wonkList rule. - Claude 7/19/26 */
/* Tint STRENGTH raised from a flat 25% (Lee 8/7/26): at 25% the three states sat inside one narrow band —
   in Classic the unowned row is --row-bg #E7F3F1, itself a mint tint, so "unowned" and "drafted" differed
   only in the saturation of ONE hue and read as a single block. The hues are deliberately UNCHANGED (teal
   still = someone else, navy still = mine); only the distance from the page moves. Still color-mix in
   SRGB, not oklab — switching mixing space would shift the hues too, and this is a strength change only.
   Drafted -> 40%. MINE -> 55%, THEN BACKED OFF TO 45% the same day: 55 overshot and Lee said so ("it went
   too far; can you un-darken just a bit"). On the Classic panel that is L* 61.3 -> 67.6, ~6 points lighter,
   which still leaves a clear step below drafted (79.7) and well below unowned (94.5) — so "mine" is still
   the darkest of the three and reads as the strongest state, just not as heavy.
   ⚠ THE TWO MODES MOVE IN OPPOSITE DIRECTIONS, so do not read this number as "darkness". On a LIGHT panel
   a bigger mix means darker; in DARK mode the same mix lightens the row away from the #161b22 page. Which
   is why 55 could look right in one mode and too heavy in the other. Reducing to 45 costs dark mode a
   little separation from the page (L* 22.1 -> 19.7 against a ~12.6 page, drafted sitting at 29.9) — still
   a visible step, and worth splitting per mode only if Lee reports the dark rows now sink into the page.
   - Claude 8/7/26 */
body.dfx-wonk #main table#wonkList tr.s1 td,
body.dfx-wonk #main table#wonkLegend tr.s1 td { background-color:color-mix(in srgb, var(--accent) 40%, var(--content-bg)); }
body.dfx-wonk #main table#wonkList tr.s2 td,
body.dfx-wonk #main table#wonkLegend tr.s2 td { background-color:color-mix(in srgb, var(--complementary) 45%, var(--content-bg)); }
/* DARK mode: those s1/s2 tints mix into the DARK --content-bg, so the rows are DARK — but the plain cell
   text (Order/Pos/Team/Bye) stayed hardcoded dark -> dark-on-dark (Lee 7/25). Lift it to --ink in dark
   mode. Links already use --link, and the position chips / ADP values keep their own colors (child spans
   with explicit color). - Claude 7/25/26 */
body.dfx-wonk.dfx-darkpanel #main table#wonkList td,
body.dfx-wonk.dfx-darkpanel #main table#wonkLegend td { color:var(--ink) !important; }
/* The Wonk's control tables (position radios, #siteButtons projection sources, Raw/Enhanced, #wonkSlider)
   also carry .centerMe (for centering), so the hub-alarms centerMe grid treatment put cell borders on them.
   The Wonk wants NO cell borders on these (Lee 7/25) — remove them on the wonk page. - Claude 7/25/26 */
body.dfx-wonk #main table.centerMe,
body.dfx-wonk #main table.centerMe td { border:0 !important; }
/* Player LISTING (#wonkList) — the desktop wonk shows a subtle cell grid; mobile had none. Add a themed
   grid so mobile Dark mode matches desktop (Lee 7/25). This is the listing, distinct from the borderless
   control tables above. Right+bottom per cell keeps the table's border-collapse:separate with single
   lines (no doubling). - Claude 7/25/26 */
/* mobile.css:422 makes #wonkList border-collapse:separate with a WHITE (#F8F8F8) table bg + 1.2px
   border-spacing, so those white gaps showed BETWEEN my skin cell borders = a white+skin double border in
   Dark mode (Lee 7/25). Kill the gap (spacing:0) and the white bg so only the single skin grid shows. */
body.dfx-wonk.dfx-darkpanel #main table#wonkList { border-spacing:0 !important; background-color:transparent !important;
  border-top:1px solid var(--panel-border, var(--accent)) !important;    /* cells only draw right+bottom, so the */
  border-left:1px solid var(--panel-border, var(--accent)) !important; } /* outer TOP + LEFT edges come from the table (Lee 7/25) */
body.dfx-wonk.dfx-darkpanel #main table#wonkList td {
  border-right:1px solid var(--panel-border, var(--accent)) !important;
  border-bottom:1px solid var(--panel-border, var(--accent)) !important;
}
/* The grid above is dfx-darkpanel-only (added 7/25 for Dark mode). Light/Classic MOBILE got no
   Wonk cell grid at all — mobile.css gives #wonkList no borders — so the listing read as borderless
   (Lee 8/13). Give the light modes the same themed grid, scoped to mobile so desktop is untouched.
   No !important needed: mobile.css sets no competing td border. - Claude 8/13/26 */
body.dfx-mobile.dfx-wonk:not(.dfx-darkpanel) #main table#wonkList {
  border-spacing:0;
  border-left:1px solid var(--panel-border, var(--accent));
}
/* NB: no table border-TOP. border-collapse is separate, so a table-level top border sits 1px OUTSIDE
   the (opaque) header cells — hidden behind them in the middle, but peeking above as a stray line and
   sticking out past the header's rounded top-right corner (Lee 8/13). The visible top edge is instead
   drawn on the header cells themselves (below), which sit flush and respect the corner. - Claude 8/13/26 */
body.dfx-mobile.dfx-wonk:not(.dfx-darkpanel) #main table#wonkList td {
  border-right:1px solid var(--panel-border, var(--accent));
  border-bottom:1px solid var(--panel-border, var(--accent));
}
/* Header row keeps the same teal (--panel-border) grid as the data cells (Lee 8/13 wants them to
   match, not a contrasting colour). The only thing it was missing was its TOP line: border-collapse
   is separate here, and the general rule only puts border-top on the TABLE, so the header row's own
   top edge went undrawn. Add it on the header cells. - Claude 8/13/26 */
body.dfx-mobile.dfx-wonk:not(.dfx-darkpanel) #main table#wonkList tr.header td {
  border-top:1px solid var(--panel-border, var(--accent));
  /* Square the top corners on mobile. The 6px rounding is keyed to the header's first/last CELL, but
     mobile hides the first column (Count), so only the last cell (Auct Val) kept a rounded corner —
     giving a square top-left / rounded top-right mismatch. Force both square so they match. - Claude 8/13/26 */
  border-radius:0;
}
/* player-name links: the hardcoded dark purple (#330066) reads on the light Classic/Light tints
   but vanishes on the dark-mode tints; fall through to the mode-aware --link, keep purple in Classic. */
body.dfx-wonk #main table#wonkList a,
body.dfx-wonk #main table#wonkList a:visited,
body.dfx-wonk #main table#wonkList a:focus { color:var(--link, #330066); }
/* "keep purple in Classic" above worked only because Classic left --link UNSET, so the #330066
   fallback fired. DataForce now DOES set --link on #main in Classic (dataforce.css), which would have
   turned these player names teal-blue. Pin the purple explicitly instead of leaning on a variable
   being undefined — no other skin changes, they were already getting #330066 here. - Claude 8/3/26 */
body.dfx-wonk[data-theme="classic"] #main table#wonkList a,
body.dfx-wonk[data-theme="classic"] #main table#wonkList a:visited,
body.dfx-wonk[data-theme="classic"] #main table#wonkList a:focus { color:#330066; }
/* ⚠ ...BUT "Classic" IS NOT THE SAME QUESTION AS "IS THE PANEL LIGHT". Lee, 8/10/26, screenshot of the
   Wonk on ONYX: "dark-on-dark print is unreadable in this skin."
   Onyx is a dark-SURFACED palette, so in Classic it keeps --content-bg #111827 / --row-bg #1e293b while
   the pin above paints the player names #330066. Measured off his screenshot: 1.08:1 on an un-tiered
   row and 1.82:1 on a tiered one — invisible, and failing on EVERY row rather than only the dark ones
   he happened to notice (the teal rows are the Wonk's inline tier wash; rows outside a tier band fall
   back to the bare --row-bg, which is why those looked worst).
   This is the exact failure dfx-darkpanel was introduced to end — its own comment says the on-dark
   rules "used to test [data-theme='dark'], which missed a dark skin in Classic" — and 51 rules in this
   file were converted. This pair was not, because it keys on --link, which modes.css defines ONLY
   inside body[data-theme="dark"]. In Classic --link is simply undefined and the #330066 fallback fires.
   So the fix is not a new colour, it is applying the existing rule to the right condition.
   ORDER IS LOAD-BEARING: this has identical specificity to the Classic pin above (2 ids, 2 classes,
   3 elements), so it MUST stay below it to win. Only onyx is in skinswitch.js's DARKSKINS, so no other
   palette in Classic is touched; onyx in Dark already had --link and is unchanged.
   The fallback repeats modes.css's own dark recipe rather than inventing a value: on onyx's #0ea5e9
   that is #7acef3 — 8.29:1 on the un-tiered row, 4.91:1 on the tiered one, both clear of 4.5.
   - Claude 8/10/26 */
body.dfx-wonk.dfx-darkpanel #main table#wonkList a,
body.dfx-wonk.dfx-darkpanel #main table#wonkList a:visited,
body.dfx-wonk.dfx-darkpanel #main table#wonkList a:focus {
  color:var(--link, color-mix(in srgb, var(--accent) 55%, white)); }
body.dfx-wonk #main table#wonkList a:hover  { color:var(--accent); }

/* Position chips (skinswitch.js wraps the Pos cell in a pill): the SAME position palette the draft
   board and roster displays use (fullpage.css #draftBoard td.QB … / desktop.css table.roster), so the
   Wonk list reads consistently with the rest of the site. Light pastel fills + dark text — the pill is
   self-contained, so it reads on both light and dark content backgrounds. - Claude 7/19/26 */
body.dfx-wonk #main .dfx-poschip{
  display:inline-block; min-width:2.3em; padding:1px 7px; border-radius:10px;
  font-size:.82em; font-weight:700; letter-spacing:.3px; text-align:center; color:#1a1a1a;
  line-height:1.5; box-shadow:0 1px 1px rgba(0,0,0,.15); }
body.dfx-wonk #main .dfx-pos-QB{ background:#FFBBFF; }  /* pink   */
body.dfx-wonk #main .dfx-pos-RB{ background:#BEFEEB; }  /* mint   */
body.dfx-wonk #main .dfx-pos-WR{ background:#FFFFB5; }  /* yellow */
body.dfx-wonk #main .dfx-pos-TE{ background:#CEA8F4; }  /* purple */
body.dfx-wonk #main .dfx-pos-PK{ background:#E4C0B1; }  /* tan  (kicker)  */
body.dfx-wonk #main .dfx-pos-DE{ background:#A8CFFF; }  /* blue (defense) */

/* NFL team logo in the Team column (skinswitch.js prepends it to the abbreviation). 20px — small
   enough to cost almost no row height, big enough that the silhouette-driven marks read; the
   abbreviation stays as the label you actually read, the logo is the color cue you scan by.
   Baseline-aligned to the text and nudged down a hair so it sits optically centred. - Claude 7/20/26
   The logos are NOT a uniform shape (square-ish GB/HOU ≈20px wide at this height, wide NYG ≈26px),
   so letting width run auto makes the abbreviations start at a different x on every row. Instead the
   logo gets a FIXED 28px slot (wider than the widest mark) with object-fit:contain, which scales each
   logo inside the box without distorting it — object-position:left then squares up the left edge, so
   both the logos and the abbreviations line up in clean columns. */
body.dfx-wonk #main .dfx-teamlogo{
  height:20px; width:28px; object-fit:contain; object-position:left center;
  vertical-align:middle; margin-right:5px; position:relative; top:-1px; }
body.dfx-wonk #main table#wonkList td[data-dfx-team]{ white-space:nowrap; }

/* LINEUP WONK — projected starters (skinswitch.js tags the row .dfx-starter when its Status cell
   reads "Starter"). In Lineup Wonk every player is on your own team, so the s2 "owned by you" tint
   is on every row and carries no information there; repurposing the row background for starters
   costs nothing and answers the actual question the page exists to answer — who am I starting?
   An accent-tinted row plus a solid left bar (inset shadow, so no layout shift); the bench rows keep the
   plain tint and read as recessed by comparison. (Bold text was dropped 7/20/26 — the tint + bar already
   emphasize enough.) This rule must stay BELOW the tr.s1/tr.s2 rules above: it ties them on specificity
   and wins on source order. - Claude 7/20/26 */
body.dfx-wonk #main table#wonkList tr.dfx-starter td{
  background-color:color-mix(in srgb, var(--accent) 24%, var(--content-bg)); }
body.dfx-wonk #main table#wonkList tr.dfx-starter td:first-child{
  box-shadow:inset 4px 0 0 var(--accent); }

/* Value-vs-ADP flag (skinswitch.js appends it to the ADP cell; thresholds are documented there).
   Semantic green/red, deliberately NOT the skin accent — this is a judgement, not brand chrome. The
   Classic/Light values are dark enough for the pale content backgrounds; dark mode lifts them so they
   stay legible on #161b22 without going neon. The "strong" tier adds weight and a faint pill so the
   biggest gaps are findable while scanning. - Claude 7/20/26 */
body.dfx-wonk{ --dfx-value:#1a7f4b; --dfx-reach:#b3352c; }
body.dfx-wonk.dfx-darkpanel{ --dfx-value:#56c98a; --dfx-reach:#ef7b70; }
/* Two slots inside the one ADP cell: the number, then the flag. Fixed widths on both turn the arrows
   into their own aligned column, which a plain inline pill can't do — a 2-digit ADP and a 3-digit one
   push the arrow to different places on every row. tabular-nums stops the digits jittering too.
   box-sizing keeps the "strong" tier's padding from widening its slot out of line. - Claude 7/20/26 */
body.dfx-wonk #main table#wonkList .dfx-adpnum{
  display:inline-block; min-width:2.2em; text-align:right; font-variant-numeric:tabular-nums; }
body.dfx-wonk #main table#wonkList .dfx-adp{
  display:inline-block; box-sizing:border-box; width:3.5em; margin-left:8px; text-align:left;
  font-weight:600; font-size:.9em; white-space:nowrap; font-variant-numeric:tabular-nums; }
body.dfx-wonk #main table#wonkList .dfx-adp-value{ color:var(--dfx-value); }
body.dfx-wonk #main table#wonkList .dfx-adp-reach{ color:var(--dfx-reach); }
/* "strong" tier = the biggest gaps. Bold weight only — the tinted pill/oval was removed 7/20/26
   (Lee didn't want the oval emphasis). No background, padding or border-radius. - Claude 7/20/26 */
body.dfx-wonk #main table#wonkList .dfx-adp.dfx-adp-strong{ font-weight:800; }
/* legend swatches inherit the same colors */
body.dfx-wonk #main table#wonkLegend .dfx-adp{ margin:0 4px 0 0; font-size:1em; }

/* Sortable Draft Wonk headers (Auct Val / ADP / Rank). Click to make that column the baseline; the active
   one gets a ▾. Pointer + no-select so double-clicks don't highlight the label. - Claude 7/20/26 */
body.dfx-wonk #main table#wonkList .dfx-sortable{
  cursor:pointer; user-select:none; -webkit-user-select:none; white-space:nowrap;
  background-color:color-mix(in srgb, var(--accent) 22%, var(--header-bg)); }   /* all three read as clickable */
body.dfx-wonk #main table#wonkList .dfx-sortable::after{ content:'\00A0\21C5'; font-size:.8em; opacity:.5; }   /* ⇅ hint on every sortable head */
body.dfx-wonk #main table#wonkList .dfx-sortable:hover{ text-decoration:underline; }
body.dfx-wonk #main table#wonkList .dfx-sortactive{   /* the one currently sorted: stronger tint + solid down-arrow */
  background-color:color-mix(in srgb, var(--accent) 42%, var(--header-bg)); }
body.dfx-wonk #main table#wonkList .dfx-sortactive::after{ content:'\00A0\25BE'; font-size:1.4em; line-height:0; vertical-align:-2px; opacity:.95; }
/* Auct Val / ADP / Rank body cells: keep the value and its rise/fall arrow on ONE line even when a
   projection set has long notes squeezing the table. Only Notes is allowed to wrap. - Claude 7/20/26 */
body.dfx-wonk #main table#wonkList td.dfx-sortcell{ white-space:nowrap; }

/* Auction-value tiers. skinswitch.js (dfxTierize) inserts one .dfx-tier row above the first player of each
   tier, but ONLY while the list is sorted by Auction Value; every re-sort clears them first. A light labelled
   rule — a hairline accent border with a small uppercase "Tier N" + its $ range — so value cliffs read at a
   glance without shouting. Tints track the skin + light/dark automatically. - Claude 7/21/26 */
body.dfx-wonk #main table#wonkList tr.dfx-tier td{
  padding:2px 12px;
  background:color-mix(in srgb, var(--accent) 10%, var(--content-bg));
  border-top:2px solid color-mix(in srgb, var(--accent) 50%, transparent);
  font-size:.72em; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  color:var(--accent); }
body.dfx-wonk #main table#wonkList tr.dfx-tier .dfx-tier-note{
  margin-left:10px; font-weight:600; letter-spacing:.02em; text-transform:none; opacity:.7; }

/* Sticky column header for the projection list. #wonkList uses ONE shared header (columns are shown
   or hidden per position by CSS), so a single pinned row is correct; skinswitch.js tags it
   .dfx-listhead. Header cells already carry an opaque --header-bg (skin-base), so rows scroll cleanly
   beneath it and no divider rule is needed. - Claude 7/20/26 */
body.dfx-wonk #main #data table tr.dfx-listhead td{
  position:sticky; top:0; z-index:2; }
/* Subscription/projection section headers (h2, now colspan=5 so they span the table) -> centred
   separators with a rule to each side; plus a thin rule BETWEEN projection sources. Source rows are
   the only ones with no colspan cell, so target tr:not(:has(td[colspan])); suppress the top rule on
   the first source under each header (tr:has(h2)+tr) so it doesn't double up with the divider. */
body.dfx-wonk #main #wonkIntro table.wonk h2       { display:flex; align-items:center; gap:16px;
                                                          text-align:center; color:var(--ink); }
body.dfx-wonk #main #wonkIntro table.wonk h2::before,
body.dfx-wonk #main #wonkIntro table.wonk h2::after { content:""; flex:1; height:2px; background:var(--accent); }
body.dfx-wonk #main #wonkIntro table.wonk > tbody > tr:not(:has(td[colspan])) > td
                                                        { border-top:1px solid color-mix(in srgb, var(--accent) 35%, var(--content-bg));
                                                          padding-top:.75em; padding-bottom:.75em; }
/* drop the rule on the first source under each header (:not(:has(td[colspan])) matches the border
   rule's specificity so this later rule wins) */
body.dfx-wonk #main #wonkIntro table.wonk > tbody > tr:has(h2) + tr:not(:has(td[colspan])) > td { border-top:0; }

/* WONK WAY sliders (jQuery UI): the hardcoded teal track (#8DC7BC, fullpage.css .ui-widget-content)
   -> the skin accent so it's on-brand in every skin and reads on light + dark; the knob stays a
   light grabbable disc with an accent edge. */
body.dfx-wonk #main .ui-slider                     { background:var(--accent); border-color:var(--accent); }
body.dfx-wonk #main .ui-slider .ui-slider-handle   { background:#f6f8f7; border:1px solid var(--accent); }
body.dfx-wonk #main .ui-slider .ui-slider-handle:hover,
body.dfx-wonk #main .ui-slider .ui-slider-handle:focus { background:#ffffff; border-color:var(--accent); }

/* ==================== LIVE DRAFT / DRAFT ENGINE (livedraft.php, draftengine.php) ====================
   These carry #fullPageHeader -> body.dfx-fullpage. The base draft.css/fullpage.css hardcode teal/blue/
   red; recolor each element to its MEANING on the skin palette. --dfx-go is the "go / draft" green,
   deliberately semantic (not the skin accent), same idea as the Wonk's value green. - Claude 7/21/26 */
/* --dfx-go now lives on body / body.dfx-darkpanel (see the SEMANTIC STATUS PALETTE block at the top
   of this file) with the SAME values, so these two fullpage-scoped declarations were removed as
   duplicates — dfx-darkpanel is set by mode on every page, fullpage included. - Claude 7/26/26 */

/* (1) Draft / Remove drop boxes. Draft = green, Remove = --danger. draft.css targets livedraft's boxes
   as #main table#liveDraft div#draftMe, so match that specificity; text stays --ink like the buttons. */
body.dfx-fullpage #main table#liveDraft div#draftMe { background-color:color-mix(in srgb, var(--dfx-go) 20%, var(--content-bg)); border-color:var(--dfx-go); }
body.dfx-fullpage #main table#liveDraft div#dropMe  { background-color:color-mix(in srgb, var(--danger) 18%, var(--content-bg)); border-color:var(--danger); }

/* (7) Owner's own team icon in the ticker ribbon (livedraft.js tags it .firstIcon) -> --complementary. */
body.dfx-fullpage #main img.firstIcon { border-color:var(--complementary); }

/* (6 + 9) Timer box (#liveClock) and status message (#draftMessage) share the box classes. Reskin them
   to MATCH the draft/remove buttons: green while running, amber warning, danger-tint as it flashes down /
   expires / your turn. Light tints + colored border + --ink text so they read in light AND dark. */
body.dfx-fullpage #main div.greenBox  { background-color:color-mix(in srgb, var(--dfx-go) 20%, var(--content-bg));  border-color:var(--dfx-go);  color:var(--ink); }
body.dfx-fullpage #main div.yellowBox { background-color:color-mix(in srgb, var(--dfx-warn-fill) 30%, var(--content-bg)); border-color:var(--dfx-warn-line); color:var(--ink); }
body.dfx-fullpage #main div.pinkBox   { background-color:color-mix(in srgb, var(--danger) 26%, var(--content-bg)); border-color:var(--danger);  color:var(--ink); }
body.dfx-fullpage #main div.redBox    { background-color:color-mix(in srgb, var(--danger) 30%, var(--content-bg)); border-color:var(--danger);  color:var(--ink); }

/* (10) Auction bid box: the blue nomination/bid UI (auctionHeader/Detail, #mode2 border, BID button,
   new-bid flash) -> --complementary. */
body.dfx-fullpage #main tr.auctionHeader td       { background-color:var(--complementary); color:#fff; }
body.dfx-fullpage #main tr.auctionDetail td       { background-color:color-mix(in srgb, var(--complementary) 22%, var(--content-bg)); color:var(--ink); }
body.dfx-fullpage #main table#topTicker div#mode2 { border-color:var(--complementary); }
body.dfx-fullpage #main button#placeBid           { background-color:var(--complementary); border-color:var(--complementary); color:#fff; background-image:none; }
/* color:#fff added 8/12/26 — the new-bid flash sets a --complementary (dark) background but left the
   text at the inherited auctionDetail var(--ink), which is dark on light skins, so the flashed high bid
   (#bidAmount in table#liveDraft) read dark-on-dark. Same treatment as #placeBid on the same fill. */
body.dfx-fullpage #main table#liveDraft td.newbid { background-color:var(--complementary); color:#fff; }

/* Header right cell: the action buttons (Alarm/Drag-Drop/Refresh/Tweet/Help) are now pills — wider than
   the plain buttons they replaced — so three of them wrapped to a 2nd line inside the fixed-width cell.
   Keep them on one line; the cell claims the width from the roomy centre league-select cell. Applies to
   every #fullPageHeader page (live draft, draft board, live games). - Claude 7/21/26 */
body.dfx-fullpage #fullPageHeader td.right { white-space:nowrap; }

/* (2 + 3) Draft action buttons — colored by state (skinswitch.js sets .dfx-attn / .dfx-comp). ALARM =
   attention; Drag/Drop ON = complementary, OFF = attention; Auto ON = attention, OFF = plain. */
body.dfx-livedraft #main button.dfx-attn,
/* color was a hardcoded #fff, which was safe only while --attention was one shared dark burnt-orange.
   It is per-palette now and most of them are a light amber, so white would be unreadable here — this
   was the ONLY fill in the sheet not already reading --attention-text. - Claude 8/3/26 */
body.dfx-livedraft #fullPageHeader button.dfx-attn { background-color:var(--attention); border:1px solid var(--attention); color:var(--attention-text,#fff); background-image:none; }
body.dfx-livedraft #main button.dfx-comp,
body.dfx-livedraft #fullPageHeader button.dfx-comp { background-color:var(--complementary); border:1px solid var(--complementary); color:#fff; background-image:none; }
/* on the teal header band, use the bright complementary like REFRESH */
body.dfx-livedraft #fullPageHeader button.dfx-comp { background-color:var(--complementary-bright); border-color:var(--complementary-bright); }
/* give header Alarm/Drag-Drop/Help-Me the REFRESH pill shape so the row reads as a set.
   #helpme added 8/18/26 (Lee): it is engine-only, was in neither this rule nor skinfx.js's
   draftBtnColor(), and so sat in the row as a plain teal rectangle between two complementary pills. */
body.dfx-livedraft #fullPageHeader button#alarm,
body.dfx-livedraft #fullPageHeader button#helpme,
body.dfx-livedraft #fullPageHeader button#dragDropButton { border-radius:13px; font-weight:bold; font-size:13px; line-height:18px; padding:2px 11px; vertical-align:middle;
  box-shadow:0 2px 5px color-mix(in srgb, var(--attention) 40%, transparent); }
/* the un-asked / un-raised state carries the complementary glow instead, to match REFRESH - Claude 8/18/26 */
body.dfx-livedraft #fullPageHeader button#dragDropButton.dfx-comp,
body.dfx-livedraft #fullPageHeader button#helpme.dfx-comp { box-shadow:0 2px 5px color-mix(in srgb, var(--complementary-bright) 40%, transparent); }
body.dfx-livedraft #fullPageHeader button#alarm:hover,
body.dfx-livedraft #fullPageHeader button#helpme:hover,
body.dfx-livedraft #fullPageHeader button#dragDropButton:hover { filter:var(--hover-lift); }

/* (4 + 5) Header top-left: Logo, Details, Draft-board, then "Live Draft". The harness puts the logo
   float:none/inline but the Details & Board imgs still float:left, which jumps them ahead of the logo
   (the "out of order"). Put all three in-flow (float:none, inline) so they render in DOM order, and
   match their heights. Details becomes the masked span, filled with --accent exactly like the cart. */
body.dfx-livedraft #fullPageHeader img#logo,
body.dfx-livedraft #fullPageHeader img#draftboard,
body.dfx-livedraft #fullPageHeader a#details { float:none; display:inline-block; vertical-align:middle; margin:.35em .3em 0 .3em; }
body.dfx-livedraft #fullPageHeader img#logo       { height:48px; width:auto; }   /* match the other fullscreen headers (Lee 7/21) */
body.dfx-livedraft #fullPageHeader img#draftboard { height:34px; width:auto; }
/* Details: WHITE (Lee 8/7/26, "can we just make that white?"). The fill was --attention, the same red
   the raised-alarm button uses — which said "something is wrong" about a plain league-info link, and
   was the weakest thing on the band besides. Measured against the header --major-bg it sat at 1.26:1
   on DataForce, 1.15 on Tailgate and 1.01 on Onyx: it was legible by HUE alone, with essentially no
   luminance separation. White is a large improvement on every palette — 3.54 / 2.43 / 2.77 on those
   three, 14.7 on Midnight — and matches the near-white --major-text the band already carries.
   (The 7/21 note here claimed --complementary-bright; the code never said that. Corrected rather than
   preserved, since a comment that misdescribes its own line is worse than none.)
   ONYX is the palette to revisit if this ever looks thin: its band is a bright cyan and its
   --major-text is deliberately near-BLACK for that reason, so white is the one place this diverges
   from the band's own text contract. Still 2.77 against today's 1.01, so it is better there too.
                                                                              - Claude 8/7/26 */
body.dfx-livedraft #fullPageHeader a#details .dfx-details {
  display:inline-block; width:34px; height:34px; vertical-align:middle;
  background-color:#ffffff;
  -webkit-mask:url(/images/details.png) center/contain no-repeat;
          mask:url(/images/details.png) center/contain no-repeat; }

/* Live-draft chat: the base (#f8f8f8 white box / #000 text) never went dark. Mirror the League Chat
   page treatment — content-bg surface, accent border, --ink text, team names -> --link. background-color
   (not shorthand) preserves the watermark. - Claude 7/21/26 */
body.dfx-livedraft #main table#liveDraft div#chat,
body.dfx-livedraft #main table#liveDraft div#emoji    { background-color:var(--content-bg); border-color:var(--accent); color:var(--ink); }
body.dfx-livedraft #main table#liveDraft div#chat span.team,
body.dfx-livedraft #main table#topTicker div#lastChat span.team { color:var(--link, var(--accent)); }

/* "On the clock" line was hardcoded teal #4A9586 -> follow --accent. */
body.dfx-livedraft #main div#onTheClock               { color:var(--accent); }

/* Auto-Bidder box: base buttons are white/#000/teal (unstyled in dark). Skin them: accent-tinted
   surface, --ink text, accent border; the selected bid -> solid accent. */
body.dfx-livedraft #main div#autobidbox               { border-color:var(--accent); }
body.dfx-livedraft #main div#autobidbox button        { color:var(--ink); background-color:color-mix(in srgb, var(--accent) 10%, var(--content-bg)); border-color:var(--accent); background-image:none; }
body.dfx-livedraft #main div#autobidbox button:hover  { background-color:color-mix(in srgb, var(--accent) 22%, var(--content-bg)); }
body.dfx-livedraft #main div#autobidbox button.selectbid { background-color:var(--accent); border-color:var(--accent); color:#fff; }

/* The body used to carry a football-field background image (background.png) whose scaly top band read as
   an "alligator" texture. That image is now removed from the app-page core CSS (desktop/mobile/fullpage
   bodies), so we NO LONGER flatten the body — doing so (background:var(--content-bg)) also wiped
   skin-base's page gradient, so the desktop/mobile outer gradient border vanished. Let skin-base's body{}
   own the background (the tokened black→teal gradient, over a solid --page-top fallback — never the field
   image, since --page-image is 'none' for every skin but Apex's intentional header). - Claude 7/22/26 */

/* The `#xmain { background-color:var(--content-bg); }` that used to sit here is REMOVED 8/4/26.
   #xmain exists on exactly two pages — calendar.php and leaguecalendar.php — and neither loaded the
   skin layer until today, so the rule had never once executed. The moment they did load it, it painted
   a near-white surface under text that is still fullpage.css:6's near-white body color, and the Draft
   Date Calendar's month title and the League Calendar's message went white-on-white.
   Not worth repairing by adding `color:var(--ink)`: that would turn two black-surfaced calendar pages
   into light panels, which is a restyle nobody asked for. Both pages look right with no rule at all —
   #xmain transparent over the body — which is exactly how they render today. - Claude 8/4/26 */


/* ---------- Locker Room controls, BOTH shells (Lee 7/30/26) ----------
   These live here rather than in desktop.css because layerHtml() loads skins-live.css on every
   shell — desktop, commish and all four mobile ones — while desktop.css never reaches the phone.
   Scoped to #lrWrap/#lrControls, which both Locker Room views have, rather than to #main, which
   only the desktop one is guaranteed to sit inside. */

/* THREE buttons on one line, all the same height. They differed because .primary and a plain button
   carry different padding; a shared min-height plus identical padding is what matches them, not
   nudging one. flex-wrap stays on so a narrow phone drops the third to its own line instead of
   overflowing. */
#lrWrap .lrButtons {display:flex; gap:.5em; align-items:stretch; flex-wrap:wrap; margin:0 0 .4em 0;}
#lrWrap .lrButtons button {margin:0; min-height:2.6em; padding:.55em .9em; flex:1 1 0; white-space:nowrap;}

/* Banner Image select lines up with the buttons under it — it used to take its natural width and
   left a ragged right edge in the column. */
#lrControls select.lrWide {width:100%; box-sizing:border-box;}

/* An untinted banner (jerseys, football) or None gives the Image Filter nothing to act on. Greyed
   rather than hidden: hiding it made the panel jump height on every dropdown change. */
#lrControls .lrDisabled {opacity:.4;}
#lrControls .lrDisabled button {cursor:default;}

/* LONG CHIP LABELS WRAPPED TO TWO LINES WHEN SELECTED (Lee 8/11/26: "When I click Monday Night, the
   bolded text doesn't fit on one line").
   Cause: `#main #lrControls button.lrOn` adds font-weight:bold, and bold is wider — "Monday Night"
   goes 79 -> 82px against a name box that was 79px, so the label wrapped and THAT ROW of the grid
   got taller the moment you clicked it. Nothing was wrong until selection, which is why it only
   showed on the chip you had just picked.
   Fix is the one the four-up Lighting rows already use (`div.lrSeg4`, desktop.css, 7/28/26): tighter
   padding plus nowrap. Horizontal padding .2em -> .05em and gap .4em -> .25em give the label box
   79 -> 84px, which clears bold "Monday Night" (82px) and bold "Leather&Laces" (80px).
   ⚠ SPECIFICITY: the padding being overridden comes from `#main #lrControls button` — TWO ids,
   (2,0,1). A rule written `#main div.lrSwatches > button.lrSwatch` is only (1,2,2) and LOSES; it
   moved the box by 1px and looked like the numbers were wrong. `#lrControls` has to be in the
   selector. Measured live before committing, and again after.
   ⚠ HEADROOM IS ONLY 2px on the longest label. If a future palette name is longer than
   "Monday Night", nowrap will make it overflow its chip rather than wrap. The next lever is the one
   lrSeg4 also pulls — `font-size:.92em` on .lrName — which buys about 6px more.
   Vertical padding is deliberately untouched: it sets the chip height, and all 18 must stay equal.
   Lives here rather than in desktop.css because skins-live.css loads after BOTH css/desktop.css and
   css/mobile.css, so one rule covers the full-screen and mobile Locker Rooms. - Claude 8/11/26 */
#main #lrControls div.lrSwatches > button.lrSwatch {padding-left:.05em; padding-right:.05em; gap:.25em;}
#main #lrControls div.lrSwatches span.lrName {white-space:nowrap;}

/* Selected palette's one-line description, sitting between the colour chips and the Banner Image
   heading (Lee 8/11/26). Text comes from skin::$blurbs via lockerroom.js apply().
   ⚠ COLOUR IS DELIBERATELY NOT SET — it INHERITS. This paragraph sits among the #lrControls <h2>s,
   which already take the surrounding text colour and are correct in every mode. Pinning it to --ink
   would be the 8/3/26 bug again: modes.css declares no Classic entry, so --ink falls through to
   skin-base's #111, and in mobile Classic #main is forced to #161b22 — 1.06:1, invisible.
   :empty collapses the element, so a palette with no description yet cannot leave a gap. */
#lrControls p.lrBlurb {margin:.55em 0 .2em 0; font-size:.92em; line-height:1.45;}
#lrControls p.lrBlurb:empty {display:none;}

/* ============================ TABLE CELL GRIDLINES ============================
   Lee 8/11/26: "the table cell divider lines are too faint. Dark mode, light mode, classic mode, lots
   have problems ... I want an understated look, but want to be able to see the divider lines. This is
   especially true of table headers." Chosen in skinlab from three weights: the 3:1 one.

   ⚠ ABOUT THE 3:1 NUMBER — CORRECTED 8/11/26, having first written this as a WCAG requirement.
   3:1 is BORROWED from WCAG 2.1 SC 1.4.11 (Non-text Contrast) as a working benchmark. It is NOT a
   compliance obligation for these lines. That criterion covers (a) user interface COMPONENTS — the
   visual information needed to identify a control and its state — and (b) GRAPHICAL OBJECTS required
   to understand the content. A table gridline is neither: it is not interactive, and the table's
   information is carried by the text and the layout, not by the border. W3C's Understanding document
   is explicit that purely decorative presentation is out of scope.
   So read the numbers below as "below the target we chose", NOT as accessibility failures, and do not
   describe the result as WCAG-compliant anywhere customer-facing. The target is still a sensible one —
   it is the lowest ratio at which a hairline reliably reads on every surface here — and the
   measurements are unaffected.

   MEASURED BEFORE, all 18 palettes x 3 modes, against that 3:1 target:
       Classic   vs rows 16/18 under   vs headers 18/18 under
       Day Game  vs rows 18/18 (1.37)  vs headers 18/18 (1.21)
       Night     vs rows 18/18 (2.04)  vs headers 18/18 (~1.1)
   Only DataForce (3.12) and Tailgate (4.43) reached it at all, and only against rows.
   AFTER: 3.01-4.80 on every surface a cell border actually touches.

   WHY HEADERS WERE ALWAYS WORST, and why this is not just "pick a darker grey": --panel-border is ONE
   OPAQUE colour per mode and it was chosen against the ROW background. A header is a tinted band much
   closer to that border in lightness, so the line that is merely faint on a row vanishes on a header.
   In Night Game the header is color-mix(--accent 40%, #20272f) — a DIFFERENT COLOUR ON EVERY PALETTE
   — so no single opaque border can be correct on all of them.

   THE LINE IS THEREFORE TRANSLUCENT, NOT A COLOUR. It composites against whatever is underneath, so it
   darkens on a light header and lightens on a dark row by itself, and one alpha holds the same
   perceived weight across all 18 palettes and all three lightings with no per-palette values at all.
   Alphas solved numerically against every surface these borders land on: 47 distinct Classic surfaces,
   3 in Day Game, 20 in Night Game. .465 black is the minimum that clears 3:1 on all of the light ones;
   .50 white the minimum on all of the dark ones.

   ⚠ SCOPED TO TABLE CELLS, NOT TO --panel-border. That token also draws button edges, panel edges, the
   chat box and the crumbs select. This request was about gridlines; moving the token would have
   restyled every button on the site as a side effect.
   ⚠ LONGHANDS, NOT THE `border-color` SHORTHAND, AND THIS IS LOAD-BEARING. These tables use the
   separate-borders recipe (border-collapse:separate, each cell draws only its right+bottom line, last
   column and last row drop theirs, the table draws the outer frame — border-radius does not apply to a
   collapsed-border table). A `border-color` shorthand rule measured as doing nothing; the explicit
   side colours work with no !important. Verified live: 72 of 80 lobby cells changed, the other 8 being
   last-column cells that carry border-right:0 by design.
   ⚠ `border-top` IS 0px ON THESE CELLS. Reading getComputedStyle(td).borderTopColor measures an
   INVISIBLE edge and will tell you a working rule is broken — it told me exactly that, twice. Measure
   borderRightColor / borderBottomColor, and check the width is not 0px first.
   ⚠ The dark rule covers BOTH dark cases: body[data-theme="dark"] is Night Game, and .dfx-darkpanel is
   Onyx in Classic (the one dark-content palette). Mobile Classic does NOT need it — #main is a dark
   box there, but its tables are opaque --content-bg, which is light on every palette except Onyx.
                                                                              - Claude 8/11/26 */
#main table#leagues td,
#main table.data td.section,
#main table.prizes td,
#main table.prizes td.heading,
#main table#legend td.DFF,
#main table#legend td.MFL,
#main table#loadData td,
#main table.loadData td,
#main table.roster td,
#main table#draftBoard td,
#main table.standingsTable td,
#main table#articleTable td {
   border-top-color:rgba(0,0,0,.465);
   border-right-color:rgba(0,0,0,.465);
   border-bottom-color:rgba(0,0,0,.465);
   border-left-color:rgba(0,0,0,.465);
}

body.dfx-darkpanel #main table#leagues td,
body.dfx-darkpanel #main table.data td.section,
body.dfx-darkpanel #main table.prizes td,
body.dfx-darkpanel #main table.prizes td.heading,
body.dfx-darkpanel #main table#legend td.DFF,
body.dfx-darkpanel #main table#legend td.MFL,
body.dfx-darkpanel #main table#loadData td,
body.dfx-darkpanel #main table.loadData td,
body.dfx-darkpanel #main table.roster td,
body.dfx-darkpanel #main table#draftBoard td,
body.dfx-darkpanel #main table.standingsTable td,
body.dfx-darkpanel #main table#articleTable td,
body[data-theme="dark"] #main table#leagues td,
body[data-theme="dark"] #main table.data td.section,
body[data-theme="dark"] #main table.prizes td,
body[data-theme="dark"] #main table.prizes td.heading,
body[data-theme="dark"] #main table#legend td.DFF,
body[data-theme="dark"] #main table#legend td.MFL,
body[data-theme="dark"] #main table#loadData td,
body[data-theme="dark"] #main table.loadData td,
body[data-theme="dark"] #main table.roster td,
body[data-theme="dark"] #main table#draftBoard td,
body[data-theme="dark"] #main table.standingsTable td,
body[data-theme="dark"] #main table#articleTable td {
   border-top-color:rgba(255,255,255,.50);
   border-right-color:rgba(255,255,255,.50);
   border-bottom-color:rgba(255,255,255,.50);
   border-left-color:rgba(255,255,255,.50);
}
